cjs-2.8.0/0000775000175000017500000000000012610172032011216 5ustar fabiofabiocjs-2.8.0/installed-tests/0000775000175000017500000000000012610172032014335 5ustar fabiofabiocjs-2.8.0/installed-tests/gjs-unit.cpp0000664000175000017500000001655312610172032016613 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include #include #include #include typedef struct { const char **coverage_paths; const char *coverage_output_path; char *filename; } GjsTestData; GjsTestData * gjs_unit_test_data_new(const char **coverage_paths, const char *coverage_output_path, char *filename) { GjsTestData *data = (GjsTestData *) g_new0(GjsTestData, 1); data->coverage_paths = coverage_paths; data->coverage_output_path = coverage_output_path; data->filename = filename; return data; } void gjs_unit_test_data_free(gpointer test_data, gpointer user_data) { GjsTestData *data = (GjsTestData *) test_data; g_free(data->filename); g_free(data); } typedef struct { GjsContext *context; GjsCoverage *coverage; } GjsTestJSFixture; static void setup(GjsTestJSFixture *fix, gconstpointer test_data) { GjsTestData *data = (GjsTestData *) test_data; fix->context = gjs_context_new (); if (data->coverage_paths) { if (!data->coverage_output_path) { g_error("GJS_UNIT_COVERAGE_OUTPUT is required when using GJS_UNIT_COVERAGE_PATHS"); } fix->coverage = gjs_coverage_new((const char **) data->coverage_paths, fix->context); } } static void teardown(GjsTestJSFixture *fix, gconstpointer test_data) { GjsTestData *data = (GjsTestData *) test_data; if (fix->coverage) { gjs_coverage_write_statistics(fix->coverage, data->coverage_output_path); g_clear_object(&fix->coverage); } gjs_memory_report("before destroying context", FALSE); g_object_unref(fix->context); gjs_memory_report("after destroying context", TRUE); } static void test(GjsTestJSFixture *fix, gconstpointer test_data) { GError *error = NULL; gboolean success; int code; GjsTestData *data = (GjsTestData *) test_data; success = gjs_context_eval_file(fix->context, data->filename, &code, &error); if (!success) g_error("%s", error->message); g_assert(error == NULL); if (code != 0) g_error("Test script returned code %d; assertions will be in gjs.log", code); } static GSList * read_all_dir_sorted (const char *dirpath) { GSList *result = NULL; GDir *dir; const char *name; dir = g_dir_open(dirpath, 0, NULL); g_assert(dir != NULL); while ((name = g_dir_read_name(dir)) != NULL) result = g_slist_prepend (result, g_strdup (name)); result = g_slist_sort(result, (GCompareFunc) strcmp); g_dir_close(dir); return result; } static char ** get_coverage_uris_from_env(const char *key) { const char *value = g_getenv(key); if (!value) return NULL; char **splitted_pathnames = g_strsplit(value, ":", -1); char **splitted_pathnamed_iterator = splitted_pathnames; for (; *splitted_pathnamed_iterator; splitted_pathnamed_iterator++) { /* Don't just have resource:// for the last element */ if (g_strcmp0(*splitted_pathnamed_iterator, "") == 0) { g_free(*splitted_pathnamed_iterator); *splitted_pathnamed_iterator = NULL; continue; } char *uri = g_strconcat("resource://", *splitted_pathnamed_iterator, NULL); g_free(*splitted_pathnamed_iterator); *splitted_pathnamed_iterator = uri; } return splitted_pathnames; } int main(int argc, char **argv) { char *js_test_dir; GSList *all_tests, *iter; GSList *all_registered_test_data = NULL; int retval; /* The tests are known to fail in the presence of the JIT; * we leak objects. * https://bugzilla.gnome.org/show_bug.cgi?id=616193 */ g_setenv("GJS_DISABLE_JIT", "1", FALSE); /* The fact that this isn't the default is kind of lame... */ g_setenv("GJS_DEBUG_OUTPUT", "stderr", FALSE); setlocale(LC_ALL, ""); g_test_init(&argc, &argv, NULL); if (g_getenv ("GJS_USE_UNINSTALLED_FILES") != NULL) { /* typelib path is handled by the environment */ js_test_dir = g_build_filename(g_getenv ("TOP_SRCDIR"), "installed-tests", "js", NULL); } else { g_irepository_prepend_search_path(INSTTESTDIR); js_test_dir = g_build_filename(INSTTESTDIR, "js", NULL); } char **coverage_paths = get_coverage_uris_from_env("GJS_UNIT_COVERAGE_PATHS"); const char *coverage_output_directory = g_getenv("GJS_UNIT_COVERAGE_OUTPUT"); all_tests = read_all_dir_sorted(js_test_dir); for (iter = all_tests; iter; iter = iter->next) { char *name = (char*) iter->data; char *test_name; char *file_name; GjsTestData *test_data; if (!(g_str_has_prefix(name, "test") && g_str_has_suffix(name, ".js"))) { g_free(name); continue; } if (g_str_has_prefix (name, "testCairo") && g_getenv ("GJS_TEST_SKIP_CAIRO")) continue; /* pretty print, drop 'test' prefix and '.js' suffix from test name */ test_name = g_strconcat("/js/", name + 4, NULL); test_name[strlen(test_name)-3] = '\0'; file_name = g_build_filename(js_test_dir, name, NULL); test_data = gjs_unit_test_data_new((const char **) coverage_paths, coverage_output_directory, file_name); g_test_add(test_name, GjsTestJSFixture, test_data, setup, test, teardown); g_free(name); g_free(test_name); all_registered_test_data = g_slist_prepend(all_registered_test_data, test_data); /* not freeing file_name or test_data yet as it's needed while running the test */ } g_free(js_test_dir); g_slist_free(all_tests); retval = g_test_run (); g_slist_foreach(all_registered_test_data, (GFunc)gjs_unit_test_data_free, all_registered_test_data); g_slist_free(all_registered_test_data); if (coverage_paths) g_strfreev(coverage_paths); return retval; } cjs-2.8.0/installed-tests/jsunit.test.in0000664000175000017500000000010012610172032017146 0ustar fabiofabio[Test] Type=session Exec=@pkglibexecdir@/installed-tests/jsunit cjs-2.8.0/installed-tests/extra/0000775000175000017500000000000012610172032015460 5ustar fabiofabiocjs-2.8.0/installed-tests/extra/gjs.supp0000664000175000017500000000424112610172032017155 0ustar fabiofabio{ g_type_init_with_debug_flags calloc Memcheck:Leak fun:calloc ... fun:g_type_init_with_debug_flags ... } { g_type_add_interface_static malloc Memcheck:Leak fun:malloc ... fun:g_type_add_interface_static ... } { g_type_add_interface_dynamic malloc Memcheck:Leak fun:malloc ... fun:g_type_add_interface_dynamic ... } { g_type_class_ref malloc Memcheck:Leak fun:malloc ... fun:g_type_class_ref ... } { g_type_register_dynamic malloc Memcheck:Leak fun:malloc ... fun:g_type_register_dynamic ... } { g_type_init_with_debug_flags malloc Memcheck:Leak fun:malloc ... fun:g_type_init_with_debug_flags ... } { g_type_init_with_debug_flags realloc Memcheck:Leak fun:realloc ... fun:g_type_init_with_debug_flags ... } { g_test_add_vtable malloc Memcheck:Leak fun:malloc ... fun:g_test_add_vtable ... } { g_test_init Memcheck:Leak fun:malloc ... fun:g_test_init ... } { g_type_register_static malloc Memcheck:Leak fun:malloc ... fun:g_type_register_static ... } { g_type_register_static realloc Memcheck:Leak fun:realloc ... fun:g_type_register_static ... } { g_type_register_fundamental never freed Memcheck:Leak fun:malloc ... fun:g_type_register_fundamental ... } { g_type_class_ref never finalized Memcheck:Leak fun:calloc fun:g_malloc0 fun:g_type_class_ref ... } { DBusGValue qdata Memcheck:Leak fun:realloc fun:g_realloc fun:g_type_set_qdata fun:_dbus_g_value_types_init ... } { gettext conditional jump Memcheck:Cond fun:__GI___strcasecmp_l fun:__gconv_open fun:_nl_find_msg fun:__dcigettext ... } { gettext uninitialized value Memcheck:Value8 fun:__GI___strcasecmp_l fun:__gconv_open fun:_nl_find_msg fun:__dcigettext ... } { font config invalid reads Memcheck:Addr4 ... fun:FcConfigParseAndLoad ... } { dynamic loader conditional jump Memcheck:Cond fun:index fun:expand_dynamic_string_token fun:_dl_map_object fun:map_doit fun:_dl_catch_error fun:do_preload fun:dl_main ... } cjs-2.8.0/installed-tests/extra/unittest.gdb0000664000175000017500000000004612610172032020015 0ustar fabiofabiorun if ($_exitcode == 0) quit end cjs-2.8.0/installed-tests/js/0000775000175000017500000000000012610172032014751 5ustar fabiofabiocjs-2.8.0/installed-tests/js/testJS1_8.js0000664000175000017500000000222312610172032017032 0ustar fabiofabio// Test SpiderMonkey JS extensions; see // https://developer.mozilla.org/en/JavaScript/New_in_JavaScript/1.8 // "const" const JSUnit = imports.jsUnit; const Everything = imports.gi.Regress; function testLet() { // "let" let foo = "bar"; let cow = "moo"; JSUnit.assertEquals(foo, "bar"); } function testMultiReturn() { // "destructuring bind" let [y, z, q] = Everything.test_torture_signature_0(42, 'foo', 7); JSUnit.assertEquals(z, 84); } function testYield() { function fib() { var i = 0, j = 1; while (true) { yield i; var t = i; i = j; j += t; } } var v = []; var g = fib(); for (var i = 0; i < 10; i++) { v.push(g.next()); } JSUnit.assertEquals(v[0], 0); JSUnit.assertEquals(v[1], 1); JSUnit.assertEquals(v[2], 1); JSUnit.assertEquals(v[3], 2); JSUnit.assertEquals(v[4], 3); JSUnit.assertEquals(v[5], 5); JSUnit.assertEquals(v[6], 8); JSUnit.assertEquals(v[7], 13); JSUnit.assertEquals(v[8], 21); JSUnit.assertEquals(v[9], 34); } JSUnit.gjstestRun(this, JSUnit.setUp, JSUnit.tearDown); cjs-2.8.0/installed-tests/js/testLocale.js0000664000175000017500000000277012610172032017414 0ustar fabiofabio// tests for JS_SetLocaleCallbacks(). const JSUnit = imports.jsUnit; function testToLocaleDateString() { let date = new Date(); // %A is the weekday name, this tests locale_to_unicode // we're basically just testing for a non-crash, since // we'd have to run in a specific locale to have any // idea about the result. date.toLocaleDateString("%A"); } function testToLocaleLowerCase() { JSUnit.assertEquals("aaa", "AAA".toLocaleLowerCase()); // String conversion is implemented internally to GLib, // and is more-or-less independent of locale. (A few // characters are handled specially for a few locales, // like i in Turkish. But not A WITH ACUTE) JSUnit.assertEquals("\u00e1", "\u00c1".toLocaleLowerCase()); } function testToLocaleUpperCase() { JSUnit.assertEquals("AAA", "aaa".toLocaleUpperCase()); JSUnit.assertEquals("\u00c1", "\u00e1".toLocaleUpperCase()); } function testToLocaleCompare() { // GLib calls out to libc for collation, so we can't really // assume anything - we could even be running in the // C locale. The below is pretty safe. JSUnit.assertEquals(-1, "a".localeCompare("b")); JSUnit.assertEquals( 0, "a".localeCompare("a")); JSUnit.assertEquals( 1, "b".localeCompare("a")); // Again test error handling when conversion fails //assertRaises(function() { "\ud800".localeCompare("a"); }); //assertRaises(function() { "a".localeCompare("\ud800"); }); } JSUnit.gjstestRun(this, JSUnit.setUp, JSUnit.tearDown); cjs-2.8.0/installed-tests/js/testFundamental.js0000664000175000017500000000064112610172032020446 0ustar fabiofabio// application/javascript;version=1.8 const JSUnit = imports.jsUnit; const Everything = imports.gi.Regress; const WarnLib = imports.gi.WarnLib; const GLib = imports.gi.GLib; const Gio = imports.gi.Gio; const GObject = imports.gi.GObject; const Lang = imports.lang; function testFundamental() { let f = new Everything.TestFundamentalSubObject('plop'); } JSUnit.gjstestRun(this, JSUnit.setUp, JSUnit.tearDown); cjs-2.8.0/installed-tests/js/testEverythingEncapsulated.js0000664000175000017500000001612412610172032022670 0ustar fabiofabio// This used to be called "Everything" const JSUnit = imports.jsUnit; const Everything = imports.gi.Regress; const GLib = imports.gi.GLib; function testStruct() { let struct = new Everything.TestStructA(); struct.some_int = 42; struct.some_int8 = 43; struct.some_double = 42.5; struct.some_enum = Everything.TestEnum.VALUE3; JSUnit.assertEquals(42, struct.some_int); JSUnit.assertEquals(43, struct.some_int8); JSUnit.assertEquals(42.5, struct.some_double); JSUnit.assertEquals(Everything.TestEnum.VALUE3, struct.some_enum); let b = struct.clone(); JSUnit.assertEquals(42, b.some_int); JSUnit.assertEquals(43, b.some_int8); JSUnit.assertEquals(42.5, b.some_double); JSUnit.assertEquals(Everything.TestEnum.VALUE3, b.some_enum); struct = new Everything.TestStructB(); struct.some_int8 = 43; struct.nested_a.some_int8 = 66; JSUnit.assertEquals(43, struct.some_int8); JSUnit.assertEquals(66, struct.nested_a.some_int8); b = struct.clone(); JSUnit.assertEquals(43, b.some_int8); JSUnit.assertEquals(66, struct.nested_a.some_int8); } function testStructConstructor() { // "Copy" an object from a hash of field values let struct = new Everything.TestStructA({ some_int: 42, some_int8: 43, some_double: 42.5, some_enum: Everything.TestEnum.VALUE3 }); JSUnit.assertEquals(42, struct.some_int); JSUnit.assertEquals(43, struct.some_int8); JSUnit.assertEquals(42.5, struct.some_double); JSUnit.assertEquals(Everything.TestEnum.VALUE3, struct.some_enum); // Make sure we catch bad field names JSUnit.assertRaises(function() { let t = new Everything.TestStructA({ junk: 42 }); }); // Copy an object from another object of the same type, shortcuts to memcpy() let copy = new Everything.TestStructA(struct); JSUnit.assertEquals(42, copy.some_int); JSUnit.assertEquals(43, copy.some_int8); JSUnit.assertEquals(42.5, copy.some_double); JSUnit.assertEquals(Everything.TestEnum.VALUE3, copy.some_enum); } function testSimpleBoxed() { let simple_boxed = new Everything.TestSimpleBoxedA(); simple_boxed.some_int = 42; simple_boxed.some_int8 = 43; simple_boxed.some_double = 42.5; simple_boxed.some_enum = Everything.TestEnum.VALUE3; JSUnit.assertEquals(42, simple_boxed.some_int); JSUnit.assertEquals(43, simple_boxed.some_int8); JSUnit.assertEquals(42.5, simple_boxed.some_double); JSUnit.assertEquals(Everything.TestEnum.VALUE3, simple_boxed.some_enum); } function testBoxedCopyConstructor() { // "Copy" an object from a hash of field values let simple_boxed = new Everything.TestSimpleBoxedA({ some_int: 42, some_int8: 43, some_double: 42.5, some_enum: Everything.TestEnum.VALUE3 }); JSUnit.assertEquals(42, simple_boxed.some_int); JSUnit.assertEquals(43, simple_boxed.some_int8); JSUnit.assertEquals(42.5, simple_boxed.some_double); JSUnit.assertEquals(Everything.TestEnum.VALUE3, simple_boxed.some_enum); // Make sure we catch bad field names JSUnit.assertRaises(function() { let t = new Everything.TestSimpleBoxedA({ junk: 42 }); }); // Copy an object from another object of the same type, shortcuts to the boxed copy let copy = new Everything.TestSimpleBoxedA(simple_boxed); JSUnit.assertTrue(copy instanceof Everything.TestSimpleBoxedA); JSUnit.assertEquals(42, copy.some_int); JSUnit.assertEquals(43, copy.some_int8); JSUnit.assertEquals(42.5, copy.some_double); JSUnit.assertEquals(Everything.TestEnum.VALUE3, copy.some_enum); } function testNestedSimpleBoxed() { let simple_boxed = new Everything.TestSimpleBoxedB(); // Test reading fields and nested fields simple_boxed.some_int8 = 42; simple_boxed.nested_a.some_int = 43; JSUnit.assertEquals(42, simple_boxed.some_int8); JSUnit.assertEquals(43, simple_boxed.nested_a.some_int); // Try assigning the nested struct field from an instance simple_boxed.nested_a = new Everything.TestSimpleBoxedA({ some_int: 53 }); JSUnit.assertEquals(53, simple_boxed.nested_a.some_int); // And directly from a hash of field values simple_boxed.nested_a = { some_int: 63 }; JSUnit.assertEquals(63, simple_boxed.nested_a.some_int); // Try constructing with a nested hash of field values let simple2 = new Everything.TestSimpleBoxedB({ some_int8: 42, nested_a: { some_int: 43, some_int8: 44, some_double: 43.5 } }); JSUnit.assertEquals(42, simple2.some_int8); JSUnit.assertEquals(43, simple2.nested_a.some_int); JSUnit.assertEquals(44, simple2.nested_a.some_int8); JSUnit.assertEquals(43.5, simple2.nested_a.some_double); } function testBoxed() { let boxed = new Everything.TestBoxed(); boxed.some_int8 = 42; JSUnit.assertEquals(42, boxed.some_int8); } function testTestStructFixedArray() { let struct = new Everything.TestStructFixedArray(); struct.frob(); JSUnit.assertEquals(7, struct.just_int); JSUnit.assertEquals(42, struct.array[0]); JSUnit.assertEquals(43, struct.array[1]); JSUnit.assertEquals(51, struct.array[9]); } function testComplexConstructor() { let boxed = new Everything.TestBoxedD('abcd', 8); JSUnit.assertEquals(12, boxed.get_magic()); } function testComplexConstructorBackwardCompatibility() { // RegressTestBoxedB has a constructor that takes multiple // arguments, but since it is directly allocatable, we keep // the old style of passing an hash of fields. // The two real world structs that have this behavior are // Clutter.Color and Clutter.ActorBox. let boxed = new Everything.TestBoxedB({ some_int8: 7, some_long: 5 }); JSUnit.assertEquals(7, boxed.some_int8); JSUnit.assertEquals(5, boxed.some_long); } function testVariantConstructor() { let str_variant = new GLib.Variant('s', 'mystring'); JSUnit.assertEquals('mystring', str_variant.get_string()[0]); JSUnit.assertEquals('mystring', str_variant.deep_unpack()); let str_variant_old = GLib.Variant.new('s', 'mystring'); JSUnit.assertTrue(str_variant.equal(str_variant_old)); let struct_variant = new GLib.Variant('(sogvau)', [ 'a string', '/a/object/path', 'asig', //nature new GLib.Variant('s', 'variant'), [ 7, 3 ] ]); JSUnit.assertEquals(5, struct_variant.n_children()); let unpacked = struct_variant.deep_unpack(); JSUnit.assertEquals('a string', unpacked[0]); JSUnit.assertEquals('/a/object/path', unpacked[1]); JSUnit.assertEquals('asig', unpacked[2]); JSUnit.assertTrue(unpacked[3] instanceof GLib.Variant); JSUnit.assertEquals('variant', unpacked[3].deep_unpack()); JSUnit.assertTrue(unpacked[4] instanceof Array); JSUnit.assertEquals(2, unpacked[4].length); } JSUnit.gjstestRun(this, JSUnit.setUp, JSUnit.tearDown); cjs-2.8.0/installed-tests/js/test0030basicBoxed.js0000664000175000017500000000040512610172032020554 0ustar fabiofabioconst JSUnit = imports.jsUnit; const Regress = imports.gi.Regress; function testBasicBoxed() { var a = new Regress.TestSimpleBoxedA(); a.some_int = 42; JSUnit.assertEquals(a.some_int, 42); } JSUnit.gjstestRun(this, JSUnit.setUp, JSUnit.tearDown); cjs-2.8.0/installed-tests/js/testClass.js0000664000175000017500000001062012610172032017253 0ustar fabiofabio// -*- mode: js; indent-tabs-mode: nil -*- const JSUnit = imports.jsUnit; const Lang = imports.lang; function assertArrayEquals(expected, got) { JSUnit.assertEquals(expected.length, got.length); for (let i = 0; i < expected.length; i ++) { JSUnit.assertEquals(expected[i], got[i]); } } const MagicBase = new Lang.Class({ Name: 'MagicBase', _init: function(a, buffer) { if (buffer) buffer.push(a); this.a = a; }, foo: function(a, buffer) { buffer.push(a); return a * 3; }, bar: function(a) { return a * 5; } }); const Magic = new Lang.Class({ Name: 'Magic', Extends: MagicBase, _init: function(a, b, buffer) { this.parent(a, buffer); if (buffer) buffer.push(b); this.b = b; }, foo: function(a, b, buffer) { let val = this.parent(a, buffer); buffer.push(b); return val * 2; }, bar: function(a, buffer) { this.foo(a, 2*a, buffer); return this.parent(a); } }); const ToStringOverride = new Lang.Class({ Name: 'ToStringOverride', toString: function() { let oldToString = this.parent(); return oldToString + '; hello'; } }); const Accessor = new Lang.Class({ Name: 'AccessorMagic', _init: function(val) { this._val = val; }, get value() { return this._val; }, set value(val) { if (val != 42) throw TypeError('Value is not a magic number'); this._val = val; } }); const AbstractBase = new Lang.Class({ Name: 'AbstractBase', Abstract: true, _init: function() { this.foo = 42; } }); const AbstractImpl = new Lang.Class({ Name: 'AbstractImpl', Extends: AbstractBase, _init: function() { this.parent(); this.bar = 42; } }); const AbstractImpl2 = new Lang.Class({ Name: 'AbstractImpl2', Extends: AbstractBase, // no _init here, we inherit the parent one }); const CustomConstruct = new Lang.Class({ Name: 'CustomConstruct', _construct: function(one, two) { return [one, two]; } }); function testClassFramework() { let newMagic = new MagicBase('A'); JSUnit.assertEquals('A', newMagic.a); } function testInheritance() { let buffer = []; let newMagic = new Magic('a', 'b', buffer); assertArrayEquals(['a', 'b'], buffer); buffer = []; let val = newMagic.foo(10, 20, buffer); assertArrayEquals([10, 20], buffer); JSUnit.assertEquals(10*6, val); } function testConstructor() { JSUnit.assertEquals(Magic, Magic.prototype.constructor); let newMagic = new Magic(); JSUnit.assertEquals(Magic, newMagic.constructor); } function testInstanceOf() { let newMagic = new Magic(); JSUnit.assertTrue(newMagic instanceof Magic); JSUnit.assertTrue(newMagic instanceof MagicBase); } function testToString() { let newMagic = new MagicBase(); JSUnit.assertEquals('[object MagicBase]', newMagic.toString()); let override = new ToStringOverride(); JSUnit.assertEquals('[object ToStringOverride]; hello', override.toString()); } function testConfigurable() { let newMagic = new MagicBase(); delete newMagic.foo; JSUnit.assertNotUndefined(newMagic.foo); } function testAccessor() { let newAccessor = new Accessor(11); JSUnit.assertEquals(11, newAccessor.value); JSUnit.assertRaises(function() { newAccessor.value = 12; }); newAccessor.value = 42; JSUnit.assertEquals(42, newAccessor.value); } function testAbstract() { JSUnit.assertRaises(function() { let newAbstract = new AbstractBase(); }); let newAbstract = new AbstractImpl(); JSUnit.assertEquals(42, newAbstract.foo); JSUnit.assertEquals(42, newAbstract.bar); newAbstract = new AbstractImpl2(); JSUnit.assertEquals(42, newAbstract.foo); } function testCrossCall() { // test that a method can call another without clobbering // __caller__ let newMagic = new Magic(); let buffer = []; let res = newMagic.bar(10, buffer); assertArrayEquals([10, 20], buffer); JSUnit.assertEquals(50, res); } function testConstruct() { let instance = new CustomConstruct(1, 2); JSUnit.assertTrue(instance instanceof Array); JSUnit.assertTrue(!(instance instanceof CustomConstruct)); assertArrayEquals([1, 2], instance); } JSUnit.gjstestRun(this, JSUnit.setUp, JSUnit.tearDown); cjs-2.8.0/installed-tests/js/testSystem.js0000664000175000017500000000063012610172032017472 0ustar fabiofabio const JSUnit = imports.jsUnit; const System = imports.system; function testAddressOf() { let o1 = new Object(); let o2 = new Object(); JSUnit.assert(System.addressOf(o1) == System.addressOf(o1)); JSUnit.assert(System.addressOf(o1) != System.addressOf(o2)); } function testVersion() { JSUnit.assert(System.version >= 13600); } JSUnit.gjstestRun(this, JSUnit.setUp, JSUnit.tearDown); cjs-2.8.0/installed-tests/js/testself.js0000664000175000017500000000204512610172032017141 0ustar fabiofabioconst JSUnit = imports.jsUnit; var someUndefined; var someNumber = 1; var someOtherNumber = 42; var someString = "hello"; var someOtherString = "world"; JSUnit.assert(true); JSUnit.assertTrue(true); JSUnit.assertFalse(false); JSUnit.assertEquals(someNumber, someNumber); JSUnit.assertEquals(someString, someString); JSUnit.assertNotEquals(someNumber, someOtherNumber); JSUnit.assertNotEquals(someString, someOtherString); JSUnit.assertNull(null); JSUnit.assertNotNull(someNumber); JSUnit.assertUndefined(someUndefined); JSUnit.assertNotUndefined(someNumber); JSUnit.assertNaN(0/0); JSUnit.assertNotNaN(someNumber); // test assertRaises() JSUnit.assertRaises(function() { throw new Object(); }); try { // calling assertRaises with non-function is an error, not assertion failure JSUnit.assertRaises(true); } catch(e) { JSUnit.assertUndefined(e.isJsUnitException); } try { // function not throwing an exception is assertion failure JSUnit.assertRaises(function() { return true; }); } catch(e) { JSUnit.assertTrue(e.isJsUnitException); } cjs-2.8.0/installed-tests/js/modules/0000775000175000017500000000000012610172032016421 5ustar fabiofabiocjs-2.8.0/installed-tests/js/modules/alwaysThrows.js0000664000175000017500000000014112610172032021462 0ustar fabiofabio// line 0 // line 1 // line 2 throw new Error("This is an error that always happens on line 3"); cjs-2.8.0/installed-tests/js/modules/foobar.js0000664000175000017500000000014312610172032020225 0ustar fabiofabio// simple test module (used by testImporter.js) var foo = "This is foo"; var bar = "This is bar"; cjs-2.8.0/installed-tests/js/modules/subA/0000775000175000017500000000000012610172032017313 5ustar fabiofabiocjs-2.8.0/installed-tests/js/modules/subA/.secret.js0000664000175000017500000000000012610172032021202 0ustar fabiofabiocjs-2.8.0/installed-tests/js/modules/subA/foo0000664000175000017500000000000012610172032020007 0ustar fabiofabiocjs-2.8.0/installed-tests/js/modules/subA/.hidden/0000775000175000017500000000000012610172032020624 5ustar fabiofabiocjs-2.8.0/installed-tests/js/modules/subA/.hidden/hidden.js0000664000175000017500000000000012610172032022403 0ustar fabiofabiocjs-2.8.0/installed-tests/js/modules/subA/subB/0000775000175000017500000000000012610172032020206 5ustar fabiofabiocjs-2.8.0/installed-tests/js/modules/subA/subB/__init__.js0000664000175000017500000000044212610172032022303 0ustar fabiofabiofunction testImporterFunction() { return "__init__ function tested"; } function ImporterClass() { this._init(); } ImporterClass.prototype = { _init : function() { this._a = "__init__ class tested"; }, testMethod : function() { return this._a; } } cjs-2.8.0/installed-tests/js/modules/subA/subB/foobar.js0000664000175000017500000000014312610172032022012 0ustar fabiofabio// simple test module (used by testImporter.js) var foo = "This is foo"; var bar = "This is bar"; cjs-2.8.0/installed-tests/js/modules/subA/subB/baz.js0000664000175000017500000000000012610172032021306 0ustar fabiofabiocjs-2.8.0/installed-tests/js/modules/modunicode.js0000664000175000017500000000010212610172032021076 0ustar fabiofabio// This file is written in UTF-8. const uval = "const ♥ utf8"; cjs-2.8.0/installed-tests/js/modules/mutualImport/0000775000175000017500000000000012610172032021123 5ustar fabiofabiocjs-2.8.0/installed-tests/js/modules/mutualImport/b.js0000664000175000017500000000012512610172032021700 0ustar fabiofabioconst A = imports.mutualImport.a; function getCount() { return A.getCount(); } cjs-2.8.0/installed-tests/js/modules/mutualImport/a.js0000664000175000017500000000030012610172032021672 0ustar fabiofabioconst B = imports.mutualImport.b; let count = 0; function incrementCount() { count++; } function getCount() { return count; } function getCountViaB() { return B.getCount(); } cjs-2.8.0/installed-tests/js/testGObjectClass.js0000664000175000017500000001607112610172032020517 0ustar fabiofabio// -*- mode: js; indent-tabs-mode: nil -*- const JSUnit = imports.jsUnit; const Lang = imports.lang; const GObject = imports.gi.GObject; const Gio = imports.gi.Gio; const MyObject = new GObject.Class({ Name: 'MyObject', Properties: { 'readwrite': GObject.ParamSpec.string('readwrite', 'ParamReadwrite', 'A read write parameter', GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE, ''), 'readonly': GObject.ParamSpec.string('readonly', 'ParamReadonly', 'A readonly parameter', GObject.ParamFlags.READABLE, ''), 'construct': GObject.ParamSpec.string('construct', 'ParamConstructOnly', 'A readwrite construct-only parameter', GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE | GObject.ParamFlags.CONSTRUCT_ONLY, 'default') }, Signals: { 'empty': { }, 'minimal': { param_types: [ GObject.TYPE_INT, GObject.TYPE_INT ] }, 'full': { flags: GObject.SignalFlags.RUN_LAST, accumulator: GObject.AccumulatorType.FIRST_WINS, return_type: GObject.TYPE_INT, param_types: [ ] }, 'run-last': { flags: GObject.SignalFlags.RUN_LAST }, 'detailed': { flags: GObject.SignalFlags.RUN_FIRST | GObject.SignalFlags.DETAILED, param_types: [ GObject.TYPE_STRING ] } }, _init: function(props) { // check that it's safe to set properties before // chaining up (priv is NULL at this point, remember) this._readwrite = 'foo'; this._readonly = 'bar'; this._constructProp = null; this._constructCalled = false; this.parent(props); }, get readwrite() { return this._readwrite; }, set readwrite(val) { if (val == 'ignore') return; this._readwrite = val; }, get readonly() { return this._readonly; }, set readonly(val) { // this should never be called this._readonly = 'bogus'; }, get construct() { return this._constructProp; }, set construct(val) { // this should be called at most once if (this._constructCalled) throw Error('Construct-Only property set more than once'); this._constructProp = val; this._constructCalled = true; }, notify_prop: function() { this._readonly = 'changed'; this.notify('readonly'); }, emit_empty: function() { this.emit('empty'); }, emit_minimal: function(one, two) { this.emit('minimal', one, two); }, emit_full: function() { return this.emit('full'); }, emit_detailed: function() { this.emit('detailed::one'); this.emit('detailed::two'); }, emit_run_last: function(callback) { this._run_last_callback = callback; this.emit('run-last'); }, on_run_last: function() { this._run_last_callback(); }, on_empty: function() { this.empty_called = true; } }); const MyApplication = new Lang.Class({ Name: 'MyApplication', Extends: Gio.Application, Signals: { 'custom': { param_types: [ GObject.TYPE_INT ] } }, _init: function(params) { this.parent(params); }, emit_custom: function(n) { this.emit('custom', n); } }); const MyInitable = new Lang.Class({ Name: 'MyInitable', Extends: GObject.Object, Implements: [ Gio.Initable ], _init: function(params) { this.parent(params); this.inited = false; }, vfunc_init: function(cancellable) { // error? JSUnit.assertTrue(cancellable instanceof Gio.Cancellable); this.inited = true; } }); const Derived = new Lang.Class({ Name: 'Derived', Extends: MyObject, _init: function() { this.parent({ readwrite: 'yes' }); } }); function testGObjectClass() { let myInstance = new MyObject(); JSUnit.assertEquals('foo', myInstance.readwrite); JSUnit.assertEquals('bar', myInstance.readonly); JSUnit.assertEquals('default', myInstance.construct); let myInstance2 = new MyObject({ readwrite: 'baz', construct: 'asdf' }); JSUnit.assertEquals('baz', myInstance2.readwrite); JSUnit.assertEquals('bar', myInstance2.readonly); JSUnit.assertEquals('asdf', myInstance2.construct); // the following would (should) cause a CRITICAL: // myInstance.readonly = 'val'; // myInstance.construct = 'val'; } function testNotify() { let myInstance = new MyObject(); let counter = 0; myInstance.connect('notify::readonly', function(obj) { if (obj.readonly == 'changed') counter++; }); myInstance.notify_prop(); myInstance.notify_prop(); JSUnit.assertEquals(2, counter); } function testSignals() { let myInstance = new MyObject(); let ok = false; myInstance.connect('empty', function() { ok = true; }); myInstance.emit_empty(); JSUnit.assertEquals(true, ok); JSUnit.assertEquals(true, myInstance.empty_called); let args = [ ]; myInstance.connect('minimal', function(emitter, one, two) { args.push(one); args.push(two); return true; }); myInstance.emit_minimal(7, 5); JSUnit.assertEquals(7, args[0]); JSUnit.assertEquals(5, args[1]); ok = true; myInstance.connect('full', function() { ok = true; return 42; }); myInstance.connect('full', function() { // this should never be called ok = false; return -1; }); let result = myInstance.emit_full(); JSUnit.assertEquals(true, ok); JSUnit.assertEquals(42, result); let stack = [ ]; myInstance.connect('run-last', function() { stack.push(1); }); myInstance.emit_run_last(function() { stack.push(2); }); JSUnit.assertEquals(1, stack[0]); JSUnit.assertEquals(2, stack[1]); } function testSubclass() { // test that we can inherit from something that's not // GObject.Object and still get all the goodies of // GObject.Class let instance = new MyApplication({ application_id: 'org.gjs.Application' }); let v; instance.connect('custom', function(app, num) { v = num; }); instance.emit_custom(73); JSUnit.assertEquals(73, v); } function testInterface() { let instance = new MyInitable(); JSUnit.assertEquals(false, instance.inited); instance.init(new Gio.Cancellable); JSUnit.assertEquals(true, instance.inited); // JSUnit.assertTrue(instance instanceof Gio.Initable) } function testDerived() { let derived = new Derived(); JSUnit.assertTrue(derived instanceof Derived); JSUnit.assertTrue(derived instanceof MyObject); JSUnit.assertEquals('yes', derived.readwrite); } JSUnit.gjstestRun(this, JSUnit.setUp, JSUnit.tearDown); cjs-2.8.0/installed-tests/js/testUnicode.js0000664000175000017500000000072012610172032017574 0ustar fabiofabioconst JSUnit = imports.jsUnit; function testUnicode() { JSUnit.assertEquals(6, 'Погода'.length); JSUnit.assertEquals(1055, 'Погода'.charCodeAt(0)); JSUnit.assertEquals(1086, 'Погода'.charCodeAt(3)); JSUnit.assertEquals("\u65e5", "日本語".charAt(0)); JSUnit.assertEquals("\u672c", "日本語".charAt(1)); JSUnit.assertEquals("\u8a9e", "日本語".charAt(2)); } JSUnit.gjstestRun(this, JSUnit.setUp, JSUnit.tearDown); cjs-2.8.0/installed-tests/js/testParamSpec.js0000664000175000017500000000702112610172032020062 0ustar fabiofabioconst JSUnit = imports.jsUnit; const Regress = imports.gi.Regress; const GObject = imports.gi.GObject; let name = 'foo-property'; let nick = 'Foo property'; let blurb = 'This is the foo property'; let flags = GObject.ParamFlags.READABLE; function testStringParamSpec() { let stringSpec = GObject.ParamSpec.string(name, nick, blurb, flags, 'Default Value'); JSUnit.assertEquals(name, stringSpec.name); JSUnit.assertEquals(nick, stringSpec._nick); JSUnit.assertEquals(blurb, stringSpec._blurb); JSUnit.assertEquals(flags, stringSpec.flags); JSUnit.assertEquals('Default Value', stringSpec.default_value); } function testIntParamSpec() { let intSpec = GObject.ParamSpec.int(name, nick, blurb, flags, -100, 100, -42); JSUnit.assertEquals(name, intSpec.name); JSUnit.assertEquals(nick, intSpec._nick); JSUnit.assertEquals(blurb, intSpec._blurb); JSUnit.assertEquals(flags, intSpec.flags); JSUnit.assertEquals(-42, intSpec.default_value); } function testUIntParamSpec() { let uintSpec = GObject.ParamSpec.uint(name, nick, blurb, flags, 20, 100, 42); JSUnit.assertEquals(name, uintSpec.name); JSUnit.assertEquals(nick, uintSpec._nick); JSUnit.assertEquals(blurb, uintSpec._blurb); JSUnit.assertEquals(flags, uintSpec.flags); JSUnit.assertEquals(42, uintSpec.default_value); } function testInt64ParamSpec() { let int64Spec = GObject.ParamSpec.int64(name, nick, blurb, flags, 0x4000, 0xffffffff, 0x2266bbff); JSUnit.assertEquals(name, int64Spec.name); JSUnit.assertEquals(nick, int64Spec._nick); JSUnit.assertEquals(blurb, int64Spec._blurb); JSUnit.assertEquals(flags, int64Spec.flags); JSUnit.assertEquals(0x2266bbff, int64Spec.default_value); } function testUInt64ParamSpec() { let uint64Spec = GObject.ParamSpec.uint64(name, nick, blurb, flags, 0, 0xffffffff, 0x2266bbff); JSUnit.assertEquals(name, uint64Spec.name); JSUnit.assertEquals(nick, uint64Spec._nick); JSUnit.assertEquals(blurb, uint64Spec._blurb); JSUnit.assertEquals(flags, uint64Spec.flags); JSUnit.assertEquals(0x2266bbff, uint64Spec.default_value); } function testEnumParamSpec() { let enumSpec = GObject.ParamSpec.enum(name, nick, blurb, flags, Regress.TestEnum, Regress.TestEnum.VALUE2); JSUnit.assertEquals(name, enumSpec.name); JSUnit.assertEquals(nick, enumSpec._nick); JSUnit.assertEquals(blurb, enumSpec._blurb); JSUnit.assertEquals(flags, enumSpec.flags); JSUnit.assertEquals(Regress.TestEnum.VALUE2, enumSpec.default_value); } function testFlagsParamSpec() { let flagsSpec = GObject.ParamSpec.flags(name, nick, blurb, flags, Regress.TestFlags, Regress.TestFlags.FLAG2); JSUnit.assertEquals(name, flagsSpec.name); JSUnit.assertEquals(nick, flagsSpec._nick); JSUnit.assertEquals(blurb, flagsSpec._blurb); JSUnit.assertEquals(flags, flagsSpec.flags); JSUnit.assertEquals(Regress.TestFlags.FLAG2, flagsSpec.default_value); } JSUnit.gjstestRun(this, JSUnit.setUp, JSUnit.tearDown); cjs-2.8.0/installed-tests/js/testTweener.js0000664000175000017500000002722112610172032017624 0ustar fabiofabioconst JSUnit = imports.jsUnit; const Tweener = imports.tweener.tweener; const Mainloop = imports.mainloop; function installFrameTicker() { // Set up Tweener to have a "frame pulse" from // our main rendering loop let ticker = { FRAME_RATE: 50, _init : function() { }, start : function() { this._currentTime = 0; let me = this; this._timeoutID = Mainloop.timeout_add(Math.floor(1000 / me.FRAME_RATE), function() { me._currentTime += 1000 / me.FRAME_RATE; me.emit('prepare-frame'); return true; }); }, stop : function() { if ('_timeoutID' in this) { Mainloop.source_remove(this._timeoutID); delete this._timeoutID; } this._currentTime = 0; }, getTime : function() { return this._currentTime; } }; imports.signals.addSignalMethods(ticker); Tweener.setFrameTicker(ticker); } function testSimpleTween() { var objectA = { x: 0, y: 0 }; var objectB = { x: 0, y: 0 }; Tweener.addTween(objectA, { x: 10, y: 10, time: 1, transition: "linear", onComplete: function() { Mainloop.quit('testTweener');}}); Tweener.addTween(objectB, { x: 10, y: 10, time: 1, delay: 0.5, transition: "linear" }); Mainloop.run('testTweener'); with (objectA) { JSUnit.assertEquals("A: x coordinate", 10, x); JSUnit.assertEquals("A: y coordinate", 10, y); } with (objectB) { JSUnit.assertEquals("B: x coordinate", 5, x); JSUnit.assertEquals("B: y coordinate", 5, y); } } function testOnFunctions() { var object = { start: false, update: false, complete: false }; Tweener.addTween(object, { time: 0.1, onStart: function () { object.start = true; }, onUpdate: function () { object.update = true; }, onComplete: function() { object.complete = true; Mainloop.quit('testOnFunctions'); }}); Mainloop.run('testOnFunctions'); with (object) { JSUnit.assertEquals("onStart was run", true, start); JSUnit.assertEquals("onUpdate was run", true, update); JSUnit.assertEquals("onComplete was run", true, complete); } } function testPause() { var objectA = { foo: 0 }; var objectB = { bar: 0 }; var objectC = { baaz: 0 }; Tweener.addTween(objectA, { foo: 100, time: 0.1 }); Tweener.addTween(objectC, { baaz: 100, time: 0.1 }); Tweener.addTween(objectB, { bar: 100, time: 0.1, onComplete: function() { Mainloop.quit('testPause');}}); Tweener.pauseTweens(objectA); JSUnit.assertEquals(false, Tweener.pauseTweens(objectB, "quux")); // This should do nothing /* Pause and resume should be equal to doing nothing */ Tweener.pauseTweens(objectC, "baaz"); Tweener.resumeTweens(objectC, "baaz"); Mainloop.run('testPause'); with (objectA) { JSUnit.assertEquals(0, foo); } with (objectB) { JSUnit.assertEquals(100, bar); } with (objectC) { JSUnit.assertEquals(100, baaz); } } function testRemoveTweens() { var object = { foo: 0, bar: 0, baaz: 0 }; Tweener.addTween(object, { foo: 50, time: 0.1, onComplete: function() { Mainloop.quit('testRemoveTweens');}}); Tweener.addTween(object, { bar: 50, time: 0.1 }); Tweener.addTween(object, { baaz: 50, time: 0.1}); /* The Tween on property foo should still be run after removing the other two */ Tweener.removeTweens(object, "bar", "baaz"); Mainloop.run('testRemoveTweens'); with (object) { JSUnit.assertEquals(50, foo); JSUnit.assertEquals(0, bar); JSUnit.assertEquals(0, baaz); } } function testConcurrent() { var objectA = { foo: 0 }; var objectB = { bar: 0 }; /* The second Tween should override the first one, as * they act on the same object, property, and at the same * time. */ Tweener.addTween(objectA, { foo: 100, time: 0.1 }); Tweener.addTween(objectA, { foo: 0, time: 0.1 }); /* In this case both tweens should be executed, as they don't * act on the object at the same time (the second one has a * delay equal to the running time of the first one) */ Tweener.addTween(objectB, { bar: 100, time: 0.1 }); Tweener.addTween(objectB, { bar: 150, time: 0.1, delay: 0.1, onComplete: function () { Mainloop.quit('testConcurrent');}}); Mainloop.run('testConcurrent'); with (objectA) { JSUnit.assertEquals(0, foo); } with (objectB) { JSUnit.assertEquals(150, bar); } } function testPauseAllResumeAll() { var objectA = { foo: 0 }; var objectB = { bar: 0 }; Tweener.addTween(objectA, { foo: 100, time: 0.1 }); Tweener.addTween(objectB, { bar: 100, time: 0.1, onComplete: function () { Mainloop.quit('testPauseAllResumeAll');}}); Tweener.pauseAllTweens(); Mainloop.timeout_add(10, function () { Tweener.resumeAllTweens(); return false; }); Mainloop.run('testPauseAllResumeAll'); with (objectA) { JSUnit.assertEquals(100, foo); } with (objectB) { JSUnit.assertEquals(100, bar); } } function testRemoveAll() { var objectA = { foo: 0 }; var objectB = { bar: 0 }; Tweener.addTween(objectA, { foo: 100, time: 0.1 }); Tweener.addTween(objectB, { bar: 100, time: 0.1 }); Tweener.removeAllTweens(); Mainloop.timeout_add(200, function () { JSUnit.assertEquals(0, objectA.foo); JSUnit.assertEquals(0, objectB.bar); Mainloop.quit('testRemoveAll'); return false; }); Mainloop.run('testRemoveAll'); } function testImmediateTween() { var object = { foo: 100 }; Tweener.addTween(object, { foo: 50, time: 0, delay: 0 }); Tweener.addTween(object, { foo: 200, time: 0.1, onStart: function () { /* The immediate tween should set it to 50 before we run */ JSUnit.assertEquals(50, object.foo); }, onComplete: function () { Mainloop.quit('testImmediateTween'); }}); Mainloop.run('testImmediateTween'); with (object) { JSUnit.assertEquals(200, foo); } } function testAddCaller() { var object = { foo: 0 }; Tweener.addCaller(object, { onUpdate: function () { object.foo += 1; }, onComplete: function () { Mainloop.quit('testAddCaller'); }, count: 10, time: 0.1 }); Mainloop.run('testAddCaller'); with (object) { JSUnit.assertEquals(10, foo); } } function testGetTweenCount() { var object = { foo: 0, bar: 0, baaz: 0, quux: 0 }; JSUnit.assertEquals(0, Tweener.getTweenCount(object)); Tweener.addTween(object, { foo: 100, time: 0.1 }); JSUnit.assertEquals(1, Tweener.getTweenCount(object)); Tweener.addTween(object, { bar: 100, time: 0.1 }); JSUnit.assertEquals(2, Tweener.getTweenCount(object)); Tweener.addTween(object, { baaz: 100, time: 0.1 }); JSUnit.assertEquals(3, Tweener.getTweenCount(object)); Tweener.addTween(object, { quux: 100, time: 0.1, onComplete: function () { Mainloop.quit('testGetTweenCount');}}); JSUnit.assertEquals(4, Tweener.getTweenCount(object)); Tweener.removeTweens(object, "bar", "baaz"); JSUnit.assertEquals(2, Tweener.getTweenCount(object)); Mainloop.run('testGetTweenCount'); JSUnit.assertEquals(0, Tweener.getTweenCount(object)); } Tweener.registerSpecialProperty( 'negative_x', function(obj) { return -obj.x; }, function(obj, val) { obj.x = -val; } ); function testSpecialProperty() { var objectA = { x: 0, y: 0 }; Tweener.addTween(objectA, { negative_x: 10, y: 10, time: 1, transition: "linear", onComplete: function() { Mainloop.quit('testSpecialProperty');}}); Mainloop.run('testSpecialProperty'); with (objectA) { JSUnit.assertEquals("A: x coordinate", -10, x); JSUnit.assertEquals("A: y coordinate", 10, y); } } Tweener.registerSpecialPropertyModifier('discrete', discrete_modifier, discrete_get); function discrete_modifier(props) { return props.map(function (prop) { return { name: prop, parameters: null }; }); } function discrete_get(begin, end, time, params) { return Math.floor(begin + time * (end - begin)); } function testSpecialPropertyModifier() { var objectA = { x: 0, y: 0, xFraction: false, yFraction: false }; Tweener.addTween(objectA, { x: 10, y: 10, time: 1, discrete: ["x"], transition: "linear", onUpdate: function() { if (objectA.x != Math.floor(objectA.x)) objectA.xFraction = true; if (objectA.y != Math.floor(objectA.y)) objectA.yFraction = true; }, onComplete: function() { Mainloop.quit('testSpecialPropertyModifier');}}); Mainloop.run('testSpecialPropertyModifier'); with (objectA) { JSUnit.assertEquals("A: x coordinate", 10, x); JSUnit.assertEquals("A: y coordinate", 10, y); JSUnit.assertEquals("A: x was fractional", false, xFraction); JSUnit.assertEquals("A: y was fractional", true, yFraction); } } Tweener.registerSpecialPropertySplitter( 'xnegy', function(val) { return [ { name: "x", value: val }, { name: "y", value: -val } ]; } ); function testSpecialPropertySplitter() { var objectA = { x: 0, y: 0 }; Tweener.addTween(objectA, { xnegy: 10, time: 1, transition: "linear", onComplete: function() { Mainloop.quit('testSpecialPropertySplitter');}}); Mainloop.run('testSpecialPropertySplitter'); with (objectA) { JSUnit.assertEquals("A: x coordinate", 10, x); JSUnit.assertEquals("A: y coordinate", -10, y); } } installFrameTicker(); JSUnit.gjstestRun(this, JSUnit.setUp, JSUnit.tearDown); cjs-2.8.0/installed-tests/js/test0010basic.js0000664000175000017500000000024612610172032017573 0ustar fabiofabioconst JSUnit = imports.jsUnit; function testBasic1() { var foo = 1+1; JSUnit.assertEquals(2, foo); } JSUnit.gjstestRun(this, JSUnit.setUp, JSUnit.tearDown); cjs-2.8.0/installed-tests/js/testEverythingBasic.js0000664000175000017500000006117212610172032021304 0ustar fabiofabio// This used to be called "Everything" const JSUnit = imports.jsUnit; const Everything = imports.gi.Regress; const WarnLib = imports.gi.WarnLib; // We use Gio to have some objects that we know exist const GLib = imports.gi.GLib; const Gio = imports.gi.Gio; const GObject = imports.gi.GObject; const Lang = imports.lang; const INT8_MIN = (-128); const INT16_MIN = (-32767-1); const INT32_MIN = (-2147483647-1); const INT64_MIN = (-9223372036854775807-1); const INT8_MAX = (127); const INT16_MAX = (32767); const INT32_MAX = (2147483647); const INT64_MAX = (9223372036854775807); const UINT8_MAX = (255); const UINT16_MAX = (65535); const UINT32_MAX = (4294967295); const UINT64_MAX = (18446744073709551615); function testLifeUniverseAndEverything() { JSUnit.assertEquals(false, Everything.test_boolean(false)); JSUnit.assertEquals(true, Everything.test_boolean(true)); JSUnit.assertEquals(42, Everything.test_int8(42)); JSUnit.assertEquals(-42, Everything.test_int8(-42)); JSUnit.assertEquals(42, Everything.test_uint8(42)); JSUnit.assertEquals(42, Everything.test_int16(42)); JSUnit.assertEquals(-42, Everything.test_int16(-42)); JSUnit.assertEquals(42, Everything.test_uint16(42)); JSUnit.assertEquals(42, Everything.test_int32(42)); JSUnit.assertEquals(-42, Everything.test_int32(-42)); JSUnit.assertEquals(42, Everything.test_uint32(42)); JSUnit.assertEquals(42, Everything.test_int64(42)); JSUnit.assertEquals(-42, Everything.test_int64(-42)); JSUnit.assertEquals(42, Everything.test_uint64(42)); JSUnit.assertEquals(42, Everything.test_short(42)); JSUnit.assertEquals(-42, Everything.test_short(-42)); JSUnit.assertEquals(42, Everything.test_ushort(42)); JSUnit.assertEquals(42, Everything.test_int(42)); JSUnit.assertEquals(-42, Everything.test_int(-42)); JSUnit.assertEquals(42, Everything.test_uint(42)); JSUnit.assertEquals(42, Everything.test_long(42)); JSUnit.assertEquals(-42, Everything.test_long(-42)); JSUnit.assertEquals(42, Everything.test_ulong(42)); JSUnit.assertEquals(42, Everything.test_ssize(42)); JSUnit.assertEquals(-42, Everything.test_ssize(-42)); JSUnit.assertEquals(42, Everything.test_size(42)); JSUnit.assertEquals(42, Everything.test_float(42)); JSUnit.assertEquals(-42, Everything.test_float(-42)); JSUnit.assertEquals(42, Everything.test_double(42)); JSUnit.assertEquals(-42, Everything.test_double(-42)); JSUnit.assertEquals("c", Everything.test_unichar("c")); JSUnit.assertEquals("", Everything.test_unichar("")); JSUnit.assertEquals("\u2665", Everything.test_unichar("\u2665")); let now = Math.floor(new Date().getTime() / 1000); let bounced = Math.floor(Everything.test_timet(now)); JSUnit.assertEquals(bounced, now); } function testLimits() { JSUnit.assertEquals(UINT8_MAX, Everything.test_uint8(UINT8_MAX)); JSUnit.assertEquals(UINT16_MAX, Everything.test_uint16(UINT16_MAX)); JSUnit.assertEquals(UINT32_MAX, Everything.test_uint32(UINT32_MAX)); // FAIL: expected 18446744073709552000, got 0 //assertEquals(UINT64_MAX, Everything.test_uint64(UINT64_MAX)); JSUnit.assertEquals(INT8_MIN, Everything.test_int8(INT8_MIN)); JSUnit.assertEquals(INT8_MAX, Everything.test_int8(INT8_MAX)); JSUnit.assertEquals(INT16_MIN, Everything.test_int16(INT16_MIN)); JSUnit.assertEquals(INT16_MAX, Everything.test_int16(INT16_MAX)); JSUnit.assertEquals(INT32_MIN, Everything.test_int32(INT32_MIN)); JSUnit.assertEquals(INT32_MAX, Everything.test_int32(INT32_MAX)); JSUnit.assertEquals(INT64_MIN, Everything.test_int64(INT64_MIN)); // FAIL: expected 9223372036854776000, got -9223372036854776000 //assertEquals(INT64_MAX, Everything.test_int64(INT64_MAX)); } function testNoImplicitConversionToUnsigned() { JSUnit.assertRaises(function() { return Everything.test_uint8(-42); }); JSUnit.assertRaises(function() { return Everything.test_uint16(-42); }); JSUnit.assertRaises(function() { return Everything.test_uint32(-42); }); JSUnit.assertRaises(function() { return Everything.test_uint64(-42); }); JSUnit.assertRaises(function() { return Everything.test_uint(-42); }); JSUnit.assertRaises(function() { return Everything.test_size(-42); }); } function testBadConstructor() { try { Gio.AppLaunchContext(); } catch (e) { JSUnit.assert(e.message.indexOf("Constructor called as normal method") >= 0); } } function testStrv() { JSUnit.assertTrue(Everything.test_strv_in(['1', '2', '3'])); // Second two are deliberately not strings JSUnit.assertRaises(function() { Everything.test_strv_in(['1', 2, 3]); }); let strv = Everything.test_strv_out(); JSUnit.assertEquals(5, strv.length); JSUnit.assertEquals("thanks", strv[0]); JSUnit.assertEquals("for", strv[1]); JSUnit.assertEquals("all", strv[2]); JSUnit.assertEquals("the", strv[3]); JSUnit.assertEquals("fish", strv[4]); strv = Everything.test_strv_out_container(); JSUnit.assertEquals(3, strv.length); JSUnit.assertEquals("1", strv[0]); JSUnit.assertEquals("2", strv[1]); JSUnit.assertEquals("3", strv[2]); } function testInAfterOut() { const str = "hello"; let len = Everything.test_int_out_utf8(str); JSUnit.assertEquals("testInAfterOut", str.length, len); } function testUtf8() { const CONST_STR = "const \u2665 utf8"; const NONCONST_STR = "nonconst \u2665 utf8"; JSUnit.assertEquals(CONST_STR, Everything.test_utf8_const_return()); JSUnit.assertEquals(NONCONST_STR, Everything.test_utf8_nonconst_return()); Everything.test_utf8_const_in(CONST_STR); JSUnit.assertEquals(NONCONST_STR, Everything.test_utf8_out()); JSUnit.assertEquals(NONCONST_STR, Everything.test_utf8_inout(CONST_STR)); JSUnit.assertEquals(NONCONST_STR, Everything.test_utf8_inout(CONST_STR)); JSUnit.assertEquals(NONCONST_STR, Everything.test_utf8_inout(CONST_STR)); JSUnit.assertEquals(NONCONST_STR, Everything.test_utf8_inout(CONST_STR)); } function testFilenameReturn() { var filenames = Everything.test_filename_return(); JSUnit.assertEquals(2, filenames.length); JSUnit.assertEquals('\u00e5\u00e4\u00f6', filenames[0]); JSUnit.assertEquals('/etc/fstab', filenames[1]); } function testStaticMeth() { let v = Everything.TestObj.new_from_file("/enoent"); JSUnit.assertTrue(v instanceof Everything.TestObj); } function testClosure() { let arguments_length = -1; let someCallback = function() { arguments_length = arguments.length; return 42; }; let i = Everything.test_closure(someCallback); JSUnit.assertEquals('callback arguments length', 0, arguments_length); JSUnit.assertEquals('callback return value', 42, i); } function testClosureOneArg() { let arguments_length = -1; let someCallback = function(someValue) { arguments_length = arguments.length; JSUnit.assertEquals(1, arguments.length); return someValue; }; let i = Everything.test_closure_one_arg(someCallback, 42); JSUnit.assertEquals('callback arguments length', 1, arguments_length); JSUnit.assertEquals('callback with one arg return value', 42, i); } function testCallback() { let callback = function() { return 42; }; JSUnit.assertEquals('Callback', Everything.test_callback(callback), 42); JSUnit.assertEquals('CallbackNull', Everything.test_callback(null), 0); JSUnit.assertRaises('CallbackUndefined', function () { Everything.test_callback(undefined); }); } function testArrayCallback() { function arrayEqual(ref, one) { JSUnit.assertEquals(ref.length, one.length); for (let i = 0; i < ref.length; i++) JSUnit.assertEquals(ref[i], one[i]); } let callback = function(ints, strings) { JSUnit.assertEquals(2, arguments.length); arrayEqual([-1, 0, 1, 2], ints); arrayEqual(["one", "two", "three"], strings); return 7; }; JSUnit.assertEquals(Everything.test_array_callback(callback), 14); JSUnit.assertRaises(function () { Everything.test_array_callback(null) }); } function testCallbackDestroyNotify() { let testObj = { called: 0, test: function(data) { this.called++; return data; } }; JSUnit.assertEquals('CallbackDestroyNotify', Everything.test_callback_destroy_notify(Lang.bind(testObj, function() { return testObj.test(42); })), 42); JSUnit.assertEquals('CallbackDestroyNotify', testObj.called, 1); JSUnit.assertEquals('CallbackDestroyNotify', Everything.test_callback_thaw_notifications(), 42); } function testCallbackAsync() { let test = function() { return 44; }; Everything.test_callback_async(test); let i = Everything.test_callback_thaw_async(); JSUnit.assertEquals('testCallbackAsyncFinish', 44, i); } function testIntValueArg() { let i = Everything.test_int_value_arg(42); JSUnit.assertEquals('Method taking a GValue', 42, i); } function testValueReturn() { let i = Everything.test_value_return(42); JSUnit.assertEquals('Method returning a GValue', 42, i); } /* GList types */ function testGListOut() { JSUnit.assertEquals("1,2,3", Everything.test_glist_nothing_return().join(',')); JSUnit.assertEquals("1,2,3", Everything.test_glist_nothing_return2().join(',')); JSUnit.assertEquals("1,2,3", Everything.test_glist_container_return().join(',')); JSUnit.assertEquals("1,2,3", Everything.test_glist_everything_return().join(',')); } function testGListIn() { const STR_LIST = ["1", "2", "3" ]; Everything.test_glist_nothing_in(STR_LIST); Everything.test_glist_nothing_in2(STR_LIST); //Everything.test_glist_container_in(STR_LIST); } /* GSList types */ function testGSListOut() { JSUnit.assertEquals("1,2,3", Everything.test_gslist_nothing_return().join(',')); JSUnit.assertEquals("1,2,3", Everything.test_gslist_nothing_return2().join(',')); JSUnit.assertEquals("1,2,3", Everything.test_gslist_container_return().join(',')); JSUnit.assertEquals("1,2,3", Everything.test_gslist_everything_return().join(',')); } function testGSListIn() { const STR_LIST = ["1", "2", "3" ]; Everything.test_gslist_nothing_in(STR_LIST); Everything.test_gslist_nothing_in2(STR_LIST); //Everything.test_gslist_container_in(STR_LIST); } /* Array tests */ function testArrayIn() { JSUnit.assertEquals(10, Everything.test_array_int_in([1,2,3,4])); JSUnit.assertEquals(10, Everything.test_array_gint8_in([1,2,3,4])); JSUnit.assertEquals(10, Everything.test_array_gint16_in([1,2,3,4])); JSUnit.assertEquals(10, Everything.test_array_gint32_in([1,2,3,4])); // FIXME: arrays of int64 are unimplemented //assertEquals(10, Everything.test_array_gint64_in([1,2,3,4])); // implicit conversions from strings to int arrays JSUnit.assertEquals(10, Everything.test_array_gint8_in("\x01\x02\x03\x04")); JSUnit.assertEquals(10, Everything.test_array_gint16_in("\x01\x02\x03\x04")); JSUnit.assertEquals(2560, Everything.test_array_gint16_in("\u0100\u0200\u0300\u0400")); // GType arrays JSUnit.assertEquals('[GSimpleAction,GIcon,GBoxed,]', Everything.test_array_gtype_in([Gio.SimpleAction, Gio.Icon, GObject.TYPE_BOXED])); JSUnit.assertRaises(function() { Everything.test_array_gtype_in(42); }); JSUnit.assertRaises(function() { Everything.test_array_gtype_in([undefined]); }); JSUnit.assertRaises(function() { // 80 is G_TYPE_OBJECT, but we don't want it to work Everything.test_array_gtype_in([80]); }); } function testArrayOut() { function arrayEqual(ref, res) { JSUnit.assertEquals(ref.length, res.length); for (let i = 0; i < ref.length; i++) JSUnit.assertEquals(ref[i], res[i]); } let array = Everything.test_array_fixed_size_int_out(); JSUnit.assertEquals(0, array[0]); JSUnit.assertEquals(4, array[4]); array = Everything.test_array_fixed_size_int_return(); JSUnit.assertEquals(0, array[0]); JSUnit.assertEquals(4, array[4]); array = Everything.test_array_int_none_out(); arrayEqual([1, 2, 3, 4, 5], array); array = Everything.test_array_int_full_out(); arrayEqual([0, 1, 2, 3, 4], array); array = Everything.test_array_int_null_out(); JSUnit.assertEquals(0, array.length); Everything.test_array_int_null_in(null); } /* GHash type */ // Convert an object to a predictable (not-hash-order-dependent) string function objToString(v) { if (typeof(v) == "object") { let keys = []; for (let k in v) keys.push(k); keys.sort(); return "{" + keys.map(function(k) { return k + ":" + objToString(v[k]); }) + "}"; } else if (typeof(v) == "string") { return '"' + v + '"'; } else { return v; } } function testGHashOut() { const HASH_STR = '{baz:"bat",foo:"bar",qux:"quux"}'; JSUnit.assertEquals(null, Everything.test_ghash_null_return()); JSUnit.assertEquals(HASH_STR, objToString(Everything.test_ghash_nothing_return())); JSUnit.assertEquals(HASH_STR, objToString(Everything.test_ghash_nothing_return2())); JSUnit.assertEquals(HASH_STR, objToString(Everything.test_ghash_container_return())); JSUnit.assertEquals(HASH_STR, objToString(Everything.test_ghash_everything_return())); } function testGHashIn() { const STR_HASH = { foo: 'bar', baz: 'bat', qux: 'quux' }; Everything.test_ghash_null_in(null); Everything.test_ghash_nothing_in(STR_HASH); Everything.test_ghash_nothing_in2(STR_HASH); } function testNestedGHashOut() { const HASH_STR = '{wibble:{baz:"bat",foo:"bar",qux:"quux"}}'; JSUnit.assertEquals(HASH_STR, objToString(Everything.test_ghash_nested_everything_return())); JSUnit.assertEquals(HASH_STR, objToString(Everything.test_ghash_nested_everything_return2())); } /* Enums */ function testEnumParam() { let e; e = Everything.test_enum_param(Everything.TestEnum.VALUE1); JSUnit.assertEquals('Enum parameter', 'value1', e); e = Everything.test_enum_param(Everything.TestEnum.VALUE3); JSUnit.assertEquals('Enum parameter', 'value3', e); e = Everything.test_unsigned_enum_param(Everything.TestEnumUnsigned.VALUE1); JSUnit.assertEquals('Enum parameter', 'value1', e); e = Everything.test_unsigned_enum_param(Everything.TestEnumUnsigned.VALUE2); JSUnit.assertEquals('Enum parameter', 'value2', e); JSUnit.assertNotUndefined("Enum $gtype", Everything.TestEnumUnsigned.$gtype); JSUnit.assertTrue("Enum $gtype enumerable", "$gtype" in Everything.TestEnumUnsigned); JSUnit.assertEquals(Number(Everything.TestError), Everything.TestError.quark()); JSUnit.assertEquals('value4', Everything.TestEnum.param(Everything.TestEnum.VALUE4)); } function testSignal() { let handlerCounter = 0; let o = new Everything.TestObj(); let theObject = null; let handlerId = o.connect('test', function(signalObject) { handlerCounter ++; theObject = signalObject; o.disconnect(handlerId); }); o.emit('test'); JSUnit.assertEquals('handler callled', 1, handlerCounter); JSUnit.assertEquals('Signal handlers gets called with right object', o, theObject); o.emit('test'); JSUnit.assertEquals('disconnected handler not called', 1, handlerCounter); } function testInvalidSignal() { let o = new Everything.TestObj(); JSUnit.assertRaises('connect to invalid signal', function() { o.connect('invalid-signal', function(o) {}); }); JSUnit.assertRaises('emit invalid signal', function() { o.emit('invalid-signal'); }); } function testSignalWithStaticScopeArg() { let o = new Everything.TestObj(); let b = new Everything.TestSimpleBoxedA({ some_int: 42, some_int8: 43, some_double: 42.5, some_enum: Everything.TestEnum.VALUE3 }); o.connect('test-with-static-scope-arg', function(signalObject, signalArg) { signalArg.some_int = 44; }); o.emit('test-with-static-scope-arg', b); JSUnit.assertEquals('signal handler was passed arg as reference', 44, b.some_int); } function testTortureSignature0() { let [y, z, q] = Everything.test_torture_signature_0(42, 'foo', 7); JSUnit.assertEquals(Math.floor(y), 42); JSUnit.assertEquals(z, 84); JSUnit.assertEquals(q, 10); } function testTortureSignature1Fail() { JSUnit.assertRaises(function () { let [success, y, z, q] = Everything.test_torture_signature_1(42, 'foo', 7); }); } function testTortureSignature1Success() { let [success, y, z, q] = Everything.test_torture_signature_1(11, 'barbaz', 8); JSUnit.assertEquals(Math.floor(y), 11); JSUnit.assertEquals(z, 22); JSUnit.assertEquals(q, 14); } function testTortureSignature2() { let [y, z, q] = Everything.test_torture_signature_2(42, function () { return 0; }, 'foo', 7); JSUnit.assertEquals(Math.floor(y), 42); JSUnit.assertEquals(z, 84); JSUnit.assertEquals(q, 10); } function testObjTortureSignature0() { let o = new Everything.TestObj(); let [y, z, q] = o.torture_signature_0(42, 'foo', 7); JSUnit.assertEquals(Math.floor(y), 42); JSUnit.assertEquals(z, 84); JSUnit.assertEquals(q, 10); } function testObjTortureSignature1Fail() { let o = new Everything.TestObj(); JSUnit.assertRaises(function () { let [success, y, z, q] = o.torture_signature_1(42, 'foo', 7); }); } function testObjTortureSignature1Success() { let o = new Everything.TestObj(); let [success, y, z, q] = o.torture_signature_1(11, 'barbaz', 8); JSUnit.assertEquals(Math.floor(y), 11); JSUnit.assertEquals(z, 22); JSUnit.assertEquals(q, 14); } function testStrvInGValue() { let v = Everything.test_strv_in_gvalue(); JSUnit.assertEquals(v.length, 3); JSUnit.assertEquals(v[0], "one"); JSUnit.assertEquals(v[1], "two"); JSUnit.assertEquals(v[2], "three"); } function testVariant() { // Cannot access the variant contents, for now let ivar = Everything.test_gvariant_i(); JSUnit.assertEquals('i', ivar.get_type_string()); JSUnit.assertTrue(ivar.equal(GLib.Variant.new_int32(1))); let svar = Everything.test_gvariant_s(); JSUnit.assertEquals('s', String.fromCharCode(svar.classify())); JSUnit.assertEquals('one', svar.get_string()[0]); let asvvar = Everything.test_gvariant_asv(); JSUnit.assertEquals(2, asvvar.n_children()); let asvar = Everything.test_gvariant_as(); let as = asvar.get_strv(); JSUnit.assertEquals('one', as[0]); JSUnit.assertEquals('two', as[1]); JSUnit.assertEquals('three', as[2]); JSUnit.assertEquals(3, as.length); } function testGError() { JSUnit.assertEquals(Gio.io_error_quark(), Number(Gio.IOErrorEnum)); try { let file = Gio.file_new_for_path("\\/,.^!@&$_don't exist"); file.read(null); } catch (x) { JSUnit.assertTrue(x instanceof Gio.IOErrorEnum); JSUnit.assertTrue(x.matches(Gio.io_error_quark(), Gio.IOErrorEnum.NOT_FOUND)); JSUnit.assertTrue(x.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND)); JSUnit.assertEquals(Gio.io_error_quark(), x.domain); JSUnit.assertEquals(Gio.IOErrorEnum.NOT_FOUND, x.code); } Everything.test_gerror_callback(function(e) { JSUnit.assertTrue(e instanceof Gio.IOErrorEnum); JSUnit.assertEquals(Gio.io_error_quark(), e.domain); JSUnit.assertEquals(Gio.IOErrorEnum.NOT_SUPPORTED, e.code); JSUnit.assertEquals('regression test error', e.message); }); Everything.test_owned_gerror_callback(function(e) { JSUnit.assertTrue(e instanceof Gio.IOErrorEnum); JSUnit.assertEquals(Gio.io_error_quark(), e.domain); JSUnit.assertEquals(Gio.IOErrorEnum.PERMISSION_DENIED, e.code); JSUnit.assertEquals('regression test owned error', e.message); }); // Calling matches() on an unpaired error used to JSUnit.assert: // https://bugzilla.gnome.org/show_bug.cgi?id=689482 try { WarnLib.throw_unpaired(); JSUnit.assertTrue(false); } catch (e) { JSUnit.assertFalse(e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND)); } } function testGErrorMessages() { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: Gio.IOErrorEnum: *'); try { let file = Gio.file_new_for_path("\\/,.^!@&$_don't exist"); file.read(null); } catch(e) { logError(e); } GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: Gio.IOErrorEnum: a message\ntestGErrorMessages@*'); try { throw new Gio.IOErrorEnum({ message: 'a message', code: 0 }); } catch(e) { logError(e); } GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: Gio.IOErrorEnum: a message\ntestGErrorMessages@*'); logError(new Gio.IOErrorEnum({ message: 'a message', code: 0 })); // No stack for GLib.Error constructor GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: Gio.IOErrorEnum: a message'); logError(new GLib.Error(Gio.IOErrorEnum, 0, 'a message')); GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: GLib.Error my-error: a message'); logError(new GLib.Error(GLib.quark_from_string('my-error'), 0, 'a message')); // Now with prefix GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: prefix: Gio.IOErrorEnum: *'); try { let file = Gio.file_new_for_path("\\/,.^!@&$_don't exist"); file.read(null); } catch(e) { logError(e, 'prefix'); } GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: prefix: Gio.IOErrorEnum: a message\ntestGErrorMessages@*'); try { throw new Gio.IOErrorEnum({ message: 'a message', code: 0 }); } catch(e) { logError(e, 'prefix'); } GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: prefix: Gio.IOErrorEnum: a message\ntestGErrorMessages@*'); logError(new Gio.IOErrorEnum({ message: 'a message', code: 0 }), 'prefix'); // No stack for GLib.Error constructor GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: prefix: Gio.IOErrorEnum: a message'); logError(new GLib.Error(Gio.IOErrorEnum, 0, 'a message'), 'prefix'); GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: prefix: GLib.Error my-error: a message'); logError(new GLib.Error(GLib.quark_from_string('my-error'), 0, 'a message'), 'prefix'); } function testWrongClassGObject() { /* Function calls */ // Everything.func_obj_null_in expects a Everything.TestObj JSUnit.assertRaises(function() { Everything.func_obj_null_in(new Gio.SimpleAction); }); JSUnit.assertRaises(function() { Everything.func_obj_null_in(new GLib.KeyFile); }); JSUnit.assertRaises(function() { Everything.func_obj_null_in(Gio.File.new_for_path('/')); }); Everything.func_obj_null_in(new Everything.TestSubObj); /* Method calls */ JSUnit.assertRaises(function() { Everything.TestObj.prototype.instance_method.call(new Gio.SimpleAction); }); JSUnit.assertRaises(function() { Everything.TestObj.prototype.instance_method.call(new GLib.KeyFile); }); Everything.TestObj.prototype.instance_method.call(new Everything.TestSubObj); } function testWrongClassGBoxed() { let simpleBoxed = new Everything.TestSimpleBoxedA; // simpleBoxed.equals expects a Everything.TestSimpleBoxedA JSUnit.assertRaises(function() { simpleBoxed.equals(new Gio.SimpleAction); }); JSUnit.assertRaises(function() { simpleBoxed.equals(new Everything.TestObj); }); JSUnit.assertRaises(function() { simpleBoxed.equals(new GLib.KeyFile); }); JSUnit.assertTrue(simpleBoxed.equals(simpleBoxed)); JSUnit.assertRaises(function() { Everything.TestSimpleBoxedA.prototype.copy.call(new Gio.SimpleAction); }); JSUnit.assertRaises(function() { Everything.TestSimpleBoxedA.prototype.copy.call(new GLib.KeyFile); }); Everything.TestSimpleBoxedA.prototype.copy.call(simpleBoxed); } JSUnit.gjstestRun(this, JSUnit.setUp, JSUnit.tearDown); cjs-2.8.0/installed-tests/js/test0040mainloop.js0000664000175000017500000000073312610172032020334 0ustar fabiofabioconst JSUnit = imports.jsUnit; var Mainloop = imports.mainloop; function testBasicMainloop() { log('running mainloop test'); Mainloop.idle_add(function() { Mainloop.quit('testMainloop'); }); Mainloop.run('testMainloop'); log('mainloop test done'); } /* A dangling mainloop idle should get removed and not leaked */ function testDanglingIdle() { Mainloop.idle_add(function() { return true; }); } JSUnit.gjstestRun(this, JSUnit.setUp, JSUnit.tearDown); cjs-2.8.0/installed-tests/js/testCairo.js0000664000175000017500000001170312610172032017246 0ustar fabiofabioconst JSUnit = imports.jsUnit; const Cairo = imports.cairo; const Everything = imports.gi.Regress; function _ts(obj) { return obj.toString().slice(8, -1); } function _createSurface() { return new Cairo.ImageSurface(Cairo.Format.ARGB32, 1, 1); } function _createContext() { return new Cairo.Context(_createSurface()); } function testContext() { let cr = _createContext(); JSUnit.assertTrue(cr instanceof Cairo.Context); } function testContextMethods() { let cr = _createContext(); JSUnit.assertTrue(cr instanceof Cairo.Context); cr.save(); cr.restore(); let surface = _createSurface(); JSUnit.assertEquals(_ts(cr.getTarget()), "CairoImageSurface"); let pattern = Cairo.SolidPattern.createRGB(1, 2, 3); cr.setSource(pattern); JSUnit.assertEquals(_ts(cr.getSource()), "CairoSolidPattern"); cr.setSourceSurface(surface, 0, 0); cr.pushGroup(); cr.popGroup(); cr.pushGroupWithContent(Cairo.Content.COLOR); cr.popGroupToSource(); cr.setSourceRGB(1, 2, 3); cr.setSourceRGBA(1, 2, 3, 4); cr.setAntialias(Cairo.Antialias.NONE); JSUnit.assertEquals("antialias", cr.getAntialias(), Cairo.Antialias.NONE); cr.setFillRule(Cairo.FillRule.EVEN_ODD); JSUnit.assertEquals("fillRule", cr.getFillRule(), Cairo.FillRule.EVEN_ODD); cr.setLineCap(Cairo.LineCap.ROUND); JSUnit.assertEquals("lineCap", cr.getLineCap(), Cairo.LineCap.ROUND); cr.setLineJoin(Cairo.LineJoin.ROUND); JSUnit.assertEquals("lineJoin", cr.getLineJoin(), Cairo.LineJoin.ROUND); cr.setLineWidth(1138); JSUnit.assertEquals("lineWidth", cr.getLineWidth(), 1138); cr.setMiterLimit(42); JSUnit.assertEquals("miterLimit", cr.getMiterLimit(), 42); cr.setOperator(Cairo.Operator.IN); JSUnit.assertEquals("operator", cr.getOperator(), Cairo.Operator.IN); cr.setTolerance(144); JSUnit.assertEquals("tolerance", cr.getTolerance(), 144); cr.clip(); cr.clipPreserve(); let rv = cr.clipExtents(); JSUnit.assertEquals("clipExtents", rv.length, 4); cr.fill(); cr.fillPreserve(); let rv = cr.fillExtents(); JSUnit.assertEquals("fillExtents", rv.length, 4); cr.mask(pattern); cr.maskSurface(surface, 0, 0); cr.paint(); cr.paintWithAlpha(1); cr.stroke(); cr.strokePreserve(); let rv = cr.strokeExtents(); JSUnit.assertEquals("strokeExtents", rv.length, 4); cr.inFill(0, 0); cr.inStroke(0, 0); cr.copyPage(); cr.showPage(); let dc = cr.getDashCount(); JSUnit.assertEquals("dashCount", dc, 0); cr.translate(10, 10); cr.scale(10, 10); cr.rotate(180); cr.identityMatrix(); let rv = cr.userToDevice(0, 0); JSUnit.assertEquals("userToDevice", rv.length, 2); let rv = cr.userToDeviceDistance(0, 0); JSUnit.assertEquals("userToDeviceDistance", rv.length, 2); let rv = cr.deviceToUser(0, 0); JSUnit.assertEquals("deviceToUser", rv.length, 2); let rv = cr.deviceToUserDistance(0, 0); JSUnit.assertEquals("deviceToUserDistance", rv.length, 2); cr.showText("foobar"); cr.moveTo(0, 0); cr.setDash([1, 0.5], 1); cr.lineTo(1, 0); cr.lineTo(1, 1); cr.lineTo(0, 1); cr.closePath(); let path = cr.copyPath(); cr.fill(); cr.appendPath(path); cr.stroke(); } function testSolidPattern() { let cr = _createContext(); let p1 = Cairo.SolidPattern.createRGB(1, 2, 3); JSUnit.assertEquals(_ts(p1), "CairoSolidPattern"); cr.setSource(p1); JSUnit.assertEquals(_ts(cr.getSource()), "CairoSolidPattern"); let p2 = Cairo.SolidPattern.createRGBA(1, 2, 3, 4); JSUnit.assertEquals(_ts(p2), "CairoSolidPattern"); cr.setSource(p2); JSUnit.assertEquals(_ts(cr.getSource()), "CairoSolidPattern"); } function testSurfacePattern() { let cr = _createContext(); let surface = _createSurface(); let p1 = new Cairo.SurfacePattern(surface); JSUnit.assertEquals(_ts(p1), "CairoSurfacePattern"); cr.setSource(p1); JSUnit.assertEquals(_ts(cr.getSource()), "CairoSurfacePattern"); } function testLinearGradient() { let cr = _createContext(); let surface = _createSurface(); let p1 = new Cairo.LinearGradient(1, 2, 3, 4); JSUnit.assertEquals(_ts(p1), "CairoLinearGradient"); cr.setSource(p1); JSUnit.assertEquals(_ts(cr.getSource()), "CairoLinearGradient"); } function testRadialGradient() { let cr = _createContext(); let surface = _createSurface(); let p1 = new Cairo.RadialGradient(1, 2, 3, 4, 5, 6); JSUnit.assertEquals(_ts(p1), "CairoRadialGradient"); cr.setSource(p1); JSUnit.assertEquals(_ts(cr.getSource()), "CairoRadialGradient"); } function testCairoSignal() { let o = new Everything.TestObj(); let called = false; o.connect('sig-with-foreign-struct', function(o, cr) { called = true; JSUnit.assertEquals(_ts(cr), "CairoContext"); }); o.emit_sig_with_foreign_struct(); JSUnit.assertTrue(called); } JSUnit.gjstestRun(this, JSUnit.setUp, JSUnit.tearDown); cjs-2.8.0/installed-tests/js/testMetaClass.js0000664000175000017500000000612412610172032020066 0ustar fabiofabio// -*- mode: js; indent-tabs-mode: nil -*- const JSUnit = imports.jsUnit; if (!('assertEquals' in this)) { /* allow running this test standalone */ imports.lang.copyPublicProperties(imports.jsUnit, this); gjstestRun = function() { return imports.jsUnit.gjstestRun(window); }; } const Lang = imports.lang; function assertArrayEquals(expected, got) { JSUnit.assertEquals(expected.length, got.length); for (let i = 0; i < expected.length; i ++) { JSUnit.assertEquals(expected[i], got[i]); } } const NormalClass = new Lang.Class({ Name: 'NormalClass', _init: function() { this.one = 1; } }); let Subclassed = []; const MetaClass = new Lang.Class({ Name: 'MetaClass', Extends: Lang.Class, _init: function(params) { Subclassed.push(params.Name); this.parent(params); if (params.Extended) { this.prototype.dynamic_method = this.wrapFunction('dynamic_method', function() { return 73; }); this.DYNAMIC_CONSTANT = 2; } } }); const CustomMetaOne = new MetaClass({ Name: 'CustomMetaOne', Extends: NormalClass, Extended: false, _init: function() { this.parent(); this.two = 2; } }); const CustomMetaTwo = new MetaClass({ Name: 'CustomMetaTwo', Extends: NormalClass, Extended: true, _init: function() { this.parent(); this.two = 2; } }); // This should inherit CustomMeta, even though // we use Lang.Class const CustomMetaSubclass = new Lang.Class({ Name: 'CustomMetaSubclass', Extends: CustomMetaOne, Extended: true, _init: function() { this.parent(); this.three = 3; } }); function testMetaClass() { assertArrayEquals(['CustomMetaOne', 'CustomMetaTwo', 'CustomMetaSubclass'], Subclassed); JSUnit.assertTrue(NormalClass instanceof Lang.Class); JSUnit.assertTrue(MetaClass instanceof Lang.Class); JSUnit.assertTrue(CustomMetaOne instanceof Lang.Class); JSUnit.assertTrue(CustomMetaOne instanceof MetaClass); JSUnit.assertEquals(2, CustomMetaTwo.DYNAMIC_CONSTANT); JSUnit.assertUndefined(CustomMetaOne.DYNAMIC_CONSTANT); } function testMetaInstance() { let instanceOne = new CustomMetaOne(); JSUnit.assertEquals(1, instanceOne.one); JSUnit.assertEquals(2, instanceOne.two); JSUnit.assertRaises(function() { instanceOne.dynamic_method(); }); let instanceTwo = new CustomMetaTwo(); JSUnit.assertEquals(1, instanceTwo.one); JSUnit.assertEquals(2, instanceTwo.two); JSUnit.assertEquals(73, instanceTwo.dynamic_method()); } function testMetaSubclass() { JSUnit.assertTrue(CustomMetaSubclass instanceof MetaClass); let instance = new CustomMetaSubclass(); JSUnit.assertEquals(1, instance.one); JSUnit.assertEquals(2, instance.two); JSUnit.assertEquals(3, instance.three); JSUnit.assertEquals(73, instance.dynamic_method()); JSUnit.assertEquals(2, CustomMetaSubclass.DYNAMIC_CONSTANT); } JSUnit.gjstestRun(this, JSUnit.setUp, JSUnit.tearDown); cjs-2.8.0/installed-tests/js/testMainloop.js0000664000175000017500000000731712610172032017775 0ustar fabiofabioconst JSUnit = imports.jsUnit; const Mainloop = imports.mainloop; function testTimeout() { var trackTimeout = { runTenTimes : 0, runOnlyOnce: 0, neverRun: 0 }; Mainloop.timeout_add(10, function() { if (trackTimeout.runTenTimes == 10) { Mainloop.quit('testtimeout'); return false; } trackTimeout.runTenTimes += 1; return true; }); Mainloop.timeout_add(10, function () { trackTimeout.runOnlyOnce += 1; return false; }); Mainloop.timeout_add(15000, function() { trackTimeout.neverRun += 1; return false; }); Mainloop.run('testtimeout'); with (trackTimeout) { JSUnit.assertEquals("run ten times", 10, runTenTimes); JSUnit.assertEquals("run only once", 1, runOnlyOnce); JSUnit.assertEquals("never run", 0, neverRun); } } function testIdle() { var trackIdles = { runTwiceCount : 0, runOnceCount : 0, neverRunsCount : 0, quitAfterManyRunsCount : 0 }; Mainloop.idle_add(function() { trackIdles.runTwiceCount += 1; if (trackIdles.runTwiceCount == 2) return false; else return true; }); Mainloop.idle_add(function() { trackIdles.runOnceCount += 1; return false; }); var neverRunsId = Mainloop.idle_add(function() { trackIdles.neverRunsCount += 1; return false; }); Mainloop.idle_add(function() { trackIdles.quitAfterManyRunsCount += 1; if (trackIdles.quitAfterManyRunsCount > 10) { Mainloop.quit('foobar'); return false; } else { return true; } }); Mainloop.source_remove(neverRunsId); Mainloop.run('foobar'); JSUnit.assertEquals("one-shot ran once", 1, trackIdles.runOnceCount); JSUnit.assertEquals("two-shot ran twice", 2, trackIdles.runTwiceCount); JSUnit.assertEquals("removed never ran", 0, trackIdles.neverRunsCount); JSUnit.assertEquals("quit after many ran 11", 11, trackIdles.quitAfterManyRunsCount); // check re-entrancy of removing closures while they // are being invoked trackIdles.removeId = Mainloop.idle_add(function() { Mainloop.source_remove(trackIdles.removeId); Mainloop.quit('foobar'); return false; }); Mainloop.run('foobar'); // Add an idle before exit, then never run main loop again. // This is to test that we remove idle callbacks when the associated // JSContext is blown away. The leak check in gjs-unit will // fail if the idle function is not garbage collected. Mainloop.idle_add(function() { fail("This should never have been called"); return true; }); } JSUnit.gjstestRun(this, JSUnit.setUp, JSUnit.tearDown); cjs-2.8.0/installed-tests/js/testReflectObject.js0000664000175000017500000000072712610172032020730 0ustar fabiofabioconst JSUnit = imports.jsUnit; function testReflect() { JSUnit.assertTrue(Reflect.parse instanceof Function); var expr = Reflect.parse("var a = 10;"); JSUnit.assertEquals("Program", expr.type); JSUnit.assertEquals("VariableDeclaration", expr.body[0].type); JSUnit.assertEquals("a",expr.body[0].declarations[0].id.name); JSUnit.assertEquals(10,expr.body[0].declarations[0].init.value); } JSUnit.gjstestRun(this, JSUnit.setUp, JSUnit.tearDown); cjs-2.8.0/installed-tests/js/testGDBus.js0000664000175000017500000003753712610172032017172 0ustar fabiofabio const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const JSUnit = imports.jsUnit; /* The methods list with their signatures. * * *** NOTE: If you add stuff here, you need to update testIntrospectReal */ var TestIface = ' \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ '; /* Test is the actual object exporting the dbus methods */ function Test() { this._init(); } const PROP_READ_WRITE_INITIAL_VALUE = 58; const PROP_WRITE_ONLY_INITIAL_VALUE = "Initial value"; Test.prototype = { _init: function(){ this._propWriteOnly = PROP_WRITE_ONLY_INITIAL_VALUE; this._propReadWrite = PROP_READ_WRITE_INITIAL_VALUE; this._impl = Gio.DBusExportedObject.wrapJSObject(TestIface, this); this._impl.export(Gio.DBus.session, '/org/gnome/gjs/Test'); }, frobateStuff: function(args) { return { hello: new GLib.Variant('s', 'world') }; }, nonJsonFrobateStuff: function(i) { if (i == 42) { return "42 it is!"; } else { return "Oops"; } }, alwaysThrowException: function() { throw Error("Exception!"); }, thisDoesNotExist: function () { /* We'll remove this later! */ }, noInParameter: function() { return "Yes!"; }, multipleInArgs: function(a, b, c, d, e) { return a + " " + b + " " + c + " " + d + " " + e; }, emitSignal: function() { this._impl.emit_signal('signalFoo', GLib.Variant.new('(s)', [ "foobar" ])); }, noReturnValue: function() { /* Empty! */ }, /* The following two functions have identical return values * in JS, but the bus message will be different. * multipleOutValues is "sss", while oneArrayOut is "as" */ multipleOutValues: function() { return [ "Hello", "World", "!" ]; }, oneArrayOut: function() { return [ "Hello", "World", "!" ]; }, /* Same thing again. In this case multipleArrayOut is "asas", * while arrayOfArrayOut is "aas". */ multipleArrayOut: function() { return [[ "Hello", "World" ], [ "World", "Hello" ]]; }, arrayOfArrayOut: function() { return [[ "Hello", "World" ], [ "World", "Hello" ]]; }, arrayOutBadSig: function() { return [ "Hello", "World", "!" ]; }, byteArrayEcho: function(binaryString) { return binaryString; }, byteEcho: function(aByte) { return aByte; }, dictEcho: function(dict) { return dict; }, /* This one is implemented asynchronously. Returns * the input arguments */ echoAsync: function(parameters, invocation) { var [someString, someInt] = parameters; GLib.idle_add(GLib.PRIORITY_DEFAULT, function() { invocation.return_value(new GLib.Variant('(si)', [someString, someInt])); return false; }); }, // boolean get PropReadOnly() { return true; }, // string set PropWriteOnly(value) { this._propWriteOnly = value; }, // variant get PropReadWrite() { return new GLib.Variant('s', this._propReadWrite.toString()); }, set PropReadWrite(value) { this._propReadWrite = value.deep_unpack(); }, structArray: function () { return [[128, 123456], [42, 654321]]; } }; var own_name_id; function testExportStuff() { let loop = GLib.MainLoop.new(null, false); new Test(); own_name_id = Gio.DBus.session.own_name('org.gnome.gjs.Test', Gio.BusNameOwnerFlags.NONE, function(name) { log("Acquired name " + name); loop.quit(); }, function(name) { log("Lost name " + name); }); loop.run(); } const ProxyClass = Gio.DBusProxy.makeProxyWrapper(TestIface); var proxy; function testInitStuff() { let loop = GLib.MainLoop.new(null, false); var theError; proxy = new ProxyClass(Gio.DBus.session, 'org.gnome.gjs.Test', '/org/gnome/gjs/Test', function (obj, error) { theError = error; proxy = obj; loop.quit(); }); loop.run(); JSUnit.assertNotNull(proxy); JSUnit.assertNull(theError); } function testFrobateStuff() { let loop = GLib.MainLoop.new(null, false); let theResult, theExcp; proxy.frobateStuffRemote({}, function(result, excp) { [theResult] = result; theExcp = excp; loop.quit(); }); loop.run(); JSUnit.assertEquals("world", theResult.hello.deep_unpack()); } /* excp must be exactly the exception thrown by the remote method (more or less) */ function testThrowException() { let loop = GLib.MainLoop.new(null, false); GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: Exception in method call: alwaysThrowException: *'); let theResult, theExcp; proxy.alwaysThrowExceptionRemote({}, function(result, excp) { theResult = result; theExcp = excp; loop.quit(); }); loop.run(); JSUnit.assertNull(theResult); JSUnit.assertNotNull(theExcp); } /* We check that the exception in the answer is not null when we try to call * a method that does not exist */ function testDoesNotExist() { let loop = GLib.MainLoop.new(null, false); let theResult, theExcp; /* First remove the method from the object! */ delete Test.prototype.thisDoesNotExist; proxy.thisDoesNotExistRemote(function (result, excp) { theResult = result; theExcp = excp; loop.quit(); }); loop.run(); JSUnit.assertNotNull(theExcp); JSUnit.assertNull(theResult); } function testNonJsonFrobateStuff() { let loop = GLib.MainLoop.new(null, false); let theResult, theExcp; proxy.nonJsonFrobateStuffRemote(42, function(result, excp) { [theResult] = result; theExcp = excp; loop.quit(); }); loop.run(); JSUnit.assertEquals("42 it is!", theResult); JSUnit.assertNull(theExcp); } function testNoInParameter() { let loop = GLib.MainLoop.new(null, false); let theResult, theExcp; proxy.noInParameterRemote(function(result, excp) { [theResult] = result; theExcp = excp; loop.quit(); }); loop.run(); JSUnit.assertEquals("Yes!", theResult); JSUnit.assertNull(theExcp); } function testMultipleInArgs() { let loop = GLib.MainLoop.new(null, false); let theResult, theExcp; proxy.multipleInArgsRemote(1, 2, 3, 4, 5, function(result, excp) { [theResult] = result; theExcp = excp; loop.quit(); }); loop.run(); JSUnit.assertEquals("1 2 3 4 5", theResult); JSUnit.assertNull(theExcp); } function testNoReturnValue() { let loop = GLib.MainLoop.new(null, false); let theResult, theExcp; proxy.noReturnValueRemote(function(result, excp) { [theResult] = result; theExcp = excp; loop.quit(); }); loop.run(); JSUnit.assertEquals(undefined, theResult); JSUnit.assertNull(theExcp); } function testEmitSignal() { let loop = GLib.MainLoop.new(null, false); let theResult, theExcp; let signalReceived = 0; let signalArgument = null; let id = proxy.connectSignal('signalFoo', function(emitter, senderName, parameters) { signalReceived ++; [signalArgument] = parameters; proxy.disconnectSignal(id); }); proxy.emitSignalRemote(function(result, excp) { [theResult] = result; theExcp = excp; if (excp) log("Signal emission exception: " + excp); loop.quit(); }); loop.run(); JSUnit.assertUndefined('result should be undefined', theResult); JSUnit.assertNull('no exception set', theExcp); JSUnit.assertEquals('number of signals received', signalReceived, 1); JSUnit.assertEquals('signal argument', signalArgument, "foobar"); } function testMultipleOutValues() { let loop = GLib.MainLoop.new(null, false); let theResult, theExcp; proxy.multipleOutValuesRemote(function(result, excp) { theResult = result; theExcp = excp; loop.quit(); }); loop.run(); JSUnit.assertEquals("Hello", theResult[0]); JSUnit.assertEquals("World", theResult[1]); JSUnit.assertEquals("!", theResult[2]); JSUnit.assertNull(theExcp); } function testOneArrayOut() { let loop = GLib.MainLoop.new(null, false); let theResult, theExcp; proxy.oneArrayOutRemote(function(result, excp) { [theResult] = result; theExcp = excp; loop.quit(); }); loop.run(); JSUnit.assertEquals("Hello", theResult[0]); JSUnit.assertEquals("World", theResult[1]); JSUnit.assertEquals("!", theResult[2]); JSUnit.assertNull(theExcp); } function testArrayOfArrayOut() { let loop = GLib.MainLoop.new(null, false); let theResult, theExcp; proxy.arrayOfArrayOutRemote(function(result, excp) { [theResult] = result; theExcp = excp; loop.quit(); }); loop.run(); let a1 = theResult[0]; let a2 = theResult[1]; JSUnit.assertEquals("Hello", a1[0]); JSUnit.assertEquals("World", a1[1]); JSUnit.assertEquals("World", a2[0]); JSUnit.assertEquals("Hello", a2[1]);; JSUnit.assertNull(theExcp); } function testMultipleArrayOut() { let loop = GLib.MainLoop.new(null, false); let theResult, theExcp; proxy.multipleArrayOutRemote(function(result, excp) { theResult = result; theExcp = excp; loop.quit(); }); loop.run(); let a1 = theResult[0]; let a2 = theResult[1]; JSUnit.assertEquals("Hello", a1[0]); JSUnit.assertEquals("World", a1[1]); JSUnit.assertEquals("World", a2[0]); JSUnit.assertEquals("Hello", a2[1]);; JSUnit.assertNull(theExcp); } /* We are returning an array but the signature says it's an integer, * so this should fail */ function testArrayOutBadSig() { let loop = GLib.MainLoop.new(null, false); let theResult, theExcp; proxy.arrayOutBadSigRemote(function(result, excp) { theResult = result; theExcp = excp; loop.quit(); }); loop.run(); JSUnit.assertNull(theResult); JSUnit.assertNotNull(theExcp); } function testAsyncImplementation() { let loop = GLib.MainLoop.new(null, false); let someString = "Hello world!"; let someInt = 42; let theResult, theExcp; proxy.echoRemote(someString, someInt, function(result, excp) { theResult = result; theExcp = excp; loop.quit(); }); loop.run(); JSUnit.assertNull(theExcp); JSUnit.assertNotNull(theResult); JSUnit.assertEquals(theResult[0], someString); JSUnit.assertEquals(theResult[1], someInt); } function testBytes() { let loop = GLib.MainLoop.new(null, false); let someBytes = [ 0, 63, 234 ]; let theResult, theExcp; for (let i = 0; i < someBytes.length; ++i) { theResult = null; theExcp = null; proxy.byteEchoRemote(someBytes[i], function(result, excp) { [theResult] = result; theExcp = excp; loop.quit(); }); loop.run(); JSUnit.assertNull(theExcp); JSUnit.assertNotNull(theResult); JSUnit.assertEquals(someBytes[i], theResult); } } function testStructArray() { let loop = GLib.MainLoop.new(null, false); let theResult, theExcp; proxy.structArrayRemote(function(result, excp) { [theResult] = result; theExcp = excp; loop.quit(); }); loop.run(); JSUnit.assertNull(theExcp); JSUnit.assertNotNull(theResult); JSUnit.assertEquals(theResult[0][0], 128); JSUnit.assertEquals(theResult[0][1], 123456); JSUnit.assertEquals(theResult[1][0], 42); JSUnit.assertEquals(theResult[1][1], 654321); } function testDictSignatures() { let loop = GLib.MainLoop.new(null, false); let someDict = { aDouble: new GLib.Variant('d', 10), // should be an integer after round trip anInteger: new GLib.Variant('i', 10.5), // should remain a double aDoubleBeforeAndAfter: new GLib.Variant('d', 10.5), }; let theResult, theExcp; proxy.dictEchoRemote(someDict, function(result, excp) { [theResult] = result; theExcp = excp; loop.quit(); }); loop.run(); JSUnit.assertNull(theExcp); JSUnit.assertNotNull(theResult); // verify the fractional part was dropped off int JSUnit.assertEquals(11, theResult['anInteger'].deep_unpack()); // and not dropped off a double JSUnit.assertEquals(10.5, theResult['aDoubleBeforeAndAfter'].deep_unpack()); // this JSUnit.assertion is useless, it will work // anyway if the result is really an int, // but it at least checks we didn't lose data JSUnit.assertEquals(10.0, theResult['aDouble'].deep_unpack()); } function testFinalize() { // Not really needed, but if we don't cleanup // memory checking will complain Gio.DBus.session.unown_name(own_name_id); } JSUnit.gjstestRun(this, JSUnit.setUp, JSUnit.tearDown); cjs-2.8.0/installed-tests/js/testLang.js0000664000175000017500000000700112610172032017066 0ustar fabiofabio// tests for imports.lang module const JSUnit = imports.jsUnit; const Lang = imports.lang; function testCountProperties() { var foo = { 'a' : 10, 'b' : 11 }; JSUnit.assertEquals("number of props", 2, Lang.countProperties(foo)); } function testCopyProperties() { var foo = { 'a' : 10, 'b' : 11 }; var bar = {}; Lang.copyProperties(foo, bar); JSUnit.assertTrue("a in bar", ('a' in bar)); JSUnit.assertTrue("b in bar", ('b' in bar)); JSUnit.assertEquals("a is 10", 10, bar.a); JSUnit.assertEquals("b is 11", 11, bar.b); JSUnit.assertEquals("2 items in bar", 2, Lang.countProperties(bar)); } function testCopyPublicProperties() { var foo = { 'a' : 10, 'b' : 11, '_c' : 12 }; var bar = {}; Lang.copyPublicProperties(foo, bar); JSUnit.assertTrue("a in bar", ('a' in bar)); JSUnit.assertTrue("b in bar", ('b' in bar)); JSUnit.assertFalse("_c in bar", ('_c' in bar)); JSUnit.assertEquals("a is 10", 10, bar.a); JSUnit.assertEquals("b is 11", 11, bar.b); JSUnit.assertEquals("2 items in bar", 2, Lang.countProperties(bar)); } function testCopyGetterSetterProperties() { var foo = { 'a' : 10, 'b' : 11, get c() { return this.a; }, set c(n) { this.a = n; }}; var bar = {}; Lang.copyProperties(foo, bar); let getterFunc = bar.__lookupGetter__("c"); let setterFunc = bar.__lookupSetter__("c"); // this should return the value of 'a' let c = bar.c; // this should set 'a' value bar.c = 13; JSUnit.assertTrue("bar has 'c' getter", (getterFunc != null)); JSUnit.assertTrue("bar has 'c' setter", (setterFunc != null)); JSUnit.assertTrue("bar 'c' value is 10", (c == 10)); JSUnit.assertTrue("bar 'a' new value is 13", (bar.a == 13)); } function testBind() { function Obj() { } Obj.prototype = { callback: function() { this.obj = this; this.args = arguments; return true; } }; let callback; let o = new Obj(); callback = Lang.bind(o, o.callback); JSUnit.assertEquals(callback(), true); JSUnit.assertNotEquals("o.obj in callback", undefined, o.obj); JSUnit.assertEquals("o.obj in callback", o, o.obj); JSUnit.assertEquals("o.args in callback", 0, o.args.length); JSUnit.assertRaises(function() { return Lang.bind(o, undefined); }); JSUnit.assertRaises(function() { return Lang.bind(undefined, function() {}); }); let o2 = new Obj(); callback = Lang.bind(o2, o2.callback, 42, 1138); JSUnit.assertEquals(callback(), true); JSUnit.assertNotEquals("o2.args in callback", undefined, o2.args); JSUnit.assertEquals("o2.args.length in callback", 2, o2.args.length); JSUnit.assertEquals("o2.args[0] in callback", 42, o2.args[0]); JSUnit.assertEquals("o2.args[1] in callback", 1138, o2.args[1]); let o3 = new Obj(); callback = Lang.bind(o3, o3.callback, 42, 1138); JSUnit.assertEquals(callback(1, 2, 3), true); JSUnit.assertNotEquals("o3.args in callback", undefined, o3.args); JSUnit.assertEquals("o3.args.length in callback", 5, o3.args.length); JSUnit.assertEquals("o3.args[0] in callback", 1, o3.args[0]); JSUnit.assertEquals("o3.args[1] in callback", 2, o3.args[1]); JSUnit.assertEquals("o3.args[2] in callback", 3, o3.args[2]); JSUnit.assertEquals("o3.args[3] in callback", 42, o3.args[3]); JSUnit.assertEquals("o3.args[4] in callback", 1138, o3.args[4]); } JSUnit.gjstestRun(this, JSUnit.setUp, JSUnit.tearDown); cjs-2.8.0/installed-tests/js/testByteArray.js0000664000175000017500000000703112610172032020112 0ustar fabiofabio// tests for imports.lang module const JSUnit = imports.jsUnit; const ByteArray = imports.byteArray; const Gio = imports.gi.Gio; function testEmptyByteArray() { let a = new ByteArray.ByteArray(); JSUnit.assertEquals("length is 0 for empty array", 0, a.length); } function testInitialSizeByteArray() { let a = new ByteArray.ByteArray(10); JSUnit.assertEquals("length is 10 for initially-sized-10 array", 10, a.length); let i; for (i = 0; i < a.length; ++i) { JSUnit.assertEquals("new array initialized to zeroes", 0, a[i]); } JSUnit.assertEquals("array had proper number of elements post-construct (counting for)", 10, i); } function testAssignment() { let a = new ByteArray.ByteArray(256); JSUnit.assertEquals("length is 256 for initially-sized-256 array", 256, a.length); let i; let count; count = 0; for (i = 0; i < a.length; ++i) { JSUnit.assertEquals("new array initialized to zeroes", 0, a[i]); a[i] = 255 - i; count += 1; } JSUnit.assertEquals("set proper number of values", 256, count); count = 0; for (i = 0; i < a.length; ++i) { JSUnit.assertEquals("assignment set expected value", 255 - i, a[i]); count += 1; } JSUnit.assertEquals("checked proper number of values", 256, count); } function testAssignmentPastEnd() { let a = new ByteArray.ByteArray(); JSUnit.assertEquals("length is 0 for empty array", 0, a.length); a[2] = 5; JSUnit.assertEquals("implicitly made length 3", 3, a.length); JSUnit.assertEquals("implicitly-created zero byte", 0, a[0]); JSUnit.assertEquals("implicitly-created zero byte", 0, a[1]); JSUnit.assertEquals("stored 5 in autocreated position", 5, a[2]); } function testAssignmentToLength() { let a = new ByteArray.ByteArray(20); JSUnit.assertEquals("length is 20 for new array", 20, a.length); a.length = 5; JSUnit.assertEquals("length is 5 after setting it to 5", 5, a.length); } function testNonIntegerAssignment() { let a = new ByteArray.ByteArray(); a[0] = 5; JSUnit.assertEquals("assigning 5 gives a byte 5", 5, a[0]); a[0] = null; JSUnit.assertEquals("assigning null gives a zero byte", 0, a[0]); a[0] = 5; JSUnit.assertEquals("assigning 5 gives a byte 5", 5, a[0]); a[0] = undefined; JSUnit.assertEquals("assigning undefined gives a zero byte", 0, a[0]); a[0] = 3.14; JSUnit.assertEquals("assigning a double rounds off", 3, a[0]); } function testFromString() { let a = ByteArray.fromString('abcd'); JSUnit.assertEquals("from string 'abcd' gives length 4", 4, a.length); JSUnit.assertEquals("'a' results in 97", 97, a[0]); JSUnit.assertEquals("'b' results in 98", 98, a[1]); JSUnit.assertEquals("'c' results in 99", 99, a[2]); JSUnit.assertEquals("'d' results in 100", 100, a[3]); } function testFromArray() { let a = ByteArray.fromArray([ 1, 2, 3, 4 ]); JSUnit.assertEquals("from array [1,2,3,4] gives length 4", 4, a.length); JSUnit.assertEquals("a[0] == 1", 1, a[0]); JSUnit.assertEquals("a[1] == 2", 2, a[1]); JSUnit.assertEquals("a[2] == 3", 3, a[2]); JSUnit.assertEquals("a[3] == 4", 4, a[3]); } function testToString() { let a = new ByteArray.ByteArray(); a[0] = 97; a[1] = 98; a[2] = 99; a[3] = 100; let s = a.toString(); JSUnit.assertEquals("toString() on 4 ascii bytes gives length 4", 4, s.length); JSUnit.assertEquals("toString() gives 'abcd'", "abcd", s); } JSUnit.gjstestRun(this, JSUnit.setUp, JSUnit.tearDown); cjs-2.8.0/installed-tests/js/testGIMarshalling.js0000664000175000017500000003337712610172032020705 0ustar fabiofabioif (!('assertEquals' in this)) { /* allow running this test standalone */ imports.lang.copyPublicProperties(imports.jsUnit, this); gjstestRun = function() { return imports.jsUnit.gjstestRun(this); }; } function assertArrayEquals(expected, got) { assertEquals(expected.length, got.length); for (let i = 0; i < expected.length; i ++) { assertEquals(expected[i], got[i]); } } const GIMarshallingTests = imports.gi.GIMarshallingTests; // We use Gio and GLib to have some objects that we know exist const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Lang = imports.lang; function testCArray() { var array, sum; var result = GIMarshallingTests.init_function(null); assertEquals(result.length, 2); var success = result[0]; var newArray = result[1]; assertEquals(newArray.length, 0); array = GIMarshallingTests.array_zero_terminated_return(); assertEquals("0", array[0]); assertEquals("1", array[1]); assertEquals("2", array[2]); assertEquals(3, array.length); array = GIMarshallingTests.array_zero_terminated_return_struct(); assertEquals(3, array.length); assertEquals(42, array[0].long_); assertEquals(43, array[1].long_); assertEquals(44, array[2].long_); array = GIMarshallingTests.array_return(); assertEquals(4, array.length); assertEquals(-1, array[0]); assertEquals(0, array[1]); assertEquals(1, array[2]); assertEquals(2, array[3]); [array, sum] = GIMarshallingTests.array_return_etc(9, 5); assertEquals(14, sum); assertEquals(4, array.length); assertEquals(9, array[0]); assertEquals(0, array[1]); assertEquals(1, array[2]); assertEquals(5, array[3]); array = GIMarshallingTests.array_out(); assertEquals(4, array.length); assertEquals(-1, array[0]); assertEquals(0, array[1]); assertEquals(1, array[2]); assertEquals(2, array[3]); [array, sum] = GIMarshallingTests.array_out_etc(9, 5); assertEquals(14, sum); assertEquals(4, array.length); assertEquals(9, array[0]); assertEquals(0, array[1]); assertEquals(1, array[2]); assertEquals(5, array[3]); array = GIMarshallingTests.array_inout([-1, 0, 1, 2]); assertEquals(5, array.length); assertEquals(-2, array[0]); assertEquals(-1, array[1]); assertEquals(0, array[2]); assertEquals(1, array[3]); assertEquals(2, array[4]); [array, sum] = GIMarshallingTests.array_inout_etc(9, [-1, 0, 1, 2], 5); assertEquals(14, sum); assertEquals(5, array.length); assertEquals(9, array[0]); assertEquals(-1, array[1]); assertEquals(0, array[2]); assertEquals(1, array[3]); assertEquals(5, array[4]); GIMarshallingTests.array_string_in(["foo", "bar"]); array = []; for (var i = 0; i < 3; i++) { array[i] = new GIMarshallingTests.BoxedStruct(); array[i].long_ = i + 1; } GIMarshallingTests.array_struct_in(array); // Run twice to ensure that copies are correctly made for (transfer full) GIMarshallingTests.array_struct_take_in(array); GIMarshallingTests.array_struct_take_in(array); GIMarshallingTests.array_uint8_in ("abcd"); GIMarshallingTests.array_enum_in([GIMarshallingTests.Enum.VALUE1, GIMarshallingTests.Enum.VALUE2, GIMarshallingTests.Enum.VALUE3]); array = [-1, 0, 1, 2]; GIMarshallingTests.array_in(array); GIMarshallingTests.array_in_len_before(array); GIMarshallingTests.array_in_len_zero_terminated(array); GIMarshallingTests.array_in_guint64_len(array); GIMarshallingTests.array_in_guint8_len(array); } function testGArray() { var array; array = GIMarshallingTests.garray_int_none_return(); assertEquals(-1, array[0]); assertEquals(0, array[1]); assertEquals(1, array[2]); assertEquals(2, array[3]); array = GIMarshallingTests.garray_utf8_none_return() assertEquals("0", array[0]); assertEquals("1", array[1]); assertEquals("2", array[2]); array = GIMarshallingTests.garray_utf8_container_return() assertEquals("0", array[0]); assertEquals("1", array[1]); assertEquals("2", array[2]); array = GIMarshallingTests.garray_utf8_full_return() assertEquals("0", array[0]); assertEquals("1", array[1]); assertEquals("2", array[2]); GIMarshallingTests.garray_int_none_in([-1, 0, 1, 2]); GIMarshallingTests.garray_utf8_none_in(["0", "1", "2"]); array = GIMarshallingTests.garray_utf8_none_out(); assertEquals("0", array[0]); assertEquals("1", array[1]); assertEquals("2", array[2]); array = GIMarshallingTests.garray_utf8_container_out(); assertEquals("0", array[0]); assertEquals("1", array[1]); assertEquals("2", array[2]); array = GIMarshallingTests.garray_utf8_full_out(); assertEquals("0", array[0]); assertEquals("1", array[1]); assertEquals("2", array[2]); } function testByteArray() { var i = 0; var refByteArray = new imports.byteArray.ByteArray(); refByteArray[i++] = 0; refByteArray[i++] = 49; refByteArray[i++] = 0xFF; refByteArray[i++] = 51; var byteArray = GIMarshallingTests.bytearray_full_return(); assertEquals(refByteArray.length, byteArray.length); for (i = 0; i < refByteArray.length; i++) assertEquals(refByteArray[i], byteArray[i]); GIMarshallingTests.bytearray_none_in(refByteArray); // Another test, with a normal array, to test conversion GIMarshallingTests.bytearray_none_in([0, 49, 0xFF, 51]); } function testGBytes() { var i = 0; var refByteArray = new imports.byteArray.ByteArray(); refByteArray[i++] = 0; refByteArray[i++] = 49; refByteArray[i++] = 0xFF; refByteArray[i++] = 51; GIMarshallingTests.gbytes_none_in(refByteArray); var bytes = GIMarshallingTests.gbytes_full_return(); GIMarshallingTests.gbytes_none_in(bytes); var array = bytes.toArray(); assertEquals(array[0], 0); assertEquals(array[1], 49); assertEquals(array[2], 0xFF); assertEquals(array[3], 51); bytes = GLib.Bytes.new([0, 49, 0xFF, 51]); GIMarshallingTests.gbytes_none_in(bytes); bytes = GLib.Bytes.new("const \u2665 utf8"); GIMarshallingTests.utf8_as_uint8array_in(bytes.toArray()); bytes = GIMarshallingTests.gbytes_full_return(); array = bytes.toArray(); // Array should just be holding a ref, not a copy assertEquals(array[1], 49); array[1] = 42; // Assignment should force to GByteArray assertEquals(array[1], 42); array[1] = 49; // Flip the value back GIMarshallingTests.gbytes_none_in(array.toGBytes()); // Now convert back to GBytes bytes = GLib.Bytes.new([97, 98, 99, 100]); GIMarshallingTests.array_uint8_in(bytes.toArray()); assertRaises(function() { GIMarshallingTests.array_uint8_in(bytes); }); } function testPtrArray() { var array; GIMarshallingTests.gptrarray_utf8_none_in(["0", "1", "2"]); var refArray = ["0", "1", "2"]; assertArrayEquals(refArray, GIMarshallingTests.gptrarray_utf8_none_return()); assertArrayEquals(refArray, GIMarshallingTests.gptrarray_utf8_container_return()); assertArrayEquals(refArray, GIMarshallingTests.gptrarray_utf8_full_return()); assertArrayEquals(refArray, GIMarshallingTests.gptrarray_utf8_none_out()); assertArrayEquals(refArray, GIMarshallingTests.gptrarray_utf8_container_out()); assertArrayEquals(refArray, GIMarshallingTests.gptrarray_utf8_full_out()); } function testGValue() { assertEquals(42, GIMarshallingTests.gvalue_return()); assertEquals(42, GIMarshallingTests.gvalue_out()); GIMarshallingTests.gvalue_in(42); GIMarshallingTests.gvalue_flat_array([42, "42", true]); // gjs doesn't support native enum types // GIMarshallingTests.gvalue_in_enum(GIMarshallingTests.Enum.VALUE_3); // Test a flat GValue round-trip return let thing = GIMarshallingTests.return_gvalue_flat_array(); assertArrayEquals([42, "42", true], thing); } function testGType() { assertEquals("void", GObject.TYPE_NONE.name); assertEquals("gchararray", GObject.TYPE_STRING.name); // Make sure "name" is readonly try { GObject.TYPE_STRING.name = "foo"; } catch(e) { } assertEquals("gchararray", GObject.TYPE_STRING.name); // Make sure "name" is permanent try { delete GObject.TYPE_STRING.name; } catch(e) { } assertEquals("gchararray", GObject.TYPE_STRING.name); // Make sure "toString" works assertEquals("[object GType for 'void']", GObject.TYPE_NONE.toString()); assertEquals("[object GType for 'gchararray']", GObject.TYPE_STRING.toString()); // Marshalling tests assertEquals(GObject.TYPE_NONE, GIMarshallingTests.gtype_return()); assertEquals(GObject.TYPE_STRING, GIMarshallingTests.gtype_string_return()); GIMarshallingTests.gtype_in(GObject.TYPE_NONE); GIMarshallingTests.gtype_in(GObject.VoidType); GIMarshallingTests.gtype_string_in(GObject.TYPE_STRING); GIMarshallingTests.gtype_string_in(GObject.String); GIMarshallingTests.gtype_string_in(String); assertEquals(GObject.TYPE_NONE, GIMarshallingTests.gtype_out()); assertEquals(GObject.TYPE_STRING, GIMarshallingTests.gtype_string_out()); assertEquals(GObject.TYPE_INT, GIMarshallingTests.gtype_inout(GObject.TYPE_NONE)); } function testGTypePrototype() { assertNull(GIRepositoryGType.name); assertEquals("[object GType prototype]", GIRepositoryGType.toString()); } function testGValueGType() { // test that inferring the GType for a primitive value or an object works // Primitives (and primitive like) GIMarshallingTests.gvalue_in_with_type(42, GObject.TYPE_INT); GIMarshallingTests.gvalue_in_with_type(42.5, GObject.TYPE_DOUBLE); GIMarshallingTests.gvalue_in_with_type('42', GObject.TYPE_STRING); GIMarshallingTests.gvalue_in_with_type(GObject.TYPE_GTYPE, GObject.TYPE_GTYPE); GIMarshallingTests.gvalue_in_with_type(42, GObject.Int); GIMarshallingTests.gvalue_in_with_type(42.5, GObject.Double); GIMarshallingTests.gvalue_in_with_type(42.5, Number); // Object and interface GIMarshallingTests.gvalue_in_with_type(new Gio.SimpleAction, Gio.SimpleAction); GIMarshallingTests.gvalue_in_with_type(new Gio.SimpleAction, GObject.Object); GIMarshallingTests.gvalue_in_with_type(new Gio.SimpleAction, GObject.TYPE_OBJECT); GIMarshallingTests.gvalue_in_with_type(new Gio.SimpleAction, Gio.SimpleAction); // Boxed and union GIMarshallingTests.gvalue_in_with_type(new GLib.KeyFile, GLib.KeyFile); GIMarshallingTests.gvalue_in_with_type(new GLib.KeyFile, GObject.TYPE_BOXED); GIMarshallingTests.gvalue_in_with_type(GLib.Variant.new('u', 42), GLib.Variant); GIMarshallingTests.gvalue_in_with_type(GLib.Variant.new('u', 42), GObject.TYPE_VARIANT); GIMarshallingTests.gvalue_in_with_type(new GIMarshallingTests.BoxedStruct, GIMarshallingTests.BoxedStruct); GIMarshallingTests.gvalue_in_with_type(GIMarshallingTests.union_returnv(), GIMarshallingTests.Union); // Other GIMarshallingTests.gvalue_in_with_type(GObject.ParamSpec.string('my-param', '', '', GObject.ParamFlags.READABLE, ''), GObject.TYPE_PARAM); // Foreign let Cairo; try { Cairo = imports.cairo; } catch(e) { return; } let surface = new Cairo.ImageSurface(Cairo.Format.ARGB32, 2, 2); let cr = new Cairo.Context(surface); GIMarshallingTests.gvalue_in_with_type(cr, Cairo.Context); GIMarshallingTests.gvalue_in_with_type(surface, Cairo.Surface); } function callback_return_value_only() { return 42; } function callback_one_out_parameter() { return 43; } function callback_multiple_out_parameters() { return [44, 45]; } function callback_return_value_and_one_out_parameter() { return [46, 47]; } function callback_return_value_and_multiple_out_parameters() { return [48, 49, 50]; } function testCallbacks() { let a, b, c; a = GIMarshallingTests.callback_return_value_only(callback_return_value_only); assertEquals(42, a); a = GIMarshallingTests.callback_one_out_parameter(callback_one_out_parameter); assertEquals(43, a); [a, b] = GIMarshallingTests.callback_multiple_out_parameters(callback_multiple_out_parameters); assertEquals(44, a); assertEquals(45, b); [a, b] = GIMarshallingTests.callback_return_value_and_one_out_parameter(callback_return_value_and_one_out_parameter); assertEquals(46, a); assertEquals(47, b); [a, b, c] = GIMarshallingTests.callback_return_value_and_multiple_out_parameters(callback_return_value_and_multiple_out_parameters); assertEquals(48, a); assertEquals(49, b); assertEquals(50, c); } const VFuncTester = new Lang.Class({ Name: 'VFuncTester', Extends: GIMarshallingTests.Object, vfunc_vfunc_return_value_only: callback_return_value_only, vfunc_vfunc_one_out_parameter: callback_one_out_parameter, vfunc_vfunc_multiple_out_parameters: callback_multiple_out_parameters, vfunc_vfunc_return_value_and_one_out_parameter: callback_return_value_and_one_out_parameter, vfunc_vfunc_return_value_and_multiple_out_parameters: callback_return_value_and_multiple_out_parameters }); function testVFuncs() { let tester = new VFuncTester(); let a, b, c; a = tester.vfunc_return_value_only(); assertEquals(42, a); a = tester.vfunc_one_out_parameter(); assertEquals(43, a); [a, b] = tester.vfunc_multiple_out_parameters(); assertEquals(44, a); assertEquals(45, b); [a, b] = tester.vfunc_return_value_and_one_out_parameter(); assertEquals(46, a); assertEquals(47, b); [a, b, c] = tester.vfunc_return_value_and_multiple_out_parameters(); assertEquals(48, a); assertEquals(49, b); assertEquals(50, c); } function testInterfaces() { let ifaceImpl = new GIMarshallingTests.InterfaceImpl(); let itself = ifaceImpl.get_as_interface(); assertEquals(ifaceImpl, itself); } gjstestRun(); cjs-2.8.0/installed-tests/js/testFormat.js0000664000175000017500000000404312610172032017440 0ustar fabiofabio// tests for imports.format module const Format = imports.format; const JSUnit = imports.jsUnit; function testEscape() { var foo = '%d%%'.format(10); JSUnit.assertEquals("escaped '%'", "10%", foo); } function testStrings() { var foo = '%s'.format("Foo"); var foobar = '%s %s'.format("Foo", "Bar"); var barfoo = '%2$s %1$s'.format("Foo", "Bar"); JSUnit.assertEquals("single string argument", "Foo", foo); JSUnit.assertEquals("two string arguments", "Foo Bar", foobar); JSUnit.assertEquals("two swapped string arguments", "Bar Foo", barfoo); } function testFixedNumbers() { var foo = '%d'.format(42); var bar = '%x'.format(42); JSUnit.assertEquals("base-10 42", "42", foo); JSUnit.assertEquals("base-16 42", "2a", bar); } function testFloating() { var foo = '%f'.format(0.125); var bar = '%.2f'.format(0.125); JSUnit.assertEquals("0.125, no precision", "0.125", foo); JSUnit.assertEquals("0.125, precision 2", "0.13", bar); } function testPadding() { let zeroFormat = '%04d'; var foo1 = zeroFormat.format(1); var foo10 = zeroFormat.format(10); var foo100 = zeroFormat.format(100); let spaceFormat = '%4d'; var bar1 = spaceFormat.format(1); var bar10 = spaceFormat.format(10); var bar100 = spaceFormat.format(100); JSUnit.assertEquals("zero-padding 1", "0001", foo1); JSUnit.assertEquals("zero-padding 10", "0010", foo10); JSUnit.assertEquals("zero-padding 100", "0100", foo100); JSUnit.assertEquals("space-padding 1", " 1", bar1); JSUnit.assertEquals("space-padding 10", " 10", bar10); JSUnit.assertEquals("space-padding 100", " 100", bar100); } function testErrors() { JSUnit.assertRaises(function() { return '%z'.format(42); }); JSUnit.assertRaises(function() { return '%.2d'.format(42); }); JSUnit.assertRaises(function() { return '%Ix'.format(42); }); JSUnit.assertRaises(function() { return '%2$d %d %1$d'.format(1, 2, 3); }); } String.prototype.format = Format.format; JSUnit.gjstestRun(this, JSUnit.setUp, JSUnit.tearDown); cjs-2.8.0/installed-tests/js/test0020importer.js0000664000175000017500000000030612610172032020351 0ustar fabiofabioconst JSUnit = imports.jsUnit; function testImporter1() { var GLib = imports.gi.GLib; JSUnit.assertEquals(GLib.MAJOR_VERSION, 2); } JSUnit.gjstestRun(this, JSUnit.setUp, JSUnit.tearDown); cjs-2.8.0/installed-tests/js/testCoverage.js0000664000175000017500000007470712610172032017761 0ustar fabiofabioconst JSUnit = imports.jsUnit; const Coverage = imports.coverage; function parseScriptForExpressionLines(script) { const ast = Reflect.parse(script); return Coverage.expressionLinesForAST(ast); } function assertArrayEquals(actual, expected, assertion) { if (actual.length != expected.length) throw "Arrays not equal length. Actual array was " + actual.length + " and Expected array was " + expected.length; for (let i = 0; i < actual.length; i++) { assertion(expected[i], actual[i]); } } function testExpressionLinesFoundForAssignmentExpressionSides() { let foundLinesOnBothExpressionSides = parseScriptForExpressionLines("var x;\n" + "x = (function() {\n" + " return 10;\n" + "})();\n"); assertArrayEquals(foundLinesOnBothExpressionSides, [1, 2, 3], JSUnit.assertEquals); } function testExpressionLinesFoundForLinesInsideFunctions() { let foundLinesInsideNamedFunction = parseScriptForExpressionLines("function f(a, b) {\n" + " let x = a;\n" + " let y = b;\n" + " return x + y;\n" + "}\n" + "\n" + "var z = f(1, 2);\n"); assertArrayEquals(foundLinesInsideNamedFunction, [2, 3, 4, 7], JSUnit.assertEquals); } function testExpressionLinesFoundForLinesInsideAnonymousFunctions() { let foundLinesInsideAnonymousFunction = parseScriptForExpressionLines("var z = (function f(a, b) {\n" + " let x = a;\n" + " let y = b;\n" + " return x + y;\n" + " })();\n"); assertArrayEquals(foundLinesInsideAnonymousFunction, [1, 2, 3, 4], JSUnit.assertEquals); } function testExpressionLinesFoundForBodyOfFunctionProperty() { let foundLinesInsideFunctionProperty = parseScriptForExpressionLines("var o = {\n" + " foo: function () {\n" + " let x = a;\n" + " }\n" + "};\n"); assertArrayEquals(foundLinesInsideFunctionProperty, [1, 2, 3], JSUnit.assertEquals); } function testExpressionLinesFoundForCallArgsOfFunctionProperty() { let foundLinesInsideCallArgs = parseScriptForExpressionLines("function f(a) {\n" + "}\n" + "f({\n" + " foo: function() {\n" + " let x = a;\n" + " }\n" + "});\n"); assertArrayEquals(foundLinesInsideCallArgs, [1, 3, 4, 5], JSUnit.assertEquals); } function testExpressionLinesFoundForMultilineCallArgs() { let foundLinesInsideMultilineCallArgs = parseScriptForExpressionLines("function f(a, b, c) {\n" + "}\n" + "f(1,\n" + " 2,\n" + " 3);\n"); assertArrayEquals(foundLinesInsideMultilineCallArgs, [1, 3, 4, 5], JSUnit.assertEquals); } function testExpressionLinesFoundForNewCallWithObject() { let foundLinesInsideObjectCallArg = parseScriptForExpressionLines("function f(o) {\n" + "}\n" + "let obj = {\n" + " Name: new f({ a: 1,\n" + " b: 2,\n" + " c: 3\n" + " })\n" + "}\n"); assertArrayEquals(foundLinesInsideObjectCallArg, [1, 3, 4, 5, 6], JSUnit.assertEquals); } function testExpressionLinesFoundForWhileLoop() { let foundLinesInsideWhileLoop = parseScriptForExpressionLines("var a = 0;\n" + "while (a < 1) {\n" + " let x = 0;\n" + " let y = 1;\n" + " a++;" + "\n" + "}\n"); assertArrayEquals(foundLinesInsideWhileLoop, [1, 2, 3, 4, 5], JSUnit.assertEquals); } function testExpressionLinesFoundForTryCatchFinally() { let foundLinesInsideTryCatchFinally = parseScriptForExpressionLines("var a = 0;\n" + "try {\n" + " a++;\n" + "} catch (e) {\n" + " a++;\n" + "} finally {\n" + " a++;\n" + "}\n"); assertArrayEquals(foundLinesInsideTryCatchFinally, [1, 2, 3, 4, 5, 7], JSUnit.assertEquals); } function testExpressionLinesFoundForCaseStatements() { let foundLinesInsideCaseStatements = parseScriptForExpressionLines("var a = 0;\n" + "switch (a) {\n" + "case 1:\n" + " a++;\n" + " break;\n" + "case 2:\n" + " a++;\n" + " break;\n" + "}\n"); assertArrayEquals(foundLinesInsideCaseStatements, [1, 2, 4, 5, 7, 8], JSUnit.assertEquals); } function testExpressionLinesFoundForLoop() { let foundLinesInsideLoop = parseScriptForExpressionLines("for (let i = 0; i < 1; i++) {\n" + " let x = 0;\n" + " let y = 1;\n" + "\n" + "}\n"); assertArrayEquals(foundLinesInsideLoop, [1, 2, 3], JSUnit.assertEquals); } function testExpressionLinesFoundForIfExits() { let foundLinesInsideIfExits = parseScriptForExpressionLines("if (1 > 0) {\n" + " let i = 0;\n" + "} else {\n" + " let j = 1;\n" + "}\n"); assertArrayEquals(foundLinesInsideIfExits, [1, 2, 4], JSUnit.assertEquals); } function testExpressionLinesFoundForFirstLineOfMultilineIfTests() { let foundLinesInsideMultilineIfTest = parseScriptForExpressionLines("if (1 > 0 &&\n" + " 2 > 0 &&\n" + " 3 > 0){\n" + " let a = 3;\n" + "}\n"); assertArrayEquals(foundLinesInsideMultilineIfTest, [1, 4], JSUnit.assertEquals); } function testExpressionLinesFoundForObjectPropertyLiterals() { let foundLinesInsideObjectPropertyLiterals = parseScriptForExpressionLines("var a = {\n" + " Name: 'foo',\n" + " Ex: 'bar'\n" + "};\n"); assertArrayEquals(foundLinesInsideObjectPropertyLiterals, [1, 2, 3], JSUnit.assertEquals); } function testExpressionLinesFoundForObjectPropertyFunction() { let foundLinesInsideObjectPropertyFunction = parseScriptForExpressionLines("var a = {\n" + " Name: function() {},\n" + "};\n"); assertArrayEquals(foundLinesInsideObjectPropertyFunction, [1, 2], JSUnit.assertEquals); } function testExpressionLinesFoundForObjectPropertyObjectExpression() { let foundLinesInsideObjectPropertyObjectExpression = parseScriptForExpressionLines("var a = {\n" + " Name: {},\n" + "};\n"); assertArrayEquals(foundLinesInsideObjectPropertyObjectExpression, [1, 2], JSUnit.assertEquals); } function testExpressionLinesFoundForObjectPropertyArrayExpression() { let foundLinesInsideObjectPropertyObjectExpression = parseScriptForExpressionLines("var a = {\n" + " Name: [],\n" + "};\n"); assertArrayEquals(foundLinesInsideObjectPropertyObjectExpression, [1, 2], JSUnit.assertEquals); } function testExpressionLinesFoundForObjectArgsToReturn() { let foundLinesInsideObjectArgsToReturn = parseScriptForExpressionLines("var a = {\n" + " Name: {},\n" + "};\n"); assertArrayEquals(foundLinesInsideObjectArgsToReturn, [1, 2], JSUnit.assertEquals); } function testExpressionLinesFoundForObjectArgsToThrow() { let foundLinesInsideObjectArgsToThrow = parseScriptForExpressionLines("function f() {\n" + " throw {\n" + " a: 1,\n" + " b: 2\n" + " }\n" + "}\n"); assertArrayEquals(foundLinesInsideObjectArgsToThrow, [2, 3, 4], JSUnit.assertEquals); } function parseScriptForFunctionNames(script) { const ast = Reflect.parse(script); return Coverage.functionsForAST(ast); } function functionDeclarationsEqual(actual, expected) { JSUnit.assertEquals(expected.name, actual.name); JSUnit.assertEquals(expected.line, actual.line); JSUnit.assertEquals(expected.n_params, actual.n_params); } function testFunctionsFoundForDeclarations() { let foundFunctionDeclarations = parseScriptForFunctionNames("function f1() {}\n" + "function f2() {}\n" + "function f3() {}\n"); assertArrayEquals(foundFunctionDeclarations, [ { name: "f1", line: 1, n_params: 0 }, { name: "f2", line: 2, n_params: 0 }, { name: "f3", line: 3, n_params: 0 } ], functionDeclarationsEqual); } function testFunctionsFoundForNestedFunctions() { let foundFunctions = parseScriptForFunctionNames("function f1() {\n" + " let f2 = function() {\n" + " let f3 = function() {\n" + " }\n" + " }\n" + "}\n"); assertArrayEquals(foundFunctions, [ { name: "f1", line: 1, n_params: 0 }, { name: null, line: 2, n_params: 0 }, { name: null, line: 3, n_params: 0 } ], functionDeclarationsEqual); } function testFunctionsFoundOnSameLineButDifferentiatedOnArgs() { /* Note the lack of newlines. This is all on * one line */ let foundFunctionsOnSameLine = parseScriptForFunctionNames("function f1() {" + " return (function (a) {" + " return function (a, b) {}" + " });" + "}"); assertArrayEquals(foundFunctionsOnSameLine, [ { name: "f1", line: 1, n_params: 0 }, { name: null, line: 1, n_params: 1 }, { name: null, line: 1, n_params: 2 } ], functionDeclarationsEqual); } function parseScriptForBranches(script) { const ast = Reflect.parse(script); return Coverage.branchesForAST(ast); } function branchInfoEqual(actual, expected) { JSUnit.assertEquals(expected.point, actual.point); assertArrayEquals(expected.exits, actual.exits, JSUnit.assertEquals); } function testBothBranchExitsFoundForSimpleBranch() { let foundBranchExitsForSimpleBranch = parseScriptForBranches("if (1) {\n" + " let a = 1;\n" + "} else {\n" + " let b = 2;\n" + "}\n"); assertArrayEquals(foundBranchExitsForSimpleBranch, [ { point: 1, exits: [2, 4] } ], branchInfoEqual); } function testSingleExitFoundForBranchWithOneConsequent() { let foundBranchExitsForSingleConsequentBranch = parseScriptForBranches("if (1) {\n" + " let a = 1.0;\n" + "}\n"); assertArrayEquals(foundBranchExitsForSingleConsequentBranch, [ { point: 1, exits: [2] } ], branchInfoEqual); } function testMultipleBranchesFoundForNestedIfElseBranches() { let foundBranchesForNestedIfElseBranches = parseScriptForBranches("if (1) {\n" + " let a = 1.0;\n" + "} else if (2) {\n" + " let b = 2.0;\n" + "} else if (3) {\n" + " let c = 3.0;\n" + "} else {\n" + " let d = 4.0;\n" + "}\n"); assertArrayEquals(foundBranchesForNestedIfElseBranches, [ /* the 'else if' line is actually an * exit for the first branch */ { point: 1, exits: [2, 3] }, { point: 3, exits: [4, 5] }, /* 'else' by itself is not executable, * it is the block it contains whcih * is */ { point: 5, exits: [6, 8] } ], branchInfoEqual); } function testSimpleTwoExitBranchWithoutBlocks() { let foundBranches = parseScriptForBranches("let a, b;\n" + "if (1)\n" + " a = 1.0\n" + "else\n" + " b = 2.0\n" + "\n"); assertArrayEquals(foundBranches, [ { point: 2, exits: [3, 5] } ], branchInfoEqual); } function testNoBranchFoundIfConsequentWasEmpty() { let foundBranches = parseScriptForBranches("let a, b;\n" + "if (1) {}\n"); assertArrayEquals(foundBranches, [], branchInfoEqual); } function testSingleExitFoundIfOnlyAlternateExitDefined() { let foundBranchesForOnlyAlternateDefinition = parseScriptForBranches("let a, b;\n" + "if (1) {}\n" + "else\n" + " a++;\n"); assertArrayEquals(foundBranchesForOnlyAlternateDefinition, [ { point: 2, exits: [4] } ], branchInfoEqual); } function testImplicitBranchFoundForWhileStatement() { let foundBranchesForWhileStatement = parseScriptForBranches("while (1) {\n" + " let a = 1;\n" + "}\n" + "let b = 2;"); assertArrayEquals(foundBranchesForWhileStatement, [ { point: 1, exits: [2] } ], branchInfoEqual); } function testImplicitBranchFoundForDoWhileStatement() { let foundBranchesForDoWhileStatement = parseScriptForBranches("do {\n" + " let a = 1;\n" + "} while (1)\n" + "let b = 2;"); assertArrayEquals(foundBranchesForDoWhileStatement, [ /* For do-while loops the branch-point is * at the 'do' condition and not the * 'while' */ { point: 1, exits: [2] } ], branchInfoEqual); } function testAllExitsFoundForCaseStatements() { let foundExitsInCaseStatement = parseScriptForBranches("let a = 1;\n" + "switch (1) {\n" + "case '1':\n" + " a++;\n" + " break;\n" + "case '2':\n" + " a++\n" + " break;\n" + "default:\n" + " a++\n" + " break;\n" + "}\n"); assertArrayEquals(foundExitsInCaseStatement, [ /* There are three potential exits here */ { point: 2, exits: [4, 7, 10] } ], branchInfoEqual); } function testAllExitsFoundForFallthroughCaseStatements() { let foundExitsInCaseStatement = parseScriptForBranches("let a = 1;\n" + "switch (1) {\n" + "case '1':\n" + "case 'a':\n" + "case 'b':\n" + " a++;\n" + " break;\n" + "case '2':\n" + " a++\n" + " break;\n" + "default:\n" + " a++\n" + " break;\n" + "}\n"); assertArrayEquals(foundExitsInCaseStatement, [ /* There are three potential exits here */ { point: 2, exits: [6, 9, 12] } ], branchInfoEqual); } function testAllNoExitsFoundForCaseStatementsWithNoopLabels() { let foundExitsInCaseStatement = parseScriptForBranches("let a = 1;\n" + "switch (1) {\n" + "case '1':\n" + "case '2':\n" + "default:\n" + "}\n"); assertArrayEquals(foundExitsInCaseStatement, [], branchInfoEqual); } function testGetNumberOfLinesInScript() { let script = "\n\n"; let number = Coverage._getNumberOfLinesForScript(script); JSUnit.assertEquals(3, number); } function testZeroExpressionLinesToCounters() { let expressionLines = []; let nLines = 1; let counters = Coverage._expressionLinesToCounters(expressionLines, nLines); assertArrayEquals([undefined], counters, JSUnit.assertEquals); } function testSingleExpressionLineToCounters() { let expressionLines = [1, 2]; let nLines = 4; let counters = Coverage._expressionLinesToCounters(expressionLines, nLines); assertArrayEquals([undefined, 0, 0, undefined], counters, JSUnit.assertEquals); } const MockFoundBranches = [ { point: 5, exits: [6, 8] }, { point: 1, exits: [2, 4] } ]; const MockNLines = 9; function testGetsSameNumberOfCountersAsNLines() { let counters = Coverage._branchesToBranchCounters(MockFoundBranches, MockNLines); JSUnit.assertEquals(MockNLines, counters.length); } function testEmptyArrayReturnedForNoBranches() { let counters = Coverage._branchesToBranchCounters([], 1); assertArrayEquals([undefined], counters, JSUnit.assertEquals); } function testBranchesOnLinesForArrayIndicies() { let counters = Coverage._branchesToBranchCounters(MockFoundBranches, MockNLines); JSUnit.assertNotEquals(undefined, counters[1]); JSUnit.assertNotEquals(undefined, counters[5]); } function testExitsSetForBranch() { let counters = Coverage._branchesToBranchCounters(MockFoundBranches, MockNLines); let countersForFirstBranch = counters[1]; assertArrayEquals(countersForFirstBranch.exits, [ { line: 2, hitCount: 0 }, { line: 4, hitCount: 0 } ], function(expectedExit, actualExit) { JSUnit.assertEquals(expectedExit.line, actualExit.line); JSUnit.assertEquals(expectedExit.hitCount, actualExit.hitCount); }); } function testLastExitIsSetToHighestExitStartLine() { let counters = Coverage._branchesToBranchCounters(MockFoundBranches, MockNLines); let countersForFirstBranch = counters[1]; JSUnit.assertEquals(4, countersForFirstBranch.lastExit); } function testHitIsAlwaysInitiallyFalse() { let counters = Coverage._branchesToBranchCounters(MockFoundBranches, MockNLines); let countersForFirstBranch = counters[1]; JSUnit.assertEquals(false, countersForFirstBranch.hit); } function testFunctionForKeyFromFunctionWithNameMatchesSchema() { let expectedFunctionKey = 'f:1:2'; let functionKeyForFunctionName = Coverage._getFunctionKeyFromReflectedFunction({ name: 'f', line: 1, n_params: 2 }); JSUnit.assertEquals(expectedFunctionKey, functionKeyForFunctionName); } function testFunctionKeyFromFunctionWithoutNameIsAnonymous() { let expectedFunctionKey = '(anonymous):2:3'; let functionKeyForAnonymousFunction = Coverage._getFunctionKeyFromReflectedFunction({ name: null, line: 2, n_params: 3 }); JSUnit.assertEquals(expectedFunctionKey, functionKeyForAnonymousFunction); } function testFunctionCounterMapReturnedForFunctionKeys() { let func = { name: 'name', line: 1, n_params: 0 }; let functionKey = Coverage._getFunctionKeyFromReflectedFunction(func); let functionCounters = Coverage._functionsToFunctionCounters([func]); JSUnit.assertEquals(0, functionCounters[functionKey].hitCount); } function testKnownFunctionsArrayPopulatedForFunctions() { let functions = [ { line: 1 }, { line: 2 } ]; let knownFunctionsArray = Coverage._populateKnownFunctions(functions, 4); assertArrayEquals(knownFunctionsArray, [undefined, true, true, undefined], JSUnit.assertEquals); } function testIncrementFunctionCountersForFunctionOnSameExecutionStartLine() { let functionKey = 'f:1:0'; let functionCounters = {}; functionCounters[functionKey] = { hitCount: 0 }; Coverage._incrementFunctionCounters(functionCounters, null, 'f', 1, 0); JSUnit.assertEquals(functionCounters[functionKey].hitCount, 1); } function testIncrementFunctionCountersForFunctionOnEarlierStartLine() { let functions = [ { name: 'name', line: 1, n_params: 0 } ]; let functionKey = Coverage._getFunctionKeyFromReflectedFunction(functions[0]); let knownFunctionsArray = Coverage._populateKnownFunctions(functions, 3); let functionCounters = Coverage._functionsToFunctionCounters(functions); /* We're entering at line two, but the function definition was actually * at line one */ Coverage._incrementFunctionCounters(functionCounters, knownFunctionsArray, 'name', 2, 0); JSUnit.assertEquals(functionCounters[functionKey].hitCount, 1); } function testIncrementFunctionCountersThrowsErrorOnUnexpectedFunction() { let functions = [ { name: 'name', line: 1, n_params: 0 } ]; let functionKey = Coverage._getFunctionKeyFromReflectedFunction(functions[0]); let knownFunctionsArray = Coverage._populateKnownFunctions(functions, 3); let functionCounters = Coverage._functionsToFunctionCounters(functions); /* We're entering at line two, but the function definition was actually * at line one */ JSUnit.assertRaises(function() { Coverage._incrementFunctionCounters(functionCounters, knownFunctionsArray, 'doesnotexist', 2, 0); }); } function testIncrementExpressionCountersThrowsIfLineOutOfRange() { let expressionCounters = [ undefined, 0 ]; JSUnit.assertRaises(function() { Coverage._incrementExpressionCounters(expressionCounters, 2); }); } function testIncrementExpressionCountersIncrementsIfInRange() { let expressionCounters = [ undefined, 0 ]; Coverage._incrementExpressionCounters(expressionCounters, 1); JSUnit.assertEquals(1, expressionCounters[1]); } function testWarnsIfWeHitANonExecutableLine() { let expressionCounters = [ undefined, 0, undefined ]; let wasCalledContainer = { calledWith: undefined }; let reporter = function(cont) { let container = cont; return function(line) { container.calledWith = line; }; } (wasCalledContainer); Coverage._incrementExpressionCounters(expressionCounters, 2, reporter); JSUnit.assertEquals(wasCalledContainer.calledWith, 2); JSUnit.assertEquals(expressionCounters[2], 1); } function testBranchTrackerSetsBranchToHitOnPointExecution() { let branchCounters = Coverage._branchesToBranchCounters(MockFoundBranches, MockNLines); let branchTracker = new Coverage._BranchTracker(branchCounters); branchTracker.incrementBranchCounters(1); JSUnit.assertEquals(true, branchCounters[1].hit); } function testBranchTrackerSetsExitToHitOnExecution() { let branchCounters = Coverage._branchesToBranchCounters(MockFoundBranches, MockNLines); let branchTracker = new Coverage._BranchTracker(branchCounters); branchTracker.incrementBranchCounters(1); branchTracker.incrementBranchCounters(2); JSUnit.assertEquals(1, branchCounters[1].exits[0].hitCount); } function testBranchTrackerFindsNextBranch() { let branchCounters = Coverage._branchesToBranchCounters(MockFoundBranches, MockNLines); let branchTracker = new Coverage._BranchTracker(branchCounters); branchTracker.incrementBranchCounters(1); branchTracker.incrementBranchCounters(2); branchTracker.incrementBranchCounters(5); JSUnit.assertEquals(true, branchCounters[5].hit); } function testConvertFunctionCountersToArray() { let functionsMap = {}; functionsMap['(anonymous):2:0'] = { hitCount: 1 }; functionsMap['name:1:0'] = { hitCount: 0 }; let expectedFunctionCountersArray = [ { name: '(anonymous):2:0', hitCount: 1 }, { name: 'name:1:0', hitCount: 0 } ]; let convertedFunctionCounters = Coverage._convertFunctionCountersToArray(functionsMap); assertArrayEquals(expectedFunctionCountersArray, convertedFunctionCounters, function(expected, actual) { JSUnit.assertEquals(expected.name, actual.name); JSUnit.assertEquals(expected.hitCount, actual.hitCount); }); } function testConvertFunctionCountersToArrayIsSorted() { let functionsMap = {}; functionsMap['name:1:0'] = { hitCount: 0 }; functionsMap['(anonymous):2:0'] = { hitCount: 1 }; let expectedFunctionCountersArray = [ { name: '(anonymous):2:0', hitCount: 1 }, { name: 'name:1:0', hitCount: 0 } ]; let convertedFunctionCounters = Coverage._convertFunctionCountersToArray(functionsMap); assertArrayEquals(expectedFunctionCountersArray, convertedFunctionCounters, function(expected, actual) { JSUnit.assertEquals(expected.name, actual.name); JSUnit.assertEquals(expected.hitCount, actual.hitCount); }); } const MockFiles = { 'filename': "let f = function() { return 1; };" }; const MockFilenames = Object.keys(MockFiles); Coverage.getFileContents = function(filename) { if (MockFiles[filename]) return MockFiles[filename]; throw new Error("Non existent"); }; function testCoverageStatisticsContainerFetchesValidStatisticsForFile() { let container = new Coverage.CoverageStatisticsContainer(MockFilenames); let statistics = container.fetchStatistics('filename'); JSUnit.assertNotEquals(undefined, statistics); } function testCoverageStatisticsContainerThrowsForNonExistingFile() { let container = new Coverage.CoverageStatisticsContainer(MockFilenames); JSUnit.assertRaises(function() { container.fetchStatistics('nonexistent'); }); } JSUnit.gjstestRun(this, JSUnit.setUp, JSUnit.tearDown); cjs-2.8.0/installed-tests/js/testSignals.js0000664000175000017500000001011212610172032017602 0ustar fabiofabioconst JSUnit = imports.jsUnit; const GLib = imports.gi.GLib; const Signals = imports.signals; function Foo() { this._init(); } Foo.prototype = { _init : function() { } }; Signals.addSignalMethods(Foo.prototype); function testSimple() { var foo = new Foo(); var id = foo.connect('bar', function(theFoo, a, b) { theFoo.a = a; theFoo.b = b; }); foo.emit('bar', "This is a", "This is b"); JSUnit.assertEquals("This is a", foo.a); JSUnit.assertEquals("This is b", foo.b); foo.disconnect(id); // this emission should do nothing foo.emit('bar', "Another a", "Another b"); // so these values should be unchanged JSUnit.assertEquals("This is a", foo.a); JSUnit.assertEquals("This is b", foo.b); } function testDisconnectDuringEmit() { var foo = new Foo(); var toRemove = []; var firstId = foo.connect('bar', function(theFoo) { theFoo.disconnect(toRemove[0]); theFoo.disconnect(toRemove[1]); }); var id = foo.connect('bar', function(theFoo) { throw new Error("This should not have been called 1"); }); toRemove.push(id); id = foo.connect('bar', function(theFoo) { throw new Error("This should not have been called 2"); }); toRemove.push(id); // emit signal; what should happen is that the second two handlers are // disconnected before they get invoked foo.emit('bar'); // clean up the last handler foo.disconnect(firstId); // poke in private implementation to sanity-check JSUnit.assertEquals('no handlers left', 0, foo._signalConnections.length); } function testMultipleSignals() { var foo = new Foo(); foo.barHandlersCalled = 0; foo.bonkHandlersCalled = 0; foo.connect('bar', function(theFoo) { theFoo.barHandlersCalled += 1; }); foo.connect('bonk', function(theFoo) { theFoo.bonkHandlersCalled += 1; }); foo.connect('bar', function(theFoo) { theFoo.barHandlersCalled += 1; }); foo.emit('bar'); JSUnit.assertEquals(2, foo.barHandlersCalled); JSUnit.assertEquals(0, foo.bonkHandlersCalled); foo.emit('bonk'); JSUnit.assertEquals(2, foo.barHandlersCalled); JSUnit.assertEquals(1, foo.bonkHandlersCalled); foo.emit('bar'); JSUnit.assertEquals(4, foo.barHandlersCalled); JSUnit.assertEquals(1, foo.bonkHandlersCalled); foo.disconnectAll(); // these post-disconnect emissions should do nothing foo.emit('bar'); foo.emit('bonk'); JSUnit.assertEquals(4, foo.barHandlersCalled); JSUnit.assertEquals(1, foo.bonkHandlersCalled); } function testExceptionInCallback() { let foo = new Foo(); foo.bar1Called = 0; foo.bar2Called = 0; foo.connect('bar', function(theFoo) { theFoo.bar1Called += 1; throw new Error("Exception we are throwing on purpose"); }); foo.connect('bar', function(theFoo) { theFoo.bar2Called += 1; }); // exception in callback does not effect other callbacks GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: Exception in callback for signal: *'); foo.emit('bar'); JSUnit.assertEquals(1, foo.bar1Called); JSUnit.assertEquals(1, foo.bar2Called); // exception in callback does not disconnect the callback GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: Exception in callback for signal: *'); foo.emit('bar'); JSUnit.assertEquals(2, foo.bar1Called); JSUnit.assertEquals(2, foo.bar2Called); } JSUnit.gjstestRun(this, JSUnit.setUp, JSUnit.tearDown); cjs-2.8.0/installed-tests/script.test.in0000664000175000017500000000011412610172032017143 0ustar fabiofabio[Test] Type=session Exec=cjs @pkglibexecdir@/installed-tests/scripts/@name@ cjs-2.8.0/installed-tests/scripts/0000775000175000017500000000000012610172032016024 5ustar fabiofabiocjs-2.8.0/installed-tests/scripts/testSystemExit.js0000664000175000017500000000006012610172032021374 0ustar fabiofabioconst System = imports.system; System.exit(0); cjs-2.8.0/Makefile-examples.am0000664000175000017500000000042412610172032015066 0ustar fabiofabioEXTRA_DIST += \ examples/clutter.js \ examples/gio-cat.js \ examples/gtk.js \ examples/http-server.js \ examples/test.jpg cjs-2.8.0/COPYING0000664000175000017500000000230612610172032012252 0ustar fabiofabioCopyright (c) 2008 litl, LLC Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. The following files contain code from Mozilla which is triple licensed under MPL1.1/LGPLv2+/GPLv2+: The console module (modules/console.c) Stack printer (gjs/stack.c) cjs-2.8.0/debian/0000775000175000017500000000000012610172032012440 5ustar fabiofabiocjs-2.8.0/test/0000775000175000017500000000000012610172032012175 5ustar fabiofabiocjs-2.8.0/test/run-with-dbus0000775000175000017500000001472412610172032014643 0ustar fabiofabio#! /bin/sh # Copyright 2008-2009 litl, LLC. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. SCRIPTNAME=$0 START_SYSTEM_BUS= START_SESSION_BUS= done=0 while [ "$done" != 1 ]; do case $1 in --system ) START_SYSTEM_BUS=1 ;; --session) START_SESSION_BUS=1 ;; * ) WRAPPED_SCRIPT=$1 done=1 ;; esac shift done if ! test -z "$GJS_FORCE_FAKE_SYSTEM_BUS" ; then echo "Forcing fake system bus" START_SYSTEM_BUS=1 fi die() { if ! test -z "$DBUS_SESSION_BUS_PID" ; then echo "killing message bus "$DBUS_SESSION_BUS_PID >&2 kill -9 $DBUS_SESSION_BUS_PID fi if ! test -z "$DBUS_SYSTEM_BUS_PID" ; then echo "killing message bus "$DBUS_SYSTEM_BUS_PID >&2 kill -9 $DBUS_SYSTEM_BUS_PID fi echo $SCRIPTNAME: $* >&2 exit 1 } ## convenient to be able to ctrl+C without leaking the message bus process trap 'die "Received terminal signal"' INT TERM HUP if test -z "$TOP_SRCDIR" ; then die "Must set TOP_SRCDIR" fi if test -z "$BUILDDIR" ; then die "Must set BUILDDIR" fi RANDOM=`uuidgen -r | cut -d '-' -f 3` ## this has to be set BEFORE we start session bus, ## or stuff the session bus starts won't get it if ! test -z "$START_SYSTEM_BUS" ; then DBUS_SYSTEM_BUS_ADDRESS="unix:abstract=/tmp/dbus-$USER-$$-$RANDOM-system" export DBUS_SYSTEM_BUS_ADDRESS fi STDERR_LOGFILE="$BUILDDIR"/stderr.log if test x"$XDG_DATA_HOME" != x ; then mkdir -p "$XDG_DATA_HOME"/logs || exit 1 STDERR_LOGFILE="$XDG_DATA_HOME"/logs/stderr.log fi /bin/rm -f "$STDERR_LOGFILE" SYSTEM_MONITOR_LOGFILE=`echo "$STDERR_LOGFILE" | sed -e s/stderr.log/dbus-system.log/g` if ! test -z "$START_SESSION_BUS" ; then CONFIG_FILE="$TOP_SRCDIR"/test/test-bus.conf if test x"$GJS_USE_UNINSTALLED_FILES" != x ; then NEW_CONFIG_FILE="$BUILDDIR"/uninstalled-test-bus.conf SED_RULE="s@session@session$PWD/test-services/@" if test x"$GJS_USE_FIXED_DBUS_ADDRESS" != x ; then # use fixed path (unix:abstract=/tmp/dbus-$USER-$DISPLAY) for session bus for # easy access across shells # (DISPLAY without ':' as dbus doesn't like it unescaped) SED_RULE="$SED_RULE;s@.*@unix:abstract=/tmp/dbus-$USER-${DISPLAY#:}@" else SED_RULE="$SED_RULE;s@.*@unix:abstract=/tmp/dbus-$USER-$$-$RANDOM@" fi sed -e "$SED_RULE" "$CONFIG_FILE" > "$NEW_CONFIG_FILE" CONFIG_FILE="$NEW_CONFIG_FILE" echo "Created config file $CONFIG_FILE with $BUILDDIR/test-services servicedir" fi unset DBUS_SESSION_BUS_ADDRESS unset DBUS_SESSION_BUS_PID echo "Running dbus-launch --config-file=$CONFIG_FILE" >&2 eval `dbus-launch --exit-with-session --sh-syntax --config-file=$CONFIG_FILE 2>"$STDERR_LOGFILE"` if test -z "$DBUS_SESSION_BUS_PID" ; then die "Failed to launch message bus for test script to run" fi echo "Started bus pid $DBUS_SESSION_BUS_PID at $DBUS_SESSION_BUS_ADDRESS" >&2 fi if ! test -z "$START_SYSTEM_BUS" ; then SYSTEM_CONFIG_FILE="$TOP_SRCDIR"/test/test-bus.conf if test x"$GJS_USE_UNINSTALLED_FILES" != x; then NEW_SYSTEM_CONFIG_FILE="$BUILDDIR"/uninstalled-system-test-bus.conf SED_RULE="s@session@system$BUILDDIR/test-system-services/@" # use fixed path (unix:abstract=/tmp/dbus-$USER-$DISPLAY) for session bus for # easy access across shells # (DISPLAY without ':' as dbus doesn't like it unescaped) SED_RULE="$SED_RULE;s@.*@$DBUS_SYSTEM_BUS_ADDRESS@" sed -e "$SED_RULE" "$SYSTEM_CONFIG_FILE" > "$NEW_SYSTEM_CONFIG_FILE" SYSTEM_CONFIG_FILE="$NEW_SYSTEM_CONFIG_FILE" echo "Created config file $SYSTEM_CONFIG_FILE with $BUILDDIR/test-system-services servicedir" else die "System bus makes no sense when installed, set GJS_USE_UNINSTALLED_FILES" fi echo "Running dbus-daemon --fork --print-pid --config-file=$SYSTEM_CONFIG_FILE" >&2 unset DBUS_SYSTEM_BUS_PID DBUS_SYSTEM_BUS_PID=`dbus-daemon --fork --print-pid --config-file=$SYSTEM_CONFIG_FILE 2>>"$STDERR_LOGFILE"` if test -z "$DBUS_SYSTEM_BUS_PID" ; then die "Failed to launch system bus for test script to run" fi echo "Started bus pid $DBUS_SYSTEM_BUS_PID at $DBUS_SYSTEM_BUS_ADDRESS" >&2 dbus-monitor --system "type='method_call'" "type='signal'" "type='method_return'" "type='error'" > "$SYSTEM_MONITOR_LOGFILE" 2>&1 & fi # Execute wrapped script echo "Running with dbus: $WRAPPED_SCRIPT $@ with stderr to $STDERR_LOGFILE" >&2 $WRAPPED_SCRIPT "$@" 2>>"$STDERR_LOGFILE" || "**************** script \"$WRAPPED_SCRIPT\" failed" if ! test -z "$DBUS_SESSION_BUS_PID" ; then kill -TERM $DBUS_SESSION_BUS_PID || "Message bus vanished! should not have happened" && echo "Killed daemon $DBUS_SESSION_BUS_PID" >&2 fi if ! test -z "$DBUS_SYSTEM_BUS_PID" ; then kill -TERM $DBUS_SYSTEM_BUS_PID || "Message bus vanished! should not have happened" && echo "Killed daemon $DBUS_SYSTEM_BUS_PID" >&2 fi sleep 2 ## be sure it really died if ! test -z "$DBUS_SESSION_BUS_PID" ; then kill -9 $DBUS_SESSION_BUS_PID > /dev/null 2>&1 || true fi if ! test -z "$DBUS_SYSTEM_BUS_PID" ; then kill -9 $DBUS_SYSTEM_BUS_PID > /dev/null 2>&1 || true fi exit 0 cjs-2.8.0/test/gjs-test-coverage/0000775000175000017500000000000012610172032015526 5ustar fabiofabiocjs-2.8.0/test/gjs-test-coverage/loadedJSFromResource.js0000664000175000017500000000003412610172032022102 0ustar fabiofabiofunction mock_function() {} cjs-2.8.0/test/gjs-tests-add-funcs.h0000664000175000017500000000164012610172032016134 0ustar fabiofabio/* * Copyright © 2013 Endless Mobile, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Authored By: Sam Spilsbury */ #ifndef GJS_TESTS_ADD_FUNCS_H #define GJS_TESTS_ADD_FUNCS_H void gjs_test_add_tests_for_coverage (); #endif cjs-2.8.0/test/mock-js-resources.gresource.xml0000664000175000017500000000027412610172032020272 0ustar fabiofabio test/gjs-test-coverage/loadedJSFromResource.js cjs-2.8.0/test/gjs-tests.cpp0000664000175000017500000002504512610172032014632 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include #include #include "gjs-tests-add-funcs.h" typedef struct _GjsUnitTestFixture GjsUnitTestFixture; struct _GjsUnitTestFixture { JSContext *context; GjsContext *gjs_context; }; static void test_error_reporter(JSContext *context, const char *message, JSErrorReport *report) { g_printerr("error reported by test: %s\n", message); } static void _gjs_unit_test_fixture_begin (GjsUnitTestFixture *fixture) { fixture->gjs_context = gjs_context_new (); fixture->context = (JSContext *) gjs_context_get_native_context (fixture->gjs_context); JS_BeginRequest(fixture->context); JS_SetErrorReporter(fixture->context, test_error_reporter); } static void _gjs_unit_test_fixture_finish (GjsUnitTestFixture *fixture) { JS_EndRequest(fixture->context); g_object_unref(fixture->gjs_context); } static void gjstest_test_func_gjs_context_construct_destroy(void) { GjsContext *context; /* Construct twice just to possibly a case where global state from * the first leaks. */ context = gjs_context_new (); g_object_unref (context); context = gjs_context_new (); g_object_unref (context); } static void gjstest_test_func_gjs_context_construct_eval(void) { GjsContext *context; int estatus; GError *error = NULL; context = gjs_context_new (); if (!gjs_context_eval (context, "1+1", -1, "", &estatus, &error)) g_error ("%s", error->message); g_object_unref (context); } #define N_ELEMS 15 static void gjstest_test_func_gjs_jsapi_util_array(void) { GjsUnitTestFixture fixture; JSContext *context; JSObject *global; GjsRootedArray *array; int i; jsval value; _gjs_unit_test_fixture_begin(&fixture); context = fixture.context; array = gjs_rooted_array_new(); global = JS_GetGlobalObject(context); JSCompartment *oldCompartment = JS_EnterCompartment(context, global); for (i = 0; i < N_ELEMS; i++) { value = STRING_TO_JSVAL(JS_NewStringCopyZ(context, "abcdefghijk")); gjs_rooted_array_append(context, array, value); } JS_GC(JS_GetRuntime(context)); for (i = 0; i < N_ELEMS; i++) { char *ascii; value = gjs_rooted_array_get(context, array, i); g_assert(JSVAL_IS_STRING(value)); gjs_string_to_utf8(context, value, &ascii); g_assert(strcmp(ascii, "abcdefghijk") == 0); g_free(ascii); } gjs_rooted_array_free(context, array, TRUE); JS_LeaveCompartment(context, oldCompartment); _gjs_unit_test_fixture_finish(&fixture); } #undef N_ELEMS static void gjstest_test_func_gjs_jsapi_util_string_js_string_utf8(void) { GjsUnitTestFixture fixture; JSContext *context; JSObject *global; const char *utf8_string = "\303\211\303\226 foobar \343\203\237"; char *utf8_result; jsval js_string; _gjs_unit_test_fixture_begin(&fixture); context = fixture.context; global = JS_GetGlobalObject(context); JSCompartment *oldCompartment = JS_EnterCompartment(context, global); g_assert(gjs_string_from_utf8(context, utf8_string, -1, &js_string) == JS_TRUE); g_assert(JSVAL_IS_STRING(js_string)); g_assert(gjs_string_to_utf8(context, js_string, &utf8_result) == JS_TRUE); JS_LeaveCompartment(context, oldCompartment); _gjs_unit_test_fixture_finish(&fixture); g_assert(g_str_equal(utf8_string, utf8_result)); g_free(utf8_result); } static void gjstest_test_func_gjs_stack_dump(void) { GjsContext *context; /* TODO this test could be better - maybe expose dumpstack as a JS API * so that we have a JS stack to dump? At least here we're getting some * coverage. */ context = gjs_context_new(); gjs_dumpstack(); g_object_unref(context); gjs_dumpstack(); } static void gjstest_test_func_gjs_jsapi_util_error_throw(void) { GjsUnitTestFixture fixture; JSContext *context; JSObject *global; jsval exc, value, previous; char *s = NULL; int strcmp_result; _gjs_unit_test_fixture_begin(&fixture); context = fixture.context; global = JS_GetGlobalObject(context); JSCompartment *oldCompartment = JS_EnterCompartment(context, global); /* Test that we can throw */ gjs_throw(context, "This is an exception %d", 42); g_assert(JS_IsExceptionPending(context)); exc = JSVAL_VOID; JS_GetPendingException(context, &exc); g_assert(!JSVAL_IS_VOID(exc)); value = JSVAL_VOID; JS_GetProperty(context, JSVAL_TO_OBJECT(exc), "message", &value); g_assert(JSVAL_IS_STRING(value)); gjs_string_to_utf8(context, value, &s); g_assert(s != NULL); strcmp_result = strcmp(s, "This is an exception 42"); free(s); if (strcmp_result != 0) g_error("Exception has wrong message '%s'", s); /* keep this around before we clear it */ previous = exc; JS_AddValueRoot(context, &previous); JS_ClearPendingException(context); g_assert(!JS_IsExceptionPending(context)); /* Check that we don't overwrite a pending exception */ JS_SetPendingException(context, previous); g_assert(JS_IsExceptionPending(context)); gjs_throw(context, "Second different exception %s", "foo"); g_assert(JS_IsExceptionPending(context)); exc = JSVAL_VOID; JS_GetPendingException(context, &exc); g_assert(!JSVAL_IS_VOID(exc)); g_assert(JSVAL_TO_OBJECT(exc) == JSVAL_TO_OBJECT(previous)); JS_RemoveValueRoot(context, &previous); JS_LeaveCompartment(context, oldCompartment); _gjs_unit_test_fixture_finish(&fixture); } static void gjstest_test_func_util_glib_strv_concat_null(void) { char **ret; ret = gjs_g_strv_concat(NULL, 0); g_assert(ret != NULL); g_assert(ret[0] == NULL); g_strfreev(ret); } static void gjstest_test_func_util_glib_strv_concat_pointers(void) { char *strv0[2] = {(char*)"foo", NULL}; char *strv1[1] = {NULL}; char **strv2 = NULL; char *strv3[2] = {(char*)"bar", NULL}; char **stuff[4]; char **ret; stuff[0] = strv0; stuff[1] = strv1; stuff[2] = strv2; stuff[3] = strv3; ret = gjs_g_strv_concat(stuff, 4); g_assert(ret != NULL); g_assert_cmpstr(ret[0], ==, strv0[0]); /* same string */ g_assert(ret[0] != strv0[0]); /* different pointer */ g_assert_cmpstr(ret[1], ==, strv3[0]); g_assert(ret[1] != strv3[0]); g_assert(ret[2] == NULL); g_strfreev(ret); } static void gjstest_test_strip_shebang_no_advance_for_no_shebang(void) { const char *script = "foo\nbar"; gssize script_len_original = strlen(script); gssize script_len = script_len_original; int line_number = 1; const char *stripped = gjs_strip_unix_shebang(script, &script_len, &line_number); g_assert_cmpstr(script, ==, stripped); g_assert(script_len == script_len_original); g_assert(line_number == 1); } static void gjstest_test_strip_shebang_advance_for_shebang(void) { const char *script = "#!foo\nbar"; gssize script_len_original = strlen(script); gssize script_len = script_len_original; int line_number = 1; const char *stripped = gjs_strip_unix_shebang(script, &script_len, &line_number); g_assert_cmpstr(stripped, ==, "bar"); g_assert(script_len == 3); g_assert(line_number == 2); } static void gjstest_test_strip_shebang_return_null_for_just_shebang(void) { const char *script = "#!foo"; gssize script_len_original = strlen(script); gssize script_len = script_len_original; int line_number = 1; const char *stripped = gjs_strip_unix_shebang(script, &script_len, &line_number); g_assert(stripped == NULL); g_assert(script_len == 0); g_assert(line_number == -1); } int main(int argc, char **argv) { gjs_crash_after_timeout(60*7); /* give the unit tests 7 minutes to complete */ g_test_init(&argc, &argv, NULL); g_test_add_func("/gjs/context/construct/destroy", gjstest_test_func_gjs_context_construct_destroy); g_test_add_func("/gjs/context/construct/eval", gjstest_test_func_gjs_context_construct_eval); g_test_add_func("/gjs/jsapi/util/array", gjstest_test_func_gjs_jsapi_util_array); g_test_add_func("/gjs/jsapi/util/error/throw", gjstest_test_func_gjs_jsapi_util_error_throw); g_test_add_func("/gjs/jsapi/util/string/js/string/utf8", gjstest_test_func_gjs_jsapi_util_string_js_string_utf8); g_test_add_func("/gjs/jsutil/strip_shebang/no_shebang", gjstest_test_strip_shebang_no_advance_for_no_shebang); g_test_add_func("/gjs/jsutil/strip_shebang/have_shebang", gjstest_test_strip_shebang_advance_for_shebang); g_test_add_func("/gjs/jsutil/strip_shebang/only_shebang", gjstest_test_strip_shebang_return_null_for_just_shebang); g_test_add_func("/gjs/stack/dump", gjstest_test_func_gjs_stack_dump); g_test_add_func("/util/glib/strv/concat/null", gjstest_test_func_util_glib_strv_concat_null); g_test_add_func("/util/glib/strv/concat/pointers", gjstest_test_func_util_glib_strv_concat_pointers); gjs_test_add_tests_for_coverage (); g_test_run(); return 0; } cjs-2.8.0/test/test-bus.conf0000664000175000017500000000362612610172032014621 0ustar fabiofabio session unix:tmpdir=/tmp 1000000000 1000000000 1000000000 120000 240000 100000 10000 100000 10000 50000 50000 50000 300000 cjs-2.8.0/test/gjs-test-coverage.cpp0000664000175000017500000016765512610172032016256 0ustar fabiofabio/* * Copyright © 2014 Endless Mobile, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Authored By: Sam Spilsbury */ #include #include #include #include #include #include #include #include #include #include #include #include typedef struct _GjsCoverageFixture { GjsContext *context; GjsCoverage *coverage; char *temporary_js_script_directory_name; char *temporary_js_script_filename; int temporary_js_script_open_handle; } GjsCoverageFixture; static void write_to_file(int handle, const char *contents) { if (write(handle, (gconstpointer) contents, sizeof(char) * strlen(contents)) == -1) g_error("Failed to write %s to file", contents); } static void write_to_file_at_beginning(int handle, const char *content) { if (ftruncate(handle, 0) == -1) g_print("Error deleting contents of test temporary file: %s\n", strerror(errno)); lseek(handle, 0, SEEK_SET); write_to_file(handle, content); } static int unlink_if_node_is_a_file(const char *path, const struct stat *sb, int typeflag) { if (typeflag == FTW_F) unlink(path); return 0; } static int rmdir_if_node_is_a_dir(const char *path, const struct stat *sb, int typeflag) { if (typeflag == FTW_D) rmdir(path); return 0; } static void recursive_delete_dir_at_path(const char *path) { /* We have to recurse twice - once to delete files, and once * to delete directories (because ftw uses preorder traversal) */ ftw(path, unlink_if_node_is_a_file, 100); ftw(path, rmdir_if_node_is_a_dir, 100); } static void gjs_coverage_fixture_set_up(gpointer fixture_data, gconstpointer user_data) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; const char *js_script = "function f () { return 1; }\n"; fixture->temporary_js_script_directory_name = g_strdup("/tmp/gjs_coverage_tmp.XXXXXX"); fixture->temporary_js_script_directory_name = mkdtemp (fixture->temporary_js_script_directory_name); if (!fixture->temporary_js_script_directory_name) g_error ("Failed to create temporary directory for test files: %s\n", strerror (errno)); fixture->temporary_js_script_filename = g_strconcat(fixture->temporary_js_script_directory_name, "/", "gjs_coverage_script_XXXXXX.js", NULL); fixture->temporary_js_script_open_handle = mkstemps(fixture->temporary_js_script_filename, 3); /* Allocate a strv that we can pass over to gjs_coverage_new */ const char *coverage_paths[] = { fixture->temporary_js_script_filename, NULL }; const char *search_paths[] = { fixture->temporary_js_script_directory_name, NULL }; fixture->context = gjs_context_new_with_search_path((char **) search_paths); fixture->coverage = gjs_coverage_new(coverage_paths, fixture->context); write_to_file(fixture->temporary_js_script_open_handle, js_script); } static void gjs_coverage_fixture_tear_down(gpointer fixture_data, gconstpointer user_data) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; unlink(fixture->temporary_js_script_filename); g_free(fixture->temporary_js_script_filename); close(fixture->temporary_js_script_open_handle); recursive_delete_dir_at_path(fixture->temporary_js_script_directory_name); g_free(fixture->temporary_js_script_directory_name); g_object_unref(fixture->coverage); g_object_unref(fixture->context); } typedef struct _GjsCoverageToSingleOutputFileFixture { GjsCoverageFixture base_fixture; char *output_file_directory; char *output_file_name; unsigned int output_file_handle; } GjsCoverageToSingleOutputFileFixture; static void gjs_coverage_to_single_output_file_fixture_set_up (gpointer fixture_data, gconstpointer user_data) { gjs_coverage_fixture_set_up (fixture_data, user_data); GjsCoverageToSingleOutputFileFixture *fixture = (GjsCoverageToSingleOutputFileFixture *) fixture_data; fixture->output_file_directory = g_build_filename(fixture->base_fixture.temporary_js_script_directory_name, "gjs_coverage_test_coverage.XXXXXX", NULL); fixture->output_file_directory = mkdtemp(fixture->output_file_directory); fixture->output_file_name = g_build_filename(fixture->output_file_directory, "coverage.lcov", NULL); fixture->output_file_handle = open(fixture->output_file_name, O_CREAT | O_CLOEXEC | O_RDWR, S_IRWXU); } static void gjs_coverage_to_single_output_file_fixture_tear_down (gpointer fixture_data, gconstpointer user_data) { GjsCoverageToSingleOutputFileFixture *fixture = (GjsCoverageToSingleOutputFileFixture *) fixture_data; unlink(fixture->output_file_name); close(fixture->output_file_handle); g_free(fixture->output_file_name); recursive_delete_dir_at_path(fixture->output_file_directory); g_free(fixture->output_file_directory); gjs_coverage_fixture_tear_down(fixture_data, user_data); } static const char * line_starting_with(const char *data, const char *needle) { const gsize needle_length = strlen (needle); const char *iter = data; while (iter) { if (strncmp (iter, needle, needle_length) == 0) return iter; iter = strstr (iter, "\n"); if (iter) iter += 1; } return NULL; } static char * write_statistics_and_get_coverage_data(GjsCoverage *coverage, const char *filename, const char *output_directory, gsize *coverage_data_length_return) { gjs_coverage_write_statistics(coverage, output_directory); gsize coverage_data_length; char *coverage_data_contents; char *output_filename = g_build_filename(output_directory, "coverage.lcov", NULL); g_file_get_contents(output_filename, &coverage_data_contents, &coverage_data_length, NULL); g_free(output_filename); if (coverage_data_length_return) *coverage_data_length_return = coverage_data_length; return coverage_data_contents; } static char * eval_script_and_get_coverage_data(GjsContext *context, GjsCoverage *coverage, const char *filename, const char *output_directory, gsize *coverage_data_length_return) { gjs_context_eval_file(context, filename, NULL, NULL); return write_statistics_and_get_coverage_data(coverage, filename, output_directory, coverage_data_length_return); } static gboolean coverage_data_contains_value_for_key(const char *data, const char *key, const char *value) { const char *sf_line = line_starting_with(data, key); if (!sf_line) return FALSE; return strncmp(&sf_line[strlen (key)], value, strlen (value)) == 0; } typedef gboolean (*CoverageDataMatchFunc) (const char *value, gpointer user_data); static gboolean coverage_data_matches_value_for_key_internal(const char *line, const char *key, CoverageDataMatchFunc match, gpointer user_data) { return (*match) (line, user_data); } static gboolean coverage_data_matches_value_for_key(const char *data, const char *key, CoverageDataMatchFunc match, gpointer user_data) { const char *line = line_starting_with(data, key); if (!line) return FALSE; return coverage_data_matches_value_for_key_internal(line, key, match, user_data); } static gboolean coverage_data_matches_any_value_for_key(const char *data, const char *key, CoverageDataMatchFunc match, gpointer user_data) { data = line_starting_with(data, key); while (data) { if (coverage_data_matches_value_for_key_internal(data, key, match, user_data)) return TRUE; data = line_starting_with(data + 1, key); } return FALSE; } static gboolean coverage_data_matches_values_for_key(const char *data, const char *key, gsize n, CoverageDataMatchFunc match, gpointer user_data, gsize data_size) { const char *line = line_starting_with (data, key); /* Keep matching. If we fail to match one of them then * bail out */ char *data_iterator = (char *) user_data; while (line && n > 0) { if (!coverage_data_matches_value_for_key_internal (line, key, match, (gpointer) data_iterator)) return FALSE; line = line_starting_with (line + 1, key); --n; data_iterator += data_size; } /* If n is zero then we've found all available matches */ if (n == 0) return TRUE; return FALSE; } static void test_covered_file_is_duplicated_into_output_if_resource(gpointer fixture_data, gconstpointer user_data) { GjsCoverageToSingleOutputFileFixture *fixture = (GjsCoverageToSingleOutputFileFixture *) fixture_data; const char *mock_resource_filename = "resource:///org/gnome/gjs/mock/test/gjs-test-coverage/loadedJSFromResource.js"; const char *coverage_scripts[] = { mock_resource_filename, NULL }; g_object_unref(fixture->base_fixture.context); g_object_unref(fixture->base_fixture.coverage); const char *search_paths[] = { fixture->base_fixture.temporary_js_script_directory_name, NULL }; fixture->base_fixture.context = gjs_context_new_with_search_path((char **) search_paths); fixture->base_fixture.coverage = gjs_coverage_new(coverage_scripts, fixture->base_fixture.context); gjs_context_eval_file(fixture->base_fixture.context, mock_resource_filename, NULL, NULL); gjs_coverage_write_statistics(fixture->base_fixture.coverage, fixture->output_file_directory); char *expected_temporary_js_script_file_path = g_build_filename(fixture->output_file_directory, "org/gnome/gjs/mock/test/gjs-test-coverage/loadedJSFromResource.js", NULL); GFile *file_for_expected_path = g_file_new_for_path(expected_temporary_js_script_file_path); g_assert(g_file_query_exists(file_for_expected_path, NULL) == TRUE); g_object_unref(file_for_expected_path); g_free(expected_temporary_js_script_file_path); } static void test_covered_file_is_duplicated_into_output_if_path(gpointer fixture_data, gconstpointer user_data) { GjsCoverageToSingleOutputFileFixture *fixture = (GjsCoverageToSingleOutputFileFixture *) fixture_data; gjs_context_eval_file(fixture->base_fixture.context, fixture->base_fixture.temporary_js_script_filename, NULL, NULL); gjs_coverage_write_statistics(fixture->base_fixture.coverage, fixture->output_file_directory); char *temporary_js_script_basename = g_filename_display_basename(fixture->base_fixture.temporary_js_script_filename); char *expected_temporary_js_script_file_path = g_build_filename(fixture->output_file_directory, temporary_js_script_basename, NULL); GFile *file_for_expected_path = g_file_new_for_path(expected_temporary_js_script_file_path); g_assert(g_file_query_exists(file_for_expected_path, NULL) == TRUE); g_object_unref(file_for_expected_path); g_free(expected_temporary_js_script_file_path); g_free(temporary_js_script_basename); } static void test_previous_contents_preserved(gpointer fixture_data, gconstpointer user_data) { GjsCoverageToSingleOutputFileFixture *fixture = (GjsCoverageToSingleOutputFileFixture *) fixture_data; const char *existing_contents = "existing_contents\n"; write_to_file(fixture->output_file_handle, existing_contents); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->base_fixture.context, fixture->base_fixture.coverage, fixture->base_fixture.temporary_js_script_filename, fixture->output_file_directory, NULL); g_assert(strstr(coverage_data_contents, existing_contents) != NULL); g_free(coverage_data_contents); } static void test_new_contents_written(gpointer fixture_data, gconstpointer user_data) { GjsCoverageToSingleOutputFileFixture *fixture = (GjsCoverageToSingleOutputFileFixture *) fixture_data; const char *existing_contents = "existing_contents\n"; write_to_file(fixture->output_file_handle, existing_contents); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->base_fixture.context, fixture->base_fixture.coverage, fixture->base_fixture.temporary_js_script_filename, fixture->output_file_directory, NULL); /* We have new content in the coverage data */ g_assert(strlen(existing_contents) != strlen(coverage_data_contents)); g_free(coverage_data_contents); } static void test_expected_source_file_name_written_to_coverage_data(gpointer fixture_data, gconstpointer user_data) { GjsCoverageToSingleOutputFileFixture *fixture = (GjsCoverageToSingleOutputFileFixture *) fixture_data; char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->base_fixture.context, fixture->base_fixture.coverage, fixture->base_fixture.temporary_js_script_filename, fixture->output_file_directory, NULL); char *temporary_js_script_basename = g_filename_display_basename(fixture->base_fixture.temporary_js_script_filename); char *expected_source_filename = g_build_filename(fixture->output_file_directory, temporary_js_script_basename, NULL); g_assert(coverage_data_contains_value_for_key(coverage_data_contents, "SF:", expected_source_filename)); g_free(expected_source_filename); g_free(temporary_js_script_basename); g_free(coverage_data_contents); } static void silence_log_func(const gchar *domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data) { } static void test_expected_entry_not_written_for_nonexistent_file(gpointer fixture_data, gconstpointer user_data) { GjsCoverageToSingleOutputFileFixture *fixture = (GjsCoverageToSingleOutputFileFixture *) fixture_data; const char *coverage_paths[] = { "doesnotexist", NULL }; g_object_unref(fixture->base_fixture.coverage); fixture->base_fixture.coverage = gjs_coverage_new(coverage_paths, fixture->base_fixture.context); /* Temporarily disable fatal mask and silence warnings */ GLogLevelFlags old_flags = g_log_set_always_fatal((GLogLevelFlags) G_LOG_LEVEL_ERROR); GLogFunc old_log_func = g_log_set_default_handler(silence_log_func, NULL); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->base_fixture.context, fixture->base_fixture.coverage, "doesnotexist", fixture->output_file_directory, NULL); g_log_set_always_fatal(old_flags); g_log_set_default_handler(old_log_func, NULL); char *temporary_js_script_basename = g_filename_display_basename("doesnotexist"); g_assert(!(coverage_data_contains_value_for_key(coverage_data_contents, "SF:", temporary_js_script_basename))); g_free(temporary_js_script_basename); } typedef enum _BranchTaken { NOT_EXECUTED, NOT_TAKEN, TAKEN } BranchTaken; typedef struct _BranchLineData { int expected_branch_line; int expected_id; BranchTaken taken; } BranchLineData; static gboolean branch_at_line_should_be_taken(const char *line, gpointer user_data) { BranchLineData *branch_data = (BranchLineData *) user_data; int line_no, branch_id, block_no, hit_count_num; char *hit_count = NULL; /* Advance past "BRDA:" */ line += 5; if (sscanf(line, "%i,%i,%i,%as", &line_no, &block_no, &branch_id, &hit_count) != 4) g_error("sscanf: %s", strerror(errno)); /* Determine the branch hit count. It will be either: * > -1 if the line containing the branch was never executed, or * > N times the branch was taken. * * The value of -1 is represented by a single "-" character, so * we should detect this case and set the value based on that */ if (strlen(hit_count) == 1 && *hit_count == '-') hit_count_num = -1; else hit_count_num = atoi(hit_count); /* The glibc extension to sscanf dynamically allocates hit_count, so * we need to free it here */ free(hit_count); const gboolean hit_correct_branch_line = branch_data->expected_branch_line == line_no; const gboolean hit_correct_branch_id = branch_data->expected_id == branch_id; gboolean branch_correctly_taken_or_not_taken; switch (branch_data->taken) { case NOT_EXECUTED: branch_correctly_taken_or_not_taken = hit_count_num == -1; break; case NOT_TAKEN: branch_correctly_taken_or_not_taken = hit_count_num == 0; break; case TAKEN: branch_correctly_taken_or_not_taken = hit_count_num > 0; break; default: g_assert_not_reached(); }; return hit_correct_branch_line && hit_correct_branch_id && branch_correctly_taken_or_not_taken; } static void test_single_branch_coverage_written_to_coverage_data(gpointer fixture_data, gconstpointer user_data) { GjsCoverageToSingleOutputFileFixture *fixture = (GjsCoverageToSingleOutputFileFixture *) fixture_data; const char *script_with_basic_branch = "let x = 0;\n" "if (x > 0)\n" " x++;\n" "else\n" " x++;\n"; /* We have to seek backwards and overwrite */ lseek(fixture->base_fixture.temporary_js_script_open_handle, 0, SEEK_SET); if (write(fixture->base_fixture.temporary_js_script_open_handle, (const char *) script_with_basic_branch, sizeof(char) * strlen(script_with_basic_branch)) == 0) g_error("Failed to basic branch script"); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->base_fixture.context, fixture->base_fixture.coverage, fixture->base_fixture.temporary_js_script_filename, fixture->output_file_directory, NULL); const BranchLineData expected_branches[] = { { 2, 0, NOT_TAKEN }, { 2, 1, TAKEN } }; const gsize expected_branches_len = G_N_ELEMENTS(expected_branches); /* There are two possible branches here, the second should be taken * and the first should not have been */ g_assert(coverage_data_matches_values_for_key(coverage_data_contents, "BRDA:", expected_branches_len, branch_at_line_should_be_taken, (gpointer) expected_branches, sizeof(BranchLineData))); g_assert(coverage_data_contains_value_for_key(coverage_data_contents, "BRF:", "2")); g_assert(coverage_data_contains_value_for_key(coverage_data_contents, "BRH:", "1")); g_free(coverage_data_contents); } static void test_multiple_branch_coverage_written_to_coverage_data(gpointer fixture_data, gconstpointer user_data) { GjsCoverageToSingleOutputFileFixture *fixture = (GjsCoverageToSingleOutputFileFixture *) fixture_data; const char *script_with_case_statements_branch = "let y;\n" "for (let x = 0; x < 3; x++) {\n" " switch (x) {\n" " case 0:\n" " y = x + 1;\n" " break;\n" " case 1:\n" " y = x + 1;\n" " break;\n" " case 2:\n" " y = x + 1;\n" " break;\n" " }\n" "}\n"; /* We have to seek backwards and overwrite */ lseek(fixture->base_fixture.temporary_js_script_open_handle, 0, SEEK_SET); if (write(fixture->base_fixture.temporary_js_script_open_handle, (const char *) script_with_case_statements_branch, sizeof(char) * strlen(script_with_case_statements_branch)) == 0) g_error("Failed to write script"); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->base_fixture.context, fixture->base_fixture.coverage, fixture->base_fixture.temporary_js_script_filename, fixture->output_file_directory, NULL); const BranchLineData expected_branches[] = { { 3, 0, TAKEN }, { 3, 1, TAKEN }, { 3, 2, TAKEN } }; const gsize expected_branches_len = G_N_ELEMENTS(expected_branches); /* There are two possible branches here, the second should be taken * and the first should not have been */ g_assert(coverage_data_matches_values_for_key(coverage_data_contents, "BRDA:", expected_branches_len, branch_at_line_should_be_taken, (gpointer) expected_branches, sizeof(BranchLineData))); g_free(coverage_data_contents); } static void test_branches_for_multiple_case_statements_fallthrough(gpointer fixture_data, gconstpointer user_data) { GjsCoverageToSingleOutputFileFixture *fixture = (GjsCoverageToSingleOutputFileFixture *) fixture_data; const char *script_with_case_statements_branch = "let y;\n" "for (let x = 0; x < 3; x++) {\n" " switch (x) {\n" " case 0:\n" " case 1:\n" " y = x + 1;\n" " break;\n" " case 2:\n" " y = x + 1;\n" " break;\n" " case 3:\n" " y = x +1;\n" " break;\n" " }\n" "}\n"; /* We have to seek backwards and overwrite */ lseek(fixture->base_fixture.temporary_js_script_open_handle, 0, SEEK_SET); if (write(fixture->base_fixture.temporary_js_script_open_handle, (const char *) script_with_case_statements_branch, sizeof(char) * strlen(script_with_case_statements_branch)) == 0) g_error("Failed to write script"); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->base_fixture.context, fixture->base_fixture.coverage, fixture->base_fixture.temporary_js_script_filename, fixture->output_file_directory, NULL); const BranchLineData expected_branches[] = { { 3, 0, TAKEN }, { 3, 1, TAKEN }, { 3, 2, NOT_TAKEN } }; const gsize expected_branches_len = G_N_ELEMENTS(expected_branches); /* There are two possible branches here, the second should be taken * and the first should not have been */ g_assert(coverage_data_matches_values_for_key(coverage_data_contents, "BRDA:", expected_branches_len, branch_at_line_should_be_taken, (gpointer) expected_branches, sizeof(BranchLineData))); g_free(coverage_data_contents); } static void test_branch_not_hit_written_to_coverage_data(gpointer fixture_data, gconstpointer user_data) { GjsCoverageToSingleOutputFileFixture *fixture = (GjsCoverageToSingleOutputFileFixture *) fixture_data; const char *script_with_never_executed_branch = "let x = 0;\n" "if (x > 0) {\n" " if (x > 0)\n" " x++;\n" "} else {\n" " x++;\n" "}\n"; write_to_file_at_beginning(fixture->base_fixture.temporary_js_script_open_handle, script_with_never_executed_branch); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->base_fixture.context, fixture->base_fixture.coverage, fixture->base_fixture.temporary_js_script_filename, fixture->output_file_directory, NULL); const BranchLineData expected_branch = { 3, 0, NOT_EXECUTED }; g_assert(coverage_data_matches_any_value_for_key(coverage_data_contents, "BRDA:", branch_at_line_should_be_taken, (gpointer) &expected_branch)); g_free(coverage_data_contents); } static gboolean has_function_name(const char *line, gpointer user_data) { /* User data is const char ** */ const char *expected_function_name = *((const char **) user_data); /* Advance past "FN:" */ line += 3; return strncmp(line, expected_function_name, strlen(expected_function_name)) == 0; } static void test_function_names_written_to_coverage_data(gpointer fixture_data, gconstpointer user_data) { GjsCoverageToSingleOutputFileFixture *fixture = (GjsCoverageToSingleOutputFileFixture *) fixture_data; const char *script_with_named_and_unnamed_functions = "function f(){}\n" "let b = function(){}\n"; write_to_file_at_beginning(fixture->base_fixture.temporary_js_script_open_handle, script_with_named_and_unnamed_functions); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->base_fixture.context, fixture->base_fixture.coverage, fixture->base_fixture.temporary_js_script_filename, fixture->output_file_directory, NULL); /* The internal hash table is sorted in alphabetical order * so the function names need to be in this order too */ const char * expected_function_names[] = { "(anonymous):2:0", "f:1:0" }; const gsize expected_function_names_len = G_N_ELEMENTS(expected_function_names); /* There are two possible branches here, the second should be taken * and the first should not have been */ g_assert(coverage_data_matches_values_for_key(coverage_data_contents, "FN:", expected_function_names_len, has_function_name, (gpointer) expected_function_names, sizeof(const char *))); g_free(coverage_data_contents); } typedef struct _FunctionHitCountData { const char *function; unsigned int hit_count_minimum; } FunctionHitCountData; static gboolean hit_count_is_more_than_for_function(const char *line, gpointer user_data) { FunctionHitCountData *data = (FunctionHitCountData *) user_data; char *detected_function = NULL; unsigned int hit_count; /* Advance past "FNDA:" */ line += 5; if (sscanf(line, "%u,%as", &hit_count, &detected_function) != 2) g_error("sscanf: %s", strerror(errno)); const gboolean function_name_match = g_strcmp0(data->function, detected_function) == 0; const gboolean hit_count_more_than = hit_count >= data->hit_count_minimum; /* See above, we must free detected_functon */ free(detected_function); return function_name_match && hit_count_more_than; } /* For functions with whitespace between their definition and * first executable line, its possible that the JS engine might * enter their frame a little later in the script than where their * definition starts. We need to handle that case */ static void test_function_hit_counts_for_big_functions_written_to_coverage_data(gpointer fixture_data, gconstpointer user_data) { GjsCoverageToSingleOutputFileFixture *fixture = (GjsCoverageToSingleOutputFileFixture *) fixture_data; const char *script_with_executed_functions = "function f(){\n" "\n" "\n" "var x = 1;\n" "}\n" "let b = function(){}\n" "f();\n" "b();\n"; write_to_file_at_beginning(fixture->base_fixture.temporary_js_script_open_handle, script_with_executed_functions); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->base_fixture.context, fixture->base_fixture.coverage, fixture->base_fixture.temporary_js_script_filename, fixture->output_file_directory, NULL); /* The internal hash table is sorted in alphabetical order * so the function names need to be in this order too */ FunctionHitCountData expected_hit_counts[] = { { "(anonymous):6:0", 1 }, { "f:1:0", 1 } }; const gsize expected_hit_count_len = G_N_ELEMENTS(expected_hit_counts); /* There are two possible branches here, the second should be taken * and the first should not have been */ g_assert(coverage_data_matches_values_for_key(coverage_data_contents, "FNDA:", expected_hit_count_len, hit_count_is_more_than_for_function, (gpointer) expected_hit_counts, sizeof(FunctionHitCountData))); g_free(coverage_data_contents); } /* For functions which start executing at a function declaration * we also need to make sure that we roll back to the real function, */ static void test_function_hit_counts_for_little_functions_written_to_coverage_data(gpointer fixture_data, gconstpointer user_data) { GjsCoverageToSingleOutputFileFixture *fixture = (GjsCoverageToSingleOutputFileFixture *) fixture_data; const char *script_with_executed_functions = "function f(){\n" "var x = function(){};\n" "}\n" "let b = function(){}\n" "f();\n" "b();\n"; write_to_file_at_beginning(fixture->base_fixture.temporary_js_script_open_handle, script_with_executed_functions); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->base_fixture.context, fixture->base_fixture.coverage, fixture->base_fixture.temporary_js_script_filename, fixture->output_file_directory, NULL); /* The internal hash table is sorted in alphabetical order * so the function names need to be in this order too */ FunctionHitCountData expected_hit_counts[] = { { "(anonymous):2:0", 0 }, { "(anonymous):4:0", 1 }, { "f:1:0", 1 } }; const gsize expected_hit_count_len = G_N_ELEMENTS(expected_hit_counts); /* There are two possible branches here, the second should be taken * and the first should not have been */ g_assert(coverage_data_matches_values_for_key(coverage_data_contents, "FNDA:", expected_hit_count_len, hit_count_is_more_than_for_function, (gpointer) expected_hit_counts, sizeof(FunctionHitCountData))); g_free(coverage_data_contents); } static void test_function_hit_counts_written_to_coverage_data(gpointer fixture_data, gconstpointer user_data) { GjsCoverageToSingleOutputFileFixture *fixture = (GjsCoverageToSingleOutputFileFixture *) fixture_data; const char *script_with_executed_functions = "function f(){}\n" "let b = function(){}\n" "f();\n" "b();\n"; write_to_file_at_beginning(fixture->base_fixture.temporary_js_script_open_handle, script_with_executed_functions); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->base_fixture.context, fixture->base_fixture.coverage, fixture->base_fixture.temporary_js_script_filename, fixture->output_file_directory, NULL); /* The internal hash table is sorted in alphabetical order * so the function names need to be in this order too */ FunctionHitCountData expected_hit_counts[] = { { "(anonymous):2:0", 1 }, { "f:1:0", 1 } }; const gsize expected_hit_count_len = G_N_ELEMENTS(expected_hit_counts); /* There are two possible branches here, the second should be taken * and the first should not have been */ g_assert(coverage_data_matches_values_for_key(coverage_data_contents, "FNDA:", expected_hit_count_len, hit_count_is_more_than_for_function, (gpointer) expected_hit_counts, sizeof(FunctionHitCountData))); g_free(coverage_data_contents); } static void test_total_function_coverage_written_to_coverage_data(gpointer fixture_data, gconstpointer user_data) { GjsCoverageToSingleOutputFileFixture *fixture = (GjsCoverageToSingleOutputFileFixture *) fixture_data; const char *script_with_some_executed_functions = "function f(){}\n" "let b = function(){}\n" "f();\n"; write_to_file_at_beginning(fixture->base_fixture.temporary_js_script_open_handle, script_with_some_executed_functions); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->base_fixture.context, fixture->base_fixture.coverage, fixture->base_fixture.temporary_js_script_filename, fixture->output_file_directory, NULL); /* More than one assert per test is bad, but we are testing interlinked concepts */ g_assert(coverage_data_contains_value_for_key(coverage_data_contents, "FNF:", "2")); g_assert(coverage_data_contains_value_for_key(coverage_data_contents, "FNH:", "1")); g_free(coverage_data_contents); } typedef struct _LineCountIsMoreThanData { unsigned int expected_lineno; unsigned int expected_to_be_more_than; } LineCountIsMoreThanData; static gboolean line_hit_count_is_more_than(const char *line, gpointer user_data) { LineCountIsMoreThanData *data = (LineCountIsMoreThanData *) user_data; const char *coverage_line = &line[3]; char *comma_ptr = NULL; unsigned int lineno = strtol(coverage_line, &comma_ptr, 10); g_assert(comma_ptr[0] == ','); char *end_ptr = NULL; unsigned int value = strtol(&comma_ptr[1], &end_ptr, 10); g_assert(end_ptr[0] == '\0' || end_ptr[0] == '\n'); return data->expected_lineno == lineno && value > data->expected_to_be_more_than; } static void test_single_line_hit_written_to_coverage_data(gpointer fixture_data, gconstpointer user_data) { GjsCoverageToSingleOutputFileFixture *fixture = (GjsCoverageToSingleOutputFileFixture *) fixture_data; char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->base_fixture.context, fixture->base_fixture.coverage, fixture->base_fixture.temporary_js_script_filename, fixture->output_file_directory, NULL); LineCountIsMoreThanData data = { 1, 0 }; g_assert(coverage_data_matches_value_for_key(coverage_data_contents, "DA:", line_hit_count_is_more_than, &data)); g_free(coverage_data_contents); } static void test_full_line_tally_written_to_coverage_data(gpointer fixture_data, gconstpointer user_data) { GjsCoverageToSingleOutputFileFixture *fixture = (GjsCoverageToSingleOutputFileFixture *) fixture_data; char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->base_fixture.context, fixture->base_fixture.coverage, fixture->base_fixture.temporary_js_script_filename, fixture->output_file_directory, NULL); /* More than one assert per test is bad, but we are testing interlinked concepts */ g_assert(coverage_data_contains_value_for_key(coverage_data_contents, "LF:", "1")); g_assert(coverage_data_contains_value_for_key(coverage_data_contents, "LH:", "1")); g_free(coverage_data_contents); } static void test_no_hits_to_coverage_data_for_unexecuted(gpointer fixture_data, gconstpointer user_data) { GjsCoverageToSingleOutputFileFixture *fixture = (GjsCoverageToSingleOutputFileFixture *) fixture_data; char *coverage_data_contents = write_statistics_and_get_coverage_data(fixture->base_fixture.coverage, fixture->base_fixture.temporary_js_script_filename, fixture->output_file_directory, NULL); /* More than one assert per test is bad, but we are testing interlinked concepts */ g_assert(coverage_data_contains_value_for_key(coverage_data_contents, "LF:", "1")); g_assert(coverage_data_contains_value_for_key(coverage_data_contents, "LH:", "0")); g_free(coverage_data_contents); } static void test_end_of_record_section_written_to_coverage_data(gpointer fixture_data, gconstpointer user_data) { GjsCoverageToSingleOutputFileFixture *fixture = (GjsCoverageToSingleOutputFileFixture *) fixture_data; char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->base_fixture.context, fixture->base_fixture.coverage, fixture->base_fixture.temporary_js_script_filename, fixture->output_file_directory, NULL); g_assert(strstr(coverage_data_contents, "end_of_record") != NULL); g_free(coverage_data_contents); } typedef struct _GjsCoverageMultipleSourcesFixture { GjsCoverageToSingleOutputFileFixture base_fixture; char *second_js_source_file_name; unsigned int second_gjs_source_file_handle; } GjsCoverageMultpleSourcesFixutre; static void gjs_coverage_multiple_source_files_to_single_output_fixture_set_up(gpointer fixture_data, gconstpointer user_data) { gjs_coverage_to_single_output_file_fixture_set_up (fixture_data, user_data); GjsCoverageMultpleSourcesFixutre *fixture = (GjsCoverageMultpleSourcesFixutre *) fixture_data; fixture->second_js_source_file_name = g_strconcat(fixture->base_fixture.base_fixture.temporary_js_script_directory_name, "/", "gjs_coverage_second_source_file_XXXXXX.js", NULL); fixture->second_gjs_source_file_handle = mkstemps(fixture->second_js_source_file_name, 3); /* Because GjsCoverage searches the coverage paths at object-creation time, * we need to destroy the previously constructed one and construct it again */ const char *coverage_paths[] = { fixture->base_fixture.base_fixture.temporary_js_script_filename, fixture->second_js_source_file_name, NULL }; g_object_unref(fixture->base_fixture.base_fixture.context); g_object_unref(fixture->base_fixture.base_fixture.coverage); const char *search_paths[] = { fixture->base_fixture.base_fixture.temporary_js_script_directory_name, NULL }; fixture->base_fixture.base_fixture.context = gjs_context_new_with_search_path((char **) search_paths); fixture->base_fixture.base_fixture.coverage = gjs_coverage_new(coverage_paths, fixture->base_fixture.base_fixture.context); char *base_name = g_path_get_basename(fixture->base_fixture.base_fixture.temporary_js_script_filename); char *base_name_without_extension = g_strndup(base_name, strlen(base_name) - 3); char *mock_script = g_strconcat("const FirstScript = imports.", base_name_without_extension, ";\n", "let a = FirstScript.f;\n" "\n", NULL); write_to_file_at_beginning(fixture->second_gjs_source_file_handle, mock_script); g_free(mock_script); g_free(base_name_without_extension); g_free(base_name); } static void gjs_coverage_multiple_source_files_to_single_output_fixture_tear_down(gpointer fixture_data, gconstpointer user_data) { GjsCoverageMultpleSourcesFixutre *fixture = (GjsCoverageMultpleSourcesFixutre *) fixture_data; unlink(fixture->second_js_source_file_name); g_free(fixture->second_js_source_file_name); close(fixture->second_gjs_source_file_handle); gjs_coverage_to_single_output_file_fixture_tear_down(fixture_data, user_data); } static void test_multiple_source_file_records_written_to_coverage_data (gpointer fixture_data, gconstpointer user_data) { GjsCoverageMultpleSourcesFixutre *fixture = (GjsCoverageMultpleSourcesFixutre *) fixture_data; char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->base_fixture.base_fixture.context, fixture->base_fixture.base_fixture.coverage, fixture->second_js_source_file_name, fixture->base_fixture.output_file_directory, NULL); const char *first_sf_record = line_starting_with(coverage_data_contents, "SF:"); const char *second_sf_record = line_starting_with(first_sf_record + 1, "SF:"); g_assert(first_sf_record != NULL); g_assert(second_sf_record != NULL); g_free(coverage_data_contents); } typedef struct _ExpectedSourceFileCoverageData { const char *source_file_path; LineCountIsMoreThanData *more_than; unsigned int n_more_than_matchers; const char expected_lines_hit_character; const char expected_lines_found_character; } ExpectedSourceFileCoverageData; static gboolean check_coverage_data_for_source_file(ExpectedSourceFileCoverageData *expected, const gsize expected_size, const char *section_start) { gsize i; for (i = 0; i < expected_size; ++i) { if (strncmp (§ion_start[3], expected[i].source_file_path, strlen (expected[i].source_file_path)) == 0) { const gboolean line_hits_match = coverage_data_matches_values_for_key (section_start, "DA:", expected[i].n_more_than_matchers, line_hit_count_is_more_than, expected[i].more_than, sizeof (LineCountIsMoreThanData)); const char *total_hits_record = line_starting_with (section_start, "LH:"); const gboolean total_hits_match = total_hits_record[3] == expected[i].expected_lines_hit_character; const char *total_found_record = line_starting_with (section_start, "LF:"); const gboolean total_found_match = total_found_record[3] == expected[i].expected_lines_found_character; return line_hits_match && total_hits_match && total_found_match; } } return FALSE; } static void test_correct_line_coverage_data_written_for_both_source_file_sectons(gpointer fixture_data, gconstpointer user_data) { GjsCoverageMultpleSourcesFixutre *fixture = (GjsCoverageMultpleSourcesFixutre *) fixture_data; char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->base_fixture.base_fixture.context, fixture->base_fixture.base_fixture.coverage, fixture->second_js_source_file_name, fixture->base_fixture.output_file_directory, NULL); LineCountIsMoreThanData first_script_matcher = { 1, 0 }; LineCountIsMoreThanData second_script_matchers[] = { { 1, 0 }, { 2, 0 } }; char *first_script_basename = g_filename_display_basename(fixture->base_fixture.base_fixture.temporary_js_script_filename); char *second_script_basename = g_filename_display_basename(fixture->second_js_source_file_name); char *first_script_output_path = g_build_filename(fixture->base_fixture.output_file_directory, first_script_basename, NULL); char *second_script_output_path = g_build_filename(fixture->base_fixture.output_file_directory, second_script_basename, NULL); ExpectedSourceFileCoverageData expected[] = { { first_script_output_path, &first_script_matcher, 1, '1', '1' }, { second_script_output_path, second_script_matchers, 2, '2', '2' } }; const gsize expected_len = G_N_ELEMENTS(expected); const char *first_sf_record = line_starting_with(coverage_data_contents, "SF:"); g_assert(check_coverage_data_for_source_file(expected, expected_len, first_sf_record)); const char *second_sf_record = line_starting_with(first_sf_record + 3, "SF:"); g_assert(check_coverage_data_for_source_file(expected, expected_len, second_sf_record)); g_free(first_script_basename); g_free(first_script_output_path); g_free(second_script_basename); g_free(second_script_output_path); g_free(coverage_data_contents); } typedef struct _FixturedTest { gsize fixture_size; GTestFixtureFunc set_up; GTestFixtureFunc tear_down; } FixturedTest; static void add_test_for_fixture(const char *name, FixturedTest *fixture, GTestFixtureFunc test_func, gconstpointer user_data) { g_test_add_vtable(name, fixture->fixture_size, user_data, fixture->set_up, test_func, fixture->tear_down); } void gjs_test_add_tests_for_coverage() { FixturedTest coverage_to_single_output_fixture = { sizeof(GjsCoverageToSingleOutputFileFixture), gjs_coverage_to_single_output_file_fixture_set_up, gjs_coverage_to_single_output_file_fixture_tear_down }; add_test_for_fixture("/gjs/coverage/file_duplicated_into_output_path", &coverage_to_single_output_fixture, test_covered_file_is_duplicated_into_output_if_path, NULL); add_test_for_fixture("/gjs/coverage/file_duplicated_full_resource_path", &coverage_to_single_output_fixture, test_covered_file_is_duplicated_into_output_if_resource, NULL); add_test_for_fixture("/gjs/coverage/contents_preserved_accumulate_mode", &coverage_to_single_output_fixture, test_previous_contents_preserved, NULL); add_test_for_fixture("/gjs/coverage/new_contents_appended_accumulate_mode", &coverage_to_single_output_fixture, test_new_contents_written, NULL); add_test_for_fixture("/gjs/coverage/expected_source_file_name_written_to_coverage_data", &coverage_to_single_output_fixture, test_expected_source_file_name_written_to_coverage_data, NULL); add_test_for_fixture("/gjs/coverage/entry_not_written_for_nonexistent_file", &coverage_to_single_output_fixture, test_expected_entry_not_written_for_nonexistent_file, NULL); add_test_for_fixture("/gjs/coverage/single_branch_coverage_written_to_coverage_data", &coverage_to_single_output_fixture, test_single_branch_coverage_written_to_coverage_data, NULL); add_test_for_fixture("/gjs/coverage/multiple_branch_coverage_written_to_coverage_data", &coverage_to_single_output_fixture, test_multiple_branch_coverage_written_to_coverage_data, NULL); add_test_for_fixture("/gjs/coverage/branches_for_multiple_case_statements_fallthrough", &coverage_to_single_output_fixture, test_branches_for_multiple_case_statements_fallthrough, NULL); add_test_for_fixture("/gjs/coverage/not_hit_branch_point_written_to_coverage_data", &coverage_to_single_output_fixture, test_branch_not_hit_written_to_coverage_data, NULL); add_test_for_fixture("/gjs/coverage/function_names_written_to_coverage_data", &coverage_to_single_output_fixture, test_function_names_written_to_coverage_data, NULL); add_test_for_fixture("/gjs/coverage/function_hit_counts_written_to_coverage_data", &coverage_to_single_output_fixture, test_function_hit_counts_written_to_coverage_data, NULL); add_test_for_fixture("/gjs/coverage/big_function_hit_counts_written_to_coverage_data", &coverage_to_single_output_fixture, test_function_hit_counts_for_big_functions_written_to_coverage_data, NULL); add_test_for_fixture("/gjs/coverage/little_function_hit_counts_written_to_coverage_data", &coverage_to_single_output_fixture, test_function_hit_counts_for_little_functions_written_to_coverage_data, NULL); add_test_for_fixture("/gjs/coverage/total_function_coverage_written_to_coverage_data", &coverage_to_single_output_fixture, test_total_function_coverage_written_to_coverage_data, NULL); add_test_for_fixture("/gjs/coverage/single_line_hit_written_to_coverage_data", &coverage_to_single_output_fixture, test_single_line_hit_written_to_coverage_data, NULL); add_test_for_fixture("/gjs/coverage/full_line_tally_written_to_coverage_data", &coverage_to_single_output_fixture, test_full_line_tally_written_to_coverage_data, NULL); add_test_for_fixture("/gjs/coverage/no_hits_for_unexecuted_file", &coverage_to_single_output_fixture, test_no_hits_to_coverage_data_for_unexecuted, NULL); add_test_for_fixture("/gjs/coverage/end_of_record_section_written_to_coverage_data", &coverage_to_single_output_fixture, test_end_of_record_section_written_to_coverage_data, NULL); FixturedTest coverage_for_multiple_files_to_single_output_fixture = { sizeof(GjsCoverageMultpleSourcesFixutre), gjs_coverage_multiple_source_files_to_single_output_fixture_set_up, gjs_coverage_multiple_source_files_to_single_output_fixture_tear_down }; add_test_for_fixture("/gjs/coverage/multiple_source_file_records_written_to_coverage_data", &coverage_for_multiple_files_to_single_output_fixture, test_multiple_source_file_records_written_to_coverage_data, NULL); add_test_for_fixture("/gjs/coverage/correct_line_coverage_data_written_for_both_sections", &coverage_for_multiple_files_to_single_output_fixture, test_correct_line_coverage_data_written_for_both_source_file_sectons, NULL); } cjs-2.8.0/Makefile-insttest.am0000664000175000017500000001230212610172032015123 0ustar fabiofabioEXTRA_DIST += \ installed-tests/jsunit.test.in \ installed-tests/script.test.in \ $(NULL) installedtestmetadir = $(datadir)/installed-tests/cjs installedtestmeta_DATA = if BUILDOPT_INSTALL_TESTS installedtestmeta_DATA += jsunit.test endif jsunit.test: installed-tests/jsunit.test.in Makefile sed -e s,@pkglibexecdir\@,$(pkglibexecdir), < $< > $@.tmp && mv $@.tmp $@ gjsinsttestdir = $(pkglibexecdir)/installed-tests gjsinsttest_PROGRAMS = if BUILDOPT_INSTALL_TESTS gjsinsttest_PROGRAMS += jsunit endif TEST_PROGS += jsunit TEST_INTROSPECTION_GIRS = jsunit_CPPFLAGS = $(AM_CPPFLAGS) $(GJS_CFLAGS) -DPKGLIBDIR=\"$(pkglibdir)\" -DINSTTESTDIR=\"$(gjsinsttestdir)\" jsunit_CFLAGS = $(AM_CFLAGS) $(GJS_CFLAGS) -I$(top_srcdir) jsunit_LDADD = $(GJS_LIBS) libcjs.la jsunit_LDFLAGS = -rpath $(pkglibdir) jsunit_SOURCES = installed-tests/gjs-unit.cpp privlibdir = $(pkglibdir) privlib_LTLIBRARIES = check_LTLIBRARIES = if BUILDOPT_INSTALL_TESTS privlib_LTLIBRARIES += libregress.la libwarnlib.la libgimarshallingtests.la else check_LTLIBRARIES += libregress.la libwarnlib.la libgimarshallingtests.la endif # This rpath /nowhere thing is the libtool upstream recommended way to # force generation of shared libraries, which we need in order for the # tests to work uninstalled. common_test_ldflags = -avoid-version -rpath /nowhere common_test_libadd = $(GJS_LIBS) nodist_libregress_la_SOURCES = $(GI_DATADIR)/tests/regress.c $(GI_DATADIR)/tests/regress.h libregress_la_CPPFLAGS = $(AM_CPPFLAGS) libregress_la_CFLAGS = $(GJS_CFLAGS) libregress_la_LDFLAGS = $(common_test_ldflags) libregress_la_LIBADD = $(common_test_libadd) libregress_scannerflags_includes = --include=Gio-2.0 if ENABLE_CAIRO libregress_la_CFLAGS += $(GJS_CAIRO_CFLAGS) libregress_la_LDFLAGS += $(GJS_CAIRO_LIBS) libregress_scannerflags_includes += --include=cairo-1.0 else libregress_la_CPPFLAGS += -D_GI_DISABLE_CAIRO endif nodist_libwarnlib_la_SOURCES = $(GI_DATADIR)/tests/warnlib.c $(GI_DATADIR)/tests/warnlib.h libwarnlib_la_CFLAGS = $(GJS_CFLAGS) libwarnlib_la_LDFLAGS = $(common_test_ldflags) libwarnlib_la_LIBADD = $(common_test_libadd) nodist_libgimarshallingtests_la_SOURCES = $(GI_DATADIR)/tests/gimarshallingtests.c $(GI_DATADIR)/tests/gimarshallingtests.h libgimarshallingtests_la_CFLAGS = $(GJS_CFLAGS) libgimarshallingtests_la_LDFLAGS = $(common_test_ldflags) libgimarshallingtests_la_LIBADD = $(common_test_libadd) Regress-1.0.gir: libregress.la Regress_1_0_gir_LIBS = libregress.la Regress_1_0_gir_FILES = $(nodist_libregress_la_SOURCES) Regress_1_0_gir_SCANNERFLAGS = --warn-all --warn-error $(libregress_scannerflags_includes) TEST_INTROSPECTION_GIRS += Regress-1.0.gir WarnLib-1.0.gir: libwarnlib.la WarnLib_1_0_gir_LIBS = libwarnlib.la WarnLib_1_0_gir_INCLUDES = Gio-2.0 WarnLib_1_0_gir_FILES = $(nodist_libwarnlib_la_SOURCES) WarnLib_1_0_gir_SCANNERFLAGS = --c-include="warnlib.h" --symbol-prefix=warnlib_ TEST_INTROSPECTION_GIRS += WarnLib-1.0.gir GIMarshallingTests-1.0.gir: libgimarshallingtests.la GIMarshallingTests_1_0_gir_LIBS = libgimarshallingtests.la GIMarshallingTests_1_0_gir_INCLUDES = Gio-2.0 GIMarshallingTests_1_0_gir_FILES = $(nodist_libgimarshallingtests_la_SOURCES) GIMarshallingTests_1_0_gir_SCANNERFLAGS = --symbol-prefix=gi_marshalling_tests --warn-all --warn-error TEST_INTROSPECTION_GIRS += GIMarshallingTests-1.0.gir $(foreach gir,$(TEST_INTROSPECTION_GIRS),$(eval $(call introspection-scanner,$(gir)))) gjsinsttest_DATA = noinst_DATA = if BUILDOPT_INSTALL_TESTS gjsinsttest_DATA += $(TEST_INTROSPECTION_GIRS:.gir=.typelib) else noinst_DATA += $(TEST_INTROSPECTION_GIRS:.gir=.typelib) endif CLEANFILES += $(TEST_INTROSPECTION_GIRS) $(TEST_INTROSPECTION_GIRS:.gir=.typelib) jstestsdir = $(gjsinsttestdir)/js dist_jstests_DATA = if BUILDOPT_INSTALL_TESTS dist_jstests_DATA += \ installed-tests/js/test0010basic.js \ installed-tests/js/test0020importer.js \ installed-tests/js/test0030basicBoxed.js \ installed-tests/js/test0040mainloop.js \ installed-tests/js/testself.js \ installed-tests/js/testByteArray.js \ installed-tests/js/testClass.js \ installed-tests/js/testCoverage.js \ installed-tests/js/testGDBus.js \ installed-tests/js/testEverythingBasic.js \ installed-tests/js/testEverythingEncapsulated.js \ installed-tests/js/testFormat.js \ installed-tests/js/testFundamental.js \ installed-tests/js/testGIMarshalling.js \ installed-tests/js/testGObjectClass.js \ installed-tests/js/testJS1_8.js \ installed-tests/js/testLang.js \ installed-tests/js/testLocale.js \ installed-tests/js/testMainloop.js \ installed-tests/js/testMetaClass.js \ installed-tests/js/testParamSpec.js \ installed-tests/js/testReflectObject.js \ installed-tests/js/testSignals.js \ installed-tests/js/testSystem.js \ installed-tests/js/testTweener.js \ installed-tests/js/testUnicode.js if ENABLE_CAIRO dist_jstests_DATA += installed-tests/js/testCairo.js endif %.test: installed-tests/scripts/%.js installed-tests/script.test.in Makefile sed -e s,@pkglibexecdir\@,$(pkglibexecdir), -e s,@name\@,$(notdir $<), < $(srcdir)/installed-tests/script.test.in > $@.tmp && mv $@.tmp $@ jsscripttestsdir = $(gjsinsttestdir)/scripts dist_jsscripttests_DATA = installed-tests/scripts/testSystemExit.js installedtestmeta_DATA += testSystemExit.test endif cjs-2.8.0/cjs-1.0.pc.in0000664000175000017500000000057512610172032013231 0ustar fabiofabioprefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ bindir=@bindir@ includedir=@includedir@ datarootdir=@datarootdir@ datadir=@datadir@ gjs_console=${bindir}/cjs-console Cflags: -I${includedir}/cjs-1.0 Requires: gobject-2.0 Requires.private: gobject-introspection-1.0 mozjs-24 Libs: -L${libdir} -lcjs Name: cjs-1.0 Description: JS bindings for GObjects Version: @VERSION@ cjs-2.8.0/modules/0000775000175000017500000000000012610172032012666 5ustar fabiofabiocjs-2.8.0/modules/format.js0000664000175000017500000000541212610172032014516 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const GjsPrivate = imports.gi.CjsPrivate; function vprintf(str, args) { let i = 0; let usePos = false; return str.replace(/%(?:([1-9][0-9]*)\$)?(I+)?([0-9]+)?(?:\.([0-9]+))?(.)/g, function (str, posGroup, flagsGroup, widthGroup, precisionGroup, genericGroup) { if (precisionGroup != '' && genericGroup != 'f') throw new Error("Precision can only be specified for 'f'"); let hasAlternativeIntFlag = (flagsGroup.indexOf('I') != -1); if (hasAlternativeIntFlag && genericGroup != 'd') throw new Error("Alternative output digits can only be specfied for 'd'"); let pos = parseInt(posGroup, 10) || 0; if (usePos == false && i == 0) usePos = pos > 0; if (usePos && pos == 0 || !usePos && pos > 0) throw new Error("Numbered and unnumbered conversion specifications cannot be mixed"); let fillChar = (widthGroup[0] == '0') ? '0' : ' '; let width = parseInt(widthGroup, 10) || 0; function fillWidth(s, c, w) { let fill = ''; for (let i = 0; i < w; i++) fill += c; return fill.substr(s.length) + s; } function getArg() { return usePos ? args[pos - 1] : args[i++]; } let s = ''; switch (genericGroup) { case '%': return '%'; break; case 's': s = String(getArg()); break; case 'd': let intV = parseInt(getArg()); if (hasAlternativeIntFlag) s = GjsPrivate.format_int_alternative_output(intV); else s = intV.toString(); break; case 'x': s = parseInt(getArg()).toString(16); break; case 'f': if (precisionGroup == '') s = parseFloat(getArg()).toString(); else s = parseFloat(getArg()).toFixed(parseInt(precisionGroup)); break; default: throw new Error('Unsupported conversion character %' + genericGroup); } return fillWidth(s, fillChar, width); }); } /* * This function is intended to extend the String object and provide * an String.format API for string formatting. * It has to be set up using String.prototype.format = Format.format; * Usage: * "somestring %s %d".format('hello', 5); * It supports %s, %d, %x and %f, for %f it also support precisions like * "%.2f".format(1.526). All specifiers can be prefixed with a minimum * field width, e.g. "%5s".format("foo"). Unless the width is prefixed * with '0', the formatted string will be padded with spaces. */ function format() { return vprintf(this, arguments); } cjs-2.8.0/modules/tweener/0000775000175000017500000000000012610172032014337 5ustar fabiofabiocjs-2.8.0/modules/tweener/equations.js0000664000175000017500000006332212610172032016713 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2008 litl, LLC. */ /** * Equations * Main equations for the Tweener class * * @author Zeh Fernando, Nate Chatellier * @version 1.0.2 */ /* Disclaimer for Robert Penner's Easing Equations license: TERMS OF USE - EASING EQUATIONS Open source under the BSD License. Copyright © 2001 Robert Penner All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the author nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // ================================================================================================================================== // TWEENING EQUATIONS functions ----------------------------------------------------------------------------------------------------- // (the original equations are Robert Penner's work as mentioned on the disclaimer) /** * Easing equation function for a simple linear tweening, with no easing. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeNone (t, b, c, d, p_params) { return c*t/d + b; } /* Useful alias */ function linear (t, b, c ,d, p_params) { return easeNone (t, b, c, d, p_params); } /** * Easing equation function for a quadratic (t^2) easing in: accelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeInQuad (t, b, c, d, p_params) { return c*(t/=d)*t + b; } /** * Easing equation function for a quadratic (t^2) easing out: decelerating to zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeOutQuad (t, b, c, d, p_params) { return -c *(t/=d)*(t-2) + b; } /** * Easing equation function for a quadratic (t^2) easing in/out: acceleration until halfway, then deceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeInOutQuad (t, b, c, d, p_params) { if ((t/=d/2) < 1) return c/2*t*t + b; return -c/2 * ((--t)*(t-2) - 1) + b; } /** * Easing equation function for a quadratic (t^2) easing out/in: deceleration until halfway, then acceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeOutInQuad (t, b, c, d, p_params) { if (t < d/2) return easeOutQuad (t*2, b, c/2, d, p_params); return easeInQuad((t*2)-d, b+c/2, c/2, d, p_params); } /** * Easing equation function for a cubic (t^3) easing in: accelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeInCubic (t, b, c, d, p_params) { return c*(t/=d)*t*t + b; } /** * Easing equation function for a cubic (t^3) easing out: decelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeOutCubic (t, b, c, d, p_params) { return c*((t=t/d-1)*t*t + 1) + b; } /** * Easing equation function for a cubic (t^3) easing in/out: acceleration until halfway, then deceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeInOutCubic (t, b, c, d, p_params) { if ((t/=d/2) < 1) return c/2*t*t*t + b; return c/2*((t-=2)*t*t + 2) + b; } /** * Easing equation function for a cubic (t^3) easing out/in: deceleration until halfway, then acceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeOutInCubic (t, b, c, d, p_params) { if (t < d/2) return easeOutCubic (t*2, b, c/2, d, p_params); return easeInCubic((t*2)-d, b+c/2, c/2, d, p_params); } /** * Easing equation function for a quartic (t^4) easing in: accelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeInQuart (t, b, c, d, p_params) { return c*(t/=d)*t*t*t + b; } /** * Easing equation function for a quartic (t^4) easing out: decelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeOutQuart (t, b, c, d, p_params) { return -c * ((t=t/d-1)*t*t*t - 1) + b; } /** * Easing equation function for a quartic (t^4) easing in/out: acceleration until halfway, then deceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeInOutQuart (t, b, c, d, p_params) { if ((t/=d/2) < 1) return c/2*t*t*t*t + b; return -c/2 * ((t-=2)*t*t*t - 2) + b; } /** * Easing equation function for a quartic (t^4) easing out/in: deceleration until halfway, then acceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeOutInQuart (t, b, c, d, p_params) { if (t < d/2) return easeOutQuart (t*2, b, c/2, d, p_params); return easeInQuart((t*2)-d, b+c/2, c/2, d, p_params); } /** * Easing equation function for a quintic (t^5) easing in: accelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeInQuint (t, b, c, d, p_params) { return c*(t/=d)*t*t*t*t + b; } /** * Easing equation function for a quintic (t^5) easing out: decelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeOutQuint (t, b, c, d, p_params) { return c*((t=t/d-1)*t*t*t*t + 1) + b; } /** * Easing equation function for a quintic (t^5) easing in/out: acceleration until halfway, then deceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeInOutQuint (t, b, c, d, p_params) { if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b; return c/2*((t-=2)*t*t*t*t + 2) + b; } /** * Easing equation function for a quintic (t^5) easing out/in: deceleration until halfway, then acceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeOutInQuint (t, b, c, d, p_params) { if (t < d/2) return easeOutQuint (t*2, b, c/2, d, p_params); return easeInQuint((t*2)-d, b+c/2, c/2, d, p_params); } /** * Easing equation function for a sinusoidal (sin(t)) easing in: accelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeInSine (t, b, c, d, p_params) { return -c * Math.cos(t/d * (Math.PI/2)) + c + b; } /** * Easing equation function for a sinusoidal (sin(t)) easing out: decelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeOutSine (t, b, c, d, p_params) { return c * Math.sin(t/d * (Math.PI/2)) + b; } /** * Easing equation function for a sinusoidal (sin(t)) easing in/out: acceleration until halfway, then deceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeInOutSine (t, b, c, d, p_params) { return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b; } /** * Easing equation function for a sinusoidal (sin(t)) easing out/in: deceleration until halfway, then acceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeOutInSine (t, b, c, d, p_params) { if (t < d/2) return easeOutSine (t*2, b, c/2, d, p_params); return easeInSine((t*2)-d, b+c/2, c/2, d, p_params); } /** * Easing equation function for an exponential (2^t) easing in: accelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeInExpo (t, b, c, d, p_params) { return (t<=0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b; } /** * Easing equation function for an exponential (2^t) easing out: decelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeOutExpo (t, b, c, d, p_params) { return (t>=d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b; } /** * Easing equation function for an exponential (2^t) easing in/out: acceleration until halfway, then deceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeInOutExpo (t, b, c, d, p_params) { if (t<=0) return b; if (t>=d) return b+c; if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b; return c/2 * (-Math.pow(2, -10 * --t) + 2) + b; } /** * Easing equation function for an exponential (2^t) easing out/in: deceleration until halfway, then acceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeOutInExpo (t, b, c, d, p_params) { if (t < d/2) return easeOutExpo (t*2, b, c/2, d, p_params); return easeInExpo((t*2)-d, b+c/2, c/2, d, p_params); } /** * Easing equation function for a circular (sqrt(1-t^2)) easing in: accelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeInCirc (t, b, c, d, p_params) { return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b; } /** * Easing equation function for a circular (sqrt(1-t^2)) easing out: decelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeOutCirc (t, b, c, d, p_params) { return c * Math.sqrt(1 - (t=t/d-1)*t) + b; } /** * Easing equation function for a circular (sqrt(1-t^2)) easing in/out: acceleration until halfway, then deceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeInOutCirc (t, b, c, d, p_params) { if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b; return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b; } /** * Easing equation function for a circular (sqrt(1-t^2)) easing out/in: deceleration until halfway, then acceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeOutInCirc (t, b, c, d, p_params) { if (t < d/2) return easeOutCirc (t*2, b, c/2, d, p_params); return easeInCirc((t*2)-d, b+c/2, c/2, d, p_params); } /** * Easing equation function for an elastic (exponentially decaying sine wave) easing in: accelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @param a Amplitude. * @param p Period. * @return The correct value. */ function easeInElastic (t, b, c, d, p_params) { if (t<=0) return b; if ((t/=d)>=1) return b+c; var p = !Boolean(p_params) || isNaN(p_params.period) ? d*.3 : p_params.period; var s; var a = !Boolean(p_params) || isNaN(p_params.amplitude) ? 0 : p_params.amplitude; if (!Boolean(a) || a < Math.abs(c)) { a = c; s = p/4; } else { s = p/(2*Math.PI) * Math.asin (c/a); } return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; } /** * Easing equation function for an elastic (exponentially decaying sine wave) easing out: decelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @param a Amplitude. * @param p Period. * @return The correct value. */ function easeOutElastic (t, b, c, d, p_params) { if (t<=0) return b; if ((t/=d)>=1) return b+c; var p = !Boolean(p_params) || isNaN(p_params.period) ? d*.3 : p_params.period; var s; var a = !Boolean(p_params) || isNaN(p_params.amplitude) ? 0 : p_params.amplitude; if (!Boolean(a) || a < Math.abs(c)) { a = c; s = p/4; } else { s = p/(2*Math.PI) * Math.asin (c/a); } return (a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b); } /** * Easing equation function for an elastic (exponentially decaying sine wave) easing in/out: acceleration until halfway, then deceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @param a Amplitude. * @param p Period. * @return The correct value. */ function easeInOutElastic (t, b, c, d, p_params) { if (t<=0) return b; if ((t/=d/2)>=2) return b+c; var p = !Boolean(p_params) || isNaN(p_params.period) ? d*(.3*1.5) : p_params.period; var s; var a = !Boolean(p_params) || isNaN(p_params.amplitude) ? 0 : p_params.amplitude; if (!Boolean(a) || a < Math.abs(c)) { a = c; s = p/4; } else { s = p/(2*Math.PI) * Math.asin (c/a); } if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b; } /** * Easing equation function for an elastic (exponentially decaying sine wave) easing out/in: deceleration until halfway, then acceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @param a Amplitude. * @param p Period. * @return The correct value. */ function easeOutInElastic (t, b, c, d, p_params) { if (t < d/2) return easeOutElastic (t*2, b, c/2, d, p_params); return easeInElastic((t*2)-d, b+c/2, c/2, d, p_params); } /** * Easing equation function for a back (overshooting cubic easing: (s+1)*t^3 - s*t^2) easing in: accelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @param s Overshoot ammount: higher s means greater overshoot (0 produces cubic easing with no overshoot, and the default value of 1.70158 produces an overshoot of 10 percent). * @return The correct value. */ function easeInBack (t, b, c, d, p_params) { var s = !Boolean(p_params) || isNaN(p_params.overshoot) ? 1.70158 : p_params.overshoot; return c*(t/=d)*t*((s+1)*t - s) + b; } /** * Easing equation function for a back (overshooting cubic easing: (s+1)*t^3 - s*t^2) easing out: decelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @param s Overshoot ammount: higher s means greater overshoot (0 produces cubic easing with no overshoot, and the default value of 1.70158 produces an overshoot of 10 percent). * @return The correct value. */ function easeOutBack (t, b, c, d, p_params) { var s = !Boolean(p_params) || isNaN(p_params.overshoot) ? 1.70158 : p_params.overshoot; return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b; } /** * Easing equation function for a back (overshooting cubic easing: (s+1)*t^3 - s*t^2) easing in/out: acceleration until halfway, then deceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @param s Overshoot ammount: higher s means greater overshoot (0 produces cubic easing with no overshoot, and the default value of 1.70158 produces an overshoot of 10 percent). * @return The correct value. */ function easeInOutBack (t, b, c, d, p_params) { var s = !Boolean(p_params) || isNaN(p_params.overshoot) ? 1.70158 : p_params.overshoot; if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b; return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b; } /** * Easing equation function for a back (overshooting cubic easing: (s+1)*t^3 - s*t^2) easing out/in: deceleration until halfway, then acceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @param s Overshoot ammount: higher s means greater overshoot (0 produces cubic easing with no overshoot, and the default value of 1.70158 produces an overshoot of 10 percent). * @return The correct value. */ function easeOutInBack (t, b, c, d, p_params) { if (t < d/2) return easeOutBack (t*2, b, c/2, d, p_params); return easeInBack((t*2)-d, b+c/2, c/2, d, p_params); } /** * Easing equation function for a bounce (exponentially decaying parabolic bounce) easing in: accelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeInBounce (t, b, c, d, p_params) { return c - easeOutBounce (d-t, 0, c, d) + b; } /** * Easing equation function for a bounce (exponentially decaying parabolic bounce) easing out: decelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeOutBounce (t, b, c, d, p_params) { if ((t/=d) < (1/2.75)) { return c*(7.5625*t*t) + b; } else if (t < (2/2.75)) { return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b; } else if (t < (2.5/2.75)) { return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b; } else { return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b; } } /** * Easing equation function for a bounce (exponentially decaying parabolic bounce) easing in/out: acceleration until halfway, then deceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeInOutBounce (t, b, c, d, p_params) { if (t < d/2) return easeInBounce (t*2, 0, c, d) * .5 + b; else return easeOutBounce (t*2-d, 0, c, d) * .5 + c*.5 + b; } /** * Easing equation function for a bounce (exponentially decaying parabolic bounce) easing out/in: deceleration until halfway, then acceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeOutInBounce (t, b, c, d, p_params) { if (t < d/2) return easeOutBounce (t*2, b, c/2, d, p_params); return easeInBounce((t*2)-d, b+c/2, c/2, d, p_params); } cjs-2.8.0/modules/tweener/tweenList.js0000664000175000017500000001035612610172032016660 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2008 litl, LLC. */ /** * The tween list object. Stores all of the properties and information that pertain to individual tweens. * * @author Nate Chatellier, Zeh Fernando * @version 1.0.4 * @private */ /* Licensed under the MIT License Copyright (c) 2006-2007 Zeh Fernando and Nate Chatellier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. http://code.google.com/p/tweener/ http://code.google.com/p/tweener/wiki/License */ function TweenList(scope, timeStart, timeComplete, useFrames, transition, transitionParams) { this._init(scope, timeStart, timeComplete, useFrames, transition, transitionParams); } TweenList.prototype = { _init: function(scope, timeStart, timeComplete, userFrames, transition, transitionParams) { this.scope = scope; this.timeStart = timeStart; this.timeComplete = timeComplete; this.userFrames = userFrames; this.transition = transition; this.transitionParams = transitionParams; /* Other default information */ this.properties = new Object(); this.isPaused = false; this.timePaused = undefined; this.isCaller = false; this.updatesSkipped = 0; this.timesCalled = 0; this.skipUpdates = 0; this.hasStarted = false; }, clone: function(omitEvents) { var tween = new TweenList(this.scope, this.timeStart, this.timeComplete, this.userFrames, this.transition, this.transitionParams); tween.properties = new Array(); for (let name in this.properties) { tween.properties[name] = this.properties[name]; } tween.skipUpdates = this.skipUpdates; tween.updatesSkipped = this.updatesSkipped; if (!omitEvents) { tween.onStart = this.onStart; tween.onUpdate = this.onUpdate; tween.onComplete = this.onComplete; tween.onOverwrite = this.onOverwrite; tween.onError = this.onError; tween.onStartParams = this.onStartParams; tween.onUpdateParams = this.onUpdateParams; tween.onCompleteParams = this.onCompleteParams; tween.onOverwriteParams = this.onOverwriteParams; tween.onStartScope = this.onStartScope; tween.onUpdateScope = this.onUpdateScope; tween.onCompleteScope = this.onCompleteScope; tween.onOverwriteScope = this.onOverwriteScope; tween.onErrorScope = this.onErrorScope; } tween.rounded = this.rounded; tween.min = this.min; tween.max = this.max; tween.isPaused = this.isPaused; tween.timePaused = this.timePaused; tween.isCaller = this.isCaller; tween.count = this.count; tween.timesCalled = this.timesCalled; tween.waitFrames = this.waitFrames; tween.hasStarted = this.hasStarted; return tween; } }; function makePropertiesChain(obj) { /* Tweener has a bunch of code here to get all the properties of all * the objects we inherit from (the objects in the 'base' property). * I don't think that applies to JavaScript... */ return obj; }; cjs-2.8.0/modules/tweener/tweener.js0000664000175000017500000006677112610172032016367 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2008 litl, LLC. */ /** * Tweener * Transition controller for movieclips, sounds, textfields and other objects * * @author Zeh Fernando, Nate Chatellier, Arthur Debert * @version 1.31.71 */ /* Licensed under the MIT License Copyright (c) 2006-2007 Zeh Fernando and Nate Chatellier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. http://code.google.com/p/tweener/ http://code.google.com/p/tweener/wiki/License */ const GLib = imports.gi.GLib; const TweenList = imports.tweener.tweenList; const Signals = imports.signals; var _inited = false; var _engineExists = false; var _transitionList = null; var _tweenList = null; var _timeScale = 1; var _specialPropertyList = []; var _specialPropertyModifierList = []; var _specialPropertySplitterList = []; /* * Ticker should implement: * * property FRAME_RATE * start() * stop() * getTime() gets time in milliseconds from start() * signal prepare-frame * */ var _ticker = null; var _prepareFrameId = 0; /* default frame ticker */ function FrameTicker() { this._init(); } FrameTicker.prototype = { FRAME_RATE: 65, _init : function() { }, start : function() { this._currentTime = 0; let me = this; this._timeoutID = GLib.timeout_add(GLib.PRIORITY_DEFAULT, Math.floor(1000 / me.FRAME_RATE), function() { me._currentTime += 1000 / me.FRAME_RATE; me.emit('prepare-frame'); return true; }); }, stop : function() { if ('_timeoutID' in this) { GLib.source_remove(this._timeoutID); delete this._timeoutID; } this._currentTime = 0; }, getTime : function() { return this._currentTime; } }; Signals.addSignalMethods(FrameTicker.prototype); _ticker = new FrameTicker(); /* TODOs: * * Special properties: * * Special properties are 'proxy' properties used in Tweener to tween * (animate) things that are not proper properties per se. One example * given is the 'frame' of an object in ActionScript, which is not an * object property. Using the special property '_frame' you could animate * it like this: * * Tweener.addTween(myMovieClip, {_frame:20, time:1}); * * which would be equivalent to applying a fast-forward to it. * * This properties need a special support in the code, and I've removed it * for now until we see the need for it in our clutter based stuff. */ /* This is a bit pointless now, but let's keep it anyway... */ function _init() { if (_inited) return; _inited = true; } function setFrameTicker(ticker) { _ticker = ticker; } function _startEngine() { if (_engineExists) return; _engineExists = true; _tweenList = new Array(); if (!_ticker) { throw new Error("Must call setFrameTicker()"); } _prepareFrameId = _ticker.connect('prepare-frame', _onEnterFrame); _ticker.start(); } function _stopEngine() { if (!_engineExists) return; _engineExists = false; _tweenList = false; _ticker.disconnect(_prepareFrameId); _prepareFrameId = 0; _ticker.stop(); } function _getCurrentTweeningTime(tweening) { return _ticker.getTime(); } function _removeTweenByIndex(i) { _tweenList[i] = null; var finalRemoval = arguments[1]; if (finalRemoval != undefined && finalRemoval) _tweenList.splice(i, 1); return true; } function _resumeTweenByIndex(i) { var tweening = _tweenList[i]; if (tweening == null || !tweening.isPaused) return false; var currentTime = _getCurrentTweeningTime(tweening); tweening.timeStart += currentTime - tweening.timePaused; tweening.timeComplete += currentTime - tweening.timePaused; tweening.timePaused = undefined; tweening.isPaused = false; return true; }; /* FIXME: any way to get the function name from the fn itself? */ function _callOnFunction(fn, fnname, scope, fallbackScope, params) { if (fn) { var eventScope = scope ? scope : fallbackScope; try { fn.apply(eventScope, params); } catch (e) { logError(e, "Error calling " + fnname); } } } function _updateTweenByIndex(i) { var tweening = _tweenList[i]; if (tweening == null || !tweening.scope) return false; var currentTime = _getCurrentTweeningTime(tweening); if (currentTime < tweening.timeStart) return true; // Hasn't started, so return true var scope = tweening.scope; var t, b, c, d, nv; var isOver = false; if (tweening.isCaller) { do { t = ((tweening.timeComplete - tweening.timeStart)/tweening.count) * (tweening.timesCalled + 1); b = tweening.timeStart; c = tweening.timeComplete - tweening.timeStart; d = tweening.timeComplete - tweening.timeStart; nv = tweening.transition(t, b, c, d); if (currentTime >= nv) { _callOnFunction(tweening.onUpdate, "onUpdate", tweening.onUpdateScope, scope, tweening.onUpdateParams); tweening.timesCalled++; if (tweening.timesCalled >= tweening.count) { isOver = true; break; } if (tweening.waitFrames) break; } } while (currentTime >= nv); } else { var mustUpdate, name; if (currentTime >= tweening.timeComplete) { isOver = true; mustUpdate = true; } else { mustUpdate = tweening.skipUpdates < 1 || !tweening.skipUpdates || tweening.updatesSkipped >= tweening.skipUpdates; } if (!tweening.hasStarted) { _callOnFunction(tweening.onStart, "onStart", tweening.onStartScope, scope, tweening.onStartParams); for (name in tweening.properties) { var pv; if (tweening.properties[name].isSpecialProperty) { // It's a special property, tunnel via the special property function if (_specialPropertyList[name].preProcess != undefined) { tweening.properties[name].valueComplete = _specialPropertyList[name].preProcess(scope, _specialPropertyList[name].parameters, tweening.properties[name].originalValueComplete, tweening.properties[name].extra); } pv = _specialPropertyList[name].getValue(scope, _specialPropertyList[name].parameters, tweening.properties[name].extra); } else { // Directly read property pv = scope[name]; } tweening.properties[name].valueStart = isNaN(pv) ? tweening.properties[name].valueComplete : pv; } mustUpdate = true; tweening.hasStarted = true; } if (mustUpdate) { for (name in tweening.properties) { var property = tweening.properties[name]; if (isOver) { // Tweening time has finished, just set it to the final value nv = property.valueComplete; } else { if (property.hasModifier) { // Modified t = currentTime - tweening.timeStart; d = tweening.timeComplete - tweening.timeStart; nv = tweening.transition(t, 0, 1, d, tweening.transitionParams); nv = property.modifierFunction(property.valueStart, property.valueComplete, nv, property.modifierParameters); } else { // Normal update t = currentTime - tweening.timeStart; b = property.valueStart; c = property.valueComplete - property.valueStart; d = tweening.timeComplete - tweening.timeStart; nv = tweening.transition(t, b, c, d, tweening.transitionParams); } } if (tweening.rounded) nv = Math.round(nv); if (tweening.min !== undefined && nv < tweening.min) nv = tweening.min; if (tweening.max !== undefined && nv > tweening.max) nv = tweening.max; if (property.isSpecialProperty) { // It's a special property, tunnel via the special property method _specialPropertyList[name].setValue(scope, nv, _specialPropertyList[name].parameters, tweening.properties[name].extra); } else { // Directly set property scope[name] = nv; } } tweening.updatesSkipped = 0; _callOnFunction(tweening.onUpdate, "onUpdate", tweening.onUpdateScope, scope, tweening.onUpdateParams); } else { tweening.updatesSkipped++; } } if (isOver) { _callOnFunction(tweening.onComplete, "onComplete", tweening.onCompleteScope, scope, tweening.onCompleteParams); } return !isOver; } function _updateTweens() { if (_tweenList.length == 0) return false; for (let i = 0; i < _tweenList.length; i++) { if (_tweenList[i] == undefined || !_tweenList[i].isPaused) { if (!_updateTweenByIndex(i)) _removeTweenByIndex(i); if (_tweenList[i] == null) { _removeTweenByIndex(i, true); i--; } } } return true; } /* Ran once every 'frame'. It's the main engine, updates all existing tweenings */ function _onEnterFrame() { if (!_updateTweens()) _stopEngine(); return true; }; const restrictedWords = { time: true, delay: true, userFrames: true, skipUpdates: true, transition: true, transitionParams: true, onStart: true, onUpdate: true, onComplete: true, onOverwrite: true, onError: true, rounded: true, min: true, max: true, onStartParams: true, onUpdateParams: true, onCompleteParams: true, onOverwriteParams: true, onStartScope: true, onUpdateScope: true, onCompleteScope: true, onOverwriteScope: true, onErrorScope: true }; function _constructPropertyList(obj) { var properties = new Object(); var modifiedProperties = new Object(); for (let istr in obj) { if (restrictedWords[istr]) continue; if (_specialPropertySplitterList[istr] != undefined) { // Special property splitter var splitProperties = _specialPropertySplitterList[istr].splitValues(obj[istr], _specialPropertySplitterList[istr].parameters); for (let i = 0; i < splitProperties.length; i++) { if (_specialPropertySplitterList[splitProperties[i].name] != undefined) { var splitProperties2 = _specialPropertySplitterList[splitProperties[i].name].splitValues(splitProperties[i].value, _specialPropertySplitterList[splitProperties[i].name].parameters); for (let j = 0; j < splitProperties2.length; j++) { properties[splitProperties2[j].name] = { valueStart: undefined, valueComplete: splitProperties2[j].value, arrayIndex: splitProperties2[j].arrayIndex, isSpecialProperty: false }; } } else { properties[splitProperties[i].name] = { valueStart: undefined, valueComplete: splitProperties[i].value, arrayIndex: splitProperties[i].arrayIndex, isSpecialProperty: false }; } } } else if (_specialPropertyModifierList[istr] != undefined) { // Special property modifier let tempModifiedProperties = _specialPropertyModifierList[istr].modifyValues(obj[istr]); for (let i = 0; i < tempModifiedProperties.length; i++) { modifiedProperties[tempModifiedProperties[i].name] = { modifierParameters: tempModifiedProperties[i].parameters, modifierFunction: _specialPropertyModifierList[istr].getValue }; } } else { properties[istr] = { valueStart: undefined, valueComplete: obj[istr] }; } } // Adds the modifiers to the list of properties for (let istr in modifiedProperties) { if (properties[istr]) { properties[istr].modifierParameters = modifiedProperties[istr].modifierParameters; properties[istr].modifierFunction = modifiedProperties[istr].modifierFunction; } } return properties; } function PropertyInfo(valueStart, valueComplete, originalValueComplete, arrayIndex, extra, isSpecialProperty, modifierFunction, modifierParameters) { this._init(valueStart, valueComplete, originalValueComplete, arrayIndex, extra, isSpecialProperty, modifierFunction, modifierParameters); } PropertyInfo.prototype = { _init: function(valueStart, valueComplete, originalValueComplete, arrayIndex, extra, isSpecialProperty, modifierFunction, modifierParameters) { this.valueStart = valueStart; this.valueComplete = valueComplete; this.originalValueComplete = originalValueComplete; this.arrayIndex = arrayIndex; this.extra = extra; this.isSpecialProperty = isSpecialProperty; this.hasModifier = Boolean(modifierFunction); this.modifierFunction = modifierFunction; this.modifierParameters = modifierParameters; } }; function _addTweenOrCaller(target, tweeningParameters, isCaller) { if (!target) return false; var scopes; // List of objects to tween if (target instanceof Array) { // The first argument is an array scopes = target.concat(); // XXX: To copy the array I guess } else { // The first argument(s) is(are) object(s) scopes = new Array(target); } var obj, istr; if (isCaller) { obj = tweeningParameters; } else { obj = TweenList.makePropertiesChain(tweeningParameters); var properties = _constructPropertyList(obj); // Verifies whether the properties exist or not, for warning messages for (istr in properties) { if (_specialPropertyList[istr] != undefined) { properties[istr].isSpecialProperty = true; } else { for (var i = 0; i < scopes.length; i++) { if (scopes[i][istr] == undefined) log("The property " + istr + " doesn't seem to be a normal object property of " + scopes[i] + " or a registered special property"); } } } } // Creates the main engine if it isn't active if (!_inited) _init(); if (!_engineExists) _startEngine(); // Creates a "safer", more strict tweening object var time = isNaN(obj.time) ? 0 : obj.time; var delay = isNaN(obj.delay) ? 0 : obj.delay; var transition; // FIXME: Tweener allows you to use functions with an all lower-case name if (typeof obj.transition == "string") { transition = imports.tweener.equations[obj.transition]; } else { transition = obj.transition; } if (!transition) transition = imports.tweener.equations["easeOutExpo"]; var tween; for (let i = 0; i < scopes.length; i++) { if (!isCaller) { // Make a copy of the properties var copyProperties = new Object(); for (istr in properties) { copyProperties[istr] = new PropertyInfo(properties[istr].valueStart, properties[istr].valueComplete, properties[istr].valueComplete, properties[istr].arrayIndex, {}, properties[istr].isSpecialProperty, properties[istr].modifierFunction, properties[istr].modifierParameters); } } tween = new TweenList.TweenList(scopes[i], _ticker.getTime() + ((delay * 1000) / _timeScale), _ticker.getTime() + (((delay * 1000) + (time * 1000)) / _timeScale), false, transition, obj.transitionParams); tween.properties = isCaller ? null : copyProperties; tween.onStart = obj.onStart; tween.onUpdate = obj.onUpdate; tween.onComplete = obj.onComplete; tween.onOverwrite = obj.onOverwrite; tween.onError = obj.onError; tween.onStartParams = obj.onStartParams; tween.onUpdateParams = obj.onUpdateParams; tween.onCompleteParams = obj.onCompleteParams; tween.onOverwriteParams = obj.onOverwriteParams; tween.onStartScope = obj.onStartScope; tween.onUpdateScope = obj.onUpdateScope; tween.onCompleteScope = obj.onCompleteScope; tween.onOverwriteScope = obj.onOverwriteScope; tween.onErrorScope = obj.onErrorScope; tween.rounded = obj.rounded; tween.min = obj.min; tween.max = obj.max; tween.skipUpdates = obj.skipUpdates; tween.isCaller = isCaller; if (isCaller) { tween.count = obj.count; tween.waitFrames = obj.waitFrames; } if (!isCaller) { // Remove other tweenings that occur at the same time removeTweensByTime(tween.scope, tween.properties, tween.timeStart, tween.timeComplete); } // And finally adds it to the list _tweenList.push(tween); // Immediate update and removal if it's an immediate tween // If not deleted, it executes at the end of this frame execution if (time == 0 && delay == 0) { var myT = _tweenList.length-1; _updateTweenByIndex(myT); _removeTweenByIndex(myT); } } return true; }; function addTween(target, tweeningParameters) { return _addTweenOrCaller(target, tweeningParameters, false); }; function addCaller(target, tweeningParameters) { return _addTweenOrCaller(target, tweeningParameters, true); }; function _getNumberOfProperties(object) { var totalProperties = 0; for (let name in object) totalProperties ++; return totalProperties; } function removeTweensByTime(scope, properties, timeStart, timeComplete) { var removed = false; var removedLocally; var name; for (let i = 0; i < _tweenList.length; i++) { removedLocally = false; if (_tweenList[i] && scope == _tweenList[i].scope && timeComplete > _tweenList[i].timeStart && timeStart < _tweenList[i].timeComplete) { for (name in _tweenList[i].properties) { if (properties[name]) { _callOnFunction(_tweenList[i].onOverwrite, "onOverwrite", _tweenList[i].onOverwriteScope, _tweenList[i].scope, _tweenList[i].onOverwriteParams); _tweenList[i].properties[name] = undefined; delete _tweenList[i].properties[name]; removedLocally = true; removed = true; } } if (removedLocally && _getNumberOfProperties(_tweenList[i].properties) == 0) { _removeTweenByIndex(i); } } } return removed; }; function _pauseTweenByIndex(i) { var tweening = _tweenList[i]; if (tweening == null || tweening.isPaused) return false; tweening.timePaused = _getCurrentTweeningTime(tweening); tweening.isPaused = true; return true; }; function _splitTweens(tween, properties) { var originalTween = _tweenList[tween]; var newTween = originalTween.clone(); var name; for (let i = 0; i < properties.length; i++) { name = properties[i]; if (originalTween.properties[name]) { originalTween.properties[name] = undefined; delete originalTween.properties[name]; } } var found = false; for (name in newTween.properties) { found = false; for (let i = 0; i < properties.length; i++) { if (properties[i] == name) { found = true; break; } } if (!found) { newTween.properties[name] = undefined; delete newTween.properties[name]; } } _tweenList.push(newTween); return _tweenList.length - 1; } function _affectTweens(affectFunction, scope, properties) { var affected = false; if (!_tweenList) return false; for (let i = 0; i < _tweenList.length; i++) { if (!_tweenList[i] || _tweenList[i].scope != scope) continue; if (properties.length == 0) { // Can check everything affectFunction(i); affected = true; } else { // Must check whether this tween must have specific properties affected var affectedProperties = new Array(); for (let j = 0; j < properties.length; j++) { if (_tweenList[i].properties[properties[j]]) { affectedProperties.push(properties[j]); } } if (affectedProperties.length > 0) { var objectProperties = _getNumberOfProperties(_tweenList[i].properties); if (objectProperties == affectedProperties.length) { // The list of properties is the same as all properties, so affect it all affectFunction(i); affected = true; } else { // The properties are mixed, so split the tween and affect only certian specific // properties var splicedTweenIndex = _splitTweens(i, affectedProperties); affectFunction(splicedTweenIndex); affected = true; } } } } return affected; }; function _isInArray(string, array) { var l = array.length; for (let i = 0; i < l; i++) { if (array[i] == string) return true; } return false; } function _affectTweensWithFunction(func, args) { var properties = new Array(); var scope = args[0]; var affected = false; var scopes; if (scope instanceof Array) { scopes = scope.concat(); } else { scopes = new Array(scope); } for (let i = 1; args[i] != undefined; i++) { if (typeof(args[i]) == "string" && !_isInArray(args[i], properties)) { if (_specialPropertySplitterList[args[i]]) { // special property, get splitter array first var sps = _specialPropertySplitterList[arguments[i]]; var specialProps = sps.splitValues(scope, null); for (let j = 0; j < specialProps.length; j++) properties.push(specialProps[j].name); } else properties.push(args[i]); } } // the return now value means: "affect at least one tween" for (let i = 0; i < scopes.length; i++) { affected = affected || _affectTweens(func, scopes[i], properties); } return affected; } function resumeTweens() { return _affectTweensWithFunction(_resumeTweenByIndex, arguments); }; function pauseTweens() { return _affectTweensWithFunction(_pauseTweenByIndex, arguments); }; function removeTweens() { return _affectTweensWithFunction(_removeTweenByIndex, arguments); }; function _mapOverTweens(func) { var rv = false; if (_tweenList == null) return false; for (let i = 0; i < _tweenList.length; i++) { if (func(i)) rv = true; } return rv; } function pauseAllTweens() { return _mapOverTweens(_pauseTweenByIndex); }; function resumeAllTweens() { return _mapOverTweens(_resumeTweenByIndex); }; function removeAllTweens() { return _mapOverTweens(_removeTweenByIndex); }; function getTweenCount(scope) { if (!_tweenList) return 0; var c = 0; for (let i = 0; i < _tweenList.length; i++) { if (_tweenList[i] && _tweenList[i].scope == scope) c += _getNumberOfProperties(_tweenList[i].properties); } return c; }; function registerSpecialProperty(name, getFunction, setFunction, parameters, preProcessFunction) { _specialPropertyList[name] = { getValue: getFunction, setValue: setFunction, parameters: parameters, preProcess: preProcessFunction }; } function registerSpecialPropertyModifier(name, modifyFunction, getFunction) { _specialPropertyModifierList[name] = { modifyValues: modifyFunction, getValue: getFunction }; } function registerSpecialPropertySplitter(name, splitFunction, parameters) { _specialPropertySplitterList[name] = { splitValues: splitFunction, parameters: parameters }; } function setTimeScale(scale) { _timeScale = scale; } function getTimeScale() { return _timeScale; } cjs-2.8.0/modules/system.cpp0000664000175000017500000001245212610172032014722 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * Copyright (c) 2012 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include #include "system.h" static JSBool gjs_address_of(JSContext *context, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(cx, vp); JSObject *target_obj; JSBool ret; char *pointer_string; jsval retval; if (!gjs_parse_args(context, "addressOf", "o", argc, argv, "object", &target_obj)) return JS_FALSE; pointer_string = g_strdup_printf("%p", target_obj); ret = gjs_string_from_utf8(context, pointer_string, -1, &retval); g_free(pointer_string); if (ret) JS_SET_RVAL(context, vp, retval); return ret; } static JSBool gjs_refcount(JSContext *context, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(cx, vp); jsval retval; JSObject *target_obj; GObject *obj; if (!gjs_parse_args(context, "refcount", "o", argc, argv, "object", &target_obj)) return JS_FALSE; if (!gjs_typecheck_object(context, target_obj, G_TYPE_OBJECT, JS_TRUE)) return JS_FALSE; obj = gjs_g_object_from_object(context, target_obj); if (obj == NULL) return JS_FALSE; retval = INT_TO_JSVAL(obj->ref_count); JS_SET_RVAL(context, vp, retval); return JS_TRUE; } static JSBool gjs_breakpoint(JSContext *context, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(cx, vp); if (!gjs_parse_args(context, "breakpoint", "", argc, argv)) return JS_FALSE; G_BREAKPOINT(); JS_SET_RVAL(context, vp, JSVAL_VOID); return JS_TRUE; } static JSBool gjs_gc(JSContext *context, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(cx, vp); if (!gjs_parse_args(context, "gc", "", argc, argv)) return JS_FALSE; JS_GC(JS_GetRuntime(context)); JS_SET_RVAL(context, vp, JSVAL_VOID); return JS_TRUE; } static JSBool gjs_exit(JSContext *context, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(cx, vp); gint32 ecode; if (!gjs_parse_args(context, "exit", "i", argc, argv, "ecode", &ecode)) return JS_FALSE; exit(ecode); return JS_TRUE; } static JSFunctionSpec module_funcs[] = { { "addressOf", JSOP_WRAPPER (gjs_address_of), 1, GJS_MODULE_PROP_FLAGS }, { "refcount", JSOP_WRAPPER (gjs_refcount), 1, GJS_MODULE_PROP_FLAGS }, { "breakpoint", JSOP_WRAPPER (gjs_breakpoint), 0, GJS_MODULE_PROP_FLAGS }, { "gc", JSOP_WRAPPER (gjs_gc), 0, GJS_MODULE_PROP_FLAGS }, { "exit", JSOP_WRAPPER (gjs_exit), 0, GJS_MODULE_PROP_FLAGS }, { NULL }, }; JSBool gjs_js_define_system_stuff(JSContext *context, JSObject **module_out) { GjsContext *gjs_context; char *program_name; jsval value; JSBool retval; JSObject *module; module = JS_NewObject (context, NULL, NULL, NULL); if (!JS_DefineFunctions(context, module, &module_funcs[0])) return JS_FALSE; retval = JS_FALSE; gjs_context = (GjsContext*) JS_GetContextPrivate(context); g_object_get(gjs_context, "program-name", &program_name, NULL); if (!gjs_string_from_utf8(context, program_name, -1, &value)) goto out; /* The name is modeled after program_invocation_name, part of the glibc */ if (!JS_DefineProperty(context, module, "programInvocationName", value, JS_PropertyStub, JS_StrictPropertyStub, GJS_MODULE_PROP_FLAGS | JSPROP_READONLY)) goto out; if (!JS_DefineProperty(context, module, "version", INT_TO_JSVAL(GJS_VERSION), JS_PropertyStub, JS_StrictPropertyStub, GJS_MODULE_PROP_FLAGS | JSPROP_READONLY)) goto out; retval = JS_TRUE; out: g_free(program_name); *module_out = module; return retval; } cjs-2.8.0/modules/cairo-gradient.cpp0000664000175000017500000000710712610172032016267 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2010 litl, LLC. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include "cairo-private.h" GJS_DEFINE_PROTO_ABSTRACT("CairoGradient", cairo_gradient) static void gjs_cairo_gradient_finalize(JSFreeOp *fop, JSObject *obj) { gjs_cairo_pattern_finalize_pattern(fop, obj); } /* Properties */ JSPropertySpec gjs_cairo_gradient_proto_props[] = { { NULL } }; /* Methods */ static JSBool addColorStopRGB_func(JSContext *context, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(context, vp); JSObject *obj = JS_THIS_OBJECT(context, vp); double offset, red, green, blue; cairo_pattern_t *pattern; if (!gjs_parse_args(context, "addColorStopRGB", "ffff", argc, argv, "offset", &offset, "red", &red, "green", &green, "blue", &blue)) return JS_FALSE; pattern = gjs_cairo_pattern_get_pattern(context, obj); cairo_pattern_add_color_stop_rgb(pattern, offset, red, green, blue); if (!gjs_cairo_check_status(context, cairo_pattern_status(pattern), "pattern")) return JS_FALSE; JS_SET_RVAL(context, vp, JSVAL_VOID); return JS_TRUE; } static JSBool addColorStopRGBA_func(JSContext *context, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(context, vp); JSObject *obj = JS_THIS_OBJECT(context, vp); double offset, red, green, blue, alpha; cairo_pattern_t *pattern; if (!gjs_parse_args(context, "addColorStopRGBA", "fffff", argc, argv, "offset", &offset, "red", &red, "green", &green, "blue", &blue, "alpha", &alpha)) return JS_FALSE; pattern = gjs_cairo_pattern_get_pattern(context, obj); cairo_pattern_add_color_stop_rgba(pattern, offset, red, green, blue, alpha); if (!gjs_cairo_check_status(context, cairo_pattern_status(pattern), "pattern")) return JS_FALSE; JS_SET_RVAL(context, vp, JSVAL_VOID); return JS_TRUE; } JSFunctionSpec gjs_cairo_gradient_proto_funcs[] = { { "addColorStopRGB", JSOP_WRAPPER((JSNative)addColorStopRGB_func), 0, 0 }, { "addColorStopRGBA", JSOP_WRAPPER((JSNative)addColorStopRGBA_func), 0, 0 }, // getColorStopRGB // getColorStopRGBA { NULL } }; cjs-2.8.0/modules/cairo.cpp0000664000175000017500000001102312610172032014464 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2010 litl, LLC. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include "cairo-private.h" JSBool gjs_cairo_check_status(JSContext *context, cairo_status_t status, const char *name) { if (status != CAIRO_STATUS_SUCCESS) { gjs_throw(context, "cairo error on %s: \"%s\" (%d)", name, cairo_status_to_string(status), status); return JS_FALSE; } return JS_TRUE; } JSBool gjs_js_define_cairo_stuff(JSContext *context, JSObject **module_out) { jsval obj; JSObject *module; JSObject *surface_proto, *pattern_proto, *gradient_proto; module = JS_NewObject (context, NULL, NULL, NULL); obj = gjs_cairo_context_create_proto(context, module, "Context", NULL); if (JSVAL_IS_NULL(obj)) return JS_FALSE; gjs_cairo_context_init(context); gjs_cairo_surface_init(context); obj = gjs_cairo_surface_create_proto(context, module, "Surface", NULL); if (JSVAL_IS_NULL(obj)) return JS_FALSE; surface_proto = JSVAL_TO_OBJECT(obj); obj = gjs_cairo_image_surface_create_proto(context, module, "ImageSurface", surface_proto); if (JSVAL_IS_NULL(obj)) return JS_FALSE; gjs_cairo_image_surface_init(context, JSVAL_TO_OBJECT(obj)); #if CAIRO_HAS_PS_SURFACE obj = gjs_cairo_ps_surface_create_proto(context, module, "PSSurface", surface_proto); if (JSVAL_IS_NULL(obj)) return JS_FALSE; #endif #if CAIRO_HAS_PDF_SURFACE obj = gjs_cairo_pdf_surface_create_proto(context, module, "PDFSurface", surface_proto); if (JSVAL_IS_NULL(obj)) return JS_FALSE; #endif #if CAIRO_HAS_SVG_SURFACE obj = gjs_cairo_svg_surface_create_proto(context, module, "SVGSurface", surface_proto); if (JSVAL_IS_NULL(obj)) return JS_FALSE; #endif obj = gjs_cairo_pattern_create_proto(context, module, "Pattern", NULL); if (JSVAL_IS_NULL(obj)) return JS_FALSE; pattern_proto = JSVAL_TO_OBJECT(obj); obj = gjs_cairo_gradient_create_proto(context, module, "Gradient", pattern_proto); if (JSVAL_IS_NULL(obj)) return JS_FALSE; gradient_proto = JSVAL_TO_OBJECT(obj); obj = gjs_cairo_linear_gradient_create_proto(context, module, "LinearGradient", gradient_proto); if (JSVAL_IS_NULL(obj)) return JS_FALSE; obj = gjs_cairo_radial_gradient_create_proto(context, module, "RadialGradient", gradient_proto); if (JSVAL_IS_NULL(obj)) return JS_FALSE; obj = gjs_cairo_surface_pattern_create_proto(context, module, "SurfacePattern", pattern_proto); if (JSVAL_IS_NULL(obj)) return JS_FALSE; obj = gjs_cairo_solid_pattern_create_proto(context, module, "SolidPattern", pattern_proto); if (JSVAL_IS_NULL(obj)) return JS_FALSE; *module_out = module; return JS_TRUE; } cjs-2.8.0/modules/cairo-context.cpp0000664000175000017500000012601612610172032016157 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2010 litl, LLC. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include #include #include "cairo-private.h" #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(mname) \ static JSBool \ mname##_func(JSContext *context, \ unsigned argc, \ jsval *vp) \ { \ JSObject *obj = JS_THIS_OBJECT(context, vp); \ cairo_t *cr; #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END \ if (!gjs_cairo_check_status(context, cairo_status(cr), "context")) \ return JS_FALSE; \ return JS_TRUE; \ } #define _GJS_CAIRO_CONTEXT_CHECK_NO_ARGS(m) \ if (argc > 0) { \ gjs_throw(context, "Context." #m "() takes no arguments"); \ return JS_FALSE; \ } #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(method, cfunc) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ cr = gjs_cairo_context_get_context(context, obj); \ cfunc(cr); \ JS_SET_RVAL(context, vp, JSVAL_VOID); \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC0I(method, cfunc) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ int ret; \ _GJS_CAIRO_CONTEXT_CHECK_NO_ARGS(method) \ cr = gjs_cairo_context_get_context(context, obj); \ ret = (int)cfunc(cr); \ JS_SET_RVAL(context, vp, INT_TO_JSVAL(ret)); \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC0B(method, cfunc) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ cairo_bool_t ret; \ _GJS_CAIRO_CONTEXT_CHECK_NO_ARGS(method) \ cr = gjs_cairo_context_get_context(context, obj); \ ret = cfunc(cr); \ JS_SET_RVAL(context, vp, BOOLEAN_TO_JSVAL(ret)); \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC2FFAFF(method, cfunc, n1, n2) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ double arg1, arg2; \ if (!gjs_parse_args(context, #method, "ff", argc, JS_ARGV(context, vp), \ #n1, &arg1, #n2, &arg2)) \ return JS_FALSE; \ cr = gjs_cairo_context_get_context(context, obj); \ cfunc(cr, &arg1, &arg2); \ if (cairo_status(cr) == CAIRO_STATUS_SUCCESS) { \ JSObject *array = JS_NewArrayObject(context, 0, NULL); \ if (!array) \ return JS_FALSE; \ jsval r; \ if (!JS_NewNumberValue(context, arg1, &r)) return JS_FALSE; \ if (!JS_SetElement(context, array, 0, &r)) return JS_FALSE; \ if (!JS_NewNumberValue(context, arg2, &r)) return JS_FALSE; \ if (!JS_SetElement(context, array, 1, &r)) return JS_FALSE; \ JS_SET_RVAL(context, vp, OBJECT_TO_JSVAL(array)); \ } \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC0AFF(method, cfunc) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ double arg1, arg2; \ _GJS_CAIRO_CONTEXT_CHECK_NO_ARGS(method) \ cr = gjs_cairo_context_get_context(context, obj); \ cfunc(cr, &arg1, &arg2); \ if (cairo_status(cr) == CAIRO_STATUS_SUCCESS) { \ JSObject *array = JS_NewArrayObject(context, 0, NULL); \ if (!array) \ return JS_FALSE; \ jsval r; \ if (!JS_NewNumberValue(context, arg1, &r)) return JS_FALSE; \ if (!JS_SetElement(context, array, 0, &r)) return JS_FALSE; \ if (!JS_NewNumberValue(context, arg2, &r)) return JS_FALSE; \ if (!JS_SetElement(context, array, 1, &r)) return JS_FALSE; \ JS_SET_RVAL(context, vp, OBJECT_TO_JSVAL(array)); \ } \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC0AFFFF(method, cfunc) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ double arg1, arg2, arg3, arg4; \ _GJS_CAIRO_CONTEXT_CHECK_NO_ARGS(method) \ cr = gjs_cairo_context_get_context(context, obj); \ cfunc(cr, &arg1, &arg2, &arg3, &arg4); \ { \ JSObject *array = JS_NewArrayObject(context, 0, NULL); \ if (!array) \ return JS_FALSE; \ jsval r; \ if (!JS_NewNumberValue(context, arg1, &r)) return JS_FALSE; \ if (!JS_SetElement(context, array, 0, &r)) return JS_FALSE; \ if (!JS_NewNumberValue(context, arg2, &r)) return JS_FALSE; \ if (!JS_SetElement(context, array, 1, &r)) return JS_FALSE; \ if (!JS_NewNumberValue(context, arg3, &r)) return JS_FALSE; \ if (!JS_SetElement(context, array, 2, &r)) return JS_FALSE; \ if (!JS_NewNumberValue(context, arg4, &r)) return JS_FALSE; \ if (!JS_SetElement(context, array, 3, &r)) return JS_FALSE; \ JS_SET_RVAL(context, vp, OBJECT_TO_JSVAL(array)); \ } \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC0F(method, cfunc) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ double ret; \ jsval retval; \ _GJS_CAIRO_CONTEXT_CHECK_NO_ARGS(method) \ cr = gjs_cairo_context_get_context(context, obj); \ ret = cfunc(cr); \ if (!JS_NewNumberValue(context, ret, &retval)) \ return JS_FALSE; \ JS_SET_RVAL(context, vp, retval); \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(method, cfunc, fmt, t1, n1) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ t1 arg1; \ if (!gjs_parse_args(context, #method, fmt, argc, JS_ARGV(context, vp), \ #n1, &arg1)) \ return JS_FALSE; \ cr = gjs_cairo_context_get_context(context, obj); \ cfunc(cr, arg1); \ JS_SET_RVAL(context, vp, JSVAL_VOID); \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC2(method, cfunc, fmt, t1, n1, t2, n2) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ t1 arg1; \ t2 arg2; \ if (!gjs_parse_args(context, #method, fmt, argc, JS_ARGV(context, vp), \ #n1, &arg1, #n2, &arg2)) \ return JS_FALSE; \ cr = gjs_cairo_context_get_context(context, obj); \ cfunc(cr, arg1, arg2); \ JS_SET_RVAL(context, vp, JSVAL_VOID); \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC2B(method, cfunc, fmt, t1, n1, t2, n2) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ t1 arg1; \ t2 arg2; \ cairo_bool_t ret; \ if (!gjs_parse_args(context, #method, fmt, argc, JS_ARGV(context, vp), \ #n1, &arg1, #n2, &arg2)) \ return JS_FALSE; \ cr = gjs_cairo_context_get_context(context, obj); \ ret = cfunc(cr, arg1, arg2); \ JS_SET_RVAL(context, vp, BOOLEAN_TO_JSVAL(ret)); \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC3(method, cfunc, fmt, t1, n1, t2, n2, t3, n3) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ t1 arg1; \ t2 arg2; \ t3 arg3; \ if (!gjs_parse_args(context, #method, fmt, argc, JS_ARGV(context, vp), \ #n1, &arg1, #n2, &arg2, #n3, &arg3)) \ return JS_FALSE; \ cr = gjs_cairo_context_get_context(context, obj); \ cfunc(cr, arg1, arg2, arg3); \ JS_SET_RVAL(context, vp, JSVAL_VOID); \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC4(method, cfunc, fmt, t1, n1, t2, n2, t3, n3, t4, n4) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ t1 arg1; \ t2 arg2; \ t3 arg3; \ t4 arg4; \ if (!gjs_parse_args(context, #method, fmt, argc, JS_ARGV(context, vp), \ #n1, &arg1, #n2, &arg2, #n3, &arg3, #n4, &arg4)) \ return JS_FALSE; \ cr = gjs_cairo_context_get_context(context, obj); \ cfunc(cr, arg1, arg2, arg3, arg4); \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC5(method, cfunc, fmt, t1, n1, t2, n2, t3, n3, t4, n4, t5, n5) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ t1 arg1; \ t2 arg2; \ t3 arg3; \ t4 arg4; \ t5 arg5; \ if (!gjs_parse_args(context, #method, fmt, argc, JS_ARGV(context, vp), \ #n1, &arg1, #n2, &arg2, #n3, &arg3, \ #n4, &arg4, #n5, &arg5)) \ return JS_FALSE; \ cr = gjs_cairo_context_get_context(context, obj); \ cfunc(cr, arg1, arg2, arg3, arg4, arg5); \ JS_SET_RVAL(context, vp, JSVAL_VOID); \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC6(method, cfunc, fmt, t1, n1, t2, n2, t3, n3, t4, n4, t5, n5, t6, n6) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ t1 arg1; \ t2 arg2; \ t3 arg3; \ t4 arg4; \ t5 arg5; \ t6 arg6; \ if (!gjs_parse_args(context, #method, fmt, argc, JS_ARGV(context, vp), \ #n1, &arg1, #n2, &arg2, #n3, &arg3, \ #n4, &arg4, #n5, &arg5, #n6, &arg6)) \ return JS_FALSE; \ cr = gjs_cairo_context_get_context(context, obj); \ cfunc(cr, arg1, arg2, arg3, arg4, arg5, arg6); \ JS_SET_RVAL(context, vp, JSVAL_VOID); \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END typedef struct { void *dummy; JSContext *context; JSObject *object; cairo_t * cr; } GjsCairoContext; GJS_DEFINE_PROTO_WITH_GTYPE("CairoContext", cairo_context, CAIRO_GOBJECT_TYPE_CONTEXT) GJS_DEFINE_PRIV_FROM_JS(GjsCairoContext, gjs_cairo_context_class); static void _gjs_cairo_context_construct_internal(JSContext *context, JSObject *obj, cairo_t *cr) { GjsCairoContext *priv; priv = g_slice_new0(GjsCairoContext); g_assert(priv_from_js(context, obj) == NULL); JS_SetPrivate(obj, priv); priv->context = context; priv->object = obj; priv->cr = cairo_reference(cr); } GJS_NATIVE_CONSTRUCTOR_DECLARE(cairo_context) { GJS_NATIVE_CONSTRUCTOR_VARIABLES(cairo_context) JSObject *surface_wrapper; cairo_surface_t *surface; cairo_t *cr; GJS_NATIVE_CONSTRUCTOR_PRELUDE(cairo_context); if (!gjs_parse_args(context, "Context", "o", argc, argv, "surface", &surface_wrapper)) return JS_FALSE; surface = gjs_cairo_surface_get_surface(context, surface_wrapper); if (!surface) { gjs_throw(context, "first argument to Context() should be a surface"); return JS_FALSE; } cr = cairo_create(surface); if (!gjs_cairo_check_status(context, cairo_status(cr), "context")) return JS_FALSE; _gjs_cairo_context_construct_internal(context, object, cr); cairo_destroy(cr); GJS_NATIVE_CONSTRUCTOR_FINISH(cairo_context); return JS_TRUE; } static void gjs_cairo_context_finalize(JSFreeOp *fop, JSObject *obj) { GjsCairoContext *priv; priv = (GjsCairoContext*) JS_GetPrivate(obj); if (priv == NULL) return; if (priv->cr != NULL) cairo_destroy(priv->cr); g_slice_free(GjsCairoContext, priv); } /* Properties */ JSPropertySpec gjs_cairo_context_proto_props[] = { { NULL } }; /* Methods */ _GJS_CAIRO_CONTEXT_DEFINE_FUNC5(arc, cairo_arc, "fffff", double, xc, double, yc, double, radius, double, angle1, double, angle2) _GJS_CAIRO_CONTEXT_DEFINE_FUNC5(arcNegative, cairo_arc_negative, "fffff", double, xc, double, yc, double, radius, double, angle1, double, angle2) _GJS_CAIRO_CONTEXT_DEFINE_FUNC6(curveTo, cairo_curve_to, "ffffff", double, x1, double, y1, double, x2, double, y2, double, x3, double, y3) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(clip, cairo_clip) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(clipPreserve, cairo_clip_preserve) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0AFFFF(clipExtents, cairo_clip_extents) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(closePath, cairo_close_path) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(copyPage, cairo_copy_page) _GJS_CAIRO_CONTEXT_DEFINE_FUNC2FFAFF(deviceToUser, cairo_device_to_user, "x", "y") _GJS_CAIRO_CONTEXT_DEFINE_FUNC2FFAFF(deviceToUserDistance, cairo_device_to_user_distance, "x", "y") _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(fill, cairo_fill) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(fillPreserve, cairo_fill_preserve) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0AFFFF(fillExtents, cairo_fill_extents) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0I(getAntialias, cairo_get_antialias) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0AFF(getCurrentPoint, cairo_get_current_point) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0I(getDashCount, cairo_get_dash_count) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0I(getFillRule, cairo_get_fill_rule) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0I(getLineCap, cairo_get_line_cap) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0I(getLineJoin, cairo_get_line_join) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0F(getLineWidth, cairo_get_line_width) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0F(getMiterLimit, cairo_get_miter_limit) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0I(getOperator, cairo_get_operator) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0F(getTolerance, cairo_get_tolerance) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0B(hasCurrentPoint, cairo_has_current_point) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(identityMatrix, cairo_identity_matrix) _GJS_CAIRO_CONTEXT_DEFINE_FUNC2B(inFill, cairo_in_fill, "ff", double, x, double, y) _GJS_CAIRO_CONTEXT_DEFINE_FUNC2B(inStroke, cairo_in_stroke, "ff", double, x, double, y) _GJS_CAIRO_CONTEXT_DEFINE_FUNC2(lineTo, cairo_line_to, "ff", double, x, double, y) _GJS_CAIRO_CONTEXT_DEFINE_FUNC2(moveTo, cairo_move_to, "ff", double, x, double, y) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(newPath, cairo_new_path) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(newSubPath, cairo_new_sub_path) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(paint, cairo_paint) _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(paintWithAlpha, cairo_paint_with_alpha, "f", double, alpha) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0AFFFF(pathExtents, cairo_path_extents) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(pushGroup, cairo_push_group) _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(pushGroupWithContent, cairo_push_group_with_content, "i", cairo_content_t, content) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(popGroupToSource, cairo_pop_group_to_source) _GJS_CAIRO_CONTEXT_DEFINE_FUNC4(rectangle, cairo_rectangle, "ffff", double, x, double, y, double, width, double, height) _GJS_CAIRO_CONTEXT_DEFINE_FUNC6(relCurveTo, cairo_rel_curve_to, "ffffff", double, dx1, double, dy1, double, dx2, double, dy2, double, dx3, double, dy3) _GJS_CAIRO_CONTEXT_DEFINE_FUNC2(relLineTo, cairo_rel_line_to, "ff", double, dx, double, dy) _GJS_CAIRO_CONTEXT_DEFINE_FUNC2(relMoveTo, cairo_rel_move_to, "ff", double, dx, double, dy) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(resetClip, cairo_reset_clip) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(restore, cairo_restore) _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(rotate, cairo_rotate, "f", double, angle) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(save, cairo_save) _GJS_CAIRO_CONTEXT_DEFINE_FUNC2(scale, cairo_scale, "ff", double, sx, double, sy) _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(setAntialias, cairo_set_antialias, "i", cairo_antialias_t, antialias) _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(setFillRule, cairo_set_fill_rule, "i", cairo_fill_rule_t, fill_rule) _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(setFontSize, cairo_set_font_size, "f", double, size) _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(setLineCap, cairo_set_line_cap, "i", cairo_line_cap_t, line_cap) _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(setLineJoin, cairo_set_line_join, "i", cairo_line_join_t, line_join) _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(setLineWidth, cairo_set_line_width, "f", double, width) _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(setMiterLimit, cairo_set_miter_limit, "f", double, limit) _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(setOperator, cairo_set_operator, "i", cairo_operator_t, op) _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(setTolerance, cairo_set_tolerance, "f", double, tolerance) _GJS_CAIRO_CONTEXT_DEFINE_FUNC3(setSourceRGB, cairo_set_source_rgb, "fff", double, red, double, green, double, blue) _GJS_CAIRO_CONTEXT_DEFINE_FUNC4(setSourceRGBA, cairo_set_source_rgba, "ffff", double, red, double, green, double, blue, double, alpha) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(showPage, cairo_show_page) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(stroke, cairo_stroke) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(strokePreserve, cairo_stroke_preserve) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0AFFFF(strokeExtents, cairo_stroke_extents) _GJS_CAIRO_CONTEXT_DEFINE_FUNC2(translate, cairo_translate, "ff", double, tx, double, ty) _GJS_CAIRO_CONTEXT_DEFINE_FUNC2FFAFF(userToDevice, cairo_user_to_device, "x", "y") _GJS_CAIRO_CONTEXT_DEFINE_FUNC2FFAFF(userToDeviceDistance, cairo_user_to_device_distance, "x", "y") static JSBool dispose_func(JSContext *context, unsigned argc, jsval *vp) { JSObject *obj = JS_THIS_OBJECT(context, vp); GjsCairoContext *priv; priv = priv_from_js(context, obj); if (priv->cr != NULL) { cairo_destroy(priv->cr); priv->cr = NULL; } JS_SET_RVAL(context, vp, JSVAL_VOID); return JS_TRUE; } static JSBool appendPath_func(JSContext *context, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(context, vp); JSObject *obj = JS_THIS_OBJECT(context, vp); JSObject *path_wrapper; cairo_path_t *path; cairo_t *cr; if (!gjs_parse_args(context, "path", "o", argc, argv, "path", &path_wrapper)) return JS_FALSE; path = gjs_cairo_path_get_path(context, path_wrapper); if (!path) { gjs_throw(context, "first argument to appendPath() should be a path"); return JS_FALSE; } cr = gjs_cairo_context_get_context(context, obj); cairo_append_path(cr, path); JS_SET_RVAL(context, vp, JSVAL_VOID); return JS_TRUE; } static JSBool copyPath_func(JSContext *context, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(context, vp); JSObject *obj = JS_THIS_OBJECT(context, vp); cairo_path_t *path; cairo_t *cr; if (!gjs_parse_args(context, "", "", argc, argv)) return JS_FALSE; cr = gjs_cairo_context_get_context(context, obj); path = cairo_copy_path(cr); JS_SET_RVAL(context, vp, OBJECT_TO_JSVAL(gjs_cairo_path_from_path(context, path))); return JS_TRUE; } static JSBool copyPathFlat_func(JSContext *context, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(context, vp); JSObject *obj = JS_THIS_OBJECT(context, vp); cairo_path_t *path; cairo_t *cr; if (!gjs_parse_args(context, "", "", argc, argv)) return JS_FALSE; cr = gjs_cairo_context_get_context(context, obj); path = cairo_copy_path_flat(cr); JS_SET_RVAL(context, vp, OBJECT_TO_JSVAL(gjs_cairo_path_from_path(context, path))); return JS_TRUE; } static JSBool mask_func(JSContext *context, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(context, vp); JSObject *obj = JS_THIS_OBJECT(context, vp); JSObject *pattern_wrapper; cairo_pattern_t *pattern; cairo_t *cr; if (!gjs_parse_args(context, "mask", "o", argc, argv, "pattern", &pattern_wrapper)) return JS_FALSE; pattern = gjs_cairo_pattern_get_pattern(context, pattern_wrapper); if (!pattern) { gjs_throw(context, "first argument to mask() should be a pattern"); return JS_FALSE; } cr = gjs_cairo_context_get_context(context, obj); cairo_mask(cr, pattern); if (!gjs_cairo_check_status(context, cairo_status(cr), "context")) return JS_FALSE; JS_SET_RVAL(context, vp, JSVAL_VOID); return JS_TRUE; } static JSBool maskSurface_func(JSContext *context, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(context, vp); JSObject *obj = JS_THIS_OBJECT(context, vp); JSObject *surface_wrapper; double x, y; cairo_surface_t *surface; cairo_t *cr; if (!gjs_parse_args(context, "maskSurface", "off", argc, argv, "surface", &surface_wrapper, "x", &x, "y", &y)) return JS_FALSE; surface = gjs_cairo_surface_get_surface(context, surface_wrapper); if (!surface) { gjs_throw(context, "first argument to maskSurface() should be a surface"); return JS_FALSE; } cr = gjs_cairo_context_get_context(context, obj); cairo_mask_surface(cr, surface, x, y); if (!gjs_cairo_check_status(context, cairo_status(cr), "context")) return JS_FALSE; JS_SET_RVAL(context, vp, JSVAL_VOID); return JS_TRUE; } static JSBool setDash_func(JSContext *context, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(context, vp); JSObject *obj = JS_THIS_OBJECT(context, vp); guint i; cairo_t *cr; JSObject *dashes; double offset; JSBool retval = JS_FALSE; guint len; GArray *dashes_c = NULL; if (!gjs_parse_args(context, "setDash", "of", argc, argv, "dashes", &dashes, "offset", &offset)) return JS_FALSE; JS_AddObjectRoot(context, &dashes); if (!JS_IsArrayObject(context, dashes)) { gjs_throw(context, "dashes must be an array"); goto out; } if (!JS_GetArrayLength(context, dashes, &len)) { gjs_throw(context, "Can't get length of dashes"); goto out; } dashes_c = g_array_sized_new (FALSE, FALSE, sizeof(double), len); for (i = 0; i < len; ++i) { jsval elem; double b; elem = JSVAL_VOID; if (!JS_GetElement(context, dashes, i, &elem)) { goto out; } if (JSVAL_IS_VOID(elem)) continue; if (!JS_ValueToNumber(context, elem, &b)) goto out; if (b <= 0) { gjs_throw(context, "Dash value must be positive"); goto out; } g_array_append_val(dashes_c, b); } cr = gjs_cairo_context_get_context(context, obj); cairo_set_dash(cr, (double*)dashes_c->data, dashes_c->len, offset); JS_SET_RVAL(context, vp, JSVAL_VOID); retval = JS_TRUE; out: if (dashes_c != NULL) g_array_free (dashes_c, TRUE); JS_RemoveObjectRoot(context, &dashes); return retval; } static JSBool setSource_func(JSContext *context, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(context, vp); JSObject *obj = JS_THIS_OBJECT(context, vp); JSObject *pattern_wrapper; cairo_pattern_t *pattern; cairo_t *cr; if (!gjs_parse_args(context, "setSource", "o", argc, argv, "pattern", &pattern_wrapper)) return JS_FALSE; pattern = gjs_cairo_pattern_get_pattern(context, pattern_wrapper); if (!pattern) { gjs_throw(context, "first argument to setSource() should be a pattern"); return JS_FALSE; } cr = gjs_cairo_context_get_context(context, obj); cairo_set_source(cr, pattern); if (!gjs_cairo_check_status(context, cairo_status(cr), "context")) return JS_FALSE; JS_SET_RVAL(context, vp, JSVAL_VOID); return JS_TRUE; } static JSBool setSourceSurface_func(JSContext *context, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(context, vp); JSObject *obj = JS_THIS_OBJECT(context, vp); JSObject *surface_wrapper; double x, y; cairo_surface_t *surface; cairo_t *cr; if (!gjs_parse_args(context, "setSourceSurface", "off", argc, argv, "surface", &surface_wrapper, "x", &x, "y", &y)) return JS_FALSE; surface = gjs_cairo_surface_get_surface(context, surface_wrapper); if (!surface) { gjs_throw(context, "first argument to setSourceSurface() should be a surface"); return JS_FALSE; } cr = gjs_cairo_context_get_context(context, obj); cairo_set_source_surface(cr, surface, x, y); if (!gjs_cairo_check_status(context, cairo_status(cr), "context")) return JS_FALSE; JS_SET_RVAL(context, vp, JSVAL_VOID); return JS_TRUE; } static JSBool showText_func(JSContext *context, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(context, vp); JSObject *obj = JS_THIS_OBJECT(context, vp); char *utf8; cairo_t *cr; if (!gjs_parse_args(context, "showText", "s", argc, argv, "utf8", &utf8)) return JS_FALSE; cr = gjs_cairo_context_get_context(context, obj); cairo_show_text(cr, utf8); g_free(utf8); if (!gjs_cairo_check_status(context, cairo_status(cr), "context")) return JS_FALSE; JS_SET_RVAL(context, vp, JSVAL_VOID); return JS_TRUE; } static JSBool selectFontFace_func(JSContext *context, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(context, vp); JSObject *obj = JS_THIS_OBJECT(context, vp); char *family; cairo_font_slant_t slant; cairo_font_weight_t weight; cairo_t *cr; if (!gjs_parse_args(context, "selectFontFace", "sii", argc, argv, "family", &family, "slang", &slant, "weight", &weight)) return JS_FALSE; cr = gjs_cairo_context_get_context(context, obj); cairo_select_font_face(cr, family, slant, weight); g_free(family); if (!gjs_cairo_check_status(context, cairo_status(cr), "context")) return JS_FALSE; JS_SET_RVAL(context, vp, JSVAL_VOID); return JS_TRUE; } static JSBool popGroup_func(JSContext *context, unsigned argc, jsval *vp) { JSObject *obj = JS_THIS_OBJECT(context, vp); cairo_t *cr; cairo_pattern_t *pattern; JSObject *pattern_wrapper; if (argc > 0) { gjs_throw(context, "Context.popGroup() takes no arguments"); return JS_FALSE; } cr = gjs_cairo_context_get_context(context, obj); pattern = cairo_pop_group(cr); if (!gjs_cairo_check_status(context, cairo_status(cr), "context")) return JS_FALSE; /* pattern belongs to the context, so keep the reference */ pattern_wrapper = gjs_cairo_pattern_from_pattern(context, pattern); if (!pattern_wrapper) { gjs_throw(context, "failed to create pattern"); return JS_FALSE; } JS_SET_RVAL(context, vp, OBJECT_TO_JSVAL(pattern_wrapper)); return JS_TRUE; } static JSBool getSource_func(JSContext *context, unsigned argc, jsval *vp) { JSObject *obj = JS_THIS_OBJECT(context, vp); cairo_t *cr; cairo_pattern_t *pattern; JSObject *pattern_wrapper; if (argc > 0) { gjs_throw(context, "Context.getSource() takes no arguments"); return JS_FALSE; } cr = gjs_cairo_context_get_context(context, obj); pattern = cairo_get_source(cr); if (!gjs_cairo_check_status(context, cairo_status(cr), "context")) return JS_FALSE; /* pattern belongs to the context, so keep the reference */ pattern_wrapper = gjs_cairo_pattern_from_pattern(context, pattern); if (!pattern_wrapper) { gjs_throw(context, "failed to create pattern"); return JS_FALSE; } JS_SET_RVAL(context, vp, OBJECT_TO_JSVAL(pattern_wrapper)); return JS_TRUE; } static JSBool getTarget_func(JSContext *context, unsigned argc, jsval *vp) { JSObject *obj = JS_THIS_OBJECT(context, vp); cairo_t *cr; cairo_surface_t *surface; JSObject *surface_wrapper; if (argc > 0) { gjs_throw(context, "Context.getTarget() takes no arguments"); return JS_FALSE; } cr = gjs_cairo_context_get_context(context, obj); surface = cairo_get_target(cr); if (!gjs_cairo_check_status(context, cairo_status(cr), "context")) return JS_FALSE; /* surface belongs to the context, so keep the reference */ surface_wrapper = gjs_cairo_surface_from_surface(context, surface); if (!surface_wrapper) { /* exception already set */ return JS_FALSE; } JS_SET_RVAL(context, vp, OBJECT_TO_JSVAL(surface_wrapper)); return JS_TRUE; } static JSBool getGroupTarget_func(JSContext *context, unsigned argc, jsval *vp) { JSObject *obj = JS_THIS_OBJECT(context, vp); cairo_t *cr; cairo_surface_t *surface; JSObject *surface_wrapper; if (argc > 0) { gjs_throw(context, "Context.getGroupTarget() takes no arguments"); return JS_FALSE; } cr = gjs_cairo_context_get_context(context, obj); surface = cairo_get_group_target(cr); if (!gjs_cairo_check_status(context, cairo_status(cr), "context")) return JS_FALSE; /* surface belongs to the context, so keep the reference */ surface_wrapper = gjs_cairo_surface_from_surface(context, surface); if (!surface_wrapper) { /* exception already set */ return JS_FALSE; } JS_SET_RVAL(context, vp, OBJECT_TO_JSVAL(surface_wrapper)); return JS_TRUE; } JSFunctionSpec gjs_cairo_context_proto_funcs[] = { { "$dispose", JSOP_WRAPPER((JSNative)dispose_func), 0, 0 }, { "appendPath", JSOP_WRAPPER((JSNative)appendPath_func), 0, 0}, { "arc", JSOP_WRAPPER((JSNative)arc_func), 0, 0 }, { "arcNegative", JSOP_WRAPPER((JSNative)arcNegative_func), 0, 0 }, { "clip", JSOP_WRAPPER((JSNative)clip_func), 0, 0 }, { "clipExtents", JSOP_WRAPPER((JSNative)clipExtents_func), 0, 0 }, { "clipPreserve", JSOP_WRAPPER((JSNative)clipPreserve_func), 0, 0 }, { "closePath", JSOP_WRAPPER((JSNative)closePath_func), 0, 0 }, { "copyPage", JSOP_WRAPPER((JSNative)copyPage_func), 0, 0 }, { "copyPath", JSOP_WRAPPER((JSNative)copyPath_func), 0, 0 }, { "copyPathFlat", JSOP_WRAPPER((JSNative)copyPathFlat_func), 0, 0 }, { "curveTo", JSOP_WRAPPER((JSNative)curveTo_func), 0, 0 }, { "deviceToUser", JSOP_WRAPPER((JSNative)deviceToUser_func), 0, 0 }, { "deviceToUserDistance",JSOP_WRAPPER((JSNative)deviceToUserDistance_func), 0, 0 }, { "fill", JSOP_WRAPPER((JSNative)fill_func), 0, 0 }, { "fillPreserve", JSOP_WRAPPER((JSNative)fillPreserve_func), 0, 0 }, { "fillExtents", JSOP_WRAPPER((JSNative)fillExtents_func), 0, 0 }, // fontExtents { "getAntialias", JSOP_WRAPPER((JSNative)getAntialias_func), 0, 0 }, { "getCurrentPoint", JSOP_WRAPPER((JSNative)getCurrentPoint_func), 0, 0 }, // getDash { "getDashCount", JSOP_WRAPPER((JSNative)getDashCount_func), 0, 0 }, { "getFillRule", JSOP_WRAPPER((JSNative)getFillRule_func), 0, 0 }, // getFontFace // getFontMatrix // getFontOptions { "getGroupTarget", JSOP_WRAPPER((JSNative)getGroupTarget_func), 0, 0 }, { "getLineCap", JSOP_WRAPPER((JSNative)getLineCap_func), 0, 0 }, { "getLineJoin", JSOP_WRAPPER((JSNative)getLineJoin_func), 0, 0 }, { "getLineWidth", JSOP_WRAPPER((JSNative)getLineWidth_func), 0, 0 }, // getMatrix { "getMiterLimit", JSOP_WRAPPER((JSNative)getMiterLimit_func), 0, 0 }, { "getOperator", JSOP_WRAPPER((JSNative)getOperator_func), 0, 0 }, // getScaledFont { "getSource", JSOP_WRAPPER((JSNative)getSource_func), 0, 0 }, { "getTarget", JSOP_WRAPPER((JSNative)getTarget_func), 0, 0 }, { "getTolerance", JSOP_WRAPPER((JSNative)getTolerance_func), 0, 0 }, // glyphPath // glyphExtents { "hasCurrentPoint", JSOP_WRAPPER((JSNative)hasCurrentPoint_func), 0, 0 }, { "identityMatrix", JSOP_WRAPPER((JSNative)identityMatrix_func), 0, 0 }, { "inFill", JSOP_WRAPPER((JSNative)inFill_func), 0, 0 }, { "inStroke", JSOP_WRAPPER((JSNative)inStroke_func), 0, 0 }, { "lineTo", JSOP_WRAPPER((JSNative)lineTo_func), 0, 0 }, { "mask", JSOP_WRAPPER((JSNative)mask_func), 0, 0 }, { "maskSurface", JSOP_WRAPPER((JSNative)maskSurface_func), 0, 0 }, { "moveTo", JSOP_WRAPPER((JSNative)moveTo_func), 0, 0 }, { "newPath", JSOP_WRAPPER((JSNative)newPath_func), 0, 0 }, { "newSubPath", JSOP_WRAPPER((JSNative)newSubPath_func), 0, 0 }, { "paint", JSOP_WRAPPER((JSNative)paint_func), 0, 0 }, { "paintWithAlpha", JSOP_WRAPPER((JSNative)paintWithAlpha_func), 0, 0 }, { "pathExtents", JSOP_WRAPPER((JSNative)pathExtents_func), 0, 0 }, { "popGroup", JSOP_WRAPPER((JSNative)popGroup_func), 0, 0 }, { "popGroupToSource", JSOP_WRAPPER((JSNative)popGroupToSource_func), 0, 0 }, { "pushGroup", JSOP_WRAPPER((JSNative)pushGroup_func), 0, 0 }, { "pushGroupWithContent", JSOP_WRAPPER((JSNative)pushGroupWithContent_func), 0, 0 }, { "rectangle", JSOP_WRAPPER((JSNative)rectangle_func), 0, 0 }, { "relCurveTo", JSOP_WRAPPER((JSNative)relCurveTo_func), 0, 0 }, { "relLineTo", JSOP_WRAPPER((JSNative)relLineTo_func), 0, 0 }, { "relMoveTo", JSOP_WRAPPER((JSNative)relMoveTo_func), 0, 0 }, { "resetClip", JSOP_WRAPPER((JSNative)resetClip_func), 0, 0 }, { "restore", JSOP_WRAPPER((JSNative)restore_func), 0, 0 }, { "rotate", JSOP_WRAPPER((JSNative)rotate_func), 0, 0 }, { "save", JSOP_WRAPPER((JSNative)save_func), 0, 0 }, { "scale", JSOP_WRAPPER((JSNative)scale_func), 0, 0 }, { "selectFontFace", JSOP_WRAPPER((JSNative)selectFontFace_func), 0, 0 }, { "setAntialias", JSOP_WRAPPER((JSNative)setAntialias_func), 0, 0 }, { "setDash", JSOP_WRAPPER((JSNative)setDash_func), 0, 0 }, // setFontFace // setFontMatrix // setFontOptions { "setFontSize", JSOP_WRAPPER((JSNative)setFontSize_func), 0, 0 }, { "setFillRule", JSOP_WRAPPER((JSNative)setFillRule_func), 0, 0 }, { "setLineCap", JSOP_WRAPPER((JSNative)setLineCap_func), 0, 0 }, { "setLineJoin", JSOP_WRAPPER((JSNative)setLineJoin_func), 0, 0 }, { "setLineWidth", JSOP_WRAPPER((JSNative)setLineWidth_func), 0, 0 }, // setMatrix { "setMiterLimit", JSOP_WRAPPER((JSNative)setMiterLimit_func), 0, 0 }, { "setOperator", JSOP_WRAPPER((JSNative)setOperator_func), 0, 0 }, // setScaledFont { "setSource", JSOP_WRAPPER((JSNative)setSource_func), 0, 0 }, { "setSourceRGB", JSOP_WRAPPER((JSNative)setSourceRGB_func), 0, 0 }, { "setSourceRGBA", JSOP_WRAPPER((JSNative)setSourceRGBA_func), 0, 0 }, { "setSourceSurface", JSOP_WRAPPER((JSNative)setSourceSurface_func), 0, 0 }, { "setTolerance", JSOP_WRAPPER((JSNative)setTolerance_func), 0, 0 }, // showGlyphs { "showPage", JSOP_WRAPPER((JSNative)showPage_func), 0, 0 }, { "showText", JSOP_WRAPPER((JSNative)showText_func), 0, 0 }, // showTextGlyphs { "stroke", JSOP_WRAPPER((JSNative)stroke_func), 0, 0 }, { "strokeExtents", JSOP_WRAPPER((JSNative)strokeExtents_func), 0, 0 }, { "strokePreserve", JSOP_WRAPPER((JSNative)strokePreserve_func), 0, 0 }, // textPath // textExtends // transform { "translate", JSOP_WRAPPER((JSNative)translate_func), 0, 0 }, { "userToDevice", JSOP_WRAPPER((JSNative)userToDevice_func), 0, 0 }, { "userToDeviceDistance", JSOP_WRAPPER((JSNative)userToDeviceDistance_func), 0, 0 }, { NULL } }; JSObject * gjs_cairo_context_from_context(JSContext *context, cairo_t *cr) { JSObject *object; object = JS_NewObject(context, &gjs_cairo_context_class, NULL, NULL); if (!object) return NULL; _gjs_cairo_context_construct_internal(context, object, cr); return object; } cairo_t * gjs_cairo_context_get_context(JSContext *context, JSObject *object) { GjsCairoContext *priv; priv = priv_from_js(context, object); if (priv == NULL) return NULL; return priv->cr; } static JSBool context_to_g_argument(JSContext *context, jsval value, const char *arg_name, GjsArgumentType argument_type, GITransfer transfer, gboolean may_be_null, GArgument *arg) { JSObject *obj; cairo_t *cr; obj = JSVAL_TO_OBJECT(value); cr = gjs_cairo_context_get_context(context, obj); if (!cr) return JS_FALSE; if (transfer == GI_TRANSFER_EVERYTHING) cairo_destroy(cr); arg->v_pointer = cr; return JS_TRUE; } static JSBool context_from_g_argument(JSContext *context, jsval *value_p, GArgument *arg) { JSObject *obj; obj = gjs_cairo_context_from_context(context, (cairo_t*)arg->v_pointer); if (!obj) return JS_FALSE; *value_p = OBJECT_TO_JSVAL(obj); return JS_TRUE; } static JSBool context_release_argument(JSContext *context, GITransfer transfer, GArgument *arg) { cairo_destroy((cairo_t*)arg->v_pointer); return JS_TRUE; } static GjsForeignInfo foreign_info = { context_to_g_argument, context_from_g_argument, context_release_argument }; void gjs_cairo_context_init(JSContext *context) { gjs_struct_foreign_register("cairo", "Context", &foreign_info); } cjs-2.8.0/modules/coverage.js0000664000175000017500000006077012610172032015031 0ustar fabiofabio/* * Copyright (c) 2014 Endless Mobile, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. * * Authored By: Sam Spilsbury */ function getSubNodesForNode(node) { let subNodes = []; switch (node.type) { /* These statements have a single body */ case 'LabelledStatement': case 'WithStatement': case 'LetStatement': case 'ForInStatement': case 'ForOfStatement': case 'FunctionDeclaration': case 'FunctionExpression': case 'ArrowExpression': case 'CatchClause': subNodes.push(node.body); break; case 'WhileStatement': case 'DoWhileStatement': subNodes.push(node.body); subNodes.push(node.test); break; case 'ForStatement': if (node.init !== null) subNodes.push(node.init); if (node.test !== null) subNodes.push(node.test); if (node.update !== null) subNodes.push(node.update); subNodes.push(node.body); break; case 'BlockStatement': Array.prototype.push.apply(subNodes, node.body); break; case 'ThrowStatement': case 'ReturnStatement': if (node.argument !== null) subNodes.push(node.argument); break; case 'ExpressionStatement': subNodes.push(node.expression); break; case 'AssignmentExpression': subNodes.push(node.left, node.right); break; case 'ObjectExpression': node.properties.forEach(function(prop) { subNodes.push(prop.value); }); break; /* It is very possible that there might be something * interesting in the function arguments, so we need to * walk them too */ case 'NewExpression': case 'CallExpression': Array.prototype.push.apply(subNodes, node.arguments); subNodes.push(node.callee); break; /* These statements might have multiple different bodies * depending on whether or not they were entered */ case 'IfStatement': subNodes = [node.test, node.consequent]; if (node.alternate !== null) subNodes.push(node.alternate); break; case 'TryStatement': subNodes = [node.block]; if (node.handler !== null) subNodes.push(node.handler); if (node.finalizer !== null) subNodes.push(node.finalizer); break; case 'SwitchStatement': for (let caseClause of node.cases) { caseClause.consequent.forEach(function(expression) { subNodes.push(expression); }); } break; /* Variable declarations might be initialized to * some expression, so traverse the tree and see if * we can get into the expression */ case 'VariableDeclaration': node.declarations.forEach(function (declarator) { if (declarator.init !== null) subNodes.push(declarator.init); }); break; } return subNodes; } function collectForSubNodes(subNodes, collector) { let result = []; if (subNodes !== undefined && subNodes.length > 0) { subNodes.forEach(function(node) { let nodeResult = collector(node); if (nodeResult !== undefined) Array.prototype.push.apply(result, nodeResult); let subNodeResults = collectForSubNodes(getSubNodesForNode(node), collector); Array.prototype.push.apply(result, subNodeResults); }); } return result; } /* Unfortunately, the Reflect API doesn't give us enough information to * uniquely identify a function. A function might be anonymous, in which * case the JS engine uses some heurisitics to get a unique string identifier * but that isn't available to us here. * * There's also the edge-case where functions with the same name might be * defined within the same scope, or multiple anonymous functions might * be defined on the same line. In that case, it will look like we entered * the same function multiple times since we can't get column information * from the engine-side. * * For instance: * * 1. function f() { * function f() { * } * } * * 2. let a = function() { function(a, b) {} }; * * 3. let a = function() { function () {} } * * We can work-around case 1 by using the line numbers to get a unique identifier. * We can work-around case 2 by using the arguments length to get a unique identifier * We can't work-around case 3. The best thing we can do is warn that coverage * reports might be inaccurate as a result */ function functionsForNode(node) { let functionNames = []; switch (node.type) { case 'FunctionDeclaration': case 'FunctionExpression': if (node.id !== null) { functionNames.push({ name: node.id.name, line: node.loc.start.line, n_params: node.params.length }); } /* If the function wasn't found, we just push a name * that looks like 'function:lineno' to signify that * this was an anonymous function. If the coverage tool * enters a function with no name (but a line number) * then it can probably use this information to * figure out which function it was */ else { functionNames.push({ name: null, line: node.loc.start.line, n_params: node.params.length }); } } return functionNames; } function functionsForAST(ast) { return collectForSubNodes(ast.body, functionsForNode); } /* If a branch' consequent is a block statement, there's * a chance that it could start on the same line, although * that's not where execution really starts. If it is * a block statement then handle the case and go * to the first line where execution starts */ function getBranchExitStartLine(branchBodyNode) { switch (branchBodyNode.type) { case 'BlockStatement': /* Hit a block statement, but nothing inside, can never * be executed, tell the upper level to move on to the next * statement */ if (branchBodyNode.body.length === 0) return -1; /* Handle the case where we have nested block statements * that never actually get to executable code by handling * all statements within a block */ for (let statement of branchBodyNode.body) { let startLine = getBranchExitStartLine(statement); if (startLine !== -1) return startLine; } /* Couldn't find an executable line inside this block */ return -1; case 'SwitchCase': /* Hit a switch, but nothing inside, can never * be executed, tell the upper level to move on to the next * statement */ if (branchBodyNode.consequent.length === 0) return -1; /* Handle the case where we have nested block statements * that never actually get to executable code by handling * all statements within a block */ for (let statement of branchBodyNode.consequent) { let startLine = getBranchExitStartLine(statement); if (startLine !== -1) { return startLine; } } /* Couldn't find an executable line inside this block */ return -1; /* These types of statements are never executable */ case 'EmptyStatement': case 'LabelledStatement': return -1; default: break; } return branchBodyNode.loc.start.line; } function branchesForNode(node) { let branches = []; let branchExitNodes = []; switch (node.type) { case 'IfStatement': branchExitNodes.push(node.consequent); if (node.alternate !== null) branchExitNodes.push(node.alternate); break; case 'WhileStatement': case 'DoWhileStatement': branchExitNodes.push(node.body); break; case 'SwitchStatement': /* The case clauses by themselves are never executable * so find the actual exits */ Array.prototype.push.apply(branchExitNodes, node.cases); break; default: break; } let branchExitStartLines = branchExitNodes.map(getBranchExitStartLine); branchExitStartLines = branchExitStartLines.filter(function(line) { return line !== -1; }); /* Branch must have at least one exit */ if (branchExitStartLines.length) { branches.push({ point: node.loc.start.line, exits: branchExitStartLines }); } return branches; } function branchesForAST(ast) { return collectForSubNodes(ast.body, branchesForNode); } function expressionLinesForNode(statement) { let expressionLines = []; let expressionNodeTypes = ['Expression', 'Declaration', 'Statement', 'Clause', 'Literal', 'Identifier']; if (expressionNodeTypes.some(function(type) { return statement.type.indexOf(type) !== -1; })) { /* These expressions aren't executable on their own */ switch (statement.type) { case 'FunctionDeclaration': case 'LiteralExpression': break; /* Perplexingly, an empty block statement is actually executable, * push it if it is */ case 'BlockStatement': if (statement.body.length !== 0) break; expressionLines.push(statement.loc.start.line); break; default: expressionLines.push(statement.loc.start.line); break; } } return expressionLines; } function deduplicate(list) { return list.filter(function(elem, pos, self) { return self.indexOf(elem) === pos; }); } function expressionLinesForAST(ast) { let allExpressions = collectForSubNodes(ast.body, expressionLinesForNode); allExpressions = deduplicate(allExpressions); return allExpressions; } function _getNumberOfLinesForScript(scriptContents) { let scriptLines = scriptContents.split("\n"); let scriptLineCount = scriptLines.length; return scriptLineCount; } /* * The created array is a 1-1 representation of the hitcount in the filename. Each * element refers to an individual line. In order to avoid confusion, our array * is zero indexed, but the zero'th line is always ignored and the first element * refers to the first line of the file. * * A value of undefined for an element means that the line is non-executable and never actually * reached. A value of 0 means that it was executable but never reached. A positive value * indicates the hit count. * * We care about non-executable lines because we don't want to report coverage misses for * lines that could have never been executed anyways. * * The reason for using a 1-1 mapping as opposed to an array of key-value pairs for executable * lines is: * 1. Lookup speed is O(1) instead of O(log(n)) * 2. There's a possibility we might hit a line which we thought was non-executable, in which * case we can neatly handle the error by marking that line executable. A hit on a line * we thought was non-executable is not as much of a problem as noise generated by * ostensible "misses" which could in fact never be executed. * */ function _expressionLinesToCounters(expressionLines, nLines) { expressionLines.sort(function(left, right) { return left - right; }); let expressionLinesIndex = 0; let counters = new Array(nLines); if (expressionLines.length === 0) return counters; for (let i = 1; i < counters.length; i++) { if (expressionLines[expressionLinesIndex] == i) { counters[i] = 0; expressionLinesIndex++; } } return counters; } /* As above, we are creating a 1-1 representation of script lines to potential branches * where each element refers to a 1-index line (with the zero'th ignored). * * Each element is a GjsCoverageBranchData which, if the line at the element * position describes a branch, will be populated with a GjsReflectedScriptBranchInfo * and an array of unsigned each specifying the hit-count for each potential branch * in the branch info */ function _branchesToBranchCounters(branches, nLines) { branches.sort(function(left, right) { return left.point - right.point; }); let branchIndex = 0; let counters = new Array(nLines); if (branches.length === 0) return counters; for (let i = 1; i < counters.length; i++) { let branch = branches[branchIndex]; let branchPoint = branch.point; if (branchPoint == i) { counters[i] = { point: branchPoint, exits: branch.exits.map(function(exit) { return { line: exit, hitCount: 0 }; }), lastExit: (function() { let lastExitLine = 0; for (let exit of branch.exits) { if (lastExitLine < exit) lastExitLine = exit; } return lastExitLine; })(), hit: false }; if (++branchIndex >= branches.length) break; } } return counters; } function _getFunctionKeyFromReflectedFunction(func) { let name = func.name !== null ? func.name : '(anonymous)'; let line = func.line; let n_params = func.n_params; return name + ':' + line + ':' + n_params; } function _functionsToFunctionCounters(functions) { let functionCounters = {}; functions.forEach(function(func) { let functionKey = _getFunctionKeyFromReflectedFunction(func); functionCounters[functionKey] = { hitCount: 0 }; }); return functionCounters; } function _populateKnownFunctions(functions, nLines) { let knownFunctions = new Array(nLines); functions.forEach(function(func) { knownFunctions[func.line] = true; }); return knownFunctions; } /** * _incrementFunctionCounters * * functionCounters: An object which is a key-value pair with the following schema: * { * "key" : { line, hitCount } * } * linesWithKnownFunctions: An array of either "true" or undefined, with true set to * each element corresponding to a line that we know has a function on it. * name: The name of the function or "(anonymous)" if it has no name * line: The line at which execution first started on this function. * nArgs: The number of arguments this function has. */ function _incrementFunctionCounters(functionCounters, linesWithKnownFunctions, name, line, nArgs) { let functionKey = name + ':' + line + ':' + nArgs; let functionCountersForKey = functionCounters[functionKey]; /* Its possible that the JS Engine might enter a funciton * at an executable line which is a little bit past the * actual definition. Roll backwards until we reach the * last known function definition line which we kept * track of earlier to see if we can find this function first */ if (functionCountersForKey === undefined) { do { --line; functionKey = name + ':' + line + ':' + nArgs; functionCountersForKey = functionCounters[functionKey]; } while(linesWithKnownFunctions[line] !== true) } if (functionCountersForKey !== undefined) { functionCountersForKey.hitCount++; } else { throw new Error("expected Reflect to find function " + functionKey); } } /** * _incrementExpressionCounters * * expressonCounters: An array of either a hit count for a found * executable line or undefined for a known non-executable line. * line: an executed line * reporter: A function a single integer to report back when * we executed lines that we didn't expect */ function _incrementExpressionCounters(expressionCounters, offsetLine, reporter) { let expressionCountersLen = expressionCounters.length; if (offsetLine >= expressionCountersLen) throw new Error("Executed line " + offsetLine + " which was past the highest-found line " + expressionCountersLen); /* If this happens it is not a huge problem - though it does * mean that the reflection machinery is not doing its job, so we should * print a debug message about it in case someone is interested. * * The reason why we don't have a proper warning is because it * is difficult to determine what the SpiderMonkey program counter * will actually pass over, especially function declarations for some * reason: * * function f(a,b) { * a = 1; * } * * In some cases, the declaration itself will be executed * but in other cases it won't be. Reflect.parse tells us that * the only two expressions on that line are a FunctionDeclaration * and BlockStatement, neither of which would ordinarily be * executed */ if (expressionCounters[offsetLine] === undefined) { if (reporter !== undefined) reporter(offsetLine); expressionCounters[offsetLine] = 0; } expressionCounters[offsetLine]++; } function _BranchTracker(branchCounters) { this._branchCounters = branchCounters; this._activeBranch = undefined; this.incrementBranchCounters = function(offsetLine) { /* Set branch exits or find a new active branch */ let activeBranch = this._activeBranch; if (activeBranch !== undefined) { activeBranch.exits.forEach(function(exit) { if (exit.line === offsetLine) { exit.hitCount++; } }); /* Only set the active branch to undefined once we're * completely outside of it, since we might be in a case statement where * we need to check every possible option before jumping to an * exit */ if (offsetLine >= activeBranch.lastExit) this._activeBranch = undefined; } let nextActiveBranch = branchCounters[offsetLine]; if (nextActiveBranch !== undefined) { this._activeBranch = nextActiveBranch; this._activeBranch.hit = true; } }; } function _convertFunctionCountersToArray(functionCounters) { let arrayReturn = []; /* functionCounters is an object so convert it to * an array-of-object using the key as a property * of that object */ for (let key in functionCounters) { let func = functionCounters[key]; arrayReturn.push({ name: key, hitCount: func.hitCount }); } arrayReturn.sort(function(left, right) { if (left.name < right.name) return -1; else if (left.name > right.name) return 1; else return 0; }); return arrayReturn; } function CoverageStatisticsContainer(files) { let pendingFiles = files; let coveredFiles = {}; function wantsStatisticsFor(filename) { return pendingFiles.indexOf(filename) !== -1; } function createStatisticsFor(filename) { let idx = pendingFiles.indexOf(filename); pendingFiles.splice(idx, 1); let contents = getFileContents(filename); let reflection = Reflect.parse(contents); let nLines = _getNumberOfLinesForScript(contents); let functions = functionsForAST(reflection); return { contents: contents, nLines: nLines, expressionCounters: _expressionLinesToCounters(expressionLinesForAST(reflection), nLines), branchCounters: _branchesToBranchCounters(branchesForAST(reflection), nLines), functionCounters: _functionsToFunctionCounters(functions), linesWithKnownFunctions: _populateKnownFunctions(functions, nLines) }; } function ensureStatisticsFor(filename) { if (!coveredFiles[filename] && wantsStatisticsFor(filename)) coveredFiles[filename] = createStatisticsFor(filename); return coveredFiles[filename]; } this.fetchStatistics = function(filename) { let statistics = ensureStatisticsFor(filename); if (statistics === undefined) throw new Error('Not tracking statistics for ' + filename); return statistics; }; } /** * Main class tying together the Debugger object and CoverageStatisticsContainer. * * It isn't poissible to unit test this class because it depends on running * Debugger which in turn depends on objects injected in from another compartment */ function CoverageStatistics(files) { this.container = new CoverageStatisticsContainer(files); let fetchStatistics = this.container.fetchStatistics.bind(this.container); /* 'debuggee' comes from the invocation from * a separate compartment inside of coverage.cpp */ this.dbg = new Debugger(debuggee); this.getNumberOfLinesFor = function(filename) { return fetchStatistics(filename).nLines; }; this.getExecutedLinesFor = function(filename) { return fetchStatistics(filename).expressionCounters; }; this.getBranchesFor = function(filename) { return fetchStatistics(filename).branchCounters; }; this.getFunctionsFor = function(filename) { let functionCounters = fetchStatistics(filename).functionCounters; return _convertFunctionCountersToArray(functionCounters); }; this.dbg.onEnterFrame = function(frame) { let statistics; try { statistics = fetchStatistics(frame.script.url); } catch (e) { /* We don't care about this frame, return */ return undefined; } /* Log function calls */ if (frame.callee !== null && frame.callee.callable) { let name = frame.callee.name ? frame.callee.name : "(anonymous)"; let line = frame.script.getOffsetLine(frame.offset); let nArgs = frame.callee.parameterNames.length; _incrementFunctionCounters(statistics.functionCounters, statistics.linesWithKnownFunctions, name, line, nArgs); } /* Upon entering the frame, the active branch is always inactive */ frame._branchTracker = new _BranchTracker(statistics.branchCounters); /* Set single-step hook */ frame.onStep = function() { /* Line counts */ let offset = this.offset; let offsetLine = this.script.getOffsetLine(offset); _incrementExpressionCounters(statistics.expressionCounters, offsetLine, function(line) { warning("executed " + frame.script.url + ":" + offsetLine + " which we thought wasn't executable"); }); this._branchTracker.incrementBranchCounters(offsetLine); }; return undefined; }; } cjs-2.8.0/modules/cairo.js0000664000175000017500000000554712610172032014334 0ustar fabiofabio// Copyright 2010 litl, LLC. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. const Lang = imports.lang; const Antialias = { DEFAULT: 0, NONE: 1, GRAY: 2, SUBPIXEL: 3 }; const Content = { COLOR : 0x1000, ALPHA : 0x2000, COLOR_ALPHA : 0x3000 }; const Extend = { NONE : 0, REPEAT : 1, REFLECT : 2, PAD : 3 }; const FillRule = { WINDING: 0, EVEN_ODD: 1 }; const Filter = { FAST : 0, GOOD : 1, BEST : 2, NEAREST : 3, BILINEAR : 4, GAUSSIAN : 5 }; const FontSlant = { NORMAL: 0, ITALIC: 1, OBLIQUE: 2 }; const FontWeight = { NORMAL : 0, BOLD : 1 }; const Format = { ARGB32 : 0, RGB24 : 1, A8 : 2, A1 : 3, // The value of 4 is reserved by a deprecated enum value RGB16_565: 5 }; const LineCap = { BUTT: 0, ROUND: 1, SQUASH: 2 }; const LineJoin = { MITER: 0, ROUND: 1, BEVEL: 2 }; const Operator = { CLEAR: 0, SOURCE: 1, OVER: 2, IN : 3, OUT : 4, ATOP : 5, DEST : 6, DEST_OVER : 7, DEST_IN : 8, DEST_OUT : 9, DEST_ATOP : 10, XOR : 11, ADD : 12, SATURATE : 13, MULTIPLY : 14, SCREEN : 15, OVERLAY : 16, DARKEN : 17, LIGHTEN : 18, COLOR_DODGE : 19, COLOR_BURN : 20, HARD_LIGHT : 21, SOFT_LIGHT : 22, DIFFERENCE : 23, EXCLUSION : 24, HSL_HUE : 25, HSL_SATURATION : 26, HSL_COLOR : 27, HSL_LUMINOSITY : 28 }; const PatternType = { SOLID : 0, SURFACE : 1, LINEAR : 2, RADIAL : 3 }; const SurfaceType = { IMAGE : 0, PDF : 1, PS : 2, XLIB : 3, XCB : 4, GLITZ : 5, QUARTZ : 6, WIN32 : 7, BEOS : 8, DIRECTFB : 9, SVG : 10, OS2 : 11, WIN32_PRINTING : 12, QUARTZ_IMAGE : 13 }; // Merge stuff defined in native code Lang.copyProperties(imports.cairoNative, this); cjs-2.8.0/modules/cairo-pdf-surface.cpp0000664000175000017500000000671512610172032016675 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2010 litl, LLC. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include "cairo-private.h" #if CAIRO_HAS_PDF_SURFACE #include GJS_DEFINE_PROTO("CairoPDFSurface", cairo_pdf_surface) GJS_NATIVE_CONSTRUCTOR_DECLARE(cairo_pdf_surface) { GJS_NATIVE_CONSTRUCTOR_VARIABLES(cairo_pdf_surface) char *filename; double width, height; cairo_surface_t *surface; GJS_NATIVE_CONSTRUCTOR_PRELUDE(cairo_pdf_surface); if (!gjs_parse_args(context, "PDFSurface", "sff", argc, argv, "filename", &filename, "width", &width, "height", &height)) return JS_FALSE; surface = cairo_pdf_surface_create(filename, width, height); if (!gjs_cairo_check_status(context, cairo_surface_status(surface), "surface")) { g_free(filename); return JS_FALSE; } gjs_cairo_surface_construct(context, object, surface); cairo_surface_destroy(surface); g_free(filename); GJS_NATIVE_CONSTRUCTOR_FINISH(cairo_pdf_surface); return JS_TRUE; } static void gjs_cairo_pdf_surface_finalize(JSFreeOp *fop, JSObject *obj) { gjs_cairo_surface_finalize_surface(fop, obj); } JSPropertySpec gjs_cairo_pdf_surface_proto_props[] = { { NULL } }; JSFunctionSpec gjs_cairo_pdf_surface_proto_funcs[] = { { NULL } }; JSObject * gjs_cairo_pdf_surface_from_surface(JSContext *context, cairo_surface_t *surface) { JSObject *object; g_return_val_if_fail(context != NULL, NULL); g_return_val_if_fail(surface != NULL, NULL); g_return_val_if_fail(cairo_surface_get_type(surface) == CAIRO_SURFACE_TYPE_PDF, NULL); object = JS_NewObject(context, &gjs_cairo_pdf_surface_class, NULL, NULL); if (!object) { gjs_throw(context, "failed to create pdf surface"); return NULL; } gjs_cairo_surface_construct(context, object, surface); return object; } #else JSObject * gjs_cairo_pdf_surface_from_surface(JSContext *context, cairo_surface_t *surface) { gjs_throw(context, "could not create PDF surface, recompile cairo and gjs with " "PDF support."); return NULL; } #endif /* CAIRO_HAS_PDF_SURFACE */ cjs-2.8.0/modules/cairo-ps-surface.cpp0000664000175000017500000000713512610172032016543 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2010 litl, LLC. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include "cairo-private.h" #if CAIRO_HAS_PS_SURFACE #include GJS_DEFINE_PROTO("CairoPSSurface", cairo_ps_surface) GJS_NATIVE_CONSTRUCTOR_DECLARE(cairo_ps_surface) { GJS_NATIVE_CONSTRUCTOR_VARIABLES(cairo_ps_surface) char *filename; double width, height; cairo_surface_t *surface; GJS_NATIVE_CONSTRUCTOR_PRELUDE(cairo_ps_surface); if (!gjs_parse_args(context, "PSSurface", "sff", argc, argv, "filename", &filename, "width", &width, "height", &height)) return JS_FALSE; surface = cairo_ps_surface_create(filename, width, height); if (!gjs_cairo_check_status(context, cairo_surface_status(surface), "surface")) { g_free(filename); return JS_FALSE; } gjs_cairo_surface_construct(context, object, surface); cairo_surface_destroy(surface); g_free(filename); GJS_NATIVE_CONSTRUCTOR_FINISH(cairo_ps_surface); return JS_TRUE; } static void gjs_cairo_ps_surface_finalize(JSFreeOp *fop, JSObject *obj) { gjs_cairo_surface_finalize_surface(fop, obj); } JSPropertySpec gjs_cairo_ps_surface_proto_props[] = { { NULL } }; JSFunctionSpec gjs_cairo_ps_surface_proto_funcs[] = { // restrictToLevel // getLevels // levelToString // setEPS // getEPS // setSize // dscBeginSetup // dscBeginPageSetup // dscComment { NULL } }; JSObject * gjs_cairo_ps_surface_from_surface(JSContext *context, cairo_surface_t *surface) { JSObject *object; g_return_val_if_fail(context != NULL, NULL); g_return_val_if_fail(surface != NULL, NULL); g_return_val_if_fail(cairo_surface_get_type(surface) == CAIRO_SURFACE_TYPE_PS, NULL); object = JS_NewObject(context, &gjs_cairo_ps_surface_class, NULL, NULL); if (!object) { gjs_throw(context, "failed to create ps surface"); return NULL; } gjs_cairo_surface_construct(context, object, surface); return object; } #else JSObject * gjs_cairo_ps_surface_from_surface(JSContext *context, cairo_surface_t *surface) { gjs_throw(context, "could not create PS surface, recompile cairo and gjs with " "PS support."); return NULL; } #endif /* CAIRO_HAS_PS_SURFACE */ cjs-2.8.0/modules/cairo-image-surface.cpp0000664000175000017500000001621512610172032017202 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2010 litl, LLC. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include "cairo-private.h" GJS_DEFINE_PROTO("CairoImageSurface", cairo_image_surface) GJS_NATIVE_CONSTRUCTOR_DECLARE(cairo_image_surface) { GJS_NATIVE_CONSTRUCTOR_VARIABLES(cairo_image_surface) int format, width, height; cairo_surface_t *surface; GJS_NATIVE_CONSTRUCTOR_PRELUDE(cairo_image_surface); // create_for_data optional parameter if (!gjs_parse_args(context, "ImageSurface", "iii", argc, argv, "format", &format, "width", &width, "height", &height)) return JS_FALSE; surface = cairo_image_surface_create((cairo_format_t) format, width, height); if (!gjs_cairo_check_status(context, cairo_surface_status(surface), "surface")) return JS_FALSE; gjs_cairo_surface_construct(context, object, surface); cairo_surface_destroy(surface); GJS_NATIVE_CONSTRUCTOR_FINISH(cairo_image_surface); return JS_TRUE; } static void gjs_cairo_image_surface_finalize(JSFreeOp *fop, JSObject *obj) { gjs_cairo_surface_finalize_surface(fop, obj); } JSPropertySpec gjs_cairo_image_surface_proto_props[] = { { NULL } }; static JSBool createFromPNG_func(JSContext *context, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(context, vp); char *filename; cairo_surface_t *surface; JSObject *surface_wrapper; if (!gjs_parse_args(context, "createFromPNG", "s", argc, argv, "filename", &filename)) return JS_FALSE; surface = cairo_image_surface_create_from_png(filename); if (!gjs_cairo_check_status(context, cairo_surface_status(surface), "surface")) return JS_FALSE; surface_wrapper = JS_NewObject(context, &gjs_cairo_image_surface_class, NULL, NULL); if (!surface_wrapper) { gjs_throw(context, "failed to create surface"); return JS_FALSE; } gjs_cairo_surface_construct(context, surface_wrapper, surface); cairo_surface_destroy(surface); JS_SET_RVAL(context, vp, OBJECT_TO_JSVAL(surface_wrapper)); return JS_TRUE; } static JSBool getFormat_func(JSContext *context, unsigned argc, jsval *vp) { JSObject *obj = JS_THIS_OBJECT(context, vp); cairo_surface_t *surface; cairo_format_t format; if (argc > 1) { gjs_throw(context, "ImageSurface.getFormat() takes no arguments"); return JS_FALSE; } surface = gjs_cairo_surface_get_surface(context, obj); format = cairo_image_surface_get_format(surface); if (!gjs_cairo_check_status(context, cairo_surface_status(surface), "surface")) return JS_FALSE; JS_SET_RVAL(context, vp, INT_TO_JSVAL(format)); return JS_TRUE; } static JSBool getWidth_func(JSContext *context, unsigned argc, jsval *vp) { JSObject *obj = JS_THIS_OBJECT(context, vp); cairo_surface_t *surface; int width; if (argc > 1) { gjs_throw(context, "ImageSurface.getWidth() takes no arguments"); return JS_FALSE; } surface = gjs_cairo_surface_get_surface(context, obj); width = cairo_image_surface_get_width(surface); if (!gjs_cairo_check_status(context, cairo_surface_status(surface), "surface")) return JS_FALSE; JS_SET_RVAL(context, vp, INT_TO_JSVAL(width)); return JS_TRUE; } static JSBool getHeight_func(JSContext *context, unsigned argc, jsval *vp) { JSObject *obj = JS_THIS_OBJECT(context, vp); cairo_surface_t *surface; int height; if (argc > 1) { gjs_throw(context, "ImageSurface.getHeight() takes no arguments"); return JS_FALSE; } surface = gjs_cairo_surface_get_surface(context, obj); height = cairo_image_surface_get_height(surface); if (!gjs_cairo_check_status(context, cairo_surface_status(surface), "surface")) return JS_FALSE; JS_SET_RVAL(context, vp, INT_TO_JSVAL(height)); return JS_TRUE; } static JSBool getStride_func(JSContext *context, unsigned argc, jsval *vp) { JSObject *obj = JS_THIS_OBJECT(context, vp); cairo_surface_t *surface; int stride; if (argc > 1) { gjs_throw(context, "ImageSurface.getStride() takes no arguments"); return JS_FALSE; } surface = gjs_cairo_surface_get_surface(context, obj); stride = cairo_image_surface_get_stride(surface); if (!gjs_cairo_check_status(context, cairo_surface_status(surface), "surface")) return JS_FALSE; JS_SET_RVAL(context, vp, INT_TO_JSVAL(stride)); return JS_TRUE; } JSFunctionSpec gjs_cairo_image_surface_proto_funcs[] = { { "createFromPNG", JSOP_WRAPPER((JSNative)createFromPNG_func), 0, 0}, // getData { "getFormat", JSOP_WRAPPER((JSNative)getFormat_func), 0, 0 }, { "getWidth", JSOP_WRAPPER((JSNative)getWidth_func), 0, 0 }, { "getHeight", JSOP_WRAPPER((JSNative)getHeight_func), 0, 0 }, { "getStride", JSOP_WRAPPER((JSNative)getStride_func), 0, 0 }, { NULL } }; JSObject * gjs_cairo_image_surface_from_surface(JSContext *context, cairo_surface_t *surface) { JSObject *object; g_return_val_if_fail(context != NULL, NULL); g_return_val_if_fail(surface != NULL, NULL); g_return_val_if_fail(cairo_surface_get_type(surface) == CAIRO_SURFACE_TYPE_IMAGE, NULL); object = JS_NewObject(context, &gjs_cairo_image_surface_class, NULL, NULL); if (!object) { gjs_throw(context, "failed to create image surface"); return NULL; } gjs_cairo_surface_construct(context, object, surface); return object; } void gjs_cairo_image_surface_init(JSContext *context, JSObject *module_obj) { if (!JS_DefineFunction(context, module_obj, "createFromPNG", (JSNative)createFromPNG_func, 1, GJS_MODULE_PROP_FLAGS)) return; } cjs-2.8.0/modules/system.h0000664000175000017500000000300612610172032014362 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * Copyright (c) 2012 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_SYSTEM_H__ #define __GJS_SYSTEM_H__ #include #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS JSBool gjs_js_define_system_stuff (JSContext *context, JSObject **module_out); G_END_DECLS #endif /* __GJS_SYSTEM_H__ */ cjs-2.8.0/modules/console.cpp0000664000175000017500000001646312610172032015046 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* vim: set ts=8 sw=4 et tw=78: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "config.h" #include #include #ifdef HAVE_LIBREADLINE #include #include #include #endif #include #include #include #include #include #include "console.h" static void gjs_console_error_reporter(JSContext *cx, const char *message, JSErrorReport *report) { int i, j, k, n; char *prefix, *tmp; const char *ctmp; if (!report) { fprintf(stderr, "%s\n", message); return; } prefix = NULL; if (report->filename) prefix = g_strdup_printf("%s:", report->filename); if (report->lineno) { tmp = prefix; prefix = g_strdup_printf("%s%u: ", tmp ? tmp : "", report->lineno); g_free(tmp); } if (JSREPORT_IS_WARNING(report->flags)) { tmp = prefix; prefix = g_strdup_printf("%s%swarning: ", tmp ? tmp : "", JSREPORT_IS_STRICT(report->flags) ? "strict " : ""); g_free(tmp); } /* embedded newlines -- argh! */ while ((ctmp = strchr(message, '\n')) != NULL) { ctmp++; if (prefix) fputs(prefix, stderr); fwrite(message, 1, ctmp - message, stderr); message = ctmp; } /* If there were no filename or lineno, the prefix might be empty */ if (prefix) fputs(prefix, stderr); fputs(message, stderr); if (!report->linebuf) { fputc('\n', stderr); goto out; } /* report->linebuf usually ends with a newline. */ n = strlen(report->linebuf); fprintf(stderr, ":\n%s%s%s%s", prefix, report->linebuf, (n > 0 && report->linebuf[n-1] == '\n') ? "" : "\n", prefix); n = ((char*)report->tokenptr) - ((char*) report->linebuf); for (i = j = 0; i < n; i++) { if (report->linebuf[i] == '\t') { for (k = (j + 8) & ~7; j < k; j++) { fputc('.', stderr); } continue; } fputc('.', stderr); j++; } fputs("^\n", stderr); out: g_free(prefix); } #ifdef HAVE_LIBREADLINE static JSBool gjs_console_readline(JSContext *cx, char **bufp, FILE *file, const char *prompt) { char *line; line = readline(prompt); if (!line) return JS_FALSE; if (line[0] != '\0') add_history(line); *bufp = line; return JS_TRUE; } #else static JSBool gjs_console_readline(JSContext *cx, char **bufp, FILE *file, const char *prompt) { char line[256]; fprintf(stdout, "%s", prompt); fflush(stdout); if (!fgets(line, sizeof line, file)) return JS_FALSE; *bufp = g_strdup(line); return JS_TRUE; } #endif JSBool gjs_console_interact(JSContext *context, unsigned argc, jsval *vp) { JSObject *object = JS_THIS_OBJECT(context, vp); gboolean eof = FALSE; jsval result; JSString *str; GString *buffer = NULL; char *temp_buf = NULL; int lineno; int startline; FILE *file = stdin; JS_SetErrorReporter(context, gjs_console_error_reporter); /* It's an interactive filehandle; drop into read-eval-print loop. */ lineno = 1; do { /* * Accumulate lines until we get a 'compilable unit' - one that either * generates an error (before running out of source) or that compiles * cleanly. This should be whenever we get a complete statement that * coincides with the end of a line. */ startline = lineno; buffer = g_string_new(""); do { if (!gjs_console_readline(context, &temp_buf, file, startline == lineno ? "cjs> " : ".... ")) { eof = JS_TRUE; break; } g_string_append(buffer, temp_buf); g_free(temp_buf); lineno++; } while (!JS_BufferIsCompilableUnit(context, object, buffer->str, buffer->len)); JS::CompileOptions options(context); options.setUTF8(true) .setFileAndLine("typein", startline); js::RootedObject rootedObj(context, object); JS::Evaluate(context, rootedObj, options, buffer->str, buffer->len, &result); gjs_schedule_gc_if_needed(context); if (JS_GetPendingException(context, &result)) { str = JS_ValueToString(context, result); JS_ClearPendingException(context); } else if (JSVAL_IS_VOID(result)) { goto next; } else { str = JS_ValueToString(context, result); } if (str) { char *display_str; display_str = gjs_value_debug_string(context, result); if (display_str != NULL) { g_fprintf(stdout, "%s\n", display_str); g_free(display_str); } } next: g_string_free(buffer, TRUE); } while (!eof); g_fprintf(stdout, "\n"); if (file != stdin) fclose(file); return JS_TRUE; } JSBool gjs_define_console_stuff(JSContext *context, JSObject **module_out) { JSObject *module; module = JS_NewObject (context, NULL, NULL, NULL); if (!JS_DefineFunction(context, module, "interact", (JSNative) gjs_console_interact, 1, GJS_MODULE_PROP_FLAGS)) return JS_FALSE; *module_out = module; return JS_TRUE; } cjs-2.8.0/modules/cairo-svg-surface.cpp0000664000175000017500000000671512610172032016723 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2010 litl, LLC. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include "cairo-private.h" #if CAIRO_HAS_SVG_SURFACE #include GJS_DEFINE_PROTO("CairoSVGSurface", cairo_svg_surface) GJS_NATIVE_CONSTRUCTOR_DECLARE(cairo_svg_surface) { GJS_NATIVE_CONSTRUCTOR_VARIABLES(cairo_svg_surface) char *filename; double width, height; cairo_surface_t *surface; GJS_NATIVE_CONSTRUCTOR_PRELUDE(cairo_svg_surface); if (!gjs_parse_args(context, "SVGSurface", "sff", argc, argv, "filename", &filename, "width", &width, "height", &height)) return JS_FALSE; surface = cairo_svg_surface_create(filename, width, height); if (!gjs_cairo_check_status(context, cairo_surface_status(surface), "surface")) { g_free(filename); return JS_FALSE; } gjs_cairo_surface_construct(context, object, surface); cairo_surface_destroy(surface); g_free(filename); GJS_NATIVE_CONSTRUCTOR_FINISH(cairo_svg_surface); return JS_TRUE; } static void gjs_cairo_svg_surface_finalize(JSFreeOp *fop, JSObject *obj) { gjs_cairo_surface_finalize_surface(fop, obj); } JSPropertySpec gjs_cairo_svg_surface_proto_props[] = { { NULL } }; JSFunctionSpec gjs_cairo_svg_surface_proto_funcs[] = { { NULL } }; JSObject * gjs_cairo_svg_surface_from_surface(JSContext *context, cairo_surface_t *surface) { JSObject *object; g_return_val_if_fail(context != NULL, NULL); g_return_val_if_fail(surface != NULL, NULL); g_return_val_if_fail(cairo_surface_get_type(surface) == CAIRO_SURFACE_TYPE_SVG, NULL); object = JS_NewObject(context, &gjs_cairo_svg_surface_class, NULL, NULL); if (!object) { gjs_throw(context, "failed to create svg surface"); return NULL; } gjs_cairo_surface_construct(context, object, surface); return object; } #else JSObject * gjs_cairo_svg_surface_from_surface(JSContext *context, cairo_surface_t *surface) { gjs_throw(context, "could not create SVG surface, recompile cairo and gjs with " "SVG support."); return NULL; } #endif /* CAIRO_HAS_SVG_SURFACE */ cjs-2.8.0/modules/cairo-linear-gradient.cpp0000664000175000017500000000613212610172032017534 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2010 litl, LLC. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include "cairo-private.h" GJS_DEFINE_PROTO("CairoLinearGradient", cairo_linear_gradient) GJS_NATIVE_CONSTRUCTOR_DECLARE(cairo_linear_gradient) { GJS_NATIVE_CONSTRUCTOR_VARIABLES(cairo_linear_gradient) double x0, y0, x1, y1; cairo_pattern_t *pattern; GJS_NATIVE_CONSTRUCTOR_PRELUDE(cairo_linear_gradient); if (!gjs_parse_args(context, "LinearGradient", "ffff", argc, argv, "x0", &x0, "y0", &y0, "x1", &x1, "y1", &y1)) return JS_FALSE; pattern = cairo_pattern_create_linear(x0, y0, x1, y1); if (!gjs_cairo_check_status(context, cairo_pattern_status(pattern), "pattern")) return JS_FALSE; gjs_cairo_pattern_construct(context, object, pattern); cairo_pattern_destroy(pattern); GJS_NATIVE_CONSTRUCTOR_FINISH(cairo_linear_gradient); return JS_TRUE; } static void gjs_cairo_linear_gradient_finalize(JSFreeOp *fop, JSObject *obj) { gjs_cairo_pattern_finalize_pattern(fop, obj); } JSPropertySpec gjs_cairo_linear_gradient_proto_props[] = { { NULL } }; JSFunctionSpec gjs_cairo_linear_gradient_proto_funcs[] = { // getLinearPoints { NULL } }; JSObject * gjs_cairo_linear_gradient_from_pattern(JSContext *context, cairo_pattern_t *pattern) { JSObject *object; g_return_val_if_fail(context != NULL, NULL); g_return_val_if_fail(pattern != NULL, NULL); g_return_val_if_fail(cairo_pattern_get_type(pattern) == CAIRO_PATTERN_TYPE_LINEAR, NULL); object = JS_NewObject(context, &gjs_cairo_linear_gradient_class, NULL, NULL); if (!object) { gjs_throw(context, "failed to create linear gradient pattern"); return NULL; } gjs_cairo_pattern_construct(context, object, pattern); return object; } cjs-2.8.0/modules/cairo-pattern.cpp0000664000175000017500000001332712610172032016150 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2010 litl, LLC. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include #include "cairo-private.h" typedef struct { void *dummy; JSContext *context; JSObject *object; cairo_pattern_t *pattern; } GjsCairoPattern; GJS_DEFINE_PROTO_ABSTRACT_WITH_GTYPE("CairoPattern", cairo_pattern, CAIRO_GOBJECT_TYPE_PATTERN) GJS_DEFINE_PRIV_FROM_JS(GjsCairoPattern, gjs_cairo_pattern_class) static void gjs_cairo_pattern_finalize(JSFreeOp *fop, JSObject *obj) { GjsCairoPattern *priv; priv = (GjsCairoPattern*) JS_GetPrivate(obj); if (priv == NULL) return; cairo_pattern_destroy(priv->pattern); g_slice_free(GjsCairoPattern, priv); } /* Properties */ JSPropertySpec gjs_cairo_pattern_proto_props[] = { { NULL } }; /* Methods */ static JSBool getType_func(JSContext *context, unsigned argc, jsval *vp) { JSObject *obj = JS_THIS_OBJECT(context, vp); cairo_pattern_t *pattern; cairo_pattern_type_t type; if (argc > 1) { gjs_throw(context, "Pattern.getType() takes no arguments"); return JS_FALSE; } pattern = gjs_cairo_pattern_get_pattern(context, obj); type = cairo_pattern_get_type(pattern); if (!gjs_cairo_check_status(context, cairo_pattern_status(pattern), "pattern")) return JS_FALSE; JS_SET_RVAL(context, vp, INT_TO_JSVAL(type)); return JS_TRUE; } JSFunctionSpec gjs_cairo_pattern_proto_funcs[] = { // getMatrix { "getType", JSOP_WRAPPER((JSNative)getType_func), 0, 0 }, // setMatrix { NULL } }; /* Public API */ /** * gjs_cairo_pattern_construct: * @context: the context * @object: object to construct * @pattern: cairo_pattern to attach to the object * * Constructs a pattern wrapper giving an empty JSObject and a * cairo pattern. A reference to @pattern will be taken. * * This is mainly used for subclasses where object is already created. */ void gjs_cairo_pattern_construct(JSContext *context, JSObject *object, cairo_pattern_t *pattern) { GjsCairoPattern *priv; g_return_if_fail(context != NULL); g_return_if_fail(object != NULL); g_return_if_fail(pattern != NULL); priv = g_slice_new0(GjsCairoPattern); g_assert(priv_from_js(context, object) == NULL); JS_SetPrivate(object, priv); priv->context = context; priv->object = object; priv->pattern = cairo_pattern_reference(pattern); } /** * gjs_cairo_pattern_finalize: * @fop: the free op * @object: object to finalize * * Destroys the resources associated with a pattern wrapper. * * This is mainly used for subclasses. */ void gjs_cairo_pattern_finalize_pattern(JSFreeOp *fop, JSObject *object) { g_return_if_fail(fop != NULL); g_return_if_fail(object != NULL); gjs_cairo_pattern_finalize(fop, object); } /** * gjs_cairo_pattern_from_pattern: * @context: the context * @pattern: cairo_pattern to attach to the object * * Constructs a pattern wrapper given cairo pattern. * A reference to @pattern will be taken. * */ JSObject * gjs_cairo_pattern_from_pattern(JSContext *context, cairo_pattern_t *pattern) { g_return_val_if_fail(context != NULL, NULL); g_return_val_if_fail(pattern != NULL, NULL); switch (cairo_pattern_get_type(pattern)) { case CAIRO_PATTERN_TYPE_SOLID: return gjs_cairo_solid_pattern_from_pattern(context, pattern); case CAIRO_PATTERN_TYPE_SURFACE: return gjs_cairo_surface_pattern_from_pattern(context, pattern); case CAIRO_PATTERN_TYPE_LINEAR: return gjs_cairo_linear_gradient_from_pattern(context, pattern); case CAIRO_PATTERN_TYPE_RADIAL: return gjs_cairo_radial_gradient_from_pattern(context, pattern); default: break; } gjs_throw(context, "failed to create pattern, unsupported pattern type %d", cairo_pattern_get_type(pattern)); return NULL; } /** * gjs_cairo_pattern_get_pattern: * @context: the context * @object: pattern wrapper * * Returns: the pattern attaches to the wrapper. * */ cairo_pattern_t * gjs_cairo_pattern_get_pattern(JSContext *context, JSObject *object) { GjsCairoPattern *priv; g_return_val_if_fail(context != NULL, NULL); g_return_val_if_fail(object != NULL, NULL); priv = (GjsCairoPattern*) JS_GetPrivate(object); if (priv == NULL) return NULL; return priv->pattern; } cjs-2.8.0/modules/modules.cpp0000664000175000017500000000313312610172032015042 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright 2013 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include "modules.h" #ifdef ENABLE_CAIRO #include "cairo-module.h" #endif #include "system.h" #include "console.h" void gjs_register_static_modules (void) { #ifdef ENABLE_CAIRO gjs_register_native_module("cairoNative", gjs_js_define_cairo_stuff); #endif gjs_register_native_module("system", gjs_js_define_system_stuff); gjs_register_native_module("console", gjs_define_console_stuff); } cjs-2.8.0/modules/console.h0000664000175000017500000000325012610172032014501 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_CONSOLE_H__ #define __GJS_CONSOLE_H__ #include #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS JSBool gjs_define_console_stuff (JSContext *context, JSObject **module_out); JSBool gjs_console_interact (JSContext *context, unsigned argc, jsval *vp); G_END_DECLS #endif /* __GJS_CONSOLE_H__ */ cjs-2.8.0/modules/cairo-module.h0000664000175000017500000000263012610172032015420 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2010 litl, LLC. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __CAIRO_MODULE_H__ #define __CAIRO_MODULE_H__ JSBool gjs_js_define_cairo_stuff (JSContext *context, JSObject **module_out); #endif /* __CAIRO_MODULE_H__ */ cjs-2.8.0/modules/cairo-solid-pattern.cpp0000664000175000017500000001006012610172032017247 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2010 litl, LLC. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include "cairo-private.h" GJS_DEFINE_PROTO_ABSTRACT("CairoSolidPattern", cairo_solid_pattern) static void gjs_cairo_solid_pattern_finalize(JSFreeOp *fop, JSObject *obj) { gjs_cairo_pattern_finalize_pattern(fop, obj); } JSPropertySpec gjs_cairo_solid_pattern_proto_props[] = { { NULL } }; static JSBool createRGB_func(JSContext *context, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(context, vp); double red, green, blue; cairo_pattern_t *pattern; JSObject *pattern_wrapper; if (!gjs_parse_args(context, "createRGB", "fff", argc, argv, "red", &red, "green", &green, "blue", &blue)) return JS_FALSE; pattern = cairo_pattern_create_rgb(red, green, blue); if (!gjs_cairo_check_status(context, cairo_pattern_status(pattern), "pattern")) return JS_FALSE; pattern_wrapper = gjs_cairo_solid_pattern_from_pattern(context, pattern); cairo_pattern_destroy(pattern); JS_SET_RVAL(context, vp, OBJECT_TO_JSVAL(pattern_wrapper)); return JS_TRUE; } static JSBool createRGBA_func(JSContext *context, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(context, vp); double red, green, blue, alpha; cairo_pattern_t *pattern; JSObject *pattern_wrapper; if (!gjs_parse_args(context, "createRGBA", "ffff", argc, argv, "red", &red, "green", &green, "blue", &blue, "alpha", &alpha)) return JS_FALSE; pattern = cairo_pattern_create_rgba(red, green, blue, alpha); if (!gjs_cairo_check_status(context, cairo_pattern_status(pattern), "pattern")) return JS_FALSE; pattern_wrapper = gjs_cairo_solid_pattern_from_pattern(context, pattern); cairo_pattern_destroy(pattern); JS_SET_RVAL(context, vp, OBJECT_TO_JSVAL(pattern_wrapper)); return JS_TRUE; } JSFunctionSpec gjs_cairo_solid_pattern_proto_funcs[] = { { "createRGB", JSOP_WRAPPER((JSNative)createRGB_func), 0, 0 }, { "createRGBA", JSOP_WRAPPER((JSNative)createRGBA_func), 0, 0 }, { NULL } }; JSObject * gjs_cairo_solid_pattern_from_pattern(JSContext *context, cairo_pattern_t *pattern) { JSObject *object; g_return_val_if_fail(context != NULL, NULL); g_return_val_if_fail(pattern != NULL, NULL); g_return_val_if_fail(cairo_pattern_get_type(pattern) == CAIRO_PATTERN_TYPE_SOLID, NULL); object = JS_NewObject(context, &gjs_cairo_solid_pattern_class, NULL, NULL); if (!object) { gjs_throw(context, "failed to create solid pattern"); return NULL; } gjs_cairo_pattern_construct(context, object, pattern); return object; } cjs-2.8.0/modules/overrides/0000775000175000017500000000000012610172032014670 5ustar fabiofabiocjs-2.8.0/modules/overrides/GLib.js0000664000175000017500000002071512610172032016050 0ustar fabiofabio// Copyright 2011 Giovanni Campagna // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. const ByteArray = imports.byteArray; let GLib; let originalVariantClass; const SIMPLE_TYPES = ['b', 'y', 'n', 'q', 'i', 'u', 'x', 't', 'h', 'd', 's', 'o', 'g']; function _read_single_type(signature, forceSimple) { let char = signature.shift(); let isSimple = false; if (SIMPLE_TYPES.indexOf(char) == -1) { if (forceSimple) throw new TypeError('Invalid GVariant signature (a simple type was expected)'); } else isSimple = true; if (char == 'm' || char == 'a') return [char].concat(_read_single_type(signature, false)); if (char == '{') { let key = _read_single_type(signature, true); let val = _read_single_type(signature, false); let close = signature.shift(); if (close != '}') throw new TypeError('Invalid GVariant signature for type DICT_ENTRY (expected "}"'); return [char].concat(key, val, close); } if (char == '(') { let res = [char]; while (true) { if (signature.length == 0) throw new TypeError('Invalid GVariant signature for type TUPLE (expected ")")'); let next = signature[0]; if (next == ')') { signature.shift(); return res.concat(next); } let el = _read_single_type(signature); res = res.concat(el); } } // Valid types are simple types, arrays, maybes, tuples, dictionary entries and variants if (!isSimple && char != 'v') throw new TypeError('Invalid GVariant signature (' + char + ' is not a valid type)'); return [char]; } function _makeBytes(byteArray) { if (byteArray instanceof ByteArray.ByteArray) return byteArray.toGBytes(); else return new GLib.Bytes(byteArray); } function _pack_variant(signature, value) { if (signature.length == 0) throw new TypeError('GVariant signature cannot be empty'); let char = signature.shift(); switch (char) { case 'b': return GLib.Variant.new_boolean(value); case 'y': return GLib.Variant.new_byte(value); case 'n': return GLib.Variant.new_int16(value); case 'q': return GLib.Variant.new_uint16(value); case 'i': return GLib.Variant.new_int32(value); case 'u': return GLib.Variant.new_uint32(value); case 'x': return GLib.Variant.new_int64(value); case 't': return GLib.Variant.new_uint64(value); case 'h': return GLib.Variant.new_handle(value); case 'd': return GLib.Variant.new_double(value); case 's': return GLib.Variant.new_string(value); case 'o': return GLib.Variant.new_object_path(value); case 'g': return GLib.Variant.new_signature(value); case 'v': return GLib.Variant.new_variant(value); case 'm': if (value != null) return GLib.Variant.new_maybe(null, _pack_variant(signature, value)); else return GLib.Variant.new_maybe(new GLib.VariantType(_read_single_type(signature, false).join('')), null); case 'a': let arrayType = _read_single_type(signature, false); if (arrayType[0] == 's') { // special case for array of strings return GLib.Variant.new_strv(value); } if (arrayType[0] == 'y') { // special case for array of bytes return GLib.Variant.new_from_bytes(new GLib.VariantType('ay'), _makeBytes(value), true); } let arrayValue = []; if (arrayType[0] == '{') { // special case for dictionaries for (let key in value) { let copy = [].concat(arrayType); let child = _pack_variant(copy, [key, value[key]]); arrayValue.push(child); } } else { for (let i = 0; i < value.length; i++) { let copy = [].concat(arrayType); let child = _pack_variant(copy, value[i]); arrayValue.push(child); } } return GLib.Variant.new_array(new GLib.VariantType(arrayType.join('')), arrayValue); case '(': let children = [ ]; for (let i = 0; i < value.length; i++) { let next = signature[0]; if (next == ')') break; children.push(_pack_variant(signature, value[i])); } if (signature[0] != ')') throw new TypeError('Invalid GVariant signature for type TUPLE (expected ")")'); signature.shift(); return GLib.Variant.new_tuple(children); case '{': let key = _pack_variant(signature, value[0]); let child = _pack_variant(signature, value[1]); if (signature[0] != '}') throw new TypeError('Invalid GVariant signature for type DICT_ENTRY (expected "}")'); signature.shift(); return GLib.Variant.new_dict_entry(key, child); default: throw new TypeError('Invalid GVariant signature (unexpected character ' + char + ')'); } } function _unpack_variant(variant, deep) { switch (String.fromCharCode(variant.classify())) { case 'b': return variant.get_boolean(); case 'y': return variant.get_byte(); case 'n': return variant.get_int16(); case 'q': return variant.get_uint16(); case 'i': return variant.get_int32(); case 'u': return variant.get_uint32(); case 'x': return variant.get_int64(); case 't': return variant.get_uint64(); case 'h': return variant.get_handle(); case 'd': return variant.get_double(); case 'o': case 'g': case 's': // g_variant_get_string has length as out argument return variant.get_string()[0]; case 'v': return variant.get_variant(); case 'm': let val = variant.get_maybe(); if (deep) return _unpack_variant(val, deep); else return val; case 'a': if (variant.is_of_type(new GLib.VariantType('a{?*}'))) { // special case containers let ret = { }; let nElements = variant.n_children(); for (let i = 0; i < nElements; i++) { // always unpack the dictionary entry, and always unpack // the key (or it cannot be added as a key) let val = _unpack_variant(variant.get_child_value(i), deep); let key; if (!deep) key = _unpack_variant(val[0], true); else key = val[0]; ret[key] = val[1]; } return ret; } if (variant.is_of_type(new GLib.VariantType('ay'))) { // special case byte arrays return variant.get_data_as_bytes().toArray(); } // fall through case '(': case '{': let ret = [ ]; let nElements = variant.n_children(); for (let i = 0; i < nElements; i++) { let val = variant.get_child_value(i); if (deep) ret.push(_unpack_variant(val, deep)); else ret.push(val); } return ret; } throw new Error('Assertion failure: this code should not be reached'); } function _init() { // this is imports.gi.GLib GLib = this; // small HACK: we add a matches() method to standard Errors so that // you can do "catch(e if e.matches(Ns.FooError, Ns.FooError.SOME_CODE))" // without checking instanceof Error.prototype.matches = function() { return false; }; this.Variant._new_internal = function(sig, value) { let signature = Array.prototype.slice.call(sig); let variant = _pack_variant(signature, value); if (signature.length != 0) throw new TypeError('Invalid GVariant signature (more than one single complete type)'); return variant; }; // Deprecate version of new GLib.Variant() this.Variant.new = function(sig, value) { return new GLib.Variant(sig, value); }; this.Variant.prototype.unpack = function() { return _unpack_variant(this, false); }; this.Variant.prototype.deep_unpack = function() { return _unpack_variant(this, true); }; this.Variant.prototype.toString = function() { return '[object variant of type "' + this.get_type_string() + '"]'; }; this.Bytes.prototype.toArray = function() { return imports.byteArray.fromGBytes(this); }; } cjs-2.8.0/modules/overrides/Gio.js0000664000175000017500000003345712610172032015760 0ustar fabiofabio// Copyright 2011 Giovanni Campagna // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. var GLib = imports.gi.GLib; var GObject = imports.gi.GObject; var GjsPrivate = imports.gi.CjsPrivate; var Lang = imports.lang; var Signals = imports.signals; var Gio; function _signatureLength(sig) { var counter = 0; // make it an array var signature = Array.prototype.slice.call(sig); while (signature.length) { GLib._read_single_type(sig); counter++; } return counter; } function _proxyInvoker(methodName, sync, inSignature, arg_array) { var replyFunc; var flags = 0; var cancellable = null; /* Convert arg_array to a *real* array */ arg_array = Array.prototype.slice.call(arg_array); /* The default replyFunc only logs the responses */ replyFunc = _logReply; var signatureLength = inSignature.length; var minNumberArgs = signatureLength; var maxNumberArgs = signatureLength + 3; if (arg_array.length < minNumberArgs) { throw new Error("Not enough arguments passed for method: " + methodName + ". Expected " + minNumberArgs + ", got " + arg_array.length); } else if (arg_array.length > maxNumberArgs) { throw new Error("Too many arguments passed for method: " + methodName + ". Maximum is " + maxNumberArgs + " + one callback and/or flags"); } while (arg_array.length > signatureLength) { var argNum = arg_array.length - 1; var arg = arg_array.pop(); if (typeof(arg) == "function" && !sync) { replyFunc = arg; } else if (typeof(arg) == "number") { flags = arg; } else if (arg instanceof Gio.Cancellable) { cancellable = arg; } else { throw new Error("Argument " + argNum + " of method " + methodName + " is " + typeof(arg) + ". It should be a callback, flags or a Gio.Cancellable"); } } var inVariant = new GLib.Variant('(' + inSignature.join('') + ')', arg_array); var asyncCallback = function (proxy, result) { var outVariant = null, succeeded = false; try { outVariant = proxy.call_finish(result); succeeded = true; } catch (e) { replyFunc(null, e); } if (succeeded) replyFunc(outVariant.deep_unpack(), null); }; if (sync) { return this.call_sync(methodName, inVariant, flags, -1, cancellable).deep_unpack(); } else { return this.call(methodName, inVariant, flags, -1, cancellable, asyncCallback); } } function _logReply(result, exc) { if (exc != null) { log("Ignored exception from dbus method: " + exc.toString()); } } function _makeProxyMethod(method, sync) { var i; var name = method.name; var inArgs = method.in_args; var inSignature = [ ]; for (i = 0; i < inArgs.length; i++) inSignature.push(inArgs[i].signature); return function() { return _proxyInvoker.call(this, name, sync, inSignature, arguments); }; } function _convertToNativeSignal(proxy, sender_name, signal_name, parameters) { Signals._emit.call(proxy, signal_name, sender_name, parameters.deep_unpack()); } function _propertyGetter(name) { let value = this.get_cached_property(name); return value ? value.deep_unpack() : null; } function _propertySetter(value, name, signature) { let variant = new GLib.Variant(signature, value); this.set_cached_property(name, variant); this.call('org.freedesktop.DBus.Properties.Set', new GLib.Variant('(ssv)', [this.g_interface_name, name, variant]), Gio.DBusCallFlags.NONE, -1, null, Lang.bind(this, function(proxy, result) { try { this.call_finish(result); } catch(e) { log('Could not set property ' + name + ' on remote object ' + this.g_object_path + ': ' + e.message); } })); } function _addDBusConvenience() { let info = this.g_interface_info; if (!info) return; if (info.signals.length > 0) this.connect('g-signal', _convertToNativeSignal); let i, methods = info.methods; for (i = 0; i < methods.length; i++) { var method = methods[i]; this[method.name + 'Remote'] = _makeProxyMethod(methods[i], false); this[method.name + 'Sync'] = _makeProxyMethod(methods[i], true); } let properties = info.properties; for (i = 0; i < properties.length; i++) { let name = properties[i].name; let signature = properties[i].signature; Object.defineProperty(this, name, { get: Lang.bind(this, _propertyGetter, name), set: Lang.bind(this, _propertySetter, name, signature), configurable: true, enumerable: true }); } } function _makeProxyWrapper(interfaceXml) { var info = _newInterfaceInfo(interfaceXml); var iname = info.name; return function(bus, name, object, asyncCallback, cancellable) { var obj = new Gio.DBusProxy({ g_connection: bus, g_interface_name: iname, g_interface_info: info, g_name: name, g_object_path: object }); if (!cancellable) cancellable = null; if (asyncCallback) obj.init_async(GLib.PRIORITY_DEFAULT, cancellable, function(initable, result) { let caughtErrorWhenInitting = null; try { initable.init_finish(result); } catch(e) { caughtErrorWhenInitting = e; } if (caughtErrorWhenInitting === null) { asyncCallback(initable, null); } else { asyncCallback(null, caughtErrorWhenInitting); } }); else obj.init(cancellable); return obj; }; } function _newNodeInfo(constructor, value) { if (typeof value == 'string') return constructor(value); else if (value instanceof XML) return constructor(value.toXMLString()); else throw TypeError('Invalid type ' + Object.prototype.toString.call(value)); } function _newInterfaceInfo(value) { var nodeInfo = Gio.DBusNodeInfo.new_for_xml(value); return nodeInfo.interfaces[0]; } function _injectToMethod(klass, method, addition) { var previous = klass[method]; klass[method] = function() { addition.apply(this, arguments); return previous.apply(this, arguments); }; } function _wrapFunction(klass, method, addition) { var previous = klass[method]; klass[method] = function() { var args = Array.prototype.slice.call(arguments); args.unshift(previous); return addition.apply(this, args); }; } function _makeOutSignature(args) { var ret = '('; for (var i = 0; i < args.length; i++) ret += args[i].signature; return ret + ')'; } function _handleMethodCall(info, impl, method_name, parameters, invocation) { // prefer a sync version if available if (this[method_name]) { let retval; try { retval = this[method_name].apply(this, parameters.deep_unpack()); } catch (e) { if (e instanceof GLib.Error) { invocation.return_gerror(e); } else { let name = e.name; if (name.indexOf('.') == -1) { // likely to be a normal JS error name = 'org.gnome.gjs.JSError.' + name; } logError(e, "Exception in method call: " + method_name); invocation.return_dbus_error(name, e.message); } return; } if (retval === undefined) { // undefined (no return value) is the empty tuple retval = new GLib.Variant('()', []); } try { if (!(retval instanceof GLib.Variant)) { // attempt packing according to out signature let methodInfo = info.lookup_method(method_name); let outArgs = methodInfo.out_args; let outSignature = _makeOutSignature(outArgs); if (outArgs.length == 1) { // if one arg, we don't require the handler wrapping it // into an Array retval = [retval]; } retval = new GLib.Variant(outSignature, retval); } invocation.return_value(retval); } catch(e) { // if we don't do this, the other side will never see a reply invocation.return_dbus_error('org.gnome.gjs.JSError.ValueError', "Service implementation returned an incorrect value type"); } } else if (this[method_name + 'Async']) { this[method_name + 'Async'](parameters.deep_unpack(), invocation); } else { log('Missing handler for DBus method ' + method_name); invocation.return_gerror(new Gio.DBusError({ code: Gio.DBusError.UNKNOWN_METHOD, message: 'Method ' + method_name + ' is not implemented' })); } } function _handlePropertyGet(info, impl, property_name) { let propInfo = info.lookup_property(property_name); let jsval = this[property_name]; if (jsval != undefined) return new GLib.Variant(propInfo.signature, jsval); else return null; } function _handlePropertySet(info, impl, property_name, new_value) { this[property_name] = new_value.deep_unpack(); } function _wrapJSObject(interfaceInfo, jsObj) { var info; if (interfaceInfo instanceof Gio.DBusInterfaceInfo) info = interfaceInfo; else info = Gio.DBusInterfaceInfo.new_for_xml(interfaceInfo); info.cache_build(); var impl = new GjsPrivate.DBusImplementation({ g_interface_info: info }); impl.connect('handle-method-call', function(impl, method_name, parameters, invocation) { return _handleMethodCall.call(jsObj, info, impl, method_name, parameters, invocation); }); impl.connect('handle-property-get', function(impl, property_name) { return _handlePropertyGet.call(jsObj, info, impl, property_name); }); impl.connect('handle-property-set', function(impl, property_name, value) { return _handlePropertySet.call(jsObj, info, impl, property_name, value); }); return impl; } function _init() { Gio = this; Gio.DBus = { get session() { return Gio.bus_get_sync(Gio.BusType.SESSION, null); }, get system() { return Gio.bus_get_sync(Gio.BusType.SYSTEM, null); }, // Namespace some functions get: Gio.bus_get, get_finish: Gio.bus_get_finish, get_sync: Gio.bus_get_sync, own_name: Gio.bus_own_name, own_name_on_connection: Gio.bus_own_name_on_connection, unown_name: Gio.bus_unown_name, watch_name: Gio.bus_watch_name, watch_name_on_connection: Gio.bus_watch_name_on_connection, unwatch_name: Gio.bus_unwatch_name }; Gio.DBusConnection.prototype.watch_name = function(name, flags, appeared, vanished) { return Gio.bus_watch_name_on_connection(this, name, flags, appeared, vanished); }; Gio.DBusConnection.prototype.unwatch_name = function(id) { return Gio.bus_unwatch_name(id); }; Gio.DBusConnection.prototype.own_name = function(name, flags, acquired, lost) { return Gio.bus_own_name_on_connection(this, name, flags, acquired, lost); }; Gio.DBusConnection.prototype.unown_name = function(id) { return Gio.bus_unown_name(id); }; _injectToMethod(Gio.DBusProxy.prototype, 'init', _addDBusConvenience); _injectToMethod(Gio.DBusProxy.prototype, 'init_async', _addDBusConvenience); Gio.DBusProxy.prototype.connectSignal = Signals._connect; Gio.DBusProxy.prototype.disconnectSignal = Signals._disconnect; Gio.DBusProxy.makeProxyWrapper = _makeProxyWrapper; // Some helpers _wrapFunction(Gio.DBusNodeInfo, 'new_for_xml', _newNodeInfo); Gio.DBusInterfaceInfo.new_for_xml = _newInterfaceInfo; Gio.DBusExportedObject = GjsPrivate.DBusImplementation; Gio.DBusExportedObject.wrapJSObject = _wrapJSObject; } cjs-2.8.0/modules/overrides/GObject.js0000664000175000017500000002750012610172032016547 0ustar fabiofabio// Copyright 2011 Jasper St. Pierre // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. const Lang = imports.lang; const Gi = imports._gi; let GObject; const GObjectMeta = new Lang.Class({ Name: 'GObjectClass', Extends: Lang.Class, _init: function (params) { // retrieve signals and remove them from params before chaining let signals = params.Signals; delete params.Signals; this.parent(params); if (signals) { for (let signalName in signals) { let obj = signals[signalName]; let flags = (obj.flags !== undefined) ? obj.flags : GObject.SignalFlags.RUN_FIRST; let accumulator = (obj.accumulator !== undefined) ? obj.accumulator : GObject.AccumulatorType.NONE; let rtype = (obj.return_type !== undefined) ? obj.return_type : GObject.TYPE_NONE; let paramtypes = (obj.param_types !== undefined) ? obj.param_types : []; try { obj.signal_id = Gi.signal_new(this.$gtype, signalName, flags, accumulator, rtype, paramtypes); } catch(e) { throw new TypeError('Invalid signal ' + signalName + ': ' + e.message); } } } let propertyObj = { }; Object.getOwnPropertyNames(params).forEach(function(name) { if (name == 'Name' || name == 'Extends' || name == 'Abstract') return; let descriptor = Object.getOwnPropertyDescriptor(params, name); if (typeof descriptor.value === 'function') { let wrapped = this.prototype[name]; if (name.slice(0, 6) == 'vfunc_') { Gi.hook_up_vfunc(this.prototype, name.slice(6), wrapped); } else if (name.slice(0, 3) == 'on_') { let id = GObject.signal_lookup(name.slice(3).replace('_', '-'), this.$gtype); if (id != 0) { GObject.signal_override_class_closure(id, this.$gtype, function() { let argArray = Array.prototype.slice.call(arguments); let emitter = argArray.shift(); wrapped.apply(emitter, argArray); }); } } } }.bind(this)); }, _isValidClass: function(klass) { let proto = klass.prototype; if (!proto) return false; // If proto == GObject.Object.prototype, then // proto.__proto__ is Object, so "proto instanceof GObject.Object" // will return false. return proto == GObject.Object.prototype || proto instanceof GObject.Object; }, // If we want an object with a custom JSClass, we can't just // use a function. We have to use a custom constructor here. _construct: function(params) { if (!params.Name) throw new TypeError("Classes require an explicit 'Name' parameter."); let name = params.Name; let gtypename; if (params.GTypeName) gtypename = params.GTypeName; else gtypename = 'Gjs_' + params.Name; if (!params.Extends) params.Extends = GObject.Object; let parent = params.Extends; if (!this._isValidClass(parent)) throw new TypeError('GObject.Class used with invalid base class (is ' + parent + ')'); let interfaces = params.Implements || []; let properties = params.Properties; delete params.Implements; delete params.Properties; let propertiesArray = []; if (properties) { for (let prop in properties) { propertiesArray.push(properties[prop]); } } let newClass = Gi.register_type(parent.prototype, gtypename, interfaces, propertiesArray); // See Class.prototype._construct in lang.js for the reasoning // behind this direct __proto__ set. newClass.__proto__ = this.constructor.prototype; newClass.__super__ = parent; newClass._init.apply(newClass, arguments); Object.defineProperty(newClass.prototype, '__metaclass__', { writable: false, configurable: false, enumerable: false, value: this.constructor }); return newClass; } }); function _init() { GObject = this; function _makeDummyClass(obj, name, upperName, gtypeName, actual) { let gtype = GObject.type_from_name(gtypeName); obj['TYPE_' + upperName] = gtype; obj[name] = function(v) { return new actual(v); }; obj[name].$gtype = gtype; } _makeDummyClass(this, 'VoidType', 'NONE', 'void', function() {}); _makeDummyClass(this, 'Char', 'CHAR', 'gchar', Number); _makeDummyClass(this, 'UChar', 'UCHAR', 'guchar', Number); _makeDummyClass(this, 'Unichar', 'UNICHAR', 'gint', String); this.TYPE_BOOLEAN = GObject.type_from_name('gboolean'); this.Boolean = Boolean; Boolean.$gtype = this.TYPE_BOOLEAN; _makeDummyClass(this, 'Int', 'INT', 'gint', Number); _makeDummyClass(this, 'UInt', 'UINT', 'guint', Number); _makeDummyClass(this, 'Long', 'LONG', 'glong', Number); _makeDummyClass(this, 'ULong', 'ULONG', 'gulong', Number); _makeDummyClass(this, 'Int64', 'INT64', 'gint64', Number); _makeDummyClass(this, 'UInt64', 'UINT64', 'guint64', Number); this.TYPE_ENUM = GObject.type_from_name('GEnum'); this.TYPE_FLAGS = GObject.type_from_name('GFlags'); _makeDummyClass(this, 'Float', 'FLOAT', 'gfloat', Number); this.TYPE_DOUBLE = GObject.type_from_name('gdouble'); this.Double = Number; Number.$gtype = this.TYPE_DOUBLE; this.TYPE_STRING = GObject.type_from_name('gchararray'); this.String = String; String.$gtype = this.TYPE_STRING; this.TYPE_POINTER = GObject.type_from_name('gpointer'); this.TYPE_BOXED = GObject.type_from_name('GBoxed'); this.TYPE_PARAM = GObject.type_from_name('GParam'); this.TYPE_INTERFACE = GObject.type_from_name('GInterface'); this.TYPE_OBJECT = GObject.type_from_name('GObject'); this.TYPE_VARIANT = GObject.type_from_name('GVariant'); _makeDummyClass(this, 'Type', 'GTYPE', 'GType', GObject.type_from_name); this.ParamSpec.char = function(name, nick, blurb, flags, minimum, maximum, default_value) { return GObject.ParamSpec._new_internal(name, GObject.Char, nick, blurb, flags, minimum, maximum, default_value); }; this.ParamSpec.uchar = function(name, nick, blurb, flags, minimum, maximum, default_value) { return GObject.ParamSpec._new_internal(name, GObject.UChar, nick, blurb, flags, minimum, maximum, default_value); }; this.ParamSpec.int = function(name, nick, blurb, flags, minimum, maximum, default_value) { return GObject.ParamSpec._new_internal(name, GObject.Int, nick, blurb, flags, minimum, maximum, default_value); }; this.ParamSpec.uint = function(name, nick, blurb, flags, minimum, maximum, default_value) { return GObject.ParamSpec._new_internal(name, GObject.UInt, nick, blurb, flags, minimum, maximum, default_value); }; this.ParamSpec.long = function(name, nick, blurb, flags, minimum, maximum, default_value) { return GObject.ParamSpec._new_internal(name, GObject.Long, nick, blurb, flags, minimum, maximum, default_value); }; this.ParamSpec.ulong = function(name, nick, blurb, flags, minimum, maximum, default_value) { return GObject.ParamSpec._new_internal(name, GObject.ULong, nick, blurb, flags, minimum, maximum, default_value); }; this.ParamSpec.int64 = function(name, nick, blurb, flags, minimum, maximum, default_value) { return GObject.ParamSpec._new_internal(name, GObject.Int64, nick, blurb, flags, minimum, maximum, default_value); }; this.ParamSpec.uint64 = function(name, nick, blurb, flags, minimum, maximum, default_value) { return GObject.ParamSpec._new_internal(name, GObject.UInt64, nick, blurb, flags, minimum, maximum, default_value); }; this.ParamSpec.float = function(name, nick, blurb, flags, minimum, maximum, default_value) { return GObject.ParamSpec._new_internal(name, GObject.Float, nick, blurb, flags, minimum, maximum, default_value); }; this.ParamSpec.boolean = function(name, nick, blurb, flags, default_value) { return GObject.ParamSpec._new_internal(name, GObject.Boolean, nick, blurb, flags, default_value); }; this.ParamSpec.flags = function(name, nick, blurb, flags, flags_type, default_value) { return GObject.ParamSpec._new_internal(name, flags_type, nick, blurb, flags, default_value); }; this.ParamSpec.enum = function(name, nick, blurb, flags, enum_type, default_value) { return GObject.ParamSpec._new_internal(name, enum_type, nick, blurb, flags, default_value); }; this.ParamSpec.double = function(name, nick, blurb, flags, minimum, maximum, default_value) { return GObject.ParamSpec._new_internal(name, GObject.Double, nick, blurb, flags, minimum, maximum, default_value); }; this.ParamSpec.string = function(name, nick, blurb, flags, default_value) { return GObject.ParamSpec._new_internal(name, GObject.String, nick, blurb, flags, default_value); }; this.ParamSpec.boxed = function(name, nick, blurb, flags, boxed_type) { return GObject.ParamSpec._new_internal(name, boxed_type, nick, blurb, flags); }; this.ParamSpec.object = function(name, nick, blurb, flags, object_type) { return GObject.ParamSpec._new_internal(name, object_type, nick, blurb, flags); }; this.ParamSpec.param = function(name, nick, blurb, flags, param_type) { return GObject.ParamSpec._new_internal(name, param_type, nick, blurb, flags); }; this.Class = GObjectMeta; this.Object.prototype.__metaclass__ = this.Class; // For compatibility with Lang.Class... we need a _construct // or the Lang.Class constructor will fail. this.Object.prototype._construct = function() { this._init.apply(this, arguments); return this; }; // fake enum for signal accumulators, keep in sync with gi/object.c this.AccumulatorType = { NONE: 0, FIRST_WINS: 1, TRUE_HANDLED: 2 }; this.Object.prototype.disconnect = function(id) { return GObject.signal_handler_disconnect(this, id); }; } cjs-2.8.0/modules/overrides/Gtk.js0000664000175000017500000000051712610172032015756 0ustar fabiofabiovar GjsPrivate = imports.gi.GjsPrivate; var Gtk; function _init() { Gtk = this; if (GjsPrivate.gtk_container_child_set_property) { Gtk.Container.prototype.child_set_property = function(child, property, value) { GjsPrivate.gtk_container_child_set_property(this, child, property, value); }; } } cjs-2.8.0/modules/modules.h0000664000175000017500000000257212610172032014515 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright 2013 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_MODULES_H__ #define __GJS_MODULES_H__ #include #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS void gjs_register_static_modules (void); G_END_DECLS #endif /* __GJS_CONSOLE_H__ */ cjs-2.8.0/modules/lang.js0000664000175000017500000001777112610172032014162 0ustar fabiofabio/* -*- mode: js; indent-tabs-mode: nil; -*- */ // Copyright (c) 2008 litl, LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. // Utilities that are "meta-language" things like manipulating object props const Gi = imports._gi; function countProperties(obj) { let count = 0; for (let property in obj) { count += 1; } return count; } function getPropertyDescriptor(obj, property) { if (obj.hasOwnProperty(property)) return Object.getOwnPropertyDescriptor(obj, property); return getPropertyDescriptor(Object.getPrototypeOf(obj), property); } function _copyProperty(source, dest, property) { let descriptor = getPropertyDescriptor(source, property); Object.defineProperty(dest, property, descriptor); } function copyProperties(source, dest) { for (let property in source) { _copyProperty(source, dest, property); } } function copyPublicProperties(source, dest) { for (let property in source) { if (typeof(property) == 'string' && property.substring(0, 1) == '_') { continue; } else { _copyProperty(source, dest, property); } } } /** * Binds obj to callback. Makes it possible to refer to "obj" * using this within the callback. * @param {object} obj the object to bind * @param {function} callback callback to bind obj in * @param arguments additional arguments to the callback * @returns: a new callback * @type: function */ function bind(obj, callback) { if (typeof(obj) != 'object') { throw new Error( "first argument to Lang.bind() must be an object, not " + typeof(obj)); } if (typeof(callback) != 'function') { throw new Error( "second argument to Lang.bind() must be a function, not " + typeof(callback)); } // Use ES5 Function.prototype.bind, but only if not passing any bindArguments, // because ES5 has them at the beginning, not at the end if (arguments.length == 2) return callback.bind(obj); let me = obj; let bindArguments = Array.prototype.slice.call(arguments, 2); return function() { let args = Array.prototype.slice.call(arguments); args = args.concat(bindArguments); return callback.apply(me, args); }; } // Class magic // Adapted from MooTools, MIT license // https://github.com/mootools/moootools-core function _Base() { throw new TypeError('Cannot instantiate abstract class _Base'); } _Base.__super__ = null; _Base.prototype._init = function() { }; _Base.prototype._construct = function() { this._init.apply(this, arguments); return this; }; _Base.prototype.__name__ = '_Base'; _Base.prototype.toString = function() { return '[object ' + this.__name__ + ']'; }; function _parent() { if (!this.__caller__) throw new TypeError("The method 'parent' cannot be called"); let caller = this.__caller__; let name = caller._name; let parent = caller._owner.__super__; let previous = parent ? parent.prototype[name] : undefined; if (!previous) throw new TypeError("The method '" + name + "' is not on the superclass"); return previous.apply(this, arguments); } function getMetaClass(params) { if (params.MetaClass) return params.MetaClass; if (params.Extends && params.Extends.prototype.__metaclass__) return params.Extends.prototype.__metaclass__; return null; } function Class(params) { let metaClass = getMetaClass(params); if (metaClass && metaClass != this.constructor) { // Trick to apply variadic arguments to constructors -- // bind the arguments into the constructor function. let args = Array.prototype.slice.call(arguments); let curried = Function.prototype.bind.apply(metaClass, [,].concat(args)); return new curried(); } else { return this._construct.apply(this, arguments); } } Class.__super__ = _Base; Class.prototype = Object.create(_Base.prototype); Class.prototype.constructor = Class; Class.prototype.__name__ = 'Class'; Class.prototype.wrapFunction = function(name, meth) { if (meth._origin) meth = meth._origin; function wrapper() { let prevCaller = this.__caller__; this.__caller__ = wrapper; let result = meth.apply(this, arguments); this.__caller__ = prevCaller; return result; } wrapper._origin = meth; wrapper._name = name; wrapper._owner = this; return wrapper; } Class.prototype.toString = function() { return '[object ' + this.__name__ + ' for ' + this.prototype.__name__ + ']'; }; Class.prototype._construct = function(params) { if (!params.Name) { throw new TypeError("Classes require an explicit 'Name' parameter."); } let name = params.Name; let parent = params.Extends; if (!parent) parent = _Base; let newClass; if (params.Abstract) { newClass = function() { throw new TypeError('Cannot instantiate abstract class ' + name); }; } else { newClass = function() { this.__caller__ = null; return this._construct.apply(this, arguments); }; } // Since it's not possible to create a constructor with // a custom [[Prototype]], we have to do this to make // "newClass instanceof Class" work, and so we can inherit // methods/properties of Class.prototype, like wrapFunction. newClass.__proto__ = this.constructor.prototype; newClass.__super__ = parent; newClass.prototype = Object.create(parent.prototype); newClass.prototype.constructor = newClass; newClass._init.apply(newClass, arguments); Object.defineProperty(newClass.prototype, '__metaclass__', { writable: false, configurable: false, enumerable: false, value: this.constructor }); return newClass; }; Class.prototype._init = function(params) { let name = params.Name; let propertyObj = { }; Object.getOwnPropertyNames(params).forEach(function(name) { if (name == 'Name' || name == 'Extends' || name == 'Abstract') return; let descriptor = Object.getOwnPropertyDescriptor(params, name); if (typeof descriptor.value === 'function') descriptor.value = this.wrapFunction(name, descriptor.value); // we inherit writable and enumerable from the property // descriptor of params (they're both true if created from an // object literal) descriptor.configurable = false; propertyObj[name] = descriptor; }.bind(this)); Object.defineProperties(this.prototype, propertyObj); Object.defineProperties(this.prototype, { '__name__': { writable: false, configurable: false, enumerable: false, value: name }, 'parent': { writable: false, configurable: false, enumerable: false, value: _parent }}); }; cjs-2.8.0/modules/modules.gresource.xml0000664000175000017500000000131112610172032017051 0ustar fabiofabio modules/tweener/equations.js modules/tweener/tweener.js modules/tweener/tweenList.js modules/overrides/GLib.js modules/overrides/Gio.js modules/overrides/GObject.js modules/overrides/Gtk.js modules/cairo.js modules/coverage.js modules/gettext.js modules/lang.js modules/mainloop.js modules/jsUnit.js modules/signals.js modules/format.js cjs-2.8.0/modules/mainloop.js0000664000175000017500000000501712610172032015045 0ustar fabiofabio/* -*- mode: js; indent-tabs-mode: nil; -*- */ // Copyright (c) 2012 Giovanni Campagna // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. // A layer of convenience and backwards-compatibility over GLib MainLoop facilities const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; var _mainLoops = {}; function run(name) { if (!_mainLoops[name]) _mainLoops[name] = GLib.MainLoop.new(null, false); _mainLoops[name].run(); } function quit(name) { if (!_mainLoops[name]) throw new Error("No main loop with this id"); let loop = _mainLoops[name]; delete _mainLoops[name]; if (!loop.is_running()) throw new Error("Main loop was stopped already"); loop.quit(); } function idle_source(handler) { let s = GLib.idle_source_new(); GObject.source_set_closure(s, handler); return s; } function idle_add(handler) { return idle_source(handler).attach(null); } function timeout_source(timeout, handler) { let s = GLib.timeout_source_new(timeout); GObject.source_set_closure(s, handler); return s; } function timeout_seconds_source(timeout, handler) { let s = GLib.timeout_source_new_seconds(timeout); GObject.source_set_closure(s, handler); return s; } function timeout_add(timeout, handler) { return timeout_source(timeout, handler).attach(null); } function timeout_add_seconds(timeout, handler) { return timeout_seconds_source(timeout, handler).attach(null); } function source_remove(id) { return GLib.source_remove(id); } cjs-2.8.0/modules/signals.js0000664000175000017500000001342312610172032014667 0ustar fabiofabio/* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ // A couple principals of this simple signal system: // 1) should look just like our GObject signal binding // 2) memory and safety matter more than speed of connect/disconnect/emit // 3) the expectation is that a given object will have a very small number of // connections, but they may be to different signal names function _connect(name, callback) { // be paranoid about callback arg since we'd start to throw from emit() // if it was messed up if (typeof(callback) != 'function') throw new Error("When connecting signal must give a callback that is a function"); // we instantiate the "signal machinery" only on-demand if anything // gets connected. if (!('_signalConnections' in this)) { this._signalConnections = []; this._nextConnectionId = 1; } let id = this._nextConnectionId; this._nextConnectionId += 1; // this makes it O(n) in total connections to emit, but I think // it's right to optimize for low memory and reentrancy-safety // rather than speed this._signalConnections.push({ 'id' : id, 'name' : name, 'callback' : callback, 'disconnected' : false }); return id; } function _disconnect(id) { if ('_signalConnections' in this) { let i; let length = this._signalConnections.length; for (i = 0; i < length; ++i) { let connection = this._signalConnections[i]; if (connection.id == id) { if (connection.disconnected) throw new Error("Signal handler id " + id + " already disconnected"); // set a flag to deal with removal during emission connection.disconnected = true; this._signalConnections.splice(i, 1); return; } } } throw new Error("No signal connection " + id + " found"); } function _signalHandlerIsConnected(id) { if (! '_signalConnections' in this) return false; for (let connection of this._signalConnections) { if (connection.id == id) { if (connection.disconnected) return false; else return true; } } return false; } function _disconnectAll() { if ('_signalConnections' in this) { while (this._signalConnections.length > 0) { _disconnect.call(this, this._signalConnections[0].id); } } } function _emit(name /* , arg1, arg2 */) { // may not be any signal handlers at all, if not then return if (!('_signalConnections' in this)) return; // To deal with re-entrancy (removal/addition while // emitting), we copy out a list of what was connected // at emission start; and just before invoking each // handler we check its disconnected flag. let handlers = []; let i; let length = this._signalConnections.length; for (i = 0; i < length; ++i) { let connection = this._signalConnections[i]; if (connection.name == name) { handlers.push(connection); } } // create arg array which is emitter + everything passed in except // signal name. Would be more convenient not to pass emitter to // the callback, but trying to be 100% consistent with GObject // which does pass it in. Also if we pass in the emitter here, // people don't create closures with the emitter in them, // which would be a cycle. let arg_array = [ this ]; // arguments[0] should be signal name so skip it length = arguments.length; for (i = 1; i < length; ++i) { arg_array.push(arguments[i]); } length = handlers.length; for (i = 0; i < length; ++i) { let connection = handlers[i]; if (!connection.disconnected) { try { // since we pass "null" for this, the global object will be used. let ret = connection.callback.apply(null, arg_array); // if the callback returns true, we don't call the next // signal handlers if (ret === true) { break; } } catch(e) { // just log any exceptions so that callbacks can't disrupt // signal emission logError(e, "Exception in callback for signal: "+name); } } } } function addSignalMethods(proto) { proto.connect = _connect; proto.disconnect = _disconnect; proto.emit = _emit; proto.signalHandlerIsConnected = _signalHandlerIsConnected; // this one is not in GObject, but useful proto.disconnectAll = _disconnectAll; } cjs-2.8.0/modules/cairo-radial-gradient.cpp0000664000175000017500000000635312610172032017523 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2010 litl, LLC. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include "cairo-private.h" GJS_DEFINE_PROTO("CairoRadialGradient", cairo_radial_gradient) GJS_NATIVE_CONSTRUCTOR_DECLARE(cairo_radial_gradient) { GJS_NATIVE_CONSTRUCTOR_VARIABLES(cairo_radial_gradient) double cx0, cy0, radius0, cx1, cy1, radius1; cairo_pattern_t *pattern; GJS_NATIVE_CONSTRUCTOR_PRELUDE(cairo_radial_gradient); if (!gjs_parse_args(context, "RadialGradient", "ffffff", argc, argv, "cx0", &cx0, "cy0", &cy0, "radius0", &radius0, "cx1", &cx1, "cy1", &cy1, "radius1", &radius1)) return JS_FALSE; pattern = cairo_pattern_create_radial(cx0, cy0, radius0, cx1, cy1, radius1); if (!gjs_cairo_check_status(context, cairo_pattern_status(pattern), "pattern")) return JS_FALSE; gjs_cairo_pattern_construct(context, object, pattern); cairo_pattern_destroy(pattern); GJS_NATIVE_CONSTRUCTOR_FINISH(cairo_radial_gradient); return JS_TRUE; } static void gjs_cairo_radial_gradient_finalize(JSFreeOp *fop, JSObject *obj) { gjs_cairo_pattern_finalize_pattern(fop, obj); } JSPropertySpec gjs_cairo_radial_gradient_proto_props[] = { { NULL } }; JSFunctionSpec gjs_cairo_radial_gradient_proto_funcs[] = { // getRadialCircles { NULL } }; JSObject * gjs_cairo_radial_gradient_from_pattern(JSContext *context, cairo_pattern_t *pattern) { JSObject *object; g_return_val_if_fail(context != NULL, NULL); g_return_val_if_fail(pattern != NULL, NULL); g_return_val_if_fail(cairo_pattern_get_type(pattern) == CAIRO_PATTERN_TYPE_RADIAL, NULL); object = JS_NewObject(context, &gjs_cairo_radial_gradient_class, NULL, NULL); if (!object) { gjs_throw(context, "failed to create radial gradient pattern"); return NULL; } gjs_cairo_pattern_construct(context, object, pattern); return object; } cjs-2.8.0/modules/cairo-surface.cpp0000664000175000017500000002061412610172032016120 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2010 litl, LLC. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include #include #include "cairo-private.h" typedef struct { void *dummy; JSContext *context; JSObject *object; cairo_surface_t *surface; } GjsCairoSurface; GJS_DEFINE_PROTO_ABSTRACT_WITH_GTYPE("CairoSurface", cairo_surface, CAIRO_GOBJECT_TYPE_SURFACE) GJS_DEFINE_PRIV_FROM_JS(GjsCairoSurface, gjs_cairo_surface_class) static void gjs_cairo_surface_finalize(JSFreeOp *fop, JSObject *obj) { GjsCairoSurface *priv; priv = (GjsCairoSurface*) JS_GetPrivate(obj); if (priv == NULL) return; cairo_surface_destroy(priv->surface); g_slice_free(GjsCairoSurface, priv); } /* Properties */ JSPropertySpec gjs_cairo_surface_proto_props[] = { { NULL } }; /* Methods */ static JSBool writeToPNG_func(JSContext *context, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(context, vp); JSObject *obj = JS_THIS_OBJECT(context, vp); char *filename; cairo_surface_t *surface; if (!gjs_parse_args(context, "writeToPNG", "s", argc, argv, "filename", &filename)) return JS_FALSE; surface = gjs_cairo_surface_get_surface(context, obj); if (!surface) { g_free(filename); return JS_FALSE; } cairo_surface_write_to_png(surface, filename); g_free(filename); if (!gjs_cairo_check_status(context, cairo_surface_status(surface), "surface")) return JS_FALSE; JS_SET_RVAL(context, vp, JSVAL_VOID); return JS_TRUE; } static JSBool getType_func(JSContext *context, unsigned argc, jsval *vp) { JSObject *obj = JS_THIS_OBJECT(context, vp); cairo_surface_t *surface; cairo_surface_type_t type; if (argc > 1) { gjs_throw(context, "Surface.getType() takes no arguments"); return JS_FALSE; } surface = gjs_cairo_surface_get_surface(context, obj); type = cairo_surface_get_type(surface); if (!gjs_cairo_check_status(context, cairo_surface_status(surface), "surface")) return JS_FALSE; JS_SET_RVAL(context, vp, INT_TO_JSVAL(type)); return JS_TRUE; } JSFunctionSpec gjs_cairo_surface_proto_funcs[] = { // flush // getContent // getFontOptions { "getType", JSOP_WRAPPER((JSNative)getType_func), 0, 0}, // markDirty // markDirtyRectangle // setDeviceOffset // getDeviceOffset // setFallbackResolution // getFallbackResolution // copyPage // showPage // hasShowTextGlyphs { "writeToPNG", JSOP_WRAPPER((JSNative)writeToPNG_func), 0, 0 }, { NULL } }; /* Public API */ /** * gjs_cairo_surface_construct: * @context: the context * @object: object to construct * @surface: cairo_surface to attach to the object * * Constructs a surface wrapper giving an empty JSObject and a * cairo surface. A reference to @surface will be taken. * * This is mainly used for subclasses where object is already created. */ void gjs_cairo_surface_construct(JSContext *context, JSObject *object, cairo_surface_t *surface) { GjsCairoSurface *priv; g_return_if_fail(context != NULL); g_return_if_fail(object != NULL); g_return_if_fail(surface != NULL); priv = g_slice_new0(GjsCairoSurface); g_assert(priv_from_js(context, object) == NULL); JS_SetPrivate(object, priv); priv->context = context; priv->object = object; priv->surface = cairo_surface_reference(surface); } /** * gjs_cairo_surface_finalize: * @fop: the free op * @object: object to finalize * * Destroys the resources associated with a surface wrapper. * * This is mainly used for subclasses. */ void gjs_cairo_surface_finalize_surface(JSFreeOp *fop, JSObject *object) { g_return_if_fail(fop != NULL); g_return_if_fail(object != NULL); gjs_cairo_surface_finalize(fop, object); } /** * gjs_cairo_surface_from_surface: * @context: the context * @surface: cairo_surface to attach to the object * * Constructs a surface wrapper given cairo surface. * A reference to @surface will be taken. * */ JSObject * gjs_cairo_surface_from_surface(JSContext *context, cairo_surface_t *surface) { JSObject *object; g_return_val_if_fail(context != NULL, NULL); g_return_val_if_fail(surface != NULL, NULL); switch (cairo_surface_get_type(surface)) { case CAIRO_SURFACE_TYPE_IMAGE: return gjs_cairo_image_surface_from_surface(context, surface); case CAIRO_SURFACE_TYPE_PDF: return gjs_cairo_pdf_surface_from_surface(context, surface); case CAIRO_SURFACE_TYPE_PS: return gjs_cairo_ps_surface_from_surface(context, surface); case CAIRO_SURFACE_TYPE_SVG: return gjs_cairo_svg_surface_from_surface(context, surface); default: break; } object = JS_NewObject(context, &gjs_cairo_surface_class, NULL, NULL); if (!object) { gjs_throw(context, "failed to create surface"); return NULL; } gjs_cairo_surface_construct(context, object, surface); return object; } /** * gjs_cairo_surface_get_surface: * @context: the context * @object: surface wrapper * * Returns: the surface attaches to the wrapper. * */ cairo_surface_t * gjs_cairo_surface_get_surface(JSContext *context, JSObject *object) { GjsCairoSurface *priv; g_return_val_if_fail(context != NULL, NULL); g_return_val_if_fail(object != NULL, NULL); priv = (GjsCairoSurface*) JS_GetPrivate(object); if (priv == NULL) return NULL; return priv->surface; } static JSBool surface_to_g_argument(JSContext *context, jsval value, const char *arg_name, GjsArgumentType argument_type, GITransfer transfer, gboolean may_be_null, GArgument *arg) { JSObject *obj; cairo_surface_t *s; obj = JSVAL_TO_OBJECT(value); s = gjs_cairo_surface_get_surface(context, obj); if (!s) return JS_FALSE; if (transfer == GI_TRANSFER_EVERYTHING) cairo_surface_destroy(s); arg->v_pointer = s; return JS_TRUE; } static JSBool surface_from_g_argument(JSContext *context, jsval *value_p, GArgument *arg) { JSObject *obj; obj = gjs_cairo_surface_from_surface(context, (cairo_surface_t*)arg->v_pointer); if (!obj) return JS_FALSE; *value_p = OBJECT_TO_JSVAL(obj); return JS_TRUE; } static JSBool surface_release_argument(JSContext *context, GITransfer transfer, GArgument *arg) { cairo_surface_destroy((cairo_surface_t*)arg->v_pointer); return JS_TRUE; } static GjsForeignInfo foreign_info = { surface_to_g_argument, surface_from_g_argument, surface_release_argument }; void gjs_cairo_surface_init(JSContext *context) { gjs_struct_foreign_register("cairo", "Surface", &foreign_info); } cjs-2.8.0/modules/cairo-path.cpp0000664000175000017500000000635012610172032015425 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2010 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include "cairo-private.h" typedef struct { JSContext *context; JSObject *object; cairo_path_t *path; } GjsCairoPath; GJS_DEFINE_PROTO_ABSTRACT("CairoPath", cairo_path) GJS_DEFINE_PRIV_FROM_JS(GjsCairoPath, gjs_cairo_path_class) static void gjs_cairo_path_finalize(JSFreeOp *fop, JSObject *obj) { GjsCairoPath *priv; priv = (GjsCairoPath*) JS_GetPrivate(obj); if (priv == NULL) return; cairo_path_destroy(priv->path); g_slice_free(GjsCairoPath, priv); } /* Properties */ JSPropertySpec gjs_cairo_path_proto_props[] = { { NULL } }; JSFunctionSpec gjs_cairo_path_proto_funcs[] = { { NULL } }; /** * gjs_cairo_path_from_path: * @context: the context * @path: cairo_path_t to attach to the object * * Constructs a pattern wrapper given cairo pattern. * NOTE: This function takes ownership of the path. */ JSObject * gjs_cairo_path_from_path(JSContext *context, cairo_path_t *path) { JSObject *object; GjsCairoPath *priv; g_return_val_if_fail(context != NULL, NULL); g_return_val_if_fail(path != NULL, NULL); object = JS_NewObject(context, &gjs_cairo_path_class, NULL, NULL); if (!object) { gjs_throw(context, "failed to create path"); return NULL; } priv = g_slice_new0(GjsCairoPath); g_assert(priv_from_js(context, object) == NULL); JS_SetPrivate(object, priv); priv->context = context; priv->object = object; priv->path = path; return object; } /** * gjs_cairo_path_get_path: * @context: the context * @object: path wrapper * * Returns: the path attached to the wrapper. * */ cairo_path_t * gjs_cairo_path_get_path(JSContext *context, JSObject *object) { GjsCairoPath *priv; g_return_val_if_fail(context != NULL, NULL); g_return_val_if_fail(object != NULL, NULL); priv = (GjsCairoPath*) JS_GetPrivate(object); if (priv == NULL) return NULL; return priv->path; } cjs-2.8.0/modules/gettext.js0000664000175000017500000000567512610172032014725 0ustar fabiofabio// Copyright 2009 Red Hat, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. /** * This module provides a convenience layer for the "gettext" family of functions, * relying on GLib for the actual implementation. * * Usage: * * const Gettext = imports.gettext; * * Gettext.textdomain("myapp"); * Gettext.bindtextdomain("myapp", "/usr/share/locale"); * * let translated = Gettext.gettext("Hello world!"); */ const GLib = imports.gi.GLib; const GjsPrivate = imports.gi.CjsPrivate; function textdomain(domain) { return GjsPrivate.textdomain(domain); } function bindtextdomain(domain, location) { return GjsPrivate.bindtextdomain(domain, location); } function gettext(msgid) { return GLib.dgettext(null, msgid); } function dgettext(domain, msgid) { return GLib.dgettext(domain, msgid); } function dcgettext(domain, msgid, category) { return GLib.dcgettext(domain, msgid, category); } function ngettext(msgid1, msgid2, n) { return GLib.dngettext(null, msgid1, msgid2, n); } function dngettext(domain, msgid1, msgid2, n) { return GLib.dngettext(domain, msgid1, msgid2, n); } // FIXME: missing dcngettext ? function pgettext(context, msgid) { return GLib.dpgettext2(null, context, msgid); } function dpgettext(domain, context, msgid) { return GLib.dpgettext2(domain, context, msgid); } /** * Create an object with bindings for gettext, ngettext, * and pgettext bound to a particular translation domain. * * @param domainName Translation domain string * @returns: an object with gettext bindings * @type: function */ var domain = function(domainName) { return { gettext: function(msgid) { return GLib.dgettext(domainName, msgid); }, ngettext: function(msgid1, msgid2, n) { return GLib.dngettext(domainName, msgid1, msgid2, n); }, pgettext: function(context, msgid) { return GLib.dpgettext2(domainName, context, msgid); } }; }; cjs-2.8.0/modules/jsUnit.js0000664000175000017500000003520712610172032014507 0ustar fabiofabio/* @author Edward Hieatt, edward@jsunit.net */ /* - JsUnit - Copyright (C) 2001-4 Edward Hieatt, edward@jsunit.net - Copyright (C) 2008 litl, LLC - - Version: MPL 1.1/GPL 2.0/LGPL 2.1 - - The contents of this file are subject to the Mozilla Public License Version - 1.1 (the "License"); you may not use this file except in compliance with - the License. You may obtain a copy of the License at - http://www.mozilla.org/MPL/ - - Software distributed under the License is distributed on an "AS IS" basis, - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - for the specific language governing rights and limitations under the - License. - - The Original Code is Edward Hieatt code. - - The Initial Developer of the Original Code is - Edward Hieatt, edward@jsunit.net. - Portions created by the Initial Developer are Copyright (C) 2003 - the Initial Developer. All Rights Reserved. - - Author Edward Hieatt, edward@jsunit.net - - Alternatively, the contents of this file may be used under the terms of - either the GNU General Public License Version 2 or later (the "GPL"), or - the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - in which case the provisions of the GPL or the LGPL are applicable instead - of those above. If you wish to allow use of your version of this file only - under the terms of either the GPL or the LGPL, and not to allow others to - use your version of this file under the terms of the MPL, indicate your - decision by deleting the provisions above and replace them with the notice - and other provisions required by the LGPL or the GPL. If you do not delete - the provisions above, a recipient may use your version of this file under - the terms of any one of the MPL, the GPL or the LGPL. */ var JSUNIT_UNDEFINED_VALUE; var JSUNIT_VERSION="2.1"; var isTestPageLoaded = false; // GJS: introduce implicit variable to avoid exceptions var top = null; //hack for NS62 bug function jsUnitFixTop() { var tempTop = top; if (!tempTop) { tempTop = window; while (tempTop.parent) { tempTop = tempTop.parent; if (tempTop.top && tempTop.top.jsUnitTestSuite) { tempTop = tempTop.top; break; } } } top = tempTop; } jsUnitFixTop(); function _displayStringForValue(aVar) { if (aVar === null) return 'null'; if (aVar === top.JSUNIT_UNDEFINED_VALUE) return 'undefined'; return aVar; } function fail(failureMessage) { throw new JsUnitException(null, failureMessage); } function error(errorMessage) { throw new Error(errorMessage); } function argumentsIncludeComments(expectedNumberOfNonCommentArgs, args) { return args.length == expectedNumberOfNonCommentArgs + 1; } function commentArg(expectedNumberOfNonCommentArgs, args) { if (argumentsIncludeComments(expectedNumberOfNonCommentArgs, args)) return args[0]; return null; } function nonCommentArg(desiredNonCommentArgIndex, expectedNumberOfNonCommentArgs, args) { return argumentsIncludeComments(expectedNumberOfNonCommentArgs, args) ? args[desiredNonCommentArgIndex] : args[desiredNonCommentArgIndex - 1]; } function _validateArguments(expectedNumberOfNonCommentArgs, args) { if (!( args.length == expectedNumberOfNonCommentArgs || (args.length == expectedNumberOfNonCommentArgs + 1 && typeof(args[0]) == 'string') )) error('Incorrect arguments passed to assert function'); } function _assert(comment, booleanValue, failureMessage) { if (!booleanValue) throw new JsUnitException(comment, failureMessage); } function assert() { _validateArguments(1, arguments); var booleanValue=nonCommentArg(1, 1, arguments); if (typeof(booleanValue) != 'boolean') error('Bad argument to assert(boolean)'); _assert(commentArg(1, arguments), booleanValue === true, 'Call to assert(boolean) with false'); } function assertTrue() { _validateArguments(1, arguments); var booleanValue=nonCommentArg(1, 1, arguments); if (typeof(booleanValue) != 'boolean') error('Bad argument to assertTrue(boolean)'); _assert(commentArg(1, arguments), booleanValue === true, 'Call to assertTrue(boolean) with false'); } function assertFalse() { _validateArguments(1, arguments); var booleanValue=nonCommentArg(1, 1, arguments); if (typeof(booleanValue) != 'boolean') error('Bad argument to assertFalse(boolean)'); _assert(commentArg(1, arguments), booleanValue === false, 'Call to assertFalse(boolean) with true'); } function assertEquals() { _validateArguments(2, arguments); var var1=nonCommentArg(1, 2, arguments); var var2=nonCommentArg(2, 2, arguments); _assert(commentArg(2, arguments), var1 === var2, 'Expected ' + var1 + ' (' + typeof(var1) + ') but was ' + _displayStringForValue(var2) + ' (' + typeof(var2) + ')'); } function assertNotEquals() { _validateArguments(2, arguments); var var1=nonCommentArg(1, 2, arguments); var var2=nonCommentArg(2, 2, arguments); _assert(commentArg(2, arguments), var1 !== var2, 'Expected not to be ' + _displayStringForValue(var2)); } function assertNull() { _validateArguments(1, arguments); var aVar=nonCommentArg(1, 1, arguments); _assert(commentArg(1, arguments), aVar === null, 'Expected null but was ' + _displayStringForValue(aVar)); } function assertNotNull() { _validateArguments(1, arguments); var aVar=nonCommentArg(1, 1, arguments); _assert(commentArg(1, arguments), aVar !== null, 'Expected not to be null'); } function assertUndefined() { _validateArguments(1, arguments); var aVar=nonCommentArg(1, 1, arguments); _assert(commentArg(1, arguments), aVar === top.JSUNIT_UNDEFINED_VALUE, 'Expected undefined but was ' + _displayStringForValue(aVar)); } function assertNotUndefined() { _validateArguments(1, arguments); var aVar=nonCommentArg(1, 1, arguments); _assert(commentArg(1, arguments), aVar !== top.JSUNIT_UNDEFINED_VALUE, 'Expected not to be undefined'); } function assertNaN() { _validateArguments(1, arguments); var aVar=nonCommentArg(1, 1, arguments); _assert(commentArg(1, arguments), isNaN(aVar), 'Expected NaN'); } function assertNotNaN() { _validateArguments(1, arguments); var aVar=nonCommentArg(1, 1, arguments); _assert(commentArg(1, arguments), !isNaN(aVar), 'Expected not NaN'); } // GJS: assertRaises(function) function assertRaises() { _validateArguments(1, arguments); var fun=nonCommentArg(1, 1, arguments); var exception; if (typeof(fun) != 'function') error("Bad argument to assertRaises(function)"); var retval; try { retval = fun(); } catch (e) { exception = e; } _assert(commentArg(1, arguments), exception !== top.JSUNIT_UNDEFINED_VALUE, "Call to assertRaises(function) did not raise an exception. Return value was " + _displayStringForValue(retval) + ' (' + typeof(retval) + ')'); } function isLoaded() { return isTestPageLoaded; } function setUp() { } function tearDown() { } function getFunctionName(aFunction) { var name = aFunction.toString().match(/function (\w*)/)[1]; if ((name == null) || (name.length == 0)) name = 'anonymous'; return name; } function parseErrorStack(excp) { var stack = []; var name; if (!excp || !excp.stack) { return stack; } var stacklist = excp.stack.split('\n'); for (var i = 0; i < stacklist.length - 1; i++) { var framedata = stacklist[i]; name = framedata.match(/^(\w*)/)[1]; if (!name) { name = 'anonymous'; } var line = framedata.match(/(:\d+)$/)[1]; if (line) { name += line; } stack[stack.length] = name; } // remove top level anonymous functions to match IE while (stack.length && stack[stack.length - 1] == 'anonymous') { stack.length = stack.length - 1; } return stack; } function JsUnitException(comment, message) { this.isJsUnitException = true; this.comment = comment; this.message = message; this.stack = (new Error()).stack; } JsUnitException.prototype = Object.create(Error.prototype, {}); function warn() { if (top.tracer != null) top.tracer.warn(arguments[0], arguments[1]); } function inform() { if (top.tracer != null) top.tracer.inform(arguments[0], arguments[1]); } function info() { inform(arguments[0], arguments[1]); } function debug() { if (top.tracer != null) top.tracer.debug(arguments[0], arguments[1]); } function setjsUnitTracer(ajsUnitTracer) { top.tracer=ajsUnitTracer; } function trim(str) { if (str == null) return null; var startingIndex = 0; var endingIndex = str.length-1; while (str.substring(startingIndex, startingIndex+1) == ' ') startingIndex++; while (str.substring(endingIndex, endingIndex+1) == ' ') endingIndex--; if (endingIndex < startingIndex) return ''; return str.substring(startingIndex, endingIndex+1); } function isBlank(str) { return trim(str) == ''; } // the functions push(anArray, anObject) and pop(anArray) // exist because the JavaScript Array.push(anObject) and Array.pop() // functions are not available in IE 5.0 function push(anArray, anObject) { anArray[anArray.length]=anObject; } function pop(anArray) { if (anArray.length>=1) { delete anArray[anArray.length - 1]; anArray.length--; } } // safe, strict access to jsUnitParmHash function jsUnitGetParm(name) { if (typeof(top.jsUnitParmHash[name]) != 'undefined') { return top.jsUnitParmHash[name]; } return null; } if (top && typeof(top.xbDEBUG) != 'undefined' && top.xbDEBUG.on && top.testManager) { top.xbDebugTraceObject('top.testManager.containerTestFrame', 'JSUnitException'); // asserts top.xbDebugTraceFunction('top.testManager.containerTestFrame', '_displayStringForValue'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'error'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'argumentsIncludeComments'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'commentArg'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'nonCommentArg'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', '_validateArguments'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', '_assert'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'assert'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'assertTrue'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'assertEquals'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'assertNotEquals'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'assertNull'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'assertNotNull'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'assertUndefined'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'assertNotUndefined'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'assertNaN'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'assertNotNaN'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'isLoaded'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'setUp'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'tearDown'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'getFunctionName'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'warn'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'inform'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'debug'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'setjsUnitTracer'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'trim'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'isBlank'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'newOnLoadEvent'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'push'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'pop'); } function newOnLoadEvent() { isTestPageLoaded = true; } function jsUnitSetOnLoad(windowRef, onloadHandler) { var isKonqueror = navigator.userAgent.indexOf('Konqueror/') != -1 || navigator.userAgent.indexOf('Safari/') != -1; if (typeof(windowRef.attachEvent) != 'undefined') { // Internet Explorer, Opera windowRef.attachEvent("onload", onloadHandler); } else if (typeof(windowRef.addEventListener) != 'undefined' && !isKonqueror){ // Mozilla, Konqueror // exclude Konqueror due to load issues windowRef.addEventListener("load", onloadHandler, false); } else if (typeof(windowRef.document.addEventListener) != 'undefined' && !isKonqueror) { // DOM 2 Events // exclude Mozilla, Konqueror due to load issues windowRef.document.addEventListener("load", onloadHandler, false); } else if (typeof(windowRef.onload) != 'undefined' && windowRef.onload) { windowRef.jsunit_original_onload = windowRef.onload; windowRef.onload = function() { windowRef.jsunit_original_onload(); onloadHandler(); }; } else { // browsers that do not support windowRef.attachEvent or // windowRef.addEventListener will override a page's own onload event windowRef.onload=onloadHandler; } } // GJS: comment out as isLoaded() isn't terribly useful for us //jsUnitSetOnLoad(window, newOnLoadEvent); // GJS: entry point to run all functions named as test*, surrounded by // calls to setUp() and tearDown() function gjstestRun(window_, setUp, tearDown) { var propName; var rv = 0; var failures = []; if (!window_) window_ = window; if (!setUp) setUp = window_.setUp; if (!tearDown) tearDown = window_.tearDown; for (propName in window_) { if (!propName.match(/^test\w+/)) continue; var testFunction = window_[propName]; if (typeof(testFunction) != 'function') continue; log("running test " + propName); setUp(); try { testFunction(); } catch (e) { var result = null; if (typeof(e.isJsUnitException) != 'undefined' && e.isJsUnitException) { result = ''; if (e.comment != null) result += ('"' + e.comment + '"\n'); result += e.message; if (e.stack) result += '\n\nStack trace follows:\n' + e.stack; // assertion failure, kind of expected so just log it and flag the // whole test as failed log(result); rv = 1; failures.push(propName); } else { // unexpected error, let the shell handle it throw e; } } tearDown(); } if (failures.length > 0) { log(failures.length + " tests failed in this file"); log("Failures were: " + failures.join(", ")); } // if gjstestRun() is the last call in a file, this becomes the // exit code of the test program, so 0 = success, 1 = failed return rv; } cjs-2.8.0/modules/cairo-surface-pattern.cpp0000664000175000017500000001406312610172032017574 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2010 litl, LLC. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include "cairo-private.h" GJS_DEFINE_PROTO("CairoSurfacePattern", cairo_surface_pattern) GJS_NATIVE_CONSTRUCTOR_DECLARE(cairo_surface_pattern) { GJS_NATIVE_CONSTRUCTOR_VARIABLES(cairo_surface_pattern) JSObject *surface_wrapper; cairo_surface_t *surface; cairo_pattern_t *pattern; GJS_NATIVE_CONSTRUCTOR_PRELUDE(cairo_surface_pattern); if (!gjs_parse_args(context, "SurfacePattern", "o", argc, argv, "surface", &surface_wrapper)) return JS_FALSE; surface = gjs_cairo_surface_get_surface(context, surface_wrapper); if (!surface) { gjs_throw(context, "first argument to SurfacePattern() should be a surface"); return JS_FALSE; } pattern = cairo_pattern_create_for_surface(surface); if (!gjs_cairo_check_status(context, cairo_pattern_status(pattern), "pattern")) return JS_FALSE; gjs_cairo_pattern_construct(context, object, pattern); cairo_pattern_destroy(pattern); GJS_NATIVE_CONSTRUCTOR_FINISH(cairo_surface_pattern); return JS_TRUE; } static void gjs_cairo_surface_pattern_finalize(JSFreeOp *fop, JSObject *obj) { gjs_cairo_pattern_finalize_pattern(fop, obj); } JSPropertySpec gjs_cairo_surface_pattern_proto_props[] = { { NULL } }; static JSBool setExtend_func(JSContext *context, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(context, vp); JSObject *obj = JS_THIS_OBJECT(context, vp); cairo_extend_t extend; cairo_pattern_t *pattern; if (!gjs_parse_args(context, "setExtend", "i", argc, argv, "extend", &extend)) return JS_FALSE; pattern = gjs_cairo_pattern_get_pattern(context, obj); cairo_pattern_set_extend(pattern, extend); if (!gjs_cairo_check_status(context, cairo_pattern_status(pattern), "pattern")) return JS_FALSE; JS_SET_RVAL(context, vp, JSVAL_VOID); return JS_TRUE; } static JSBool getExtend_func(JSContext *context, unsigned argc, jsval *vp) { JSObject *obj = JS_THIS_OBJECT(context, vp); cairo_extend_t extend; cairo_pattern_t *pattern; if (argc > 0) { gjs_throw(context, "SurfacePattern.getExtend() requires no arguments"); return JS_FALSE; } pattern = gjs_cairo_pattern_get_pattern(context, obj); extend = cairo_pattern_get_extend(pattern); if (!gjs_cairo_check_status(context, cairo_pattern_status(pattern), "pattern")) return JS_FALSE; JS_SET_RVAL(context, vp, INT_TO_JSVAL(extend)); return JS_TRUE; } static JSBool setFilter_func(JSContext *context, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(context, vp); JSObject *obj = JS_THIS_OBJECT(context, vp); cairo_filter_t filter; cairo_pattern_t *pattern; if (!gjs_parse_args(context, "setFilter", "i", argc, argv, "filter", &filter)) return JS_FALSE; pattern = gjs_cairo_pattern_get_pattern(context, obj); cairo_pattern_set_filter(pattern, filter); if (!gjs_cairo_check_status(context, cairo_pattern_status(pattern), "pattern")) return JS_FALSE; JS_SET_RVAL(context, vp, JSVAL_VOID); return JS_TRUE; } static JSBool getFilter_func(JSContext *context, unsigned argc, jsval *vp) { JSObject *obj = JS_THIS_OBJECT(context, vp); cairo_filter_t filter; cairo_pattern_t *pattern; if (argc > 0) { gjs_throw(context, "SurfacePattern.getFilter() requires no arguments"); return JS_FALSE; } pattern = gjs_cairo_pattern_get_pattern(context, obj); filter = cairo_pattern_get_filter(pattern); if (!gjs_cairo_check_status(context, cairo_pattern_status(pattern), "pattern")) return JS_FALSE; JS_SET_RVAL(context, vp, INT_TO_JSVAL(filter)); return JS_TRUE; } JSFunctionSpec gjs_cairo_surface_pattern_proto_funcs[] = { { "setExtend", JSOP_WRAPPER((JSNative)setExtend_func), 0, 0 }, { "getExtend", JSOP_WRAPPER((JSNative)getExtend_func), 0, 0 }, { "setFilter", JSOP_WRAPPER((JSNative)setFilter_func), 0, 0 }, { "getFilter", JSOP_WRAPPER((JSNative)getFilter_func), 0, 0 }, { NULL } }; JSObject * gjs_cairo_surface_pattern_from_pattern(JSContext *context, cairo_pattern_t *pattern) { JSObject *object; g_return_val_if_fail(context != NULL, NULL); g_return_val_if_fail(pattern != NULL, NULL); g_return_val_if_fail(cairo_pattern_get_type(pattern) == CAIRO_PATTERN_TYPE_SURFACE, NULL); object = JS_NewObject(context, &gjs_cairo_surface_pattern_class, NULL, NULL); if (!object) { gjs_throw(context, "failed to create surface pattern"); return NULL; } gjs_cairo_pattern_construct(context, object, pattern); return object; } cjs-2.8.0/modules/cairo-private.h0000664000175000017500000002353212610172032015611 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2010 litl, LLC. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __CAIRO_PRIVATE_H__ #define __CAIRO_PRIVATE_H__ #include "cairo-module.h" #include JSBool gjs_cairo_check_status (JSContext *context, cairo_status_t status, const char *name); jsval gjs_cairo_context_create_proto (JSContext *context, JSObject *module, const char *proto_name, JSObject *parent); cairo_t * gjs_cairo_context_get_context (JSContext *context, JSObject *object); JSObject * gjs_cairo_context_from_context (JSContext *context, cairo_t *cr); void gjs_cairo_context_init (JSContext *context); void gjs_cairo_surface_init (JSContext *context); /* cairo_path_t */ jsval gjs_cairo_path_create_proto (JSContext *context, JSObject *module, const char *proto_name, JSObject *parent); JSObject * gjs_cairo_path_from_path (JSContext *context, cairo_path_t *path); cairo_path_t * gjs_cairo_path_get_path (JSContext *context, JSObject *path_wrapper); /* surface */ jsval gjs_cairo_surface_create_proto (JSContext *context, JSObject *module, const char *proto_name, JSObject *parent); void gjs_cairo_surface_construct (JSContext *context, JSObject *object, cairo_surface_t *surface); void gjs_cairo_surface_finalize_surface (JSFreeOp *fop, JSObject *object); JSObject * gjs_cairo_surface_from_surface (JSContext *context, cairo_surface_t *surface); cairo_surface_t* gjs_cairo_surface_get_surface (JSContext *context, JSObject *object); /* image surface */ jsval gjs_cairo_image_surface_create_proto (JSContext *context, JSObject *module, const char *proto_name, JSObject *parent); void gjs_cairo_image_surface_init (JSContext *context, JSObject *object); JSObject * gjs_cairo_image_surface_from_surface (JSContext *context, cairo_surface_t *surface); /* postscript surface */ #ifdef CAIRO_HAS_PS_SURFACE jsval gjs_cairo_ps_surface_create_proto (JSContext *context, JSObject *module, const char *proto_name, JSObject *parent); #endif JSObject * gjs_cairo_ps_surface_from_surface (JSContext *context, cairo_surface_t *surface); /* pdf surface */ #ifdef CAIRO_HAS_PDF_SURFACE jsval gjs_cairo_pdf_surface_create_proto (JSContext *context, JSObject *module, const char *proto_name, JSObject *parent); #endif JSObject * gjs_cairo_pdf_surface_from_surface (JSContext *context, cairo_surface_t *surface); /* svg surface */ #ifdef CAIRO_HAS_SVG_SURFACE jsval gjs_cairo_svg_surface_create_proto (JSContext *context, JSObject *module, const char *proto_name, JSObject *parent); #endif JSObject * gjs_cairo_svg_surface_from_surface (JSContext *context, cairo_surface_t *surface); /* pattern */ jsval gjs_cairo_pattern_create_proto (JSContext *context, JSObject *module, const char *proto_name, JSObject *parent); void gjs_cairo_pattern_construct (JSContext *context, JSObject *object, cairo_pattern_t *pattern); void gjs_cairo_pattern_finalize_pattern (JSFreeOp *fop, JSObject *object); JSObject* gjs_cairo_pattern_from_pattern (JSContext *context, cairo_pattern_t *pattern); cairo_pattern_t* gjs_cairo_pattern_get_pattern (JSContext *context, JSObject *object); /* gradient */ jsval gjs_cairo_gradient_create_proto (JSContext *context, JSObject *module, const char *proto_name, JSObject *parent); /* linear gradient */ jsval gjs_cairo_linear_gradient_create_proto (JSContext *context, JSObject *module, const char *proto_name, JSObject *parent); JSObject * gjs_cairo_linear_gradient_from_pattern (JSContext *context, cairo_pattern_t *pattern); /* radial gradient */ jsval gjs_cairo_radial_gradient_create_proto (JSContext *context, JSObject *module, const char *proto_name, JSObject *parent); JSObject * gjs_cairo_radial_gradient_from_pattern (JSContext *context, cairo_pattern_t *pattern); /* surface pattern */ jsval gjs_cairo_surface_pattern_create_proto (JSContext *context, JSObject *module, const char *proto_name, JSObject *parent); JSObject * gjs_cairo_surface_pattern_from_pattern (JSContext *context, cairo_pattern_t *pattern); /* solid pattern */ jsval gjs_cairo_solid_pattern_create_proto (JSContext *context, JSObject *module, const char *proto_name, JSObject *parent); JSObject * gjs_cairo_solid_pattern_from_pattern (JSContext *context, cairo_pattern_t *pattern); #endif /* __CAIRO_PRIVATE_H__ */ cjs-2.8.0/Makefile-modules.am0000664000175000017500000000471612610172032014730 0ustar fabiofabio NATIVE_MODULES = libconsole.la libsystem.la libmodules_resources.la if ENABLE_CAIRO NATIVE_MODULES += libcairoNative.la endif noinst_LTLIBRARIES += $(NATIVE_MODULES) libcjs_la_LIBADD += $(NATIVE_MODULES) JS_NATIVE_MODULE_CFLAGS = \ $(AM_CPPFLAGS) \ -DGJS_COMPILATION \ $(GJS_CFLAGS) JS_NATIVE_MODULE_LIBADD = \ $(GJS_LIBS) modules_resource_files = $(shell glib-compile-resources --sourcedir=$(srcdir) --generate-dependencies $(srcdir)/modules/modules.gresource.xml) modules-resources.h: $(srcdir)/modules/modules.gresource.xml $(modules_resource_files) $(AM_V_GEN) glib-compile-resources --target=$@ --sourcedir=$(srcdir) --sourcedir=$(builddir) --generate --c-name modules_resources $< modules-resources.c: $(srcdir)/modules/modules.gresource.xml $(modules_resource_files) $(AM_V_GEN) glib-compile-resources --target=$@ --sourcedir=$(srcdir) --sourcedir=$(builddir) --generate --c-name modules_resources $< EXTRA_DIST += $(modules_resource_files) $(srcdir)/modules/modules.gresource.xml nodist_libmodules_resources_la_SOURCES = modules-resources.c modules-resources.h CLEANFILES += $(nodist_libmodules_resources_la_SOURCES) libmodules_resources_la_CPPFLAGS = $(JS_NATIVE_MODULE_CFLAGS) libmodules_resources_la_LIBADD = $(JS_NATIVE_MODULE_LIBADD) libcairoNative_la_CPPFLAGS = $(JS_NATIVE_MODULE_CFLAGS) $(GJS_CAIRO_CFLAGS) libcairoNative_la_LIBADD = $(JS_NATIVE_MODULE_LIBADD) $(GJS_CAIRO_LIBS) libcairoNative_la_SOURCES = \ modules/cairo-private.h \ modules/cairo-module.h \ modules/cairo-context.cpp \ modules/cairo-path.cpp \ modules/cairo-surface.cpp \ modules/cairo-image-surface.cpp \ modules/cairo-ps-surface.cpp \ modules/cairo-pdf-surface.cpp \ modules/cairo-svg-surface.cpp \ modules/cairo-pattern.cpp \ modules/cairo-gradient.cpp \ modules/cairo-linear-gradient.cpp \ modules/cairo-radial-gradient.cpp \ modules/cairo-surface-pattern.cpp \ modules/cairo-solid-pattern.cpp \ modules/cairo.cpp libsystem_la_CPPFLAGS = $(JS_NATIVE_MODULE_CFLAGS) libsystem_la_LIBADD = $(JS_NATIVE_MODULE_LIBADD) libsystem_la_SOURCES = modules/system.h modules/system.cpp libconsole_la_CPPFLAGS = $(JS_NATIVE_MODULE_CFLAGS) libconsole_la_LIBADD = $(JS_NATIVE_MODULE_LIBADD) $(READLINE_LIBS) libconsole_la_SOURCES = modules/console.h modules/console.cpp cjs-2.8.0/cjs-internals-1.0.pc.in0000664000175000017500000000055412610172032015223 0ustar fabiofabioprefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ bindir=@bindir@ includedir=@includedir@ datarootdir=@datarootdir@ datadir=@datadir@ jsdir=@gjsjsdir@ Cflags: -I${includedir}/cjs-1.0 Requires: cjs-1.0 gobject-introspection-1.0 mozjs-24 Name: cjs-internals-1.0 Description: Internal API for cjs (for modules and embedders); uses mozjs Version: @VERSION@ cjs-2.8.0/configure.ac0000664000175000017500000001721012610172032013505 0ustar fabiofabio# -*- Autoconf -*- # Process this file with autoconf to produce a configure script. m4_define(pkg_major_version, 2) m4_define(pkg_minor_version, 8) m4_define(pkg_micro_version, 0) 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) AC_PREREQ(2.61) AC_INIT([cjs], pkg_version, [https://github.com/linuxmint/cjs]) AM_INIT_AUTOMAKE([dist-xz no-dist-gzip]) AC_CONFIG_SRCDIR([cjs/console.cpp]) AC_CONFIG_HEADER([config.h]) AC_DEFINE([GJS_VERSION], pkg_int_version, [The gjs version as an integer]) GETTEXT_PACKAGE=cjs AC_SUBST([GETTEXT_PACKAGE]) AC_DEFINE_UNQUOTED([GETTEXT_PACKAGE], ["$GETTEXT_PACKAGE"], [The name of the gettext domain]) AM_MAINTAINER_MODE([enable]) m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) # our first pkg-config invocation is conditional, ensure macros still work PKG_PROG_PKG_CONFIG AC_PROG_CC AM_PROG_CC_C_O AC_PROG_CXX AC_ISC_POSIX AC_HEADER_STDC # no stupid static libraries AM_DISABLE_STATIC # avoid libtool for LTCOMPILE, use it only to link AC_PROG_LIBTOOL dnl DOLT # Checks for typedefs, structures, and compiler characteristics. AC_C_CONST GNOME_CXX_WARNINGS([maximum]) GNOME_MAINTAINER_MODE_DEFINES # coverage AC_ARG_ENABLE([coverage], [AS_HELP_STRING([--enable-coverage], [enable code coverage])], , [enable_coverage=no]) if test x$enable_coverage = xyes; then if test x$GCC != xyes; then AC_MSG_ERROR([GCC is required for --enable-coverage]) fi AC_PATH_TOOL([LCOV], [lcov]) AC_PATH_TOOL([GENHTML], [genhtml]) if test x$LCOV = x || test x$GENHTML = x; then AC_MSG_ERROR([lcov and genhtml are required for --enable-coverage]) fi CFLAGS="$CFLAGS -g -O0 --coverage" CXXFLAGS="$CXXFLAGS -g -O0 --coverage" LIBS="$LIBS -lgcov" fi AM_CONDITIONAL([ENABLE_COVERAGE], [test x$enable_coverage = xyes]) # Checks for libraries. m4_define(glib_required_version, 2.37.3) AC_CHECK_HEADERS([malloc.h]) AC_CHECK_FUNCS(mallinfo) GOBJECT_INTROSPECTION_REQUIRE([1.33.10]) common_packages="gmodule-2.0 gthread-2.0 gio-2.0 >= glib_required_version mozjs-24" gjs_packages="gobject-introspection-1.0 libffi $common_packages" gjs_cairo_packages="cairo cairo-gobject $common_packages" gjs_gdbus_packages="gobject-2.0 >= glib_required_version gio-2.0" gjs_gtk_packages="gtk+-3.0" # gjs-tests links against everything gjstests_packages="$gjstests_packages $gjs_packages" PKG_CHECK_MODULES([GOBJECT], [gobject-2.0 >= glib_required_version]) PKG_CHECK_MODULES([GJS], [$gjs_packages]) PKG_CHECK_MODULES([GJS_GDBUS], [$gjs_gdbus_packages]) PKG_CHECK_MODULES([GJSTESTS], [$gjstests_packages]) # Optional cairo dep (enabled by default) AC_ARG_WITH(cairo, AS_HELP_STRING([--without-cairo], [Use cairo @<:@default=yes@:>@]), [], [with_cairo=yes]) AS_IF([test x$with_cairo = xyes], [ PKG_CHECK_MODULES([GJS_CAIRO], [$gjs_cairo_packages], have_cairo=yes, have_cairo=no) ]) AM_CONDITIONAL(ENABLE_CAIRO, test x$have_cairo = xyes) AS_IF([test x$have_cairo = xyes], [ AC_DEFINE([ENABLE_CAIRO],[1],[Define if you want to build with cairo support]) ]) # Optional GTK+ dep (enabled by default) AC_ARG_WITH(gtk, AS_HELP_STRING([--without-gtk], [Use GTK+ @<:@default=yes@:>@]), [], [with_gtk=yes]) AS_IF([test x$with_gtk = xyes], [ PKG_CHECK_MODULES([GJS_GTK], [$gjs_gtk_packages], have_gtk=yes, have_gtk=no) ], [have_gtk=no]) AM_CONDITIONAL(ENABLE_GTK, test x$have_gtk = xyes) AS_IF([test x$have_gtk = xyes], [ AC_DEFINE([ENABLE_GTK],[1],[Define if you want to build with GTK+ support]) ]) GI_DATADIR=$($PKG_CONFIG --variable=gidatadir gobject-introspection-1.0) AC_SUBST(GI_DATADIR) # readline LIBS_no_readline=$LIBS # On some systems we need to link readline to a termcap compatible # library. gjs_cv_lib_readline=no AC_MSG_CHECKING([how to link readline libs]) for gjs_libtermcap in "" ncursesw ncurses curses termcap; do if test -z "$gjs_libtermcap"; then READLINE_LIBS="-lreadline" else READLINE_LIBS="-lreadline -l$gjs_libtermcap" fi LIBS="$READLINE_LIBS $LIBS_no_readline" AC_LINK_IFELSE( [AC_LANG_CALL([],[readline])], [gjs_cv_lib_readline=yes]) if test $gjs_cv_lib_readline = yes; then break fi done if test $gjs_cv_lib_readline = no; then AC_MSG_RESULT([none]) READLINE_LIBS="" else AC_MSG_RESULT([$READLINE_LIBS]) AC_DEFINE(HAVE_LIBREADLINE, 1, [Define if you have the readline library (-lreadline).]) fi AC_TRY_CPP([#include ], have_readline=yes, have_readline=no) AM_CONDITIONAL([HAVE_READLINE], [test x$have_readline = xyes]) AC_SUBST([READLINE_LIBS]) AC_SUBST([HAVE_READLINE]) # End of readline checks: restore LIBS LIBS=$LIBS_no_readline AC_CHECK_FUNCS([backtrace]) AC_ARG_ENABLE(installed_tests, AS_HELP_STRING([--enable-installed-tests], [Install test programs (default: no)]),, [enable_installed_tests=no]) AM_CONDITIONAL(BUILDOPT_INSTALL_TESTS, test x$enable_installed_tests = xyes) dnl dnl Tracing dnl AC_MSG_CHECKING([whether to include systemtap tracing support]) AC_ARG_ENABLE([systemtap], [AS_HELP_STRING([--enable-systemtap], [Enable inclusion of systemtap trace support])], [ENABLE_SYSTEMTAP="${enableval}"], [ENABLE_SYSTEMTAP='no']) AC_MSG_RESULT(${ENABLE_SYSTEMTAP}) AC_MSG_CHECKING([whether to include dtrace tracing support]) AC_ARG_ENABLE([dtrace], [AS_HELP_STRING([--enable-dtrace], [Enable inclusion of dtrace trace support])], [ENABLE_DTRACE="${enableval}"], [ENABLE_DTRACE='no']) AC_MSG_RESULT(${ENABLE_DTRACE}) AM_CONDITIONAL([ENABLE_SYSTEMTAP], [test x$ENABLE_SYSTEMTAP = xyes]) AM_CONDITIONAL([ENABLE_DTRACE], [test x$ENABLE_DTRACE = xyes -o x$ENABLE_SYSTEMTAP = xyes]) if test "x${ENABLE_DTRACE}" = xyes -o "x${ENABLE_SYSTEMTAP}" = xyes; then AC_CHECK_PROGS(DTRACE, dtrace) if test -z "$DTRACE"; then AC_MSG_ERROR([dtrace not found]) fi AC_CHECK_HEADER([sys/sdt.h], [SDT_H_FOUND='yes'], [SDT_H_FOUND='no'; AC_MSG_ERROR([tracing support needs sys/sdt.h header])]) AC_DEFINE([HAVE_DTRACE], [1], [Define to 1 if using dtrace probes.]) fi # Checks for typedefs, structures, and compiler characteristics. AC_C_CONST dnl dnl Check for -Bsymbolic-functions linker flag used to avoid dnl intra-library PLT jumps, if available. dnl AC_ARG_ENABLE(Bsymbolic, [AS_HELP_STRING([--disable-Bsymbolic], [avoid linking with -Bsymbolic])],, [SAVED_LDFLAGS="${LDFLAGS}" AC_MSG_CHECKING([for -Bsymbolic-functions linker flag]) LDFLAGS=-Wl,-Bsymbolic-functions AC_TRY_LINK([], [int main (void) { return 0; }], AC_MSG_RESULT(yes) enable_Bsymbolic=yes, AC_MSG_RESULT(no) enable_Bsymbolic=no) LDFLAGS="${SAVED_LDFLAGS}"]) if test "x${enable_Bsymbolic}" = "xyes"; then EXTRA_LINK_FLAGS=-Wl,-Bsymbolic-functions fi AC_SUBST(EXTRA_LINK_FLAGS) gjsjsdir="\${datadir}/cjs-1.0" AC_SUBST([gjsjsdir]) dnl automake 1.11/1.12 defines this but does not substitute it AC_SUBST([pkglibexecdir], ["${libexecdir}/${PACKAGE}"]) AC_CONFIG_FILES([Makefile cjs-1.0.pc cjs-internals-1.0.pc]) AC_OUTPUT AC_MSG_RESULT([ $PACKAGE_NAME $VERSION GJS_CFLAGS: ${GJS_CFLAGS} GJS_LIBS: ${GJS_LIBS} cairo: ${have_cairo} GTK+: ${have_gtk} readline: ${have_readline} dtrace: ${ENABLE_DTRACE} systemtap: ${ENABLE_SYSTEMTAP} ]) cjs-2.8.0/gi/0000775000175000017500000000000012610172032011615 5ustar fabiofabiocjs-2.8.0/gi/arg.cpp0000664000175000017500000033175612610172032013111 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "arg.h" #include "gtype.h" #include "object.h" #include "foreign.h" #include "fundamental.h" #include "boxed.h" #include "union.h" #include "param.h" #include "value.h" #include "gerror.h" #include "cjs/byteArray.h" #include #include #include JSBool _gjs_flags_value_is_valid(JSContext *context, GType gtype, gint64 value) { GFlagsValue *v; guint32 tmpval; void *klass; /* FIXME: Do proper value check for flags with GType's */ if (gtype == G_TYPE_NONE) return JS_TRUE; klass = g_type_class_ref(gtype); /* check all bits are defined for flags.. not necessarily desired */ tmpval = (guint32)value; if (tmpval != value) { /* Not a guint32 */ gjs_throw(context, "0x%" G_GINT64_MODIFIER "x is not a valid value for flags %s", value, g_type_name(G_TYPE_FROM_CLASS(klass))); return JS_FALSE; } while (tmpval) { v = g_flags_get_first_value((GFlagsClass *) klass, tmpval); if (!v) { gjs_throw(context, "0x%x is not a valid value for flags %s", (guint32)value, g_type_name(G_TYPE_FROM_CLASS(klass))); return JS_FALSE; } tmpval &= ~v->value; } g_type_class_unref(klass); return JS_TRUE; } JSBool _gjs_enum_value_is_valid(JSContext *context, GIEnumInfo *enum_info, gint64 value) { JSBool found; int n_values; int i; n_values = g_enum_info_get_n_values(enum_info); found = JS_FALSE; for (i = 0; i < n_values; ++i) { GIValueInfo *value_info; gint64 enum_value; value_info = g_enum_info_get_value(enum_info, i); enum_value = g_value_info_get_value(value_info); g_base_info_unref((GIBaseInfo *)value_info); if (enum_value == value) { found = JS_TRUE; break; } } if (!found) { gjs_throw(context, "%" G_GINT64_MODIFIER "d is not a valid value for enumeration %s", value, g_base_info_get_name((GIBaseInfo *)enum_info)); } return found; } static gboolean _gjs_enum_uses_signed_type (GIEnumInfo *enum_info) { switch (g_enum_info_get_storage_type (enum_info)) { case GI_TYPE_TAG_INT8: case GI_TYPE_TAG_INT16: case GI_TYPE_TAG_INT32: case GI_TYPE_TAG_INT64: return TRUE; default: return FALSE; } } /* This is hacky - g_function_info_invoke() and g_field_info_get/set_field() expect * arg->v_int to have the enum value in arg->v_int and depend on all flags and * enumerations being passed on the stack in a 32-bit field. See FIXME comment in * g_field_info_get_field. The same assumption of enums cast to 32-bit signed integers * is found in g_value_set_enum/g_value_set_flags(). */ gint64 _gjs_enum_from_int (GIEnumInfo *enum_info, int int_value) { if (_gjs_enum_uses_signed_type (enum_info)) return (gint64)int_value; else return (gint64)(guint32)int_value; } /* Here for symmetry, but result is the same for the two cases */ static int _gjs_enum_to_int (GIEnumInfo *enum_info, gint64 value) { return (int)value; } /* Check if an argument of the given needs to be released if we created it * from a JS value to pass it into a function and aren't transfering ownership. */ static gboolean type_needs_release (GITypeInfo *type_info, GITypeTag type_tag) { switch (type_tag) { case GI_TYPE_TAG_UTF8: case GI_TYPE_TAG_FILENAME: case GI_TYPE_TAG_ARRAY: case GI_TYPE_TAG_GLIST: case GI_TYPE_TAG_GSLIST: case GI_TYPE_TAG_GHASH: case GI_TYPE_TAG_ERROR: return TRUE; case GI_TYPE_TAG_INTERFACE: { GIBaseInfo* interface_info; GIInfoType interface_type; GType gtype; gboolean needs_release; interface_info = g_type_info_get_interface(type_info); g_assert(interface_info != NULL); interface_type = g_base_info_get_type(interface_info); switch(interface_type) { case GI_INFO_TYPE_STRUCT: case GI_INFO_TYPE_ENUM: case GI_INFO_TYPE_FLAGS: case GI_INFO_TYPE_OBJECT: case GI_INFO_TYPE_INTERFACE: case GI_INFO_TYPE_UNION: case GI_INFO_TYPE_BOXED: /* These are subtypes of GIRegisteredTypeInfo for which the * cast is safe */ gtype = g_registered_type_info_get_g_type ((GIRegisteredTypeInfo*)interface_info); break; case GI_INFO_TYPE_VALUE: /* Special case for GValues */ gtype = G_TYPE_VALUE; break; default: /* Everything else */ gtype = G_TYPE_NONE; break; } if (g_type_is_a(gtype, G_TYPE_CLOSURE)) needs_release = TRUE; else if (g_type_is_a(gtype, G_TYPE_VALUE)) needs_release = g_type_info_is_pointer(type_info); else needs_release = FALSE; g_base_info_unref(interface_info); return needs_release; } default: return FALSE; } } /* Check if an argument of the given needs to be released if we obtained it * from out argument (or the return value), and we're transferring ownership */ static JSBool type_needs_out_release(GITypeInfo *type_info, GITypeTag type_tag) { switch (type_tag) { case GI_TYPE_TAG_UTF8: case GI_TYPE_TAG_FILENAME: case GI_TYPE_TAG_ARRAY: case GI_TYPE_TAG_GLIST: case GI_TYPE_TAG_GSLIST: case GI_TYPE_TAG_GHASH: case GI_TYPE_TAG_ERROR: return TRUE; case GI_TYPE_TAG_INTERFACE: { GIBaseInfo* interface_info; GIInfoType interface_type; gboolean needs_release; interface_info = g_type_info_get_interface(type_info); g_assert(interface_info != NULL); interface_type = g_base_info_get_type(interface_info); switch(interface_type) { case GI_INFO_TYPE_ENUM: case GI_INFO_TYPE_FLAGS: needs_release = FALSE; break; default: needs_release = TRUE; } g_base_info_unref(interface_info); return needs_release; } default: return FALSE; } } static JSBool gjs_array_to_g_list(JSContext *context, jsval array_value, unsigned int length, GITypeInfo *param_info, GITransfer transfer, GITypeTag list_type, GList **list_p, GSList **slist_p) { guint32 i; GList *list; GSList *slist; jsval elem; list = NULL; slist = NULL; if (transfer == GI_TRANSFER_CONTAINER) { if (type_needs_release (param_info, g_type_info_get_tag(param_info))) { /* FIXME: to make this work, we'd have to keep a list of temporary * GArguments for the function call so we could free them after * the surrounding container had been freed by the callee. */ gjs_throw(context, "Container transfer for in parameters not supported"); return JS_FALSE; } transfer = GI_TRANSFER_NOTHING; } for (i = 0; i < length; ++i) { GArgument elem_arg = { 0 }; elem = JSVAL_VOID; if (!JS_GetElement(context, JSVAL_TO_OBJECT(array_value), i, &elem)) { gjs_throw(context, "Missing array element %u", i); return JS_FALSE; } /* FIXME we don't know if the list elements can be NULL. * gobject-introspection needs to tell us this. * Always say they can't for now. */ if (!gjs_value_to_g_argument(context, elem, param_info, NULL, GJS_ARGUMENT_LIST_ELEMENT, transfer, FALSE, &elem_arg)) { return JS_FALSE; } if (list_type == GI_TYPE_TAG_GLIST) { /* GList */ list = g_list_prepend(list, elem_arg.v_pointer); } else { /* GSList */ slist = g_slist_prepend(slist, elem_arg.v_pointer); } } list = g_list_reverse(list); slist = g_slist_reverse(slist); *list_p = list; *slist_p = slist; return JS_TRUE; } static JSBool gjs_object_to_g_hash(JSContext *context, jsval hash_value, GITypeInfo *key_param_info, GITypeInfo *val_param_info, GITransfer transfer, GHashTable **hash_p) { GHashTable *result = NULL; JSObject *props; JSObject *iter; jsid prop_id; g_assert(JSVAL_IS_OBJECT(hash_value)); props = JSVAL_TO_OBJECT(hash_value); if (transfer == GI_TRANSFER_CONTAINER) { if (type_needs_release (key_param_info, g_type_info_get_tag(key_param_info)) || type_needs_release (val_param_info, g_type_info_get_tag(val_param_info))) { /* FIXME: to make this work, we'd have to keep a list of temporary * GArguments for the function call so we could free them after * the surrounding container had been freed by the callee. */ gjs_throw(context, "Container transfer for in parameters not supported"); return JS_FALSE; } transfer = GI_TRANSFER_NOTHING; } iter = JS_NewPropertyIterator(context, props); if (iter == NULL) return JS_FALSE; prop_id = JSID_VOID; if (!JS_NextProperty(context, iter, &prop_id)) return JS_FALSE; /* Don't use key/value destructor functions here, because we can't * construct correct ones in general if the value type is complex. * Rely on the type-aware g_argument_release functions. */ result = g_hash_table_new(g_str_hash, g_str_equal); while (!JSID_IS_VOID(prop_id)) { jsval key_js, val_js; GArgument key_arg = { 0 }, val_arg = { 0 }; if (!JS_IdToValue(context, prop_id, &key_js)) goto free_hash_and_fail; /* Type check key type. */ if (!gjs_value_to_g_argument(context, key_js, key_param_info, NULL, GJS_ARGUMENT_HASH_ELEMENT, transfer, FALSE /* don't allow null */, &key_arg)) goto free_hash_and_fail; if (!JS_GetPropertyById(context, props, prop_id, &val_js)) goto free_hash_and_fail; /* Type check and convert value to a c type */ if (!gjs_value_to_g_argument(context, val_js, val_param_info, NULL, GJS_ARGUMENT_HASH_ELEMENT, transfer, TRUE /* allow null */, &val_arg)) goto free_hash_and_fail; g_hash_table_insert(result, key_arg.v_pointer, val_arg.v_pointer); prop_id = JSID_VOID; if (!JS_NextProperty(context, iter, &prop_id)) goto free_hash_and_fail; } *hash_p = result; return JS_TRUE; free_hash_and_fail: g_hash_table_destroy(result); return JS_FALSE; } JSBool gjs_array_from_strv(JSContext *context, jsval *value_p, const char **strv) { JSObject *obj; jsval elem; guint i; JSBool result = JS_FALSE; obj = JS_NewArrayObject(context, 0, NULL); if (obj == NULL) return JS_FALSE; *value_p = OBJECT_TO_JSVAL(obj); elem = JSVAL_VOID; JS_AddValueRoot(context, &elem); for (i = 0; strv[i] != NULL; i++) { if (!gjs_string_from_utf8 (context, strv[i], -1, &elem)) goto out; if (!JS_DefineElement(context, obj, i, elem, NULL, NULL, JSPROP_ENUMERATE)) { goto out; } } result = JS_TRUE; out: JS_RemoveValueRoot(context, &elem); return result; } JSBool gjs_array_to_strv(JSContext *context, jsval array_value, unsigned int length, void **arr_p) { char **result; guint32 i; result = g_new0(char *, length+1); for (i = 0; i < length; ++i) { jsval elem; elem = JSVAL_VOID; if (!JS_GetElement(context, JSVAL_TO_OBJECT(array_value), i, &elem)) { g_free(result); gjs_throw(context, "Missing array element %u", i); return JS_FALSE; } if (!JSVAL_IS_STRING(elem)) { gjs_throw(context, "Invalid element in string array"); g_strfreev(result); return JS_FALSE; } if (!gjs_string_to_utf8(context, elem, (char **)&(result[i]))) { g_strfreev(result); return JS_FALSE; } } *arr_p = result; return JS_TRUE; } static JSBool gjs_string_to_intarray(JSContext *context, jsval string_val, GITypeInfo *param_info, void **arr_p, gsize *length) { GITypeTag element_type; char *result; guint16 *result16; element_type = g_type_info_get_tag(param_info); switch (element_type) { case GI_TYPE_TAG_INT8: case GI_TYPE_TAG_UINT8: if (!gjs_string_to_utf8(context, string_val, &result)) return JS_FALSE; *arr_p = result; *length = strlen(result); return JS_TRUE; case GI_TYPE_TAG_INT16: case GI_TYPE_TAG_UINT16: if (!gjs_string_get_uint16_data(context, string_val, &result16, length)) return JS_FALSE; *arr_p = result16; return JS_TRUE; default: /* can't convert a string to this type */ gjs_throw(context, "Cannot convert string to array of '%s'", g_type_tag_to_string (element_type)); return JS_FALSE; } } static JSBool gjs_array_to_intarray(JSContext *context, jsval array_value, unsigned int length, void **arr_p, unsigned intsize, gboolean is_signed) { /* nasty union types in an attempt to unify the various int types */ union { guint32 u; gint32 i; } intval; void *result; unsigned i; /* add one so we're always zero terminated */ result = g_malloc0((length+1) * intsize); for (i = 0; i < length; ++i) { jsval elem; JSBool success; elem = JSVAL_VOID; if (!JS_GetElement(context, JSVAL_TO_OBJECT(array_value), i, &elem)) { g_free(result); gjs_throw(context, "Missing array element %u", i); return JS_FALSE; } /* do whatever sign extension is appropriate */ success = (is_signed) ? JS_ValueToECMAInt32(context, elem, &(intval.i)) : JS_ValueToECMAUint32(context, elem, &(intval.u)); if (!success) { g_free(result); gjs_throw(context, "Invalid element in int array"); return JS_FALSE; } /* Note that this is truncating assignment. */ switch (intsize) { case 1: ((guint8*)result)[i] = (gint8) intval.u; break; case 2: ((guint16*)result)[i] = (gint16) intval.u; break; case 4: ((guint32*)result)[i] = (gint32) intval.u; break; default: g_assert_not_reached(); } } *arr_p = result; return JS_TRUE; } static JSBool gjs_gtypearray_to_array(JSContext *context, jsval array_value, unsigned int length, void **arr_p) { GType *result; unsigned i; /* add one so we're always zero terminated */ result = (GType *) g_malloc0((length+1) * sizeof(GType)); for (i = 0; i < length; ++i) { jsval elem; GType gtype; elem = JSVAL_VOID; if (!JS_GetElement(context, JSVAL_TO_OBJECT(array_value), i, &elem)) { g_free(result); gjs_throw(context, "Missing array element %u", i); return JS_FALSE; } if (!JSVAL_IS_OBJECT(elem)) goto err; gtype = gjs_gtype_get_actual_gtype(context, JSVAL_TO_OBJECT(elem)); if (gtype == G_TYPE_INVALID) goto err; result[i] = gtype; } *arr_p = result; return JS_TRUE; err: g_free(result); gjs_throw(context, "Invalid element in GType array"); return JS_FALSE; } static JSBool gjs_array_to_floatarray(JSContext *context, jsval array_value, unsigned int length, void **arr_p, gboolean is_double) { unsigned int i; void *result; /* add one so we're always zero terminated */ result = g_malloc0((length+1) * (is_double ? sizeof(double) : sizeof(float))); for (i = 0; i < length; ++i) { jsval elem; double val; JSBool success; elem = JSVAL_VOID; if (!JS_GetElement(context, JSVAL_TO_OBJECT(array_value), i, &elem)) { g_free(result); gjs_throw(context, "Missing array element %u", i); return JS_FALSE; } /* do whatever sign extension is appropriate */ success = JS_ValueToNumber(context, elem, &val); if (!success) { g_free(result); gjs_throw(context, "Invalid element in array"); return JS_FALSE; } /* Note that this is truncating assignment. */ if (is_double) { double *darray = (double*)result; darray[i] = val; } else { float *farray = (float*)result; farray[i] = val; } } *arr_p = result; return JS_TRUE; } static JSBool gjs_array_to_ptrarray(JSContext *context, jsval array_value, unsigned int length, GITransfer transfer, GITypeInfo *param_info, void **arr_p) { unsigned int i; /* Always one extra element, to cater for null terminated arrays */ void **array = (void **) g_malloc((length + 1) * sizeof(gpointer)); array[length] = NULL; for (i = 0; i < length; i++) { jsval elem; GIArgument arg; arg.v_pointer = NULL; JSBool success; elem = JSVAL_VOID; if (!JS_GetElement(context, JSVAL_TO_OBJECT(array_value), i, &elem)) { g_free(array); gjs_throw(context, "Missing array element %u", i); return JS_FALSE; } success = gjs_value_to_g_argument (context, elem, param_info, NULL, /* arg name */ GJS_ARGUMENT_ARRAY_ELEMENT, transfer, FALSE, /* absent better information, FALSE for now */ &arg); if (!success) { g_free(array); gjs_throw(context, "Invalid element in array"); return JS_FALSE; } array[i] = arg.v_pointer; } *arr_p = array; return JS_TRUE; } static JSBool gjs_array_to_flat_gvalue_array(JSContext *context, jsval array_value, unsigned int length, void **arr_p) { GValue *values = g_new0(GValue, length); unsigned int i; JSBool result = JS_TRUE; for (i = 0; i < length; i ++) { jsval elem; elem = JSVAL_VOID; if (!JS_GetElement(context, JSVAL_TO_OBJECT(array_value), i, &elem)) { g_free(values); gjs_throw(context, "Missing array element %u", i); return JS_FALSE; } result = gjs_value_to_g_value(context, elem, &values[i]); if (!result) break; } if (result) *arr_p = values; return result; } static JSBool gjs_array_from_flat_gvalue_array(JSContext *context, gpointer array, unsigned int length, jsval *value) { GValue *values = (GValue *)array; unsigned int i; jsval *elems = g_newa(jsval, length); JSBool result = JS_TRUE; for (i = 0; i < length; i ++) { GValue *value = &values[i]; result = gjs_value_from_g_value(context, &elems[i], value); if (!result) break; } if (result) { JSObject *jsarray; jsarray = JS_NewArrayObject(context, length, elems); *value = OBJECT_TO_JSVAL(jsarray); } return result; } static gboolean is_gvalue(GIBaseInfo *info, GIInfoType info_type) { gboolean result = FALSE; switch(info_type) { case GI_INFO_TYPE_VALUE: result = TRUE; break; case GI_INFO_TYPE_STRUCT: case GI_INFO_TYPE_OBJECT: case GI_INFO_TYPE_INTERFACE: case GI_INFO_TYPE_BOXED: { GType gtype; gtype = g_registered_type_info_get_g_type((GIRegisteredTypeInfo *) info); result = g_type_is_a(gtype, G_TYPE_VALUE); } break; default: break; } return result; } static gboolean is_gvalue_flat_array(GITypeInfo *param_info, GITypeTag element_type) { GIBaseInfo *interface_info; GIInfoType info_type; gboolean result; if (element_type != GI_TYPE_TAG_INTERFACE) return FALSE; interface_info = g_type_info_get_interface(param_info); info_type = g_base_info_get_type(interface_info); /* Special case for GValue "flat arrays" */ result = (is_gvalue(interface_info, info_type) && !g_type_info_is_pointer(param_info)); g_base_info_unref(interface_info); return result; } static JSBool gjs_array_to_array(JSContext *context, jsval array_value, gsize length, GITransfer transfer, GITypeInfo *param_info, void **arr_p) { enum { UNSIGNED=FALSE, SIGNED=TRUE }; GITypeTag element_type; element_type = g_type_info_get_tag(param_info); /* Special case for GValue "flat arrays" */ if (is_gvalue_flat_array(param_info, element_type)) return gjs_array_to_flat_gvalue_array(context, array_value, length, arr_p); if (element_type == GI_TYPE_TAG_INTERFACE) { GIBaseInfo *interface_info = g_type_info_get_interface(param_info); GIInfoType info_type = g_base_info_get_type(interface_info); if (info_type == GI_INFO_TYPE_ENUM || info_type == GI_INFO_TYPE_FLAGS) element_type = g_enum_info_get_storage_type ((GIEnumInfo*) interface_info); g_base_info_unref(interface_info); } switch (element_type) { case GI_TYPE_TAG_UTF8: return gjs_array_to_strv (context, array_value, length, arr_p); case GI_TYPE_TAG_UINT8: return gjs_array_to_intarray (context, array_value, length, arr_p, 1, UNSIGNED); case GI_TYPE_TAG_INT8: return gjs_array_to_intarray (context, array_value, length, arr_p, 1, SIGNED); case GI_TYPE_TAG_UINT16: return gjs_array_to_intarray (context, array_value, length, arr_p, 2, UNSIGNED); case GI_TYPE_TAG_INT16: return gjs_array_to_intarray (context, array_value, length, arr_p, 2, SIGNED); case GI_TYPE_TAG_UINT32: return gjs_array_to_intarray (context, array_value, length, arr_p, 4, UNSIGNED); case GI_TYPE_TAG_INT32: return gjs_array_to_intarray (context, array_value, length, arr_p, 4, SIGNED); case GI_TYPE_TAG_FLOAT: return gjs_array_to_floatarray (context, array_value, length, arr_p, FALSE); case GI_TYPE_TAG_DOUBLE: return gjs_array_to_floatarray (context, array_value, length, arr_p, TRUE); case GI_TYPE_TAG_GTYPE: return gjs_gtypearray_to_array (context, array_value, length, arr_p); /* Everything else is a pointer type */ case GI_TYPE_TAG_INTERFACE: case GI_TYPE_TAG_ARRAY: case GI_TYPE_TAG_GLIST: case GI_TYPE_TAG_GSLIST: case GI_TYPE_TAG_GHASH: case GI_TYPE_TAG_ERROR: case GI_TYPE_TAG_FILENAME: return gjs_array_to_ptrarray(context, array_value, length, transfer == GI_TRANSFER_CONTAINER ? GI_TRANSFER_NOTHING : transfer, param_info, arr_p); default: gjs_throw(context, "Unhandled array element type %d", element_type); return JS_FALSE; } } static GArray* gjs_g_array_new_for_type(JSContext *context, unsigned int length, GITypeInfo *param_info) { GITypeTag element_type; guint element_size; element_type = g_type_info_get_tag(param_info); if (element_type == GI_TYPE_TAG_INTERFACE) { GIInterfaceInfo *interface_info = g_type_info_get_interface(param_info); GIInfoType interface_type = g_base_info_get_type(interface_info); if (interface_type == GI_INFO_TYPE_ENUM || interface_type == GI_INFO_TYPE_FLAGS) element_type = g_enum_info_get_storage_type((GIEnumInfo*) interface_info); g_base_info_unref((GIBaseInfo*) interface_info); } switch (element_type) { case GI_TYPE_TAG_UINT8: case GI_TYPE_TAG_INT8: element_size = sizeof(guint8); break; case GI_TYPE_TAG_UINT16: case GI_TYPE_TAG_INT16: element_size = sizeof(guint16); break; case GI_TYPE_TAG_UINT32: case GI_TYPE_TAG_INT32: element_size = sizeof(guint32); break; case GI_TYPE_TAG_UINT64: case GI_TYPE_TAG_INT64: element_size = sizeof(guint64); break; case GI_TYPE_TAG_FLOAT: element_size = sizeof(gfloat); break; case GI_TYPE_TAG_DOUBLE: element_size = sizeof(gdouble); break; case GI_TYPE_TAG_GTYPE: element_size = sizeof(GType); break; case GI_TYPE_TAG_INTERFACE: case GI_TYPE_TAG_UTF8: case GI_TYPE_TAG_FILENAME: case GI_TYPE_TAG_ARRAY: case GI_TYPE_TAG_GLIST: case GI_TYPE_TAG_GSLIST: case GI_TYPE_TAG_GHASH: case GI_TYPE_TAG_ERROR: element_size = sizeof(gpointer); break; default: gjs_throw(context, "Unhandled GArray element-type %d", element_type); return NULL; } return g_array_sized_new(TRUE, FALSE, element_size, length); } static gchar * get_argument_display_name(const char *arg_name, GjsArgumentType arg_type) { switch (arg_type) { case GJS_ARGUMENT_ARGUMENT: return g_strdup_printf("Argument '%s'", arg_name); case GJS_ARGUMENT_RETURN_VALUE: return g_strdup("Return value"); case GJS_ARGUMENT_FIELD: return g_strdup_printf("Field '%s'", arg_name); case GJS_ARGUMENT_LIST_ELEMENT: return g_strdup("List element"); case GJS_ARGUMENT_HASH_ELEMENT: return g_strdup("Hash element"); case GJS_ARGUMENT_ARRAY_ELEMENT: return g_strdup("Array element"); } g_assert_not_reached (); } static const char * type_tag_to_human_string(GITypeInfo *type_info) { GITypeTag tag; tag = g_type_info_get_tag(type_info); if (tag == GI_TYPE_TAG_INTERFACE) { GIBaseInfo *interface; const char *ret; interface = g_type_info_get_interface(type_info); ret = g_info_type_to_string(g_base_info_get_type(interface)); g_base_info_unref(interface); return ret; } else { return g_type_tag_to_string(tag); } } static void throw_invalid_argument(JSContext *context, jsval value, GITypeInfo *arginfo, const char *arg_name, GjsArgumentType arg_type) { gchar *display_name = get_argument_display_name(arg_name, arg_type); gjs_throw(context, "Expected type %s for %s but got type '%s'", type_tag_to_human_string(arginfo), display_name, JS_GetTypeName(context, JS_TypeOfValue(context, value))); g_free(display_name); } static JSBool gjs_array_to_explicit_array_internal(JSContext *context, jsval value, GITypeInfo *type_info, const char *arg_name, GjsArgumentType arg_type, GITransfer transfer, gboolean may_be_null, gpointer *contents, gsize *length_p) { JSBool ret = JS_FALSE; GITypeInfo *param_info; jsid length_name; JSBool found_length; param_info = g_type_info_get_param_type(type_info, 0); if ((JSVAL_IS_NULL(value) && !may_be_null) || (!JSVAL_IS_STRING(value) && !JSVAL_IS_OBJECT(value) && !JSVAL_IS_NULL(value))) { throw_invalid_argument(context, value, param_info, arg_name, arg_type); goto out; } length_name = gjs_context_get_const_string(context, GJS_STRING_LENGTH); if (JSVAL_IS_NULL(value)) { *contents = NULL; *length_p = 0; } else if (JSVAL_IS_STRING(value)) { /* Allow strings as int8/uint8/int16/uint16 arrays */ if (!gjs_string_to_intarray(context, value, param_info, contents, length_p)) goto out; } else if (JS_HasPropertyById(context, JSVAL_TO_OBJECT(value), length_name, &found_length) && found_length) { jsval length_value; guint32 length; if (!gjs_object_require_property(context, JSVAL_TO_OBJECT(value), NULL, length_name, &length_value) || !JS_ValueToECMAUint32(context, length_value, &length)) { goto out; } else { if (!gjs_array_to_array(context, value, length, transfer, param_info, contents)) goto out; *length_p = length; } } else { throw_invalid_argument(context, value, param_info, arg_name, arg_type); goto out; } ret = JS_TRUE; out: g_base_info_unref((GIBaseInfo*) param_info); return ret; } JSBool gjs_value_to_g_argument(JSContext *context, jsval value, GITypeInfo *type_info, const char *arg_name, GjsArgumentType arg_type, GITransfer transfer, gboolean may_be_null, GArgument *arg) { GITypeTag type_tag; gboolean wrong; gboolean out_of_range; gboolean report_type_mismatch; gboolean nullable_type; type_tag = g_type_info_get_tag( (GITypeInfo*) type_info); gjs_debug_marshal(GJS_DEBUG_GFUNCTION, "Converting jsval to GArgument %s", g_type_tag_to_string(type_tag)); nullable_type = FALSE; wrong = FALSE; /* return JS_FALSE */ out_of_range = FALSE; report_type_mismatch = FALSE; /* wrong=TRUE, and still need to gjs_throw a type problem */ switch (type_tag) { case GI_TYPE_TAG_VOID: nullable_type = TRUE; arg->v_pointer = NULL; /* just so it isn't uninitialized */ break; case GI_TYPE_TAG_INT8: { gint32 i; if (!JS_ValueToInt32(context, value, &i)) wrong = TRUE; if (i > G_MAXINT8 || i < G_MININT8) out_of_range = TRUE; arg->v_int8 = (gint8)i; break; } case GI_TYPE_TAG_UINT8: { guint32 i; if (!JS_ValueToECMAUint32(context, value, &i)) wrong = TRUE; if (i > G_MAXUINT8) out_of_range = TRUE; arg->v_uint8 = (guint8)i; break; } case GI_TYPE_TAG_INT16: { gint32 i; if (!JS_ValueToInt32(context, value, &i)) wrong = TRUE; if (i > G_MAXINT16 || i < G_MININT16) out_of_range = TRUE; arg->v_int16 = (gint16)i; break; } case GI_TYPE_TAG_UINT16: { guint32 i; if (!JS_ValueToECMAUint32(context, value, &i)) wrong = TRUE; if (i > G_MAXUINT16) out_of_range = TRUE; arg->v_uint16 = (guint16)i; break; } case GI_TYPE_TAG_INT32: if (!JS_ValueToInt32(context, value, &arg->v_int)) wrong = TRUE; break; case GI_TYPE_TAG_UINT32: { gdouble i; if (!JS_ValueToNumber(context, value, &i)) wrong = TRUE; if (i > G_MAXUINT32 || i < 0) out_of_range = TRUE; arg->v_uint32 = (guint32)i; break; } case GI_TYPE_TAG_INT64: { double v; if (!JS_ValueToNumber(context, value, &v)) wrong = TRUE; if (v > G_MAXINT64 || v < G_MININT64) out_of_range = TRUE; arg->v_int64 = v; } break; case GI_TYPE_TAG_UINT64: { double v; if (!JS_ValueToNumber(context, value, &v)) wrong = TRUE; if (v < 0) out_of_range = TRUE; /* XXX we fail with values close to G_MAXUINT64 */ arg->v_uint64 = v; } break; case GI_TYPE_TAG_BOOLEAN: if (!JS_ValueToBoolean(context, value, &arg->v_boolean)) wrong = TRUE; break; case GI_TYPE_TAG_FLOAT: { double v; if (!JS_ValueToNumber(context, value, &v)) wrong = TRUE; if (v > G_MAXFLOAT || v < - G_MAXFLOAT) out_of_range = TRUE; arg->v_float = (gfloat)v; } break; case GI_TYPE_TAG_DOUBLE: if (!JS_ValueToNumber(context, value, &arg->v_double)) wrong = TRUE; break; case GI_TYPE_TAG_UNICHAR: if (JSVAL_IS_STRING(value)) { if (!gjs_unichar_from_string(context, value, &arg->v_uint32)) wrong = TRUE; } else { wrong = TRUE; report_type_mismatch = TRUE; } break; case GI_TYPE_TAG_GTYPE: if (JSVAL_IS_OBJECT(value)) { GType gtype; gtype = gjs_gtype_get_actual_gtype(context, JSVAL_TO_OBJECT(value)); if (gtype == G_TYPE_INVALID) wrong = TRUE; arg->v_ssize = gtype; } else { wrong = TRUE; report_type_mismatch = TRUE; } break; case GI_TYPE_TAG_FILENAME: nullable_type = TRUE; if (JSVAL_IS_NULL(value)) { arg->v_pointer = NULL; } else if (JSVAL_IS_STRING(value)) { char *filename_str; if (gjs_string_to_filename(context, value, &filename_str)) // doing this as a separate step to avoid type-punning arg->v_pointer = filename_str; else wrong = TRUE; } else { wrong = TRUE; report_type_mismatch = TRUE; } break; case GI_TYPE_TAG_UTF8: nullable_type = TRUE; if (JSVAL_IS_NULL(value)) { arg->v_pointer = NULL; } else if (JSVAL_IS_STRING(value)) { char *utf8_str; if (gjs_string_to_utf8(context, value, &utf8_str)) // doing this as a separate step to avoid type-punning arg->v_pointer = utf8_str; else wrong = TRUE; } else { wrong = TRUE; report_type_mismatch = TRUE; } break; case GI_TYPE_TAG_ERROR: nullable_type = TRUE; if (JSVAL_IS_NULL(value)) { arg->v_pointer = NULL; } else if (JSVAL_IS_OBJECT(value)) { if (gjs_typecheck_gerror(context, JSVAL_TO_OBJECT(value), JS_TRUE)) { arg->v_pointer = gjs_gerror_from_error(context, JSVAL_TO_OBJECT(value)); if (transfer != GI_TRANSFER_NOTHING) arg->v_pointer = g_error_copy ((const GError *) arg->v_pointer); } else { wrong = TRUE; } } else { wrong = TRUE; report_type_mismatch = TRUE; } break; case GI_TYPE_TAG_INTERFACE: { GIBaseInfo* interface_info; GIInfoType interface_type; GType gtype; gboolean expect_object; interface_info = g_type_info_get_interface(type_info); g_assert(interface_info != NULL); interface_type = g_base_info_get_type(interface_info); if (interface_type == GI_INFO_TYPE_ENUM || interface_type == GI_INFO_TYPE_FLAGS) { nullable_type = FALSE; expect_object = FALSE; } else { nullable_type = TRUE; expect_object = TRUE; } switch(interface_type) { case GI_INFO_TYPE_STRUCT: if (g_struct_info_is_foreign((GIStructInfo*)interface_info)) { JSBool ret; ret = gjs_struct_foreign_convert_to_g_argument( context, value, interface_info, arg_name, arg_type, transfer, may_be_null, arg); g_base_info_unref(interface_info); return ret; } /* fall through */ case GI_INFO_TYPE_ENUM: case GI_INFO_TYPE_FLAGS: case GI_INFO_TYPE_OBJECT: case GI_INFO_TYPE_INTERFACE: case GI_INFO_TYPE_UNION: case GI_INFO_TYPE_BOXED: /* These are subtypes of GIRegisteredTypeInfo for which the * cast is safe */ gtype = g_registered_type_info_get_g_type ((GIRegisteredTypeInfo*)interface_info); break; case GI_INFO_TYPE_VALUE: /* Special case for GValues */ gtype = G_TYPE_VALUE; break; default: /* Everything else */ gtype = G_TYPE_NONE; break; } if (gtype != G_TYPE_NONE) gjs_debug_marshal(GJS_DEBUG_GFUNCTION, "gtype of INTERFACE is %s", g_type_name(gtype)); if (gtype == G_TYPE_VALUE) { GValue gvalue = { 0, }; if (gjs_value_to_g_value(context, value, &gvalue)) { arg->v_pointer = g_boxed_copy (G_TYPE_VALUE, &gvalue); g_value_unset (&gvalue); } else { arg->v_pointer = NULL; wrong = TRUE; } } else if (expect_object != JSVAL_IS_OBJECT(value)) { /* JSVAL_IS_OBJECT handles null too */ wrong = TRUE; report_type_mismatch = TRUE; break; } else if (JSVAL_IS_NULL(value)) { arg->v_pointer = NULL; } else if (JSVAL_IS_OBJECT(value)) { /* Handle Struct/Union first since we don't necessarily need a GType for them */ if ((interface_type == GI_INFO_TYPE_STRUCT || interface_type == GI_INFO_TYPE_BOXED) && /* We special case Closures later, so skip them here */ !g_type_is_a(gtype, G_TYPE_CLOSURE)) { JSObject *obj = JSVAL_TO_OBJECT(value); if (g_type_is_a(gtype, G_TYPE_BYTES) && gjs_typecheck_bytearray(context, obj, FALSE)) { arg->v_pointer = gjs_byte_array_get_bytes(context, obj); } else if (g_type_is_a(gtype, G_TYPE_ERROR)) { if (!gjs_typecheck_gerror(context, JSVAL_TO_OBJECT(value), JS_TRUE)) { arg->v_pointer = NULL; wrong = TRUE; } else { arg->v_pointer = gjs_gerror_from_error(context, JSVAL_TO_OBJECT(value)); } } else { if (!gjs_typecheck_boxed(context, JSVAL_TO_OBJECT(value), interface_info, gtype, JS_TRUE)) { arg->v_pointer = NULL; wrong = TRUE; } else { arg->v_pointer = gjs_c_struct_from_boxed(context, JSVAL_TO_OBJECT(value)); } } if (!wrong && transfer != GI_TRANSFER_NOTHING) { if (g_type_is_a(gtype, G_TYPE_BOXED)) arg->v_pointer = g_boxed_copy (gtype, arg->v_pointer); else if (g_type_is_a(gtype, G_TYPE_VARIANT)) g_variant_ref ((GVariant *) arg->v_pointer); else { gjs_throw(context, "Can't transfer ownership of a structure type not registered as boxed"); arg->v_pointer = NULL; wrong = TRUE; } } } else if (interface_type == GI_INFO_TYPE_UNION) { if (gjs_typecheck_union(context, JSVAL_TO_OBJECT(value), interface_info, gtype, JS_TRUE)) { arg->v_pointer = gjs_c_union_from_union(context, JSVAL_TO_OBJECT(value)); if (transfer != GI_TRANSFER_NOTHING) { if (g_type_is_a(gtype, G_TYPE_BOXED)) arg->v_pointer = g_boxed_copy (gtype, arg->v_pointer); else { gjs_throw(context, "Can't transfer ownership of a union type not registered as boxed"); arg->v_pointer = NULL; wrong = TRUE; } } } else { arg->v_pointer = NULL; wrong = TRUE; } } else if (gtype != G_TYPE_NONE) { if (g_type_is_a(gtype, G_TYPE_OBJECT)) { if (gjs_typecheck_object(context, JSVAL_TO_OBJECT(value), gtype, JS_TRUE)) { arg->v_pointer = gjs_g_object_from_object(context, JSVAL_TO_OBJECT(value)); if (transfer != GI_TRANSFER_NOTHING) g_object_ref(G_OBJECT(arg->v_pointer)); } else { arg->v_pointer = NULL; wrong = TRUE; } } else if (g_type_is_a(gtype, G_TYPE_BOXED)) { if (g_type_is_a(gtype, G_TYPE_CLOSURE)) { arg->v_pointer = gjs_closure_new_marshaled(context, JSVAL_TO_OBJECT(value), "boxed"); g_closure_ref((GClosure *) arg->v_pointer); g_closure_sink((GClosure *) arg->v_pointer); } else { /* Should have been caught above as STRUCT/BOXED/UNION */ gjs_throw(context, "Boxed type %s registered for unexpected interface_type %d", g_type_name(gtype), interface_type); } } else if (G_TYPE_IS_INSTANTIATABLE(gtype)) { if (gjs_typecheck_fundamental(context, JSVAL_TO_OBJECT(value), gtype, JS_TRUE)) { arg->v_pointer = gjs_g_fundamental_from_object(context, JSVAL_TO_OBJECT(value)); if (transfer != GI_TRANSFER_NOTHING) gjs_fundamental_ref(context, arg->v_pointer); } else { arg->v_pointer = NULL; wrong = TRUE; } } else if (G_TYPE_IS_INTERFACE(gtype)) { /* Could be a GObject interface that's missing a prerequisite, or could be a fundamental */ if (gjs_typecheck_object(context, JSVAL_TO_OBJECT(value), gtype, JS_FALSE)) { arg->v_pointer = gjs_g_object_from_object(context, JSVAL_TO_OBJECT(value)); if (transfer != GI_TRANSFER_NOTHING) g_object_ref(arg->v_pointer); } else if (gjs_typecheck_fundamental(context, JSVAL_TO_OBJECT(value), gtype, JS_FALSE)) { arg->v_pointer = gjs_g_fundamental_from_object(context, JSVAL_TO_OBJECT(value)); if (transfer != GI_TRANSFER_NOTHING) gjs_fundamental_ref(context, arg->v_pointer); } else { /* Call again with throw=TRUE to set the exception */ gjs_typecheck_object(context, JSVAL_TO_OBJECT(value), gtype, JS_TRUE); arg->v_pointer = NULL; wrong = TRUE; } } else { gjs_throw(context, "Unhandled GType %s unpacking GArgument from Object", g_type_name(gtype)); arg->v_pointer = NULL; wrong = TRUE; } } else { gjs_throw(context, "Unexpected unregistered type unpacking GArgument from Object"); } if (arg->v_pointer == NULL) { gjs_debug(GJS_DEBUG_GFUNCTION, "conversion of JSObject %p type %s to type %s failed", JSVAL_TO_OBJECT(value), JS_GetTypeName(context, JS_TypeOfValue(context, value)), g_base_info_get_name ((GIBaseInfo *)interface_info)); /* gjs_throw should have been called already */ wrong = TRUE; } } else if (JSVAL_IS_NUMBER(value)) { if (interface_type == GI_INFO_TYPE_ENUM) { gint64 value_int64; if (!gjs_value_to_int64 (context, value, &value_int64)) wrong = TRUE; else if (!_gjs_enum_value_is_valid(context, (GIEnumInfo *)interface_info, value_int64)) wrong = TRUE; else arg->v_int = _gjs_enum_to_int ((GIEnumInfo *)interface_info, value_int64); } else if (interface_type == GI_INFO_TYPE_FLAGS) { gint64 value_int64; if (!gjs_value_to_int64 (context, value, &value_int64)) wrong = TRUE; else if (!_gjs_flags_value_is_valid(context, gtype, value_int64)) wrong = TRUE; else arg->v_int = _gjs_enum_to_int ((GIEnumInfo *)interface_info, value_int64); } else if (gtype == G_TYPE_NONE) { gjs_throw(context, "Unexpected unregistered type unpacking GArgument from Number"); wrong = TRUE; } else { gjs_throw(context, "Unhandled GType %s unpacking GArgument from Number", g_type_name(gtype)); wrong = TRUE; } } else { gjs_debug(GJS_DEBUG_GFUNCTION, "JSObject type '%s' is neither null nor an object", JS_GetTypeName(context, JS_TypeOfValue(context, value))); wrong = TRUE; report_type_mismatch = TRUE; } g_base_info_unref( (GIBaseInfo*) interface_info); } break; case GI_TYPE_TAG_GLIST: case GI_TYPE_TAG_GSLIST: { jsid length_name; JSBool found_length; length_name = gjs_context_get_const_string(context, GJS_STRING_LENGTH); /* nullable_type=FALSE; while a list can be NULL in C, that * means empty array in JavaScript, it doesn't mean null in * JavaScript. */ if (!JSVAL_IS_NULL(value) && JSVAL_IS_OBJECT(value) && JS_HasPropertyById(context, JSVAL_TO_OBJECT(value), length_name, &found_length) && found_length) { jsval length_value; guint32 length; if (!gjs_object_require_property(context, JSVAL_TO_OBJECT(value), NULL, length_name, &length_value) || !JS_ValueToECMAUint32(context, length_value, &length)) { wrong = TRUE; } else { GList *list; GSList *slist; GITypeInfo *param_info; param_info = g_type_info_get_param_type(type_info, 0); g_assert(param_info != NULL); list = NULL; slist = NULL; if (!gjs_array_to_g_list(context, value, length, param_info, transfer, type_tag, &list, &slist)) { wrong = TRUE; } if (type_tag == GI_TYPE_TAG_GLIST) { arg->v_pointer = list; } else { arg->v_pointer = slist; } g_base_info_unref((GIBaseInfo*) param_info); } } else { wrong = TRUE; report_type_mismatch = TRUE; } break; } case GI_TYPE_TAG_GHASH: if (JSVAL_IS_NULL(value)) { arg->v_pointer = NULL; if (!may_be_null) { wrong = TRUE; report_type_mismatch = TRUE; } } else if (!JSVAL_IS_OBJECT(value)) { wrong = TRUE; report_type_mismatch = TRUE; } else { GITypeInfo *key_param_info, *val_param_info; GHashTable *ghash; key_param_info = g_type_info_get_param_type(type_info, 0); g_assert(key_param_info != NULL); val_param_info = g_type_info_get_param_type(type_info, 1); g_assert(val_param_info != NULL); if (!gjs_object_to_g_hash(context, value, key_param_info, val_param_info, transfer, &ghash)) { wrong = TRUE; } else { arg->v_pointer = ghash; } g_base_info_unref((GIBaseInfo*) key_param_info); g_base_info_unref((GIBaseInfo*) val_param_info); } break; case GI_TYPE_TAG_ARRAY: { gpointer data; gsize length; GIArrayType array_type = g_type_info_get_array_type(type_info); /* First, let's handle the case where we're passed an instance * of our own byteArray class. */ if (JSVAL_IS_OBJECT(value) && gjs_typecheck_bytearray(context, JSVAL_TO_OBJECT(value), FALSE)) { JSObject *bytearray_obj = JSVAL_TO_OBJECT(value); if (array_type == GI_ARRAY_TYPE_BYTE_ARRAY) { arg->v_pointer = gjs_byte_array_get_byte_array(context, bytearray_obj); break; } else { /* Fall through, !handled */ } } if (!gjs_array_to_explicit_array_internal(context, value, type_info, arg_name, arg_type, transfer, may_be_null, &data, &length)) { wrong = TRUE; break; } if (array_type == GI_ARRAY_TYPE_C) { arg->v_pointer = data; } else if (array_type == GI_ARRAY_TYPE_ARRAY) { GITypeInfo *param_info = g_type_info_get_param_type(type_info, 0); GArray *array = gjs_g_array_new_for_type(context, length, param_info); if (!array) wrong = TRUE; else { g_array_append_vals(array, data, length); arg->v_pointer = array; } g_free(data); g_base_info_unref((GIBaseInfo*) param_info); } else if (array_type == GI_ARRAY_TYPE_BYTE_ARRAY) { GByteArray *byte_array = g_byte_array_sized_new(length); g_byte_array_append(byte_array, (const guint8 *) data, length); arg->v_pointer = byte_array; g_free(data); } else if (array_type == GI_ARRAY_TYPE_PTR_ARRAY) { GPtrArray *array = g_ptr_array_sized_new(length); g_ptr_array_set_size(array, length); memcpy(array->pdata, data, sizeof(gpointer) * length); arg->v_pointer = array; g_free(data); } break; } default: g_warning("Unhandled type %s for JavaScript to GArgument conversion", g_type_tag_to_string(type_tag)); wrong = TRUE; report_type_mismatch = TRUE; break; } if (G_UNLIKELY(wrong)) { if (report_type_mismatch) { throw_invalid_argument(context, value, type_info, arg_name, arg_type); } return JS_FALSE; } else if (G_UNLIKELY(out_of_range)) { gchar *display_name = get_argument_display_name (arg_name, arg_type); gjs_throw(context, "value is out of range for %s (type %s)", display_name, g_type_tag_to_string(type_tag)); g_free (display_name); return JS_FALSE; } else if (nullable_type && arg->v_pointer == NULL && !may_be_null) { gchar *display_name = get_argument_display_name (arg_name, arg_type); gjs_throw(context, "%s (type %s) may not be null", display_name, g_type_tag_to_string(type_tag)); g_free (display_name); return JS_FALSE; } else { return JS_TRUE; } } /* If a callback function with a return value throws, we still have * to return something to C. This function defines what that something * is. It basically boils down to memset(arg, 0, sizeof(*arg)), but * gives as a bit more future flexibility and also will work if * libffi passes us a buffer that only has room for the appropriate * branch of GArgument. (Currently it appears that the return buffer * has a fixed size large enough for the union of all types.) */ void gjs_g_argument_init_default(JSContext *context, GITypeInfo *type_info, GArgument *arg) { GITypeTag type_tag; type_tag = g_type_info_get_tag( (GITypeInfo*) type_info); switch (type_tag) { case GI_TYPE_TAG_VOID: arg->v_pointer = NULL; /* just so it isn't uninitialized */ break; case GI_TYPE_TAG_INT8: arg->v_int8 = 0; break; case GI_TYPE_TAG_UINT8: arg->v_uint8 = 0; break; case GI_TYPE_TAG_INT16: arg->v_int16 = 0; break; case GI_TYPE_TAG_UINT16: arg->v_uint16 = 0; break; case GI_TYPE_TAG_INT32: arg->v_int = 0; break; case GI_TYPE_TAG_UINT32: case GI_TYPE_TAG_UNICHAR: arg->v_uint32 = 0; break; case GI_TYPE_TAG_INT64: arg->v_int64 = 0; break; case GI_TYPE_TAG_UINT64: arg->v_uint64 = 0; case GI_TYPE_TAG_BOOLEAN: arg->v_boolean = FALSE; break; case GI_TYPE_TAG_FLOAT: arg->v_float = 0.0f; break; case GI_TYPE_TAG_DOUBLE: arg->v_double = 0.0; break; case GI_TYPE_TAG_GTYPE: arg->v_ssize = 0; break; case GI_TYPE_TAG_FILENAME: case GI_TYPE_TAG_UTF8: case GI_TYPE_TAG_GLIST: case GI_TYPE_TAG_GSLIST: case GI_TYPE_TAG_ERROR: arg->v_pointer = NULL; break; case GI_TYPE_TAG_INTERFACE: { GIBaseInfo* interface_info; GIInfoType interface_type; interface_info = g_type_info_get_interface(type_info); g_assert(interface_info != NULL); interface_type = g_base_info_get_type(interface_info); switch(interface_type) { case GI_INFO_TYPE_ENUM: case GI_INFO_TYPE_FLAGS: arg->v_int = 0; break; case GI_INFO_TYPE_VALUE: /* Better to use a non-NULL value holding NULL? */ arg->v_pointer = NULL; break; default: arg->v_pointer = NULL; break; } g_base_info_unref( (GIBaseInfo*) interface_info); } break; case GI_TYPE_TAG_GHASH: /* Possibly better to return an empty hash table? */ arg->v_pointer = NULL; break; case GI_TYPE_TAG_ARRAY: arg->v_pointer = NULL; break; default: g_warning("Unhandled type %s for default GArgument initialization", g_type_tag_to_string(type_tag)); break; } } JSBool gjs_value_to_arg(JSContext *context, jsval value, GIArgInfo *arg_info, GArgument *arg) { GITypeInfo type_info; g_arg_info_load_type(arg_info, &type_info); return gjs_value_to_g_argument(context, value, &type_info, g_base_info_get_name( (GIBaseInfo*) arg_info), (g_arg_info_is_return_value(arg_info) ? GJS_ARGUMENT_RETURN_VALUE : GJS_ARGUMENT_ARGUMENT), g_arg_info_get_ownership_transfer(arg_info), g_arg_info_may_be_null(arg_info), arg); } JSBool gjs_value_to_explicit_array (JSContext *context, jsval value, GIArgInfo *arg_info, GArgument *arg, gsize *length_p) { GITypeInfo type_info; g_arg_info_load_type(arg_info, &type_info); return gjs_array_to_explicit_array_internal(context, value, &type_info, g_base_info_get_name((GIBaseInfo*) arg_info), GJS_ARGUMENT_ARGUMENT, g_arg_info_get_ownership_transfer(arg_info), g_arg_info_may_be_null(arg_info), &arg->v_pointer, length_p); } static JSBool gjs_array_from_g_list (JSContext *context, jsval *value_p, GITypeTag list_tag, GITypeInfo *param_info, GList *list, GSList *slist) { JSObject *obj; unsigned int i; jsval elem; GArgument arg; JSBool result; obj = JS_NewArrayObject(context, 0, NULL); if (obj == NULL) return JS_FALSE; *value_p = OBJECT_TO_JSVAL(obj); elem = JSVAL_VOID; JS_AddValueRoot(context, &elem); result = JS_FALSE; i = 0; if (list_tag == GI_TYPE_TAG_GLIST) { for ( ; list != NULL; list = list->next) { arg.v_pointer = list->data; if (!gjs_value_from_g_argument(context, &elem, param_info, &arg, TRUE)) goto out; if (!JS_DefineElement(context, obj, i, elem, NULL, NULL, JSPROP_ENUMERATE)) { goto out; } ++i; } } else { for ( ; slist != NULL; slist = slist->next) { arg.v_pointer = slist->data; if (!gjs_value_from_g_argument(context, &elem, param_info, &arg, TRUE)) goto out; if (!JS_DefineElement(context, obj, i, elem, NULL, NULL, JSPROP_ENUMERATE)) { goto out; } ++i; } } result = JS_TRUE; out: JS_RemoveValueRoot(context, &elem); return result; } static JSBool gjs_array_from_carray_internal (JSContext *context, jsval *value_p, GITypeInfo *param_info, guint length, gpointer array) { JSObject *obj; jsval elem; GArgument arg; JSBool result; GITypeTag element_type; guint i; result = JS_FALSE; element_type = g_type_info_get_tag(param_info); if (is_gvalue_flat_array(param_info, element_type)) return gjs_array_from_flat_gvalue_array(context, array, length, value_p); /* Special case array(guint8) */ if (element_type == GI_TYPE_TAG_UINT8) { GByteArray gbytearray; gbytearray.data = (guint8 *) array; gbytearray.len = length; obj = gjs_byte_array_from_byte_array (context, &gbytearray); if (obj == NULL) return JS_FALSE; *value_p = OBJECT_TO_JSVAL(obj); return JS_TRUE; } obj = JS_NewArrayObject(context, 0, NULL); if (obj == NULL) return JS_FALSE; *value_p = OBJECT_TO_JSVAL(obj); elem = JSVAL_VOID; JS_AddValueRoot(context, &elem); #define ITERATE(type) \ for (i = 0; i < length; i++) { \ arg.v_##type = *(((g##type*)array) + i); \ if (!gjs_value_from_g_argument(context, &elem, param_info, &arg, TRUE)) \ goto finally; \ if (!JS_DefineElement(context, obj, i, elem, NULL, NULL, \ JSPROP_ENUMERATE)) \ goto finally; \ } switch (element_type) { case GI_TYPE_TAG_UINT8: ITERATE(uint8); break; case GI_TYPE_TAG_INT8: ITERATE(int8); break; case GI_TYPE_TAG_UINT16: ITERATE(uint16); break; case GI_TYPE_TAG_INT16: ITERATE(int16); break; case GI_TYPE_TAG_UINT32: ITERATE(uint32); break; case GI_TYPE_TAG_INT32: ITERATE(int32); break; case GI_TYPE_TAG_UINT64: ITERATE(uint64); break; case GI_TYPE_TAG_INT64: ITERATE(int64); break; case GI_TYPE_TAG_FLOAT: ITERATE(float); break; case GI_TYPE_TAG_DOUBLE: ITERATE(double); break; case GI_TYPE_TAG_GTYPE: case GI_TYPE_TAG_UTF8: case GI_TYPE_TAG_FILENAME: case GI_TYPE_TAG_ARRAY: case GI_TYPE_TAG_INTERFACE: case GI_TYPE_TAG_GLIST: case GI_TYPE_TAG_GSLIST: case GI_TYPE_TAG_GHASH: case GI_TYPE_TAG_ERROR: ITERATE(pointer); break; default: gjs_throw(context, "Unknown Array element-type %d", element_type); goto finally; } #undef ITERATE result = JS_TRUE; finally: JS_RemoveValueRoot(context, &elem); return result; } static JSBool gjs_array_from_fixed_size_array (JSContext *context, jsval *value_p, GITypeInfo *type_info, gpointer array) { gint length; GITypeInfo *param_info; JSBool res; length = g_type_info_get_array_fixed_size(type_info); g_assert (length != -1); param_info = g_type_info_get_param_type(type_info, 0); res = gjs_array_from_carray_internal(context, value_p, param_info, length, array); g_base_info_unref((GIBaseInfo*)param_info); return res; } JSBool gjs_value_from_explicit_array(JSContext *context, jsval *value_p, GITypeInfo *type_info, GArgument *arg, int length) { GITypeInfo *param_info; JSBool res; param_info = g_type_info_get_param_type(type_info, 0); res = gjs_array_from_carray_internal(context, value_p, param_info, length, arg->v_pointer); g_base_info_unref((GIBaseInfo*)param_info); return res; } static JSBool gjs_array_from_boxed_array (JSContext *context, jsval *value_p, GIArrayType array_type, GITypeInfo *param_info, GArgument *arg) { GArray *array; GPtrArray *ptr_array; gpointer data = NULL; gsize length = 0; if (arg->v_pointer == NULL) { *value_p = JSVAL_NULL; return TRUE; } switch(array_type) { case GI_ARRAY_TYPE_BYTE_ARRAY: /* GByteArray is just a typedef for GArray internally */ case GI_ARRAY_TYPE_ARRAY: array = (GArray*)(arg->v_pointer); data = array->data; length = array->len; break; case GI_ARRAY_TYPE_PTR_ARRAY: ptr_array = (GPtrArray*)(arg->v_pointer); data = ptr_array->pdata; length = ptr_array->len; break; default: g_assert_not_reached(); } return gjs_array_from_carray_internal(context, value_p, param_info, length, data); } static JSBool gjs_array_from_zero_terminated_c_array (JSContext *context, jsval *value_p, GITypeInfo *param_info, gpointer c_array) { JSObject *obj; jsval elem; GArgument arg; JSBool result; GITypeTag element_type; guint i; result = JS_FALSE; element_type = g_type_info_get_tag(param_info); /* Special case array(guint8) */ if (element_type == GI_TYPE_TAG_UINT8) { GByteArray gbytearray; gbytearray.data = (guint8 *) c_array; gbytearray.len = strlen((const char *) c_array); obj = gjs_byte_array_from_byte_array (context, &gbytearray); if (obj == NULL) return JS_FALSE; *value_p = OBJECT_TO_JSVAL(obj); return JS_TRUE; } obj = JS_NewArrayObject(context, 0, NULL); if (obj == NULL) return JS_FALSE; *value_p = OBJECT_TO_JSVAL(obj); elem = JSVAL_VOID; JS_AddValueRoot(context, &elem); #define ITERATE(type) \ do { \ g##type *array = (g##type *) c_array; \ for (i = 0; array[i]; i++) { \ arg.v_##type = array[i]; \ if (!gjs_value_from_g_argument(context, &elem, param_info, &arg, TRUE)) \ goto finally; \ if (!JS_DefineElement(context, obj, i, elem, NULL, NULL, \ JSPROP_ENUMERATE)) \ goto finally; \ } \ } while(0); switch (element_type) { /* We handle GI_TYPE_TAG_UINT8 above. */ case GI_TYPE_TAG_INT8: ITERATE(int8); break; case GI_TYPE_TAG_UINT16: ITERATE(uint16); break; case GI_TYPE_TAG_INT16: ITERATE(int16); break; case GI_TYPE_TAG_UINT32: ITERATE(uint32); break; case GI_TYPE_TAG_INT32: ITERATE(int32); break; case GI_TYPE_TAG_UINT64: ITERATE(uint64); break; case GI_TYPE_TAG_INT64: ITERATE(int64); break; case GI_TYPE_TAG_FLOAT: ITERATE(float); break; case GI_TYPE_TAG_DOUBLE: ITERATE(double); break; case GI_TYPE_TAG_GTYPE: case GI_TYPE_TAG_UTF8: case GI_TYPE_TAG_FILENAME: case GI_TYPE_TAG_ARRAY: case GI_TYPE_TAG_INTERFACE: case GI_TYPE_TAG_GLIST: case GI_TYPE_TAG_GSLIST: case GI_TYPE_TAG_GHASH: case GI_TYPE_TAG_ERROR: ITERATE(pointer); break; default: gjs_throw(context, "Unknown element-type %d", element_type); goto finally; } #undef ITERATE result = JS_TRUE; finally: JS_RemoveValueRoot(context, &elem); return result; } static JSBool gjs_object_from_g_hash (JSContext *context, jsval *value_p, GITypeInfo *key_param_info, GITypeInfo *val_param_info, GHashTable *hash) { GHashTableIter iter; JSObject *obj; JSString *keystr; char *keyutf8 = NULL; jsval keyjs, valjs; GArgument keyarg, valarg; JSBool result; // a NULL hash table becomes a null JS value if (hash==NULL) { *value_p = JSVAL_NULL; return JS_TRUE; } obj = JS_NewObject(context, NULL, NULL, NULL); if (obj == NULL) return JS_FALSE; *value_p = OBJECT_TO_JSVAL(obj); JS_AddObjectRoot(context, &obj); keyjs = JSVAL_VOID; JS_AddValueRoot(context, &keyjs); valjs = JSVAL_VOID; JS_AddValueRoot(context, &valjs); keystr = NULL; JS_AddStringRoot(context, &keystr); result = JS_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; keystr = JS_ValueToString(context, keyjs); if (!keystr) goto out; if (!gjs_string_to_utf8(context, STRING_TO_JSVAL(keystr), &keyutf8)) goto out; if (!gjs_value_from_g_argument(context, &valjs, val_param_info, &valarg, TRUE)) goto out; if (!JS_DefineProperty(context, obj, keyutf8, valjs, NULL, NULL, JSPROP_ENUMERATE)) goto out; g_free(keyutf8); keyutf8 = NULL; } result = JS_TRUE; out: if (keyutf8) g_free(keyutf8); JS_RemoveObjectRoot(context, &obj); JS_RemoveValueRoot(context, &keyjs); JS_RemoveValueRoot(context, &valjs); JS_RemoveStringRoot(context, &keystr); return result; } JSBool gjs_value_from_g_argument (JSContext *context, jsval *value_p, GITypeInfo *type_info, GArgument *arg, gboolean copy_structs) { GITypeTag type_tag; type_tag = g_type_info_get_tag( (GITypeInfo*) type_info); gjs_debug_marshal(GJS_DEBUG_GFUNCTION, "Converting GArgument %s to jsval", g_type_tag_to_string(type_tag)); *value_p = JSVAL_NULL; switch (type_tag) { case GI_TYPE_TAG_VOID: *value_p = JSVAL_VOID; /* or JSVAL_NULL ? */ break; case GI_TYPE_TAG_BOOLEAN: *value_p = BOOLEAN_TO_JSVAL(!!arg->v_int); break; case GI_TYPE_TAG_INT32: return JS_NewNumberValue(context, arg->v_int, value_p); case GI_TYPE_TAG_UINT32: return JS_NewNumberValue(context, arg->v_uint, value_p); case GI_TYPE_TAG_INT64: return JS_NewNumberValue(context, arg->v_int64, value_p); case GI_TYPE_TAG_UINT64: return JS_NewNumberValue(context, arg->v_uint64, value_p); case GI_TYPE_TAG_UINT16: return JS_NewNumberValue(context, arg->v_uint16, value_p); case GI_TYPE_TAG_INT16: return JS_NewNumberValue(context, arg->v_int16, value_p); case GI_TYPE_TAG_UINT8: return JS_NewNumberValue(context, arg->v_uint8, value_p); case GI_TYPE_TAG_INT8: return JS_NewNumberValue(context, arg->v_int8, value_p); case GI_TYPE_TAG_FLOAT: return JS_NewNumberValue(context, arg->v_float, value_p); case GI_TYPE_TAG_DOUBLE: return JS_NewNumberValue(context, arg->v_double, value_p); case GI_TYPE_TAG_GTYPE: { JSObject *obj; obj = gjs_gtype_create_gtype_wrapper(context, arg->v_ssize); *value_p = OBJECT_TO_JSVAL(obj); } break; case GI_TYPE_TAG_UNICHAR: { char utf8[7]; gint bytes; /* Preserve the bidirectional mapping between 0 and "" */ if (arg->v_uint32 == 0) { return gjs_string_from_utf8 (context, "", 0, value_p); } else if (!g_unichar_validate (arg->v_uint32)) { gjs_throw(context, "Invalid unicode codepoint %" G_GUINT32_FORMAT, arg->v_uint32); return JS_FALSE; } else { bytes = g_unichar_to_utf8 (arg->v_uint32, utf8); return gjs_string_from_utf8 (context, (char*)utf8, bytes, value_p); } } case GI_TYPE_TAG_FILENAME: if (arg->v_pointer) return gjs_string_from_filename(context, (const char *) arg->v_pointer, -1, value_p); else { /* For NULL we'll return JSVAL_NULL, which is already set * in *value_p */ return JS_TRUE; } case GI_TYPE_TAG_UTF8: if (arg->v_pointer) return gjs_string_from_utf8(context, (const char *) arg->v_pointer, -1, value_p); else { /* For NULL we'll return JSVAL_NULL, which is already set * in *value_p */ return JS_TRUE; } case GI_TYPE_TAG_ERROR: { if (arg->v_pointer) { JSObject *obj = gjs_error_from_gerror(context, (GError *) arg->v_pointer, FALSE); if (obj) { *value_p = OBJECT_TO_JSVAL(obj); return JS_TRUE; } return JS_FALSE; } return JS_TRUE; } case GI_TYPE_TAG_INTERFACE: { jsval value; GIBaseInfo* interface_info; GIInfoType interface_type; GType gtype; gboolean gtype_is_object, gtype_is_interface; interface_info = g_type_info_get_interface(type_info); g_assert(interface_info != NULL); value = JSVAL_VOID; interface_type = g_base_info_get_type(interface_info); if (interface_type == GI_INFO_TYPE_UNRESOLVED) { gjs_throw(context, "Unable to resolve arg type '%s'", g_base_info_get_name(interface_info)); goto out; } /* Enum/Flags are aren't pointer types, unlike the other interface subtypes */ if (interface_type == GI_INFO_TYPE_ENUM) { gint64 value_int64 = _gjs_enum_from_int ((GIEnumInfo *)interface_info, arg->v_int); if (_gjs_enum_value_is_valid(context, (GIEnumInfo *)interface_info, value_int64)) { jsval tmp; if (JS_NewNumberValue(context, value_int64, &tmp)) value = tmp; } goto out; } else if (interface_type == GI_INFO_TYPE_FLAGS) { gint64 value_int64 = _gjs_enum_from_int ((GIEnumInfo *)interface_info, arg->v_int); gtype = g_registered_type_info_get_g_type((GIRegisteredTypeInfo*)interface_info); if (_gjs_flags_value_is_valid(context, gtype, value_int64)) { jsval tmp; if (JS_NewNumberValue(context, value_int64, &tmp)) value = tmp; } goto out; } else if (interface_type == GI_INFO_TYPE_STRUCT && g_struct_info_is_foreign((GIStructInfo*)interface_info)) { JSBool ret; ret = gjs_struct_foreign_convert_from_g_argument(context, value_p, interface_info, arg); g_base_info_unref(interface_info); return ret; } /* Everything else is a pointer type, NULL is the easy case */ if (arg->v_pointer == NULL) { value = JSVAL_NULL; goto out; } gtype = g_registered_type_info_get_g_type((GIRegisteredTypeInfo*)interface_info); if (G_TYPE_IS_INSTANTIATABLE(gtype) || G_TYPE_IS_INTERFACE(gtype)) gtype = G_TYPE_FROM_INSTANCE(arg->v_pointer); gjs_debug_marshal(GJS_DEBUG_GFUNCTION, "gtype of INTERFACE is %s", g_type_name(gtype)); /* Test GValue and GError before Struct, or it will be handled as the latter */ if (g_type_is_a(gtype, G_TYPE_VALUE)) { if (!gjs_value_from_g_value(context, &value, (const GValue *) arg->v_pointer)) value = JSVAL_VOID; /* Make sure error is flagged */ goto out; } if (g_type_is_a(gtype, G_TYPE_ERROR)) { JSObject *obj; obj = gjs_error_from_gerror(context, (GError *) arg->v_pointer, FALSE); if (obj) value = OBJECT_TO_JSVAL(obj); else value = JSVAL_VOID; goto out; } if (interface_type == GI_INFO_TYPE_STRUCT || interface_type == GI_INFO_TYPE_BOXED) { JSObject *obj; GjsBoxedCreationFlags flags; if (copy_structs) flags = GJS_BOXED_CREATION_NONE; else if (g_type_is_a(gtype, G_TYPE_VARIANT)) flags = GJS_BOXED_CREATION_NONE; else flags = GJS_BOXED_CREATION_NO_COPY; obj = gjs_boxed_from_c_struct(context, (GIStructInfo *)interface_info, arg->v_pointer, flags); if (obj) value = OBJECT_TO_JSVAL(obj); goto out; } else if (interface_type == GI_INFO_TYPE_UNION) { JSObject *obj; obj = gjs_union_from_c_union(context, (GIUnionInfo *)interface_info, arg->v_pointer); if (obj) value = OBJECT_TO_JSVAL(obj); goto out; } if (g_type_is_a(gtype, G_TYPE_OBJECT)) { JSObject *obj; obj = gjs_object_from_g_object(context, G_OBJECT(arg->v_pointer)); if (obj) value = OBJECT_TO_JSVAL(obj); } else if (g_type_is_a(gtype, G_TYPE_BOXED) || g_type_is_a(gtype, G_TYPE_ENUM) || g_type_is_a(gtype, G_TYPE_FLAGS)) { /* Should have been handled above */ gjs_throw(context, "Type %s registered for unexpected interface_type %d", g_type_name(gtype), interface_type); return JS_FALSE; } else if (g_type_is_a(gtype, G_TYPE_PARAM)) { JSObject *obj; obj = gjs_param_from_g_param(context, G_PARAM_SPEC(arg->v_pointer)); if (obj) value = OBJECT_TO_JSVAL(obj); } else if (gtype == G_TYPE_NONE) { gjs_throw(context, "Unexpected unregistered type packing GArgument into jsval"); } else if (G_TYPE_IS_INSTANTIATABLE(gtype) || G_TYPE_IS_INTERFACE(gtype)) { JSObject *obj; obj = gjs_object_from_g_fundamental(context, (GIObjectInfo *)interface_info, arg->v_pointer); if (obj) value = OBJECT_TO_JSVAL(obj); } else { gjs_throw(context, "Unhandled GType %s packing GArgument into jsval", g_type_name(gtype)); } out: g_base_info_unref( (GIBaseInfo*) interface_info); if (JSVAL_IS_VOID(value)) return JS_FALSE; *value_p = value; } break; case GI_TYPE_TAG_ARRAY: if (arg->v_pointer == NULL) { /* OK, but no conversion to do */ } else if (g_type_info_get_array_type(type_info) == GI_ARRAY_TYPE_C) { if (g_type_info_is_zero_terminated(type_info)) { GITypeInfo *param_info; JSBool result; param_info = g_type_info_get_param_type(type_info, 0); g_assert(param_info != NULL); result = gjs_array_from_zero_terminated_c_array(context, value_p, param_info, arg->v_pointer); g_base_info_unref((GIBaseInfo*) param_info); return result; } else { /* arrays with length are handled outside of this function */ return gjs_array_from_fixed_size_array(context, value_p, type_info, arg->v_pointer); } } else if (g_type_info_get_array_type(type_info) == GI_ARRAY_TYPE_BYTE_ARRAY) { JSObject *array = gjs_byte_array_from_byte_array(context, (GByteArray*)arg->v_pointer); if (!array) { gjs_throw(context, "Couldn't convert GByteArray to a ByteArray"); return JS_FALSE; } *value_p = OBJECT_TO_JSVAL(array); } else { /* this assumes the array type is one of GArray, GPtrArray or * GByteArray */ GITypeInfo *param_info; gboolean result; param_info = g_type_info_get_param_type(type_info, 0); g_assert(param_info != NULL); result = gjs_array_from_boxed_array(context, value_p, g_type_info_get_array_type(type_info), param_info, arg); g_base_info_unref((GIBaseInfo*) param_info); return result; } break; case GI_TYPE_TAG_GLIST: case GI_TYPE_TAG_GSLIST: { GITypeInfo *param_info; gboolean result; param_info = g_type_info_get_param_type(type_info, 0); g_assert(param_info != NULL); result = gjs_array_from_g_list(context, value_p, type_tag, param_info, type_tag == GI_TYPE_TAG_GLIST ? (GList *) arg->v_pointer : NULL, type_tag == GI_TYPE_TAG_GSLIST ? (GSList *) arg->v_pointer : NULL); g_base_info_unref((GIBaseInfo*) param_info); return result; } break; case GI_TYPE_TAG_GHASH: { GITypeInfo *key_param_info, *val_param_info; gboolean result; key_param_info = g_type_info_get_param_type(type_info, 0); g_assert(key_param_info != NULL); val_param_info = g_type_info_get_param_type(type_info, 1); g_assert(val_param_info != NULL); result = gjs_object_from_g_hash(context, value_p, key_param_info, val_param_info, (GHashTable *) arg->v_pointer); g_base_info_unref((GIBaseInfo*) key_param_info); g_base_info_unref((GIBaseInfo*) val_param_info); return result; } break; default: g_warning("Unhandled type %s converting GArgument to JavaScript", g_type_tag_to_string(type_tag)); return JS_FALSE; } return JS_TRUE; } static JSBool gjs_g_arg_release_internal(JSContext *context, GITransfer transfer, GITypeInfo *type_info, GITypeTag type_tag, GArgument *arg); typedef struct { JSContext *context; GITypeInfo *key_param_info, *val_param_info; GITransfer transfer; JSBool failed; } GHR_closure; static gboolean gjs_ghr_helper(gpointer key, gpointer val, gpointer user_data) { GHR_closure *c = (GHR_closure *) user_data; GArgument key_arg, val_arg; key_arg.v_pointer = key; val_arg.v_pointer = val; if (!gjs_g_arg_release_internal(c->context, c->transfer, c->key_param_info, g_type_info_get_tag(c->key_param_info), &key_arg)) c->failed = JS_TRUE; if (!gjs_g_arg_release_internal(c->context, c->transfer, c->val_param_info, g_type_info_get_tag(c->val_param_info), &val_arg)) c->failed = JS_TRUE; return TRUE; } /* We need to handle GI_TRANSFER_NOTHING differently for out parameters * (free nothing) and for in parameters (free any temporaries we've * allocated */ #define TRANSFER_IN_NOTHING (GI_TRANSFER_EVERYTHING + 1) static JSBool gjs_g_arg_release_internal(JSContext *context, GITransfer transfer, GITypeInfo *type_info, GITypeTag type_tag, GArgument *arg) { JSBool failed; g_assert(transfer != GI_TRANSFER_NOTHING); failed = JS_FALSE; switch (type_tag) { case GI_TYPE_TAG_VOID: case GI_TYPE_TAG_BOOLEAN: case GI_TYPE_TAG_INT8: case GI_TYPE_TAG_UINT8: case GI_TYPE_TAG_INT16: case GI_TYPE_TAG_UINT16: case GI_TYPE_TAG_INT32: case GI_TYPE_TAG_UINT32: case GI_TYPE_TAG_INT64: case GI_TYPE_TAG_UINT64: case GI_TYPE_TAG_FLOAT: case GI_TYPE_TAG_DOUBLE: case GI_TYPE_TAG_UNICHAR: case GI_TYPE_TAG_GTYPE: break; case GI_TYPE_TAG_FILENAME: case GI_TYPE_TAG_UTF8: g_free(arg->v_pointer); break; case GI_TYPE_TAG_ERROR: if (transfer != TRANSFER_IN_NOTHING) g_error_free ((GError *) arg->v_pointer); break; case GI_TYPE_TAG_INTERFACE: { GIBaseInfo* interface_info; GIInfoType interface_type; GType gtype; gboolean gtype_is_object, gtype_is_interface; interface_info = g_type_info_get_interface(type_info); g_assert(interface_info != NULL); interface_type = g_base_info_get_type(interface_info); if (interface_type == GI_INFO_TYPE_STRUCT && g_struct_info_is_foreign((GIStructInfo*)interface_info)) return gjs_struct_foreign_release_g_argument(context, transfer, interface_info, arg); if (interface_type == GI_INFO_TYPE_ENUM || interface_type == GI_INFO_TYPE_FLAGS) goto out; /* Anything else is a pointer */ if (arg->v_pointer == NULL) goto out; gtype = g_registered_type_info_get_g_type((GIRegisteredTypeInfo*)interface_info); if (G_TYPE_IS_INSTANTIATABLE(gtype) || G_TYPE_IS_INTERFACE(gtype)) gtype = G_TYPE_FROM_INSTANCE(arg->v_pointer); gjs_debug_marshal(GJS_DEBUG_GFUNCTION, "gtype of INTERFACE is %s", g_type_name(gtype)); /* In gjs_value_from_g_argument we handle Struct/Union types without a * registered GType, but here we are specifically handling a GArgument that * *owns* its value, and that is non-sensical for such types, so we * don't have to worry about it. */ gtype_is_object = g_type_is_a(gtype, G_TYPE_OBJECT); gtype_is_interface = g_type_is_a(gtype, G_TYPE_INTERFACE); if (gtype_is_object || gtype_is_interface) { if (gtype_is_object) { if (transfer != TRANSFER_IN_NOTHING) g_object_unref(G_OBJECT(arg->v_pointer)); } else { if (transfer != TRANSFER_IN_NOTHING) gjs_fundamental_unref(context, arg->v_pointer); } } else if (g_type_is_a(gtype, G_TYPE_CLOSURE)) { g_closure_unref((GClosure *) arg->v_pointer); } else if (g_type_is_a(gtype, G_TYPE_VALUE)) { /* G_TYPE_VALUE is-a G_TYPE_BOXED, but we special case it */ if (g_type_info_is_pointer (type_info)) g_boxed_free(gtype, arg->v_pointer); else g_value_unset ((GValue *) arg->v_pointer); } else if (g_type_is_a(gtype, G_TYPE_BOXED)) { if (transfer != TRANSFER_IN_NOTHING) g_boxed_free(gtype, arg->v_pointer); } else if (g_type_is_a(gtype, G_TYPE_VARIANT)) { if (transfer != TRANSFER_IN_NOTHING) g_variant_unref ((GVariant *) arg->v_pointer); } else if (gtype == G_TYPE_NONE) { if (transfer != TRANSFER_IN_NOTHING) { gjs_throw(context, "Don't know how to release GArgument: not an object or boxed type"); failed = JS_TRUE; } } else if (G_TYPE_IS_INSTANTIATABLE(gtype)) { if (transfer != TRANSFER_IN_NOTHING) gjs_fundamental_unref(context, arg->v_pointer); } else { gjs_throw(context, "Unhandled GType %s releasing GArgument", g_type_name(gtype)); failed = JS_TRUE; } out: g_base_info_unref( (GIBaseInfo*) interface_info); } break; case GI_TYPE_TAG_GLIST: if (transfer != GI_TRANSFER_CONTAINER) { GITypeInfo *param_info; GList *list; param_info = g_type_info_get_param_type(type_info, 0); g_assert(param_info != NULL); for (list = (GList *) arg->v_pointer; list != NULL; list = list->next) { GArgument elem; elem.v_pointer = list->data; if (!gjs_g_arg_release_internal(context, transfer, param_info, g_type_info_get_tag(param_info), &elem)) { failed = JS_TRUE; } } g_base_info_unref((GIBaseInfo*) param_info); } g_list_free((GList *) arg->v_pointer); break; case GI_TYPE_TAG_ARRAY: { GIArrayType array_type = g_type_info_get_array_type(type_info); if (arg->v_pointer == NULL) { /* OK */ } else if (array_type == GI_ARRAY_TYPE_C) { GITypeInfo *param_info; GITypeTag element_type; param_info = g_type_info_get_param_type(type_info, 0); element_type = g_type_info_get_tag(param_info); if (is_gvalue_flat_array(param_info, element_type)) { if (transfer != GI_TRANSFER_CONTAINER) { gint len = g_type_info_get_array_fixed_size(type_info); gint i; if (len < 0) { gjs_throw(context, "Releasing a flat GValue array that was not fixed-size or was nested" "inside another container. This is not supported (and will leak)"); g_base_info_unref(param_info); return JS_FALSE; } for (i = 0; i < len; i++) { GValue *v = ((GValue*)arg->v_pointer) + i; g_value_unset(v); } } g_free(arg->v_pointer); g_base_info_unref(param_info); return JS_TRUE; } switch (element_type) { case GI_TYPE_TAG_UTF8: case GI_TYPE_TAG_FILENAME: if (transfer == GI_TRANSFER_CONTAINER) g_free(arg->v_pointer); else g_strfreev ((gchar **) arg->v_pointer); break; case GI_TYPE_TAG_UINT8: case GI_TYPE_TAG_UINT16: case GI_TYPE_TAG_UINT32: case GI_TYPE_TAG_INT8: case GI_TYPE_TAG_INT16: case GI_TYPE_TAG_INT32: case GI_TYPE_TAG_GTYPE: g_free (arg->v_pointer); break; case GI_TYPE_TAG_INTERFACE: case GI_TYPE_TAG_GLIST: case GI_TYPE_TAG_GSLIST: case GI_TYPE_TAG_ARRAY: case GI_TYPE_TAG_GHASH: case GI_TYPE_TAG_ERROR: if (transfer != GI_TRANSFER_CONTAINER && type_needs_out_release(param_info, element_type)) { if (g_type_info_is_zero_terminated (type_info)) { gpointer *array; GArgument elem; for (array = (void **) arg->v_pointer; *array; array++) { elem.v_pointer = *array; if (!gjs_g_arg_release_internal(context, GI_TRANSFER_EVERYTHING, param_info, element_type, &elem)) { failed = JS_TRUE; } } } else { gint len = g_type_info_get_array_fixed_size(type_info); gint i; GArgument elem; g_assert(len != -1); for (i = 0; i < len; i++) { elem.v_pointer = ((gpointer*)arg->v_pointer)[i]; if (!gjs_g_arg_release_internal(context, GI_TRANSFER_EVERYTHING, param_info, element_type, &elem)) { failed = TRUE; } } } } g_free (arg->v_pointer); break; default: gjs_throw(context, "Releasing a C array with explicit length, that was nested" "inside another container. This is not supported (and will leak)"); failed = JS_TRUE; } g_base_info_unref((GIBaseInfo*) param_info); } else if (array_type == GI_ARRAY_TYPE_ARRAY) { GITypeInfo *param_info; GITypeTag element_type; param_info = g_type_info_get_param_type(type_info, 0); element_type = g_type_info_get_tag(param_info); switch (element_type) { case GI_TYPE_TAG_UINT8: case GI_TYPE_TAG_UINT16: case GI_TYPE_TAG_UINT32: case GI_TYPE_TAG_UINT64: case GI_TYPE_TAG_INT8: case GI_TYPE_TAG_INT16: case GI_TYPE_TAG_INT32: case GI_TYPE_TAG_INT64: case GI_TYPE_TAG_GTYPE: g_array_free((GArray*) arg->v_pointer, TRUE); break; case GI_TYPE_TAG_UTF8: case GI_TYPE_TAG_FILENAME: case GI_TYPE_TAG_ARRAY: case GI_TYPE_TAG_INTERFACE: case GI_TYPE_TAG_GLIST: case GI_TYPE_TAG_GSLIST: case GI_TYPE_TAG_GHASH: case GI_TYPE_TAG_ERROR: if (transfer == GI_TRANSFER_CONTAINER) { g_array_free((GArray*) arg->v_pointer, TRUE); } else if (type_needs_out_release (param_info, element_type)) { GArray *array = (GArray *) arg->v_pointer; guint i; for (i = 0; i < array->len; i++) { GArgument arg; arg.v_pointer = g_array_index (array, gpointer, i); gjs_g_arg_release_internal(context, transfer, param_info, element_type, &arg); } g_array_free (array, TRUE); } break; default: gjs_throw(context, "Don't know how to release GArray element-type %d", element_type); failed = JS_TRUE; } g_base_info_unref((GIBaseInfo*) param_info); } else if (array_type == GI_ARRAY_TYPE_BYTE_ARRAY) { g_byte_array_unref ((GByteArray*)arg->v_pointer); } else if (array_type == GI_ARRAY_TYPE_PTR_ARRAY) { GITypeInfo *param_info; GPtrArray *array; param_info = g_type_info_get_param_type(type_info, 0); array = (GPtrArray *) arg->v_pointer; if (transfer != GI_TRANSFER_CONTAINER) { guint i; for (i = 0; i < array->len; i++) { GArgument arg; arg.v_pointer = g_ptr_array_index (array, i); gjs_g_argument_release(context, transfer, param_info, &arg); } } g_ptr_array_free(array, TRUE); g_base_info_unref((GIBaseInfo*) param_info); } else { g_assert_not_reached(); } break; } case GI_TYPE_TAG_GSLIST: if (transfer != GI_TRANSFER_CONTAINER) { GITypeInfo *param_info; GSList *slist; param_info = g_type_info_get_param_type(type_info, 0); g_assert(param_info != NULL); for (slist = (GSList *) arg->v_pointer; slist != NULL; slist = slist->next) { GArgument elem; elem.v_pointer = slist->data; if (!gjs_g_arg_release_internal(context, transfer, param_info, g_type_info_get_tag(param_info), &elem)) { failed = JS_TRUE; } } g_base_info_unref((GIBaseInfo*) param_info); } g_slist_free((GSList *) arg->v_pointer); break; case GI_TYPE_TAG_GHASH: if (arg->v_pointer) { if (transfer == GI_TRANSFER_CONTAINER) g_hash_table_steal_all ((GHashTable *) arg->v_pointer); else { GHR_closure c = { context, NULL, NULL, transfer, JS_FALSE }; c.key_param_info = g_type_info_get_param_type(type_info, 0); g_assert(c.key_param_info != NULL); c.val_param_info = g_type_info_get_param_type(type_info, 1); g_assert(c.val_param_info != NULL); g_hash_table_foreach_steal ((GHashTable *) arg->v_pointer, gjs_ghr_helper, &c); failed = c.failed; g_base_info_unref ((GIBaseInfo *)c.key_param_info); g_base_info_unref ((GIBaseInfo *)c.val_param_info); } g_hash_table_destroy ((GHashTable *) arg->v_pointer); } break; default: g_warning("Unhandled type %s releasing GArgument", g_type_tag_to_string(type_tag)); return JS_FALSE; } return !failed; } JSBool gjs_g_argument_release(JSContext *context, GITransfer transfer, GITypeInfo *type_info, GArgument *arg) { GITypeTag type_tag; if (transfer == GI_TRANSFER_NOTHING) return JS_TRUE; type_tag = g_type_info_get_tag( (GITypeInfo*) type_info); gjs_debug_marshal(GJS_DEBUG_GFUNCTION, "Releasing GArgument %s out param or return value", g_type_tag_to_string(type_tag)); return gjs_g_arg_release_internal(context, transfer, type_info, type_tag, arg); } JSBool gjs_g_argument_release_in_arg(JSContext *context, GITransfer transfer, GITypeInfo *type_info, GArgument *arg) { GITypeTag type_tag; /* GI_TRANSFER_EVERYTHING: we don't own the argument anymore. * GI_TRANSFER_CONTAINER: * - non-containers: treated as GI_TRANSFER_EVERYTHING * - containers: See FIXME in gjs_array_to_g_list(); currently * an error and we won't get here. */ if (transfer != GI_TRANSFER_NOTHING) return JS_TRUE; type_tag = g_type_info_get_tag( (GITypeInfo*) type_info); gjs_debug_marshal(GJS_DEBUG_GFUNCTION, "Releasing GArgument %s in param", g_type_tag_to_string(type_tag)); if (type_needs_release (type_info, type_tag)) return gjs_g_arg_release_internal(context, (GITransfer) TRANSFER_IN_NOTHING, type_info, type_tag, arg); return JS_TRUE; } JSBool gjs_g_argument_release_in_array (JSContext *context, GITransfer transfer, GITypeInfo *type_info, guint length, GArgument *arg) { GITypeInfo *param_type; gpointer *array; GArgument elem; guint i; JSBool ret = JS_TRUE; GITypeTag type_tag; if (transfer != GI_TRANSFER_NOTHING) return JS_TRUE; gjs_debug_marshal(GJS_DEBUG_GFUNCTION, "Releasing GArgument array in param"); array = (gpointer *) arg->v_pointer; param_type = g_type_info_get_param_type(type_info, 0); type_tag = g_type_info_get_tag(param_type); if (is_gvalue_flat_array(param_type, type_tag)) { for (i = 0; i < length; i++) { GValue *v = ((GValue*)array) + i; g_value_unset(v); } } if (type_needs_release(param_type, type_tag)) { for (i = 0; i < length; i++) { elem.v_pointer = array[i]; if (!gjs_g_arg_release_internal(context, (GITransfer) TRANSFER_IN_NOTHING, param_type, type_tag, &elem)) { ret = JS_FALSE; break; } } } g_base_info_unref(param_type); g_free(array); return ret; } JSBool gjs_g_argument_release_out_array (JSContext *context, GITransfer transfer, GITypeInfo *type_info, guint length, GArgument *arg) { GITypeInfo *param_type; gpointer *array; GArgument elem; guint i; JSBool ret = JS_TRUE; GITypeTag type_tag; if (transfer == GI_TRANSFER_NOTHING) return JS_TRUE; gjs_debug_marshal(GJS_DEBUG_GFUNCTION, "Releasing GArgument array out param"); array = (gpointer *) arg->v_pointer; param_type = g_type_info_get_param_type(type_info, 0); type_tag = g_type_info_get_tag(param_type); if (transfer != GI_TRANSFER_CONTAINER && type_needs_out_release(param_type, type_tag)) { for (i = 0; i < length; i++) { elem.v_pointer = array[i]; if (!gjs_g_arg_release_internal(context, GI_TRANSFER_EVERYTHING, param_type, type_tag, &elem)) { ret = JS_FALSE; } } } g_base_info_unref(param_type); g_free(array); return ret; } cjs-2.8.0/gi/boxed.h0000664000175000017500000000526512610172032013077 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_BOXED_H__ #define __GJS_BOXED_H__ #include #include "cjs/jsapi-util.h" #include G_BEGIN_DECLS typedef enum { GJS_BOXED_CREATION_NONE = 0, GJS_BOXED_CREATION_NO_COPY = (1 << 0) } GjsBoxedCreationFlags; /* Hack for now... why doesn't gobject-introspection have this? */ typedef GIStructInfo GIBoxedInfo; void gjs_define_boxed_class (JSContext *context, JSObject *in_object, GIBoxedInfo *info); JSObject* gjs_lookup_boxed_prototype (JSContext *context, GIBoxedInfo *info); void* gjs_c_struct_from_boxed (JSContext *context, JSObject *obj); JSObject* gjs_boxed_from_c_struct (JSContext *context, GIStructInfo *info, void *gboxed, GjsBoxedCreationFlags flags); JSBool gjs_typecheck_boxed (JSContext *context, JSObject *obj, GIStructInfo *expected_info, GType expected_type, JSBool throw_error); G_END_DECLS #endif /* __GJS_BOXED_H__ */ cjs-2.8.0/gi/interface.cpp0000664000175000017500000001455112610172032014267 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * Copyright (c) 2012 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "function.h" #include "gtype.h" #include "interface.h" #include #include #include #include typedef struct { GIInterfaceInfo *info; GType gtype; } Interface; extern struct JSClass gjs_interface_class; GJS_DEFINE_PRIV_FROM_JS(Interface, gjs_interface_class) GJS_NATIVE_CONSTRUCTOR_DEFINE_ABSTRACT(interface) static void interface_finalize(JSFreeOp *fop, JSObject *obj) { Interface *priv; priv = (Interface*) JS_GetPrivate(obj); if (priv == NULL) return; if (priv->info != NULL) g_base_info_unref((GIBaseInfo*)priv->info); GJS_DEC_COUNTER(interface); g_slice_free(Interface, priv); } static JSBool gjs_define_static_methods(JSContext *context, JSObject *constructor, GType gtype, GIInterfaceInfo *info) { int i; int n_methods; n_methods = g_interface_info_get_n_methods(info); for (i = 0; i < n_methods; i++) { GIFunctionInfo *meth_info; GIFunctionInfoFlags flags; meth_info = g_interface_info_get_method (info, i); flags = g_function_info_get_flags (meth_info); /* Anything that isn't a method we put on the prototype of the * constructor. This includes introspection * methods, as well as the forthcoming "static methods" * support. We may want to change this to use * GI_FUNCTION_IS_CONSTRUCTOR and GI_FUNCTION_IS_STATIC or the * like in the near future. */ if (!(flags & GI_FUNCTION_IS_METHOD)) { gjs_define_function(context, constructor, gtype, (GICallableInfo *)meth_info); } g_base_info_unref((GIBaseInfo*) meth_info); } return JS_TRUE; } static JSBool interface_new_resolve(JSContext *context, JSObject **obj, jsid *id, unsigned flags, JSObject **objp) { Interface *priv; char *name; JSBool ret = JS_FALSE; GIFunctionInfo *method_info; *objp = NULL; if (!gjs_get_string_id(context, *id, &name)) return JS_TRUE; priv = priv_from_js(context, *obj); if (priv == NULL) goto out; method_info = g_interface_info_find_method((GIInterfaceInfo*) priv->info, name); if (method_info != NULL) { if (gjs_define_function(context, *obj, priv->gtype, (GICallableInfo*)method_info) == NULL) { g_base_info_unref((GIBaseInfo*)method_info); goto out; } *objp = *obj; g_base_info_unref((GIBaseInfo*)method_info); } ret = JS_TRUE; out: g_free (name); return ret; } struct JSClass gjs_interface_class = { "GObject_Interface", JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE, JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, (JSResolveOp) interface_new_resolve, JS_ConvertStub, interface_finalize, NULL, NULL, NULL, NULL, NULL }; JSPropertySpec gjs_interface_proto_props[] = { { NULL } }; JSFunctionSpec gjs_interface_proto_funcs[] = { { NULL } }; JSBool gjs_define_interface_class(JSContext *context, JSObject *in_object, GIInterfaceInfo *info) { Interface *priv; const char *constructor_name; JSObject *constructor; JSObject *prototype; jsval value; constructor_name = g_base_info_get_name((GIBaseInfo*)info); if (!gjs_init_class_dynamic(context, in_object, NULL, g_base_info_get_namespace((GIBaseInfo*)info), constructor_name, &gjs_interface_class, gjs_interface_constructor, 0, /* props of prototype */ &gjs_interface_proto_props[0], /* funcs of prototype */ &gjs_interface_proto_funcs[0], /* props of constructor, MyConstructor.myprop */ NULL, /* funcs of constructor, MyConstructor.myfunc() */ NULL, &prototype, &constructor)) { g_error("Can't init class %s", constructor_name); } GJS_INC_COUNTER(interface); priv = g_slice_new0(Interface); priv->info = info; priv->gtype = g_registered_type_info_get_g_type(priv->info); g_base_info_ref((GIBaseInfo*)priv->info); JS_SetPrivate(prototype, priv); gjs_define_static_methods(context, constructor, priv->gtype, priv->info); value = OBJECT_TO_JSVAL(gjs_gtype_create_gtype_wrapper(context, priv->gtype)); JS_DefineProperty(context, constructor, "$gtype", value, NULL, NULL, JSPROP_PERMANENT); return JS_TRUE; } cjs-2.8.0/gi/param.cpp0000664000175000017500000004377212610172032013436 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include "param.h" #include "arg.h" #include "object.h" #include "repo.h" #include "gtype.h" #include #include #include typedef struct { GParamSpec *gparam; /* NULL if we are the prototype and not an instance */ } Param; extern struct JSClass gjs_param_class; GJS_DEFINE_PRIV_FROM_JS(Param, gjs_param_class) static GIFieldInfo * find_field_info(GIObjectInfo *info, gchar *name) { int i; GIFieldInfo *field_info; /* GParamSpecs aren't very big. We could optimize this so that it isn't * O(N), but for the biggest GParamSpec, N=5, so it doesn't really matter. */ for (i = 0; i < g_object_info_get_n_fields((GIObjectInfo*)info); i++) { field_info = g_object_info_get_field((GIObjectInfo*)info, i); if (g_str_equal(name, g_base_info_get_name((GIBaseInfo*)field_info))) return field_info; g_base_info_unref((GIBaseInfo*)field_info); } return NULL; } /* a hook on getting a property; set value_p to override property's value. * Return value is JS_FALSE on OOM/exception. */ static JSBool param_get_prop(JSContext *context, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleValue value_p) { JSBool success; Param *priv; GParamSpec *pspec; char *name; GType gtype; GIObjectInfo *info = NULL, *parent_info = NULL; GIFieldInfo *field_info = NULL; GITypeInfo *type_info = NULL; GIArgument arg; if (!gjs_get_string_id(context, id, &name)) return JS_TRUE; /* not something we affect, but no error */ priv = priv_from_js(context, obj); if (priv == NULL) { g_free(name); return JS_FALSE; /* wrong class */ } success = JS_FALSE; pspec = priv->gparam; gtype = G_TYPE_FROM_INSTANCE(pspec); info = (GIObjectInfo*)g_irepository_find_by_gtype(g_irepository_get_default(), gtype); if (info == NULL) { /* We may have a non-introspectable GParamSpec subclass here. Just return VOID. */ value_p.set(JSVAL_VOID); success = JS_TRUE; goto out; } parent_info = g_object_info_get_parent(info); field_info = find_field_info(info, name); if (field_info == NULL) { /* Try it on the parent GParamSpec for generic GParamSpec properties. */ field_info = find_field_info(parent_info, name); } if (field_info == NULL) { value_p.set(JSVAL_VOID); success = JS_TRUE; goto out; } type_info = g_field_info_get_type(field_info); if (!g_field_info_get_field(field_info, priv->gparam, &arg)) { gjs_throw(context, "Reading field %s.%s is not supported", g_base_info_get_name(info), g_base_info_get_name((GIBaseInfo*)field_info)); goto out; } if (!gjs_value_from_g_argument(context, value_p.address(), type_info, &arg, TRUE)) goto out; success = JS_TRUE; out: if (field_info != NULL) g_base_info_unref((GIBaseInfo*)field_info); if (type_info != NULL) g_base_info_unref((GIBaseInfo*)type_info); if (info != NULL) g_base_info_unref((GIBaseInfo*)info); if (parent_info != NULL) g_base_info_unref((GIBaseInfo*)parent_info); g_free(name); return success; } GJS_NATIVE_CONSTRUCTOR_DECLARE(param) { GJS_NATIVE_CONSTRUCTOR_VARIABLES(param) GJS_NATIVE_CONSTRUCTOR_PRELUDE(param); GJS_INC_COUNTER(param); GJS_NATIVE_CONSTRUCTOR_FINISH(param); return JS_TRUE; } static void param_finalize(JSFreeOp *fop, JSObject *obj) { Param *priv; priv = (Param*) JS_GetPrivate(obj); gjs_debug_lifecycle(GJS_DEBUG_GPARAM, "finalize, obj %p priv %p", obj, priv); if (priv == NULL) return; /* wrong class? */ if (priv->gparam) { g_param_spec_unref(priv->gparam); priv->gparam = NULL; } GJS_DEC_COUNTER(param); g_slice_free(Param, priv); } static JSBool param_new_internal(JSContext *cx, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(cx, vp); GParamSpec *pspec = NULL; JSBool ret = JS_FALSE; gchar *method_name; gchar *prop_name; JSObject *prop_gtype_jsobj; GType prop_gtype; GType prop_type; gchar *nick; gchar *blurb; GParamFlags flags; jsval foo; if (!gjs_parse_args(cx, "GObject.ParamSpec._new_internal", "!sossi", argc, argv, "prop_name", &prop_name, "prop_gtype", &prop_gtype_jsobj, "nick", &nick, "blurb", &blurb, "flags", &flags)) return JS_FALSE; prop_gtype = gjs_gtype_get_actual_gtype(cx, prop_gtype_jsobj); prop_type = G_TYPE_FUNDAMENTAL(prop_gtype); method_name = g_strdup_printf("GObject.ParamSpec.%s", g_type_name(prop_type)); argv += 5; argc -= 5; switch (prop_type) { case G_TYPE_UCHAR: case G_TYPE_CHAR: { gchar *minimum, *maximum, *default_value; if (!gjs_parse_args(cx, method_name, "sss", argc, argv, "minimum", &minimum, "maximum", &maximum, "default_value", &default_value)) goto out; if (prop_type == G_TYPE_CHAR) pspec = g_param_spec_char(prop_name, nick, blurb, minimum[0], maximum[0], default_value[0], flags); else pspec = g_param_spec_uchar(prop_name, nick, blurb, minimum[0], maximum[0], default_value[0], flags); g_free(minimum); g_free(maximum); g_free(default_value); } break; case G_TYPE_INT: case G_TYPE_UINT: case G_TYPE_LONG: case G_TYPE_ULONG: case G_TYPE_INT64: case G_TYPE_UINT64: { gint64 minimum, maximum, default_value; if (!gjs_parse_args(cx, method_name, "ttt", argc, argv, "minimum", &minimum, "maximum", &maximum, "default_value", &default_value)) goto out; switch (prop_type) { case G_TYPE_INT: pspec = g_param_spec_int(prop_name, nick, blurb, minimum, maximum, default_value, flags); break; case G_TYPE_UINT: pspec = g_param_spec_uint(prop_name, nick, blurb, minimum, maximum, default_value, flags); break; case G_TYPE_LONG: pspec = g_param_spec_long(prop_name, nick, blurb, minimum, maximum, default_value, flags); break; case G_TYPE_ULONG: pspec = g_param_spec_ulong(prop_name, nick, blurb, minimum, maximum, default_value, flags); break; case G_TYPE_INT64: pspec = g_param_spec_int64(prop_name, nick, blurb, minimum, maximum, default_value, flags); break; case G_TYPE_UINT64: pspec = g_param_spec_uint64(prop_name, nick, blurb, minimum, maximum, default_value, flags); break; } } break; case G_TYPE_BOOLEAN: { gboolean default_value; if (!gjs_parse_args(cx, method_name, "b", argc, argv, "default_value", &default_value)) goto out; default_value = JSVAL_TO_BOOLEAN(argv[0]); pspec = g_param_spec_boolean(prop_name, nick, blurb, default_value, flags); } break; case G_TYPE_ENUM: { GIEnumInfo *info; gint64 default_value; if (!gjs_parse_args(cx, method_name, "t", argc, argv, "default_value", &default_value)) goto out; info = g_irepository_find_by_gtype(g_irepository_get_default(), prop_gtype); if (!_gjs_enum_value_is_valid(cx, info, default_value)) goto out; pspec = g_param_spec_enum(prop_name, nick, blurb, prop_gtype, default_value, flags); } break; case G_TYPE_FLAGS: { gint64 default_value; if (!gjs_parse_args(cx, method_name, "t", argc, argv, "default_value", &default_value)) goto out; if (!_gjs_flags_value_is_valid(cx, prop_gtype, default_value)) goto out; pspec = g_param_spec_flags(prop_name, nick, blurb, prop_gtype, default_value, flags); } break; case G_TYPE_FLOAT: case G_TYPE_DOUBLE: { gfloat minimum, maximum, default_value; if (!gjs_parse_args(cx, "GObject.ParamSpec.float", "fff", argc, argv, "minimum", &minimum, "maximum", &maximum, "default_value", &default_value)) goto out; if (prop_type == G_TYPE_FLOAT) pspec = g_param_spec_float(prop_name, nick, blurb, minimum, maximum, default_value, flags); else pspec = g_param_spec_double(prop_name, nick, blurb, minimum, maximum, default_value, flags); } break; case G_TYPE_STRING: { gchar *default_value; if (!gjs_parse_args(cx, method_name, "s", argc, argv, "default_value", &default_value)) goto out; pspec = g_param_spec_string(prop_name, nick, blurb, default_value, flags); g_free (default_value); } break; case G_TYPE_PARAM: pspec = g_param_spec_param(prop_name, nick, blurb, prop_gtype, flags); break; case G_TYPE_BOXED: pspec = g_param_spec_boxed(prop_name, nick, blurb, prop_gtype, flags); break; case G_TYPE_POINTER: pspec = g_param_spec_pointer(prop_name, nick, blurb, flags); break; case G_TYPE_OBJECT: pspec = g_param_spec_object(prop_name, nick, blurb, prop_gtype, flags); break; default: gjs_throw(cx, "Could not create param spec for type '%s'", g_type_name(prop_gtype)); goto out; } ret = JS_TRUE; foo = OBJECT_TO_JSVAL(gjs_param_from_g_param(cx, pspec)); JS_SET_RVAL(cx, vp, foo); out: g_free(method_name); g_free(prop_name); g_free(nick); g_free(blurb); return ret; } /* The bizarre thing about this vtable is that it applies to both * instances of the object, and to the prototype that instances of the * class have. */ struct JSClass gjs_param_class = { "GObject_ParamSpec", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_DeletePropertyStub, param_get_prop, JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, param_finalize, NULL, NULL, NULL, NULL, NULL }; JSPropertySpec gjs_param_proto_props[] = { { NULL } }; JSFunctionSpec gjs_param_proto_funcs[] = { { NULL } }; static JSFunctionSpec gjs_param_constructor_funcs[] = { { "_new_internal", JSOP_WRAPPER((JSNative)param_new_internal), 0, 0 }, { NULL } }; static JSObject* gjs_lookup_param_prototype(JSContext *context) { JSObject *in_object; JSObject *constructor; jsid gobject_name; jsval value; gobject_name = gjs_intern_string_to_id(context, "GObject"); in_object = gjs_lookup_namespace_object_by_name(context, gobject_name); if (G_UNLIKELY (!in_object)) return NULL; if (!JS_GetProperty(context, in_object, "ParamSpec", &value)) return NULL; if (G_UNLIKELY (!JSVAL_IS_OBJECT(value) || JSVAL_IS_NULL(value))) return NULL; constructor = JSVAL_TO_OBJECT(value); g_assert(constructor != NULL); if (!gjs_object_get_property_const(context, constructor, GJS_STRING_PROTOTYPE, &value)) return NULL; if (G_UNLIKELY (!JSVAL_IS_OBJECT(value))) return NULL; return JSVAL_TO_OBJECT(value); } void gjs_define_param_class(JSContext *context, JSObject *in_object) { const char *constructor_name; JSObject *prototype; jsval value; JSObject *constructor; constructor_name = "ParamSpec"; if (!gjs_init_class_dynamic(context, in_object, NULL, "GObject", constructor_name, &gjs_param_class, gjs_param_constructor, 0, /* props of prototype */ &gjs_param_proto_props[0], /* funcs of prototype */ &gjs_param_proto_funcs[0], /* props of constructor, MyConstructor.myprop */ NULL, /* funcs of constructor, MyConstructor.myfunc() */ gjs_param_constructor_funcs, &prototype, &constructor)) { g_error("Can't init class %s", constructor_name); } value = OBJECT_TO_JSVAL(gjs_gtype_create_gtype_wrapper(context, G_TYPE_PARAM)); JS_DefineProperty(context, constructor, "$gtype", value, NULL, NULL, JSPROP_PERMANENT); gjs_debug(GJS_DEBUG_GPARAM, "Defined class %s prototype is %p class %p in object %p", constructor_name, prototype, JS_GetClass(prototype), in_object); } JSObject* gjs_param_from_g_param(JSContext *context, GParamSpec *gparam) { JSObject *obj; JSObject *proto; Param *priv; if (gparam == NULL) return NULL; gjs_debug(GJS_DEBUG_GPARAM, "Wrapping %s '%s' on %s with JSObject", g_type_name(G_TYPE_FROM_INSTANCE((GTypeInstance*) gparam)), gparam->name, g_type_name(gparam->owner_type)); proto = gjs_lookup_param_prototype(context); obj = JS_NewObjectWithGivenProto(context, JS_GetClass(proto), proto, gjs_get_import_global (context)); GJS_INC_COUNTER(param); priv = g_slice_new0(Param); JS_SetPrivate(obj, priv); priv->gparam = gparam; g_param_spec_ref (gparam); gjs_debug(GJS_DEBUG_GPARAM, "JSObject created with param instance %p type %s", priv->gparam, g_type_name(G_TYPE_FROM_INSTANCE((GTypeInstance*) priv->gparam))); return obj; } GParamSpec* gjs_g_param_from_param(JSContext *context, JSObject *obj) { Param *priv; if (obj == NULL) return NULL; priv = priv_from_js(context, obj); return priv->gparam; } JSBool gjs_typecheck_param(JSContext *context, JSObject *object, GType expected_type, JSBool throw_error) { Param *priv; JSBool result; if (!do_base_typecheck(context, object, throw_error)) return JS_FALSE; priv = priv_from_js(context, object); if (priv->gparam == NULL) { if (throw_error) { gjs_throw_custom(context, "TypeError", "Object is GObject.ParamSpec.prototype, not an object instance - " "cannot convert to a GObject.ParamSpec instance"); } return JS_FALSE; } if (expected_type != G_TYPE_NONE) result = g_type_is_a (G_TYPE_FROM_INSTANCE (priv->gparam), expected_type); else result = JS_TRUE; if (!result && throw_error) { gjs_throw_custom(context, "TypeError", "Object is of type %s - cannot convert to %s", g_type_name(G_TYPE_FROM_INSTANCE (priv->gparam)), g_type_name(expected_type)); } return result; } cjs-2.8.0/gi/value.h0000664000175000017500000000451112610172032013103 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_VALUE_H__ #define __GJS_VALUE_H__ #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS JSBool gjs_value_to_g_value (JSContext *context, jsval value, GValue *gvalue); JSBool gjs_value_to_g_value_no_copy (JSContext *context, jsval value, GValue *gvalue); JSBool gjs_value_from_g_value (JSContext *context, jsval *value_p, const GValue *gvalue); GClosure* gjs_closure_new_marshaled (JSContext *context, JSObject *callable, const char *description); GClosure* gjs_closure_new_for_signal (JSContext *context, JSObject *callable, const char *description, guint signal_id); G_END_DECLS #endif /* __GJS_VALUE_H__ */ cjs-2.8.0/gi/arg.h0000664000175000017500000001202312610172032012535 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_ARG_H__ #define __GJS_ARG_H__ #include #include "cjs/jsapi-util.h" #include G_BEGIN_DECLS /* Different roles for a GArgument */ typedef enum { GJS_ARGUMENT_ARGUMENT, GJS_ARGUMENT_RETURN_VALUE, GJS_ARGUMENT_FIELD, GJS_ARGUMENT_LIST_ELEMENT, GJS_ARGUMENT_HASH_ELEMENT, GJS_ARGUMENT_ARRAY_ELEMENT } GjsArgumentType; JSBool gjs_value_to_arg (JSContext *context, jsval value, GIArgInfo *arg_info, GArgument *arg); JSBool gjs_value_to_explicit_array (JSContext *context, jsval value, GIArgInfo *arg_info, GArgument *arg, gsize *length_p); void gjs_g_argument_init_default (JSContext *context, GITypeInfo *type_info, GArgument *arg); JSBool gjs_value_to_g_argument (JSContext *context, jsval value, GITypeInfo *type_info, const char *arg_name, GjsArgumentType argument_type, GITransfer transfer, gboolean may_be_null, GArgument *arg); JSBool gjs_value_from_g_argument (JSContext *context, jsval *value_p, GITypeInfo *type_info, GArgument *arg, gboolean copy_structs); JSBool gjs_value_from_explicit_array (JSContext *context, jsval *value_p, GITypeInfo *type_info, GArgument *arg, int length); JSBool gjs_g_argument_release (JSContext *context, GITransfer transfer, GITypeInfo *type_info, GArgument *arg); JSBool gjs_g_argument_release_out_array (JSContext *context, GITransfer transfer, GITypeInfo *type_info, guint length, GArgument *arg); JSBool gjs_g_argument_release_in_array (JSContext *context, GITransfer transfer, GITypeInfo *type_info, guint length, GArgument *arg); JSBool gjs_g_argument_release_in_arg (JSContext *context, GITransfer transfer, GITypeInfo *type_info, GArgument *arg); JSBool _gjs_flags_value_is_valid (JSContext *context, GType gtype, gint64 value); JSBool _gjs_enum_value_is_valid (JSContext *context, GIEnumInfo *enum_info, gint64 value); gint64 _gjs_enum_from_int (GIEnumInfo *enum_info, int int_value); JSBool gjs_array_from_strv (JSContext *context, jsval *value_p, const char **strv); JSBool gjs_array_to_strv (JSContext *context, jsval array_value, unsigned int length, void **arr_p); G_END_DECLS #endif /* __GJS_ARG_H__ */ cjs-2.8.0/gi/enumeration.cpp0000664000175000017500000001641012610172032014651 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include "repo.h" #include "gtype.h" #include "function.h" #include #include #include "enumeration.h" JSObject* gjs_lookup_enumeration(JSContext *context, GIEnumInfo *info) { JSObject *in_object; const char *enum_name; jsval value; in_object = gjs_lookup_namespace_object(context, (GIBaseInfo*) info); if (G_UNLIKELY (!in_object)) return NULL; enum_name = g_base_info_get_name((GIBaseInfo*) info); if (!JS_GetProperty(context, in_object, enum_name, &value)) return NULL; if (G_UNLIKELY (!JSVAL_IS_OBJECT(value))) return NULL; return JSVAL_TO_OBJECT(value); } static JSBool gjs_define_enum_value(JSContext *context, JSObject *in_object, GIValueInfo *info) { const char *value_name; char *fixed_name; gsize i; gint64 value_val; jsval value_js; value_name = g_base_info_get_name( (GIBaseInfo*) info); value_val = g_value_info_get_value(info); /* g-i converts enum members such as GDK_GRAVITY_SOUTH_WEST to * Gdk.GravityType.south-west (where 'south-west' is value_name) * Convert back to all SOUTH_WEST. */ fixed_name = g_ascii_strup(value_name, -1); for (i = 0; fixed_name[i]; ++i) { char c = fixed_name[i]; if (!(('A' <= c && c <= 'Z') || ('0' <= c && c <= '9'))) fixed_name[i] = '_'; } gjs_debug(GJS_DEBUG_GENUM, "Defining enum value %s (fixed from %s) %" G_GINT64_MODIFIER "d", fixed_name, value_name, value_val); if (!JS_NewNumberValue(context, value_val, &value_js) || !JS_DefineProperty(context, in_object, fixed_name, value_js, NULL, NULL, GJS_MODULE_PROP_FLAGS)) { gjs_throw(context, "Unable to define enumeration value %s %" G_GINT64_FORMAT " (no memory most likely)", fixed_name, value_val); g_free(fixed_name); return JS_FALSE; } g_free(fixed_name); return JS_TRUE; } JSBool gjs_define_enum_values(JSContext *context, JSObject *in_object, GIEnumInfo *info) { GType gtype; int i, n_values; jsval value; /* Fill in enum values first, so we don't define the enum itself until we're * sure we can finish successfully. */ n_values = g_enum_info_get_n_values(info); for (i = 0; i < n_values; ++i) { GIValueInfo *value_info = g_enum_info_get_value(info, i); gboolean failed; failed = !gjs_define_enum_value(context, in_object, value_info); g_base_info_unref( (GIBaseInfo*) value_info); if (failed) { return JS_FALSE; } } gtype = g_registered_type_info_get_g_type((GIRegisteredTypeInfo*)info); value = OBJECT_TO_JSVAL(gjs_gtype_create_gtype_wrapper(context, gtype)); JS_DefineProperty(context, in_object, "$gtype", value, NULL, NULL, JSPROP_PERMANENT); return JS_TRUE; } JSBool gjs_define_enum_static_methods(JSContext *context, JSObject *constructor, GIEnumInfo *enum_info) { int i, n_methods; n_methods = g_enum_info_get_n_methods(enum_info); for (i = 0; i < n_methods; i++) { GIFunctionInfo *meth_info; GIFunctionInfoFlags flags; meth_info = g_enum_info_get_method(enum_info, i); flags = g_function_info_get_flags(meth_info); g_warn_if_fail(!(flags & GI_FUNCTION_IS_METHOD)); /* Anything that isn't a method we put on the prototype of the * constructor. This includes introspection * methods, as well as the forthcoming "static methods" * support. We may want to change this to use * GI_FUNCTION_IS_CONSTRUCTOR and GI_FUNCTION_IS_STATIC or the * like in the near future. */ if (!(flags & GI_FUNCTION_IS_METHOD)) { gjs_define_function(context, constructor, G_TYPE_NONE, (GICallableInfo *)meth_info); } g_base_info_unref((GIBaseInfo*) meth_info); } return JS_TRUE; } JSBool gjs_define_enumeration(JSContext *context, JSObject *in_object, GIEnumInfo *info) { const char *enum_name; JSObject *enum_obj; /* An enumeration is simply an object containing integer attributes for * each enum value. It does not have a special JSClass. * * We could make this more typesafe and also print enum values as strings * if we created a class for each enum and made the enum values instances * of that class. However, it would have a lot more overhead and just * be more complicated in general. I think this is fine. */ enum_name = g_base_info_get_name( (GIBaseInfo*) info); enum_obj = JS_NewObject(context, NULL, NULL, gjs_get_import_global (context)); if (enum_obj == NULL) { g_error("Could not create enumeration %s.%s", g_base_info_get_namespace( (GIBaseInfo*) info), enum_name); } /* https://bugzilla.mozilla.org/show_bug.cgi?id=599651 means we * can't just pass in the global as the parent */ JS_SetParent(context, enum_obj, gjs_get_import_global (context)); if (!gjs_define_enum_values(context, enum_obj, info)) return JS_FALSE; gjs_define_enum_static_methods (context, enum_obj, info); gjs_debug(GJS_DEBUG_GENUM, "Defining %s.%s as %p", g_base_info_get_namespace( (GIBaseInfo*) info), enum_name, enum_obj); if (!JS_DefineProperty(context, in_object, enum_name, OBJECT_TO_JSVAL(enum_obj), NULL, NULL, GJS_MODULE_PROP_FLAGS)) { gjs_throw(context, "Unable to define enumeration property (no memory most likely)"); return JS_FALSE; } return JS_TRUE; } cjs-2.8.0/gi/ns.h0000664000175000017500000000265012610172032012411 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_NS_H__ #define __GJS_NS_H__ #include #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS JSObject* gjs_create_ns(JSContext *context, const char *ns_name); G_END_DECLS #endif /* __GJS_NS_H__ */ cjs-2.8.0/gi/interface.h0000664000175000017500000000307212610172032013730 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * Copyright (c) 2012 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_INTERFACE_H__ #define __GJS_INTERFACE_H__ #include #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS JSBool gjs_define_interface_class (JSContext *context, JSObject *in_object, GIInterfaceInfo *info); G_END_DECLS #endif /* __GJS_INTERFACE_H__ */ cjs-2.8.0/gi/param.h0000664000175000017500000000367212610172032013076 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_PARAM_H__ #define __GJS_PARAM_H__ #include #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS void gjs_define_param_class (JSContext *context, JSObject *in_object); GParamSpec* gjs_g_param_from_param (JSContext *context, JSObject *obj); JSObject* gjs_param_from_g_param (JSContext *context, GParamSpec *param); JSBool gjs_typecheck_param (JSContext *context, JSObject *obj, GType expected_type, JSBool throw_error); G_END_DECLS #endif /* __GJS_PARAM_H__ */ cjs-2.8.0/gi/repo.h0000664000175000017500000000533312610172032012737 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_REPO_H__ #define __GJS_REPO_H__ #include #include #include G_BEGIN_DECLS JSBool gjs_define_repo (JSContext *context, JSObject **module_out, const char *name); const char* gjs_info_type_name (GIInfoType type); JSObject* gjs_lookup_private_namespace (JSContext *context); JSObject* gjs_lookup_namespace_object (JSContext *context, GIBaseInfo *info); JSObject* gjs_lookup_namespace_object_by_name (JSContext *context, jsid name); JSObject* gjs_lookup_function_object (JSContext *context, GIFunctionInfo *info); JSObject * gjs_lookup_generic_prototype (JSContext *context, GIBaseInfo *info); JSBool gjs_define_info (JSContext *context, JSObject *in_object, GIBaseInfo *info); char* gjs_camel_from_hyphen (const char *hyphen_name); char* gjs_hyphen_from_camel (const char *camel_name); #if GJS_VERBOSE_ENABLE_GI_USAGE void _gjs_log_info_usage(GIBaseInfo *info); #endif G_END_DECLS #endif /* __GJS_REPO_H__ */ cjs-2.8.0/gi/gjs_gi_trace.h0000664000175000017500000000316512610172032014413 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2010 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. * * Author: Colin Walters */ #ifndef __GJS_TRACE_H__ #define __GJS_TRACE_H__ #ifndef GETTEXT_PACKAGE #error "config.h must be included prior to gjs_trace.h" #endif #ifdef HAVE_DTRACE /* include the generated probes header and put markers in code */ #include "gjs_gi_probes.h" #define TRACE(probe) probe #else /* Wrap the probe to allow it to be removed when no systemtap available */ #define TRACE(probe) #endif #endif /* __GJS_TRACE_H__ */ cjs-2.8.0/gi/union.cpp0000664000175000017500000003761112610172032013461 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include /* include first for logging related #define used in repo.h */ #include #include "union.h" #include "arg.h" #include "object.h" #include #include #include "repo.h" #include "proxyutils.h" #include "function.h" #include "gtype.h" #include typedef struct { GIUnionInfo *info; void *gboxed; /* NULL if we are the prototype and not an instance */ GType gtype; } Union; extern struct JSClass gjs_union_class; GJS_DEFINE_PRIV_FROM_JS(Union, gjs_union_class) /* * Like JSResolveOp, but flags provide contextual information as follows: * * JSRESOLVE_QUALIFIED a qualified property id: obj.id or obj[id], not id * JSRESOLVE_ASSIGNING obj[id] is on the left-hand side of an assignment * JSRESOLVE_DETECTING 'if (o.p)...' or similar detection opcode sequence * JSRESOLVE_DECLARING var, const, or boxed prolog declaration opcode * JSRESOLVE_CLASSNAME class name used when constructing * * The *objp out parameter, on success, should be null to indicate that id * was not resolved; and non-null, referring to obj or one of its prototypes, * if id was resolved. */ static JSBool union_new_resolve(JSContext *context, JSObject **obj, jsid *id, unsigned flags, JSObject **objp) { Union *priv; char *name; JSBool ret = JS_TRUE; *objp = NULL; if (!gjs_get_string_id(context, *id, &name)) return JS_TRUE; /* not resolved, but no error */ priv = priv_from_js(context, *obj); gjs_debug_jsprop(GJS_DEBUG_GBOXED, "Resolve prop '%s' hook obj %p priv %p", name, *obj, priv); if (priv == NULL) { ret = JS_FALSE; /* wrong class */ goto out; } if (priv->gboxed == NULL) { /* We are the prototype, so look for methods and other class properties */ GIFunctionInfo *method_info; method_info = g_union_info_find_method((GIUnionInfo*) priv->info, name); if (method_info != NULL) { JSObject *union_proto; const char *method_name; #if GJS_VERBOSE_ENABLE_GI_USAGE _gjs_log_info_usage((GIBaseInfo*) method_info); #endif method_name = g_base_info_get_name( (GIBaseInfo*) method_info); gjs_debug(GJS_DEBUG_GBOXED, "Defining method %s in prototype for %s.%s", method_name, g_base_info_get_namespace( (GIBaseInfo*) priv->info), g_base_info_get_name( (GIBaseInfo*) priv->info)); union_proto = *obj; if (gjs_define_function(context, union_proto, g_registered_type_info_get_g_type(priv->info), method_info) == NULL) { g_base_info_unref( (GIBaseInfo*) method_info); ret = JS_FALSE; goto out; } *objp = union_proto; /* we defined the prop in object_proto */ g_base_info_unref( (GIBaseInfo*) method_info); } } else { /* We are an instance, not a prototype, so look for * per-instance props that we want to define on the * JSObject. Generally we do not want to cache these in JS, we * want to always pull them from the C object, or JS would not * see any changes made from C. So we use the get/set prop * hooks, not this resolve hook. */ } out: g_free(name); return ret; } static void* union_new(JSContext *context, JSObject *obj, /* "this" for constructor */ GIUnionInfo *info) { int n_methods; int i; /* Find a zero-args constructor and call it */ n_methods = g_union_info_get_n_methods(info); for (i = 0; i < n_methods; ++i) { GIFunctionInfo *func_info; GIFunctionInfoFlags flags; func_info = g_union_info_get_method(info, i); flags = g_function_info_get_flags(func_info); if ((flags & GI_FUNCTION_IS_CONSTRUCTOR) != 0 && g_callable_info_get_n_args((GICallableInfo*) func_info) == 0) { jsval rval; rval = JSVAL_NULL; gjs_invoke_c_function_uncached(context, func_info, obj, 0, NULL, &rval); g_base_info_unref((GIBaseInfo*) func_info); /* We are somewhat wasteful here; invoke_c_function() above * creates a JSObject wrapper for the union that we immediately * discard. */ if (JSVAL_IS_NULL(rval)) return NULL; else return gjs_c_union_from_union(context, JSVAL_TO_OBJECT(rval)); } g_base_info_unref((GIBaseInfo*) func_info); } gjs_throw(context, "Unable to construct union type %s since it has no zero-args , can only wrap an existing one", g_base_info_get_name((GIBaseInfo*) info)); return NULL; } GJS_NATIVE_CONSTRUCTOR_DECLARE(union) { GJS_NATIVE_CONSTRUCTOR_VARIABLES(union) Union *priv; Union *proto_priv; JSObject *proto; void *gboxed; GJS_NATIVE_CONSTRUCTOR_PRELUDE(union); priv = g_slice_new0(Union); GJS_INC_COUNTER(boxed); g_assert(priv_from_js(context, object) == NULL); JS_SetPrivate(object, priv); gjs_debug_lifecycle(GJS_DEBUG_GBOXED, "union constructor, obj %p priv %p", object, priv); JS_GetPrototype(context, object, &proto); gjs_debug_lifecycle(GJS_DEBUG_GBOXED, "union instance __proto__ is %p", proto); /* If we're the prototype, then post-construct we'll fill in priv->info. * If we are not the prototype, though, then we'll get ->info from the * prototype and then create a GObject if we don't have one already. */ proto_priv = priv_from_js(context, proto); if (proto_priv == NULL) { gjs_debug(GJS_DEBUG_GBOXED, "Bad prototype set on union? Must match JSClass of object. JS error should have been reported."); return JS_FALSE; } priv->info = proto_priv->info; g_base_info_ref( (GIBaseInfo*) priv->info); priv->gtype = proto_priv->gtype; /* union_new happens to be implemented by calling * gjs_invoke_c_function(), which returns a jsval. * The returned "gboxed" here is owned by that jsval, * not by us. */ gboxed = union_new(context, object, priv->info); if (gboxed == NULL) { return JS_FALSE; } /* Because "gboxed" is owned by a jsval and will * be garbage collected, we make a copy here to be * owned by us. */ priv->gboxed = g_boxed_copy(priv->gtype, gboxed); gjs_debug_lifecycle(GJS_DEBUG_GBOXED, "JSObject created with union instance %p type %s", priv->gboxed, g_type_name(priv->gtype)); GJS_NATIVE_CONSTRUCTOR_FINISH(union); return JS_TRUE; } static void union_finalize(JSFreeOp *fop, JSObject *obj) { Union *priv; priv = (Union*) JS_GetPrivate(obj); gjs_debug_lifecycle(GJS_DEBUG_GBOXED, "finalize, obj %p priv %p", obj, priv); if (priv == NULL) return; /* wrong class? */ if (priv->gboxed) { g_boxed_free(g_registered_type_info_get_g_type( (GIRegisteredTypeInfo*) priv->info), priv->gboxed); priv->gboxed = NULL; } if (priv->info) { g_base_info_unref( (GIBaseInfo*) priv->info); priv->info = NULL; } GJS_DEC_COUNTER(boxed); g_slice_free(Union, priv); } static JSBool to_string_func(JSContext *context, unsigned argc, jsval *vp) { JSObject *obj = JS_THIS_OBJECT(context, vp); Union *priv; JSBool ret = JS_FALSE; jsval retval; if (!priv_from_js_with_typecheck(context, obj, &priv)) goto out; if (!_gjs_proxy_to_string_func(context, obj, "union", (GIBaseInfo*)priv->info, priv->gtype, priv->gboxed, &retval)) goto out; ret = JS_TRUE; JS_SET_RVAL(context, vp, retval); out: return ret; } /* The bizarre thing about this vtable is that it applies to both * instances of the object, and to the prototype that instances of the * class have. */ struct JSClass gjs_union_class = { "GObject_Union", JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE, JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, (JSResolveOp) union_new_resolve, /* needs cast since it's the new resolve signature */ JS_ConvertStub, union_finalize, NULL, NULL, NULL, NULL, NULL }; JSPropertySpec gjs_union_proto_props[] = { { NULL } }; JSFunctionSpec gjs_union_proto_funcs[] = { { "toString", JSOP_WRAPPER((JSNative)to_string_func), 0, 0 }, { NULL } }; JSBool gjs_define_union_class(JSContext *context, JSObject *in_object, GIUnionInfo *info) { const char *constructor_name; JSObject *prototype; jsval value; Union *priv; GType gtype; JSObject *constructor; /* For certain unions, we may be able to relax this in the future by * directly allocating union memory, as we do for structures in boxed.c */ gtype = g_registered_type_info_get_g_type( (GIRegisteredTypeInfo*) info); if (gtype == G_TYPE_NONE) { gjs_throw(context, "Unions must currently be registered as boxed types"); return JS_FALSE; } /* See the comment in gjs_define_object_class() for an * explanation of how this all works; Union is pretty much the * same as Object. */ constructor_name = g_base_info_get_name( (GIBaseInfo*) info); if (!gjs_init_class_dynamic(context, in_object, NULL, g_base_info_get_namespace( (GIBaseInfo*) info), constructor_name, &gjs_union_class, gjs_union_constructor, 0, /* props of prototype */ &gjs_union_proto_props[0], /* funcs of prototype */ &gjs_union_proto_funcs[0], /* props of constructor, MyConstructor.myprop */ NULL, /* funcs of constructor, MyConstructor.myfunc() */ NULL, &prototype, &constructor)) { g_error("Can't init class %s", constructor_name); } GJS_INC_COUNTER(boxed); priv = g_slice_new0(Union); priv->info = info; g_base_info_ref( (GIBaseInfo*) priv->info); priv->gtype = gtype; JS_SetPrivate(prototype, priv); gjs_debug(GJS_DEBUG_GBOXED, "Defined class %s prototype is %p class %p in object %p", constructor_name, prototype, JS_GetClass(prototype), in_object); value = OBJECT_TO_JSVAL(gjs_gtype_create_gtype_wrapper(context, gtype)); JS_DefineProperty(context, constructor, "$gtype", value, NULL, NULL, JSPROP_PERMANENT); return JS_TRUE; } JSObject* gjs_union_from_c_union(JSContext *context, GIUnionInfo *info, void *gboxed) { JSObject *obj; JSObject *proto; Union *priv; GType gtype; if (gboxed == NULL) return NULL; /* For certain unions, we may be able to relax this in the future by * directly allocating union memory, as we do for structures in boxed.c */ gtype = g_registered_type_info_get_g_type( (GIRegisteredTypeInfo*) info); if (gtype == G_TYPE_NONE) { gjs_throw(context, "Unions must currently be registered as boxed types"); return NULL; } gjs_debug_marshal(GJS_DEBUG_GBOXED, "Wrapping union %s %p with JSObject", g_base_info_get_name((GIBaseInfo *)info), gboxed); proto = gjs_lookup_generic_prototype(context, (GIUnionInfo*) info); obj = JS_NewObjectWithGivenProto(context, JS_GetClass(proto), proto, gjs_get_import_global (context)); GJS_INC_COUNTER(boxed); priv = g_slice_new0(Union); JS_SetPrivate(obj, priv); priv->info = info; g_base_info_ref( (GIBaseInfo *) priv->info); priv->gtype = gtype; priv->gboxed = g_boxed_copy(gtype, gboxed); return obj; } void* gjs_c_union_from_union(JSContext *context, JSObject *obj) { Union *priv; if (obj == NULL) return NULL; priv = priv_from_js(context, obj); return priv->gboxed; } JSBool gjs_typecheck_union(JSContext *context, JSObject *object, GIStructInfo *expected_info, GType expected_type, JSBool throw_error) { Union *priv; JSBool result; if (!do_base_typecheck(context, object, throw_error)) return JS_FALSE; priv = priv_from_js(context, object); if (priv->gboxed == NULL) { if (throw_error) { gjs_throw_custom(context, "TypeError", "Object is %s.%s.prototype, not an object instance - cannot convert to a union instance", g_base_info_get_namespace( (GIBaseInfo*) priv->info), g_base_info_get_name( (GIBaseInfo*) priv->info)); } return JS_FALSE; } if (expected_type != G_TYPE_NONE) result = g_type_is_a (priv->gtype, expected_type); else if (expected_info != NULL) result = g_base_info_equal((GIBaseInfo*) priv->info, (GIBaseInfo*) expected_info); else result = JS_TRUE; if (!result && throw_error) { if (expected_info != NULL) { gjs_throw_custom(context, "TypeError", "Object is of type %s.%s - cannot convert to %s.%s", g_base_info_get_namespace((GIBaseInfo*) priv->info), g_base_info_get_name((GIBaseInfo*) priv->info), g_base_info_get_namespace((GIBaseInfo*) expected_info), g_base_info_get_name((GIBaseInfo*) expected_info)); } else { gjs_throw_custom(context, "TypeError", "Object is of type %s.%s - cannot convert to %s", g_base_info_get_namespace((GIBaseInfo*) priv->info), g_base_info_get_name((GIBaseInfo*) priv->info), g_type_name(expected_type)); } } return result; } cjs-2.8.0/gi/gtype.h0000664000175000017500000000413512610172032013121 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * Copyright (c) 2012 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_GTYPE_H__ #define __GJS_GTYPE_H__ #include #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS jsval gjs_gtype_create_proto (JSContext *context, JSObject *module, const char *proto_name, JSObject *parent); JSObject * gjs_gtype_create_gtype_wrapper (JSContext *context, GType gtype); GType gjs_gtype_get_actual_gtype (JSContext *context, JSObject *object); JSBool gjs_typecheck_gtype (JSContext *context, JSObject *obj, JSBool throw_error); G_END_DECLS #endif /* __GJS_INTERFACE_H__ */ cjs-2.8.0/gi/ns.cpp0000664000175000017500000001657512610172032012757 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "ns.h" #include "repo.h" #include "param.h" #include #include #include #include #include typedef struct { char *gi_namespace; } Ns; extern struct JSClass gjs_ns_class; GJS_DEFINE_PRIV_FROM_JS(Ns, gjs_ns_class) /* * Like JSResolveOp, but flags provide contextual information as follows: * * JSRESOLVE_QUALIFIED a qualified property id: obj.id or obj[id], not id * JSRESOLVE_ASSIGNING obj[id] is on the left-hand side of an assignment * JSRESOLVE_DETECTING 'if (o.p)...' or similar detection opcode sequence * JSRESOLVE_DECLARING var, const, or function prolog declaration opcode * JSRESOLVE_CLASSNAME class name used when constructing * * The *objp out parameter, on success, should be null to indicate that id * was not resolved; and non-null, referring to obj or one of its prototypes, * if id was resolved. */ static JSBool ns_new_resolve(JSContext *context, JSObject **obj, jsid *id, unsigned flags, JSObject **objp) { Ns *priv; char *name; GIRepository *repo; GIBaseInfo *info; JSBool ret = JS_FALSE; *objp = NULL; if (!gjs_get_string_id(context, *id, &name)) return JS_TRUE; /* not resolved, but no error */ /* let Object.prototype resolve these */ if (strcmp(name, "valueOf") == 0 || strcmp(name, "toString") == 0) { ret = JS_TRUE; goto out; } priv = priv_from_js(context, *obj); gjs_debug_jsprop(GJS_DEBUG_GNAMESPACE, "Resolve prop '%s' hook obj %p priv %p", name, *obj, priv); if (priv == NULL) { ret = JS_TRUE; /* we are the prototype, or have the wrong class */ goto out; } JS_BeginRequest(context); repo = g_irepository_get_default(); info = g_irepository_find_by_name(repo, priv->gi_namespace, name); if (info == NULL) { /* No property defined, but no error either, so return TRUE */ JS_EndRequest(context); ret = JS_TRUE; goto out; } gjs_debug(GJS_DEBUG_GNAMESPACE, "Found info type %s for '%s' in namespace '%s'", gjs_info_type_name(g_base_info_get_type(info)), g_base_info_get_name(info), g_base_info_get_namespace(info)); if (gjs_define_info(context, *obj, info)) { g_base_info_unref(info); *objp = *obj; /* we defined the property in this object */ ret = JS_TRUE; } else { gjs_debug(GJS_DEBUG_GNAMESPACE, "Failed to define info '%s'", g_base_info_get_name(info)); g_base_info_unref(info); } JS_EndRequest(context); out: g_free(name); return ret; } GJS_NATIVE_CONSTRUCTOR_DEFINE_ABSTRACT(ns) static void ns_finalize(JSFreeOp *fop, JSObject *obj) { Ns *priv; priv = (Ns *)JS_GetPrivate(obj); gjs_debug_lifecycle(GJS_DEBUG_GNAMESPACE, "finalize, obj %p priv %p", obj, priv); if (priv == NULL) return; /* we are the prototype, not a real instance */ if (priv->gi_namespace) g_free(priv->gi_namespace); GJS_DEC_COUNTER(ns); g_slice_free(Ns, priv); } /* The bizarre thing about this vtable is that it applies to both * instances of the object, and to the prototype that instances of the * class have. */ struct JSClass gjs_ns_class = { "GIRepositoryNamespace", JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE, JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, (JSResolveOp) ns_new_resolve, /* needs cast since it's the new resolve signature */ JS_ConvertStub, ns_finalize, JSCLASS_NO_OPTIONAL_MEMBERS }; JSPropertySpec gjs_ns_proto_props[] = { { NULL } }; JSFunctionSpec gjs_ns_proto_funcs[] = { { NULL } }; static JSObject* ns_new(JSContext *context, const char *ns_name) { JSObject *ns; JSObject *global; Ns *priv; JSBool found; /* put constructor in the global namespace */ global = gjs_get_import_global(context); if (!JS_HasProperty(context, global, gjs_ns_class.name, &found)) return JS_FALSE; if (!found) { JSObject *prototype; prototype = JS_InitClass(context, global, /* parent prototype JSObject* for * prototype; NULL for * Object.prototype */ NULL, &gjs_ns_class, /* constructor for instances (NULL for * none - just name the prototype like * Math - rarely correct) */ gjs_ns_constructor, /* number of constructor args */ 0, /* props of prototype */ &gjs_ns_proto_props[0], /* funcs of prototype */ &gjs_ns_proto_funcs[0], /* props of constructor, MyConstructor.myprop */ NULL, /* funcs of constructor, MyConstructor.myfunc() */ NULL); if (prototype == NULL) g_error("Can't init class %s", gjs_ns_class.name); gjs_debug(GJS_DEBUG_GNAMESPACE, "Initialized class %s prototype %p", gjs_ns_class.name, prototype); } ns = JS_NewObject(context, &gjs_ns_class, NULL, global); if (ns == NULL) g_error("No memory to create ns object"); priv = g_slice_new0(Ns); GJS_INC_COUNTER(ns); g_assert(priv_from_js(context, ns) == NULL); JS_SetPrivate(ns, priv); gjs_debug_lifecycle(GJS_DEBUG_GNAMESPACE, "ns constructor, obj %p priv %p", ns, priv); priv = priv_from_js(context, ns); priv->gi_namespace = g_strdup(ns_name); return ns; } JSObject* gjs_create_ns(JSContext *context, const char *ns_name) { return ns_new(context, ns_name); } cjs-2.8.0/gi/gjs_gi_probes.d0000664000175000017500000000021112610172032014570 0ustar fabiofabioprovider gjs { probe object__proxy__new(void*, void*, char *, char *); probe object__proxy__finalize(void*, void*, char *, char *); }; cjs-2.8.0/gi/repo.cpp0000664000175000017500000005760712610172032013305 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "repo.h" #include "ns.h" #include "function.h" #include "object.h" #include "param.h" #include "boxed.h" #include "union.h" #include "enumeration.h" #include "arg.h" #include "foreign.h" #include "fundamental.h" #include "interface.h" #include "gerror.h" #include #include #include #include #include #include typedef struct { void *dummy; } Repo; extern struct JSClass gjs_repo_class; GJS_DEFINE_PRIV_FROM_JS(Repo, gjs_repo_class) static JSObject * lookup_override_function(JSContext *, jsid); static JSBool get_version_for_ns (JSContext *context, JSObject *repo_obj, jsid ns_id, char **version) { jsid versions_name; jsval versions_val; JSObject *versions; jsval version_val; versions_name = gjs_context_get_const_string(context, GJS_STRING_GI_VERSIONS); if (!gjs_object_require_property(context, repo_obj, "GI repository object", versions_name, &versions_val) || !JSVAL_IS_OBJECT(versions_val)) { gjs_throw(context, "No 'versions' property in GI repository object"); return JS_FALSE; } versions = JSVAL_TO_OBJECT(versions_val); *version = NULL; if (JS_GetPropertyById(context, versions, ns_id, &version_val) && JSVAL_IS_STRING(version_val)) { gjs_string_to_utf8(context, version_val, version); } return JS_TRUE; } static JSBool resolve_namespace_object(JSContext *context, JSObject *repo_obj, jsid ns_id, const char *ns_name) { GIRepository *repo; GError *error; char *version; JSObject *override; jsval result; JSObject *gi_namespace = NULL; JSBool ret = JS_FALSE; char *check_name = NULL; if (g_strcmp0 (ns_name, "GMenu") == 0) { check_name = g_strdup ("CMenu"); } else { check_name = g_strdup (ns_name); } JS_BeginRequest(context); if (!get_version_for_ns(context, repo_obj, ns_id, &version)) goto out; repo = g_irepository_get_default(); error = NULL; g_irepository_require(repo, check_name, version, (GIRepositoryLoadFlags) 0, &error); if (error != NULL) { gjs_throw(context, "Requiring %s, version %s: %s", check_name, version?version:"none", error->message); g_error_free(error); g_free(version); goto out; } g_free(version); /* Defines a property on "obj" (the javascript repo object) * with the given namespace name, pointing to that namespace * in the repo. */ gi_namespace = gjs_create_ns(context, check_name); JS_AddObjectRoot(context, &gi_namespace); /* Define the property early, to avoid reentrancy issues if the override module looks for namespaces that import this */ if (!JS_DefineProperty(context, repo_obj, ns_name, OBJECT_TO_JSVAL(gi_namespace), NULL, NULL, GJS_MODULE_PROP_FLAGS)) g_error("no memory to define ns property"); override = lookup_override_function(context, ns_id); if (override && !JS_CallFunctionValue (context, gi_namespace, /* thisp */ OBJECT_TO_JSVAL(override), /* callee */ 0, /* argc */ NULL, /* argv */ &result)) goto out; gjs_debug(GJS_DEBUG_GNAMESPACE, "Defined namespace '%s' %p in GIRepository %p", ns_name, gi_namespace, repo_obj); ret = JS_TRUE; gjs_schedule_gc_if_needed(context); out: if (gi_namespace) JS_RemoveObjectRoot(context, &gi_namespace); JS_EndRequest(context); g_free (check_name); return ret; } /* * Like JSResolveOp, but flags provide contextual information as follows: * * JSRESOLVE_QUALIFIED a qualified property id: obj.id or obj[id], not id * JSRESOLVE_ASSIGNING obj[id] is on the left-hand side of an assignment * JSRESOLVE_DETECTING 'if (o.p)...' or similar detection opcode sequence * JSRESOLVE_DECLARING var, const, or function prolog declaration opcode * JSRESOLVE_CLASSNAME class name used when constructing * * The *objp out parameter, on success, should be null to indicate that id * was not resolved; and non-null, referring to obj or one of its prototypes, * if id was resolved. */ static JSBool repo_new_resolve(JSContext *context, JSObject **obj, jsid *id, unsigned flags, JSObject **objp) { Repo *priv; char *name; JSBool ret = JS_TRUE; *objp = NULL; if (!gjs_get_string_id(context, *id, &name)) return JS_TRUE; /* not resolved, but no error */ /* let Object.prototype resolve these */ if (strcmp(name, "valueOf") == 0 || strcmp(name, "toString") == 0) goto out; priv = priv_from_js(context, *obj); gjs_debug_jsprop(GJS_DEBUG_GREPO, "Resolve prop '%s' hook obj %p priv %p", name, obj, priv); if (priv == NULL) /* we are the prototype, or have the wrong class */ goto out; if (!resolve_namespace_object(context, *obj, *id, name)) { ret = JS_FALSE; } else { *objp = *obj; /* store the object we defined the prop in */ } out: g_free(name); return ret; } GJS_NATIVE_CONSTRUCTOR_DEFINE_ABSTRACT(repo) static void repo_finalize(JSFreeOp *fop, JSObject *obj) { Repo *priv; priv = (Repo*) JS_GetPrivate(obj); gjs_debug_lifecycle(GJS_DEBUG_GREPO, "finalize, obj %p priv %p", obj, priv); if (priv == NULL) return; /* we are the prototype, not a real instance */ GJS_DEC_COUNTER(repo); g_slice_free(Repo, priv); } /* The bizarre thing about this vtable is that it applies to both * instances of the object, and to the prototype that instances of the * class have. */ struct JSClass gjs_repo_class = { "GIRepository", /* means "new GIRepository()" works */ JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE, JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, (JSResolveOp) repo_new_resolve, /* needs cast since it's the new resolve signature */ JS_ConvertStub, repo_finalize, JSCLASS_NO_OPTIONAL_MEMBERS }; JSPropertySpec gjs_repo_proto_props[] = { { NULL } }; JSFunctionSpec gjs_repo_proto_funcs[] = { { NULL } }; static JSObject* repo_new(JSContext *context) { Repo *priv; JSObject *repo; JSObject *global; JSObject *versions; JSObject *private_ns; JSBool found; jsid versions_name, private_ns_name; global = gjs_get_import_global(context); if (!JS_HasProperty(context, global, gjs_repo_class.name, &found)) return NULL; if (!found) { JSObject *prototype; prototype = JS_InitClass(context, global, /* parent prototype JSObject* for * prototype; NULL for * Object.prototype */ NULL, &gjs_repo_class, /* constructor for instances (NULL for * none - just name the prototype like * Math - rarely correct) */ gjs_repo_constructor, /* number of constructor args */ 0, /* props of prototype */ &gjs_repo_proto_props[0], /* funcs of prototype */ &gjs_repo_proto_funcs[0], /* props of constructor, MyConstructor.myprop */ NULL, /* funcs of constructor, MyConstructor.myfunc() */ NULL); if (prototype == NULL) g_error("Can't init class %s", gjs_repo_class.name); gjs_debug(GJS_DEBUG_GREPO, "Initialized class %s prototype %p", gjs_repo_class.name, prototype); } repo = JS_NewObject(context, &gjs_repo_class, NULL, global); if (repo == NULL) { gjs_throw(context, "No memory to create repo object"); return JS_FALSE; } priv = g_slice_new0(Repo); GJS_INC_COUNTER(repo); g_assert(priv_from_js(context, repo) == NULL); JS_SetPrivate(repo, priv); gjs_debug_lifecycle(GJS_DEBUG_GREPO, "repo constructor, obj %p priv %p", repo, priv); versions = JS_NewObject(context, NULL, NULL, global); versions_name = gjs_context_get_const_string(context, GJS_STRING_GI_VERSIONS); JS_DefinePropertyById(context, repo, versions_name, OBJECT_TO_JSVAL(versions), NULL, NULL, JSPROP_PERMANENT); private_ns = JS_NewObject(context, NULL, NULL, global); private_ns_name = gjs_context_get_const_string(context, GJS_STRING_PRIVATE_NS_MARKER); JS_DefinePropertyById(context, repo, private_ns_name, OBJECT_TO_JSVAL(private_ns), NULL, NULL, JSPROP_PERMANENT); /* FIXME - hack to make namespaces load, since * gobject-introspection does not yet search a path properly. */ { jsval value; JS_GetProperty(context, repo, "GLib", &value); } return repo; } JSBool gjs_define_repo(JSContext *context, JSObject **module_out, const char *name) { JSObject *repo; repo = repo_new(context); *module_out = repo; return JS_TRUE; } static JSBool gjs_define_constant(JSContext *context, JSObject *in_object, GIConstantInfo *info) { jsval value; GArgument garg = { 0, }; GITypeInfo *type_info; const char *name; JSBool ret = JS_FALSE; type_info = g_constant_info_get_type(info); g_constant_info_get_value(info, &garg); if (!gjs_value_from_g_argument(context, &value, type_info, &garg, TRUE)) goto out; name = g_base_info_get_name((GIBaseInfo*) info); if (JS_DefineProperty(context, in_object, name, value, NULL, NULL, GJS_MODULE_PROP_FLAGS)) ret = JS_TRUE; out: g_constant_info_free_value (info, &garg); g_base_info_unref((GIBaseInfo*) type_info); return ret; } #if GJS_VERBOSE_ENABLE_GI_USAGE void _gjs_log_info_usage(GIBaseInfo *info) { #define DIRECTION_STRING(d) ( ((d) == GI_DIRECTION_IN) ? "IN" : ((d) == GI_DIRECTION_OUT) ? "OUT" : "INOUT" ) #define TRANSFER_STRING(t) ( ((t) == GI_TRANSFER_NOTHING) ? "NOTHING" : ((t) == GI_TRANSFER_CONTAINER) ? "CONTAINER" : "EVERYTHING" ) { char *details; GIInfoType info_type; GIBaseInfo *container; info_type = g_base_info_get_type(info); if (info_type == GI_INFO_TYPE_FUNCTION) { GString *args; int n_args; int i; GITransfer retval_transfer; args = g_string_new("{ "); n_args = g_callable_info_get_n_args((GICallableInfo*) info); for (i = 0; i < n_args; ++i) { GIArgInfo *arg; GIDirection direction; GITransfer transfer; arg = g_callable_info_get_arg((GICallableInfo*)info, i); direction = g_arg_info_get_direction(arg); transfer = g_arg_info_get_ownership_transfer(arg); g_string_append_printf(args, "{ GI_DIRECTION_%s, GI_TRANSFER_%s }, ", DIRECTION_STRING(direction), TRANSFER_STRING(transfer)); g_base_info_unref((GIBaseInfo*) arg); } if (args->len > 2) g_string_truncate(args, args->len - 2); /* chop comma */ g_string_append(args, " }"); retval_transfer = g_callable_info_get_caller_owns((GICallableInfo*) info); details = g_strdup_printf(".details = { .func = { .retval_transfer = GI_TRANSFER_%s, .n_args = %d, .args = %s } }", TRANSFER_STRING(retval_transfer), n_args, args->str); g_string_free(args, TRUE); } else { details = g_strdup_printf(".details = { .nothing = {} }"); } container = g_base_info_get_container(info); gjs_debug_gi_usage("{ GI_INFO_TYPE_%s, \"%s\", \"%s\", \"%s\", %s },", gjs_info_type_name(info_type), g_base_info_get_namespace(info), container ? g_base_info_get_name(container) : "", g_base_info_get_name(info), details); g_free(details); } } #endif /* GJS_VERBOSE_ENABLE_GI_USAGE */ JSBool gjs_define_info(JSContext *context, JSObject *in_object, GIBaseInfo *info) { #if GJS_VERBOSE_ENABLE_GI_USAGE _gjs_log_info_usage(info); #endif switch (g_base_info_get_type(info)) { case GI_INFO_TYPE_FUNCTION: { JSObject *f; f = gjs_define_function(context, in_object, 0, (GICallableInfo*) info); if (f == NULL) return JS_FALSE; } break; case GI_INFO_TYPE_OBJECT: { GType gtype; gtype = g_registered_type_info_get_g_type((GIRegisteredTypeInfo*)info); if (g_type_is_a (gtype, G_TYPE_PARAM)) { gjs_define_param_class(context, in_object); } else if (g_type_is_a (gtype, G_TYPE_OBJECT)) { gjs_define_object_class(context, in_object, (GIObjectInfo*) info, gtype, NULL); } else if (G_TYPE_IS_INSTANTIATABLE(gtype)) { if (!gjs_define_fundamental_class(context, in_object, (GIObjectInfo*)info, NULL, NULL)) { gjs_throw (context, "Unsupported fundamental class creation for type %s", g_type_name(gtype)); return JS_FALSE; } } else { gjs_throw (context, "Unsupported type %s, deriving from fundamental %s", g_type_name(gtype), g_type_name(g_type_fundamental(gtype))); return JS_FALSE; } } break; case GI_INFO_TYPE_STRUCT: case GI_INFO_TYPE_BOXED: gjs_define_boxed_class(context, in_object, (GIBoxedInfo*) info); break; case GI_INFO_TYPE_UNION: if (!gjs_define_union_class(context, in_object, (GIUnionInfo*) info)) return JS_FALSE; break; case GI_INFO_TYPE_ENUM: if (g_enum_info_get_error_domain((GIEnumInfo*) info)) { /* define as GError subclass */ gjs_define_error_class(context, in_object, (GIEnumInfo*) info); break; } /* fall through */ case GI_INFO_TYPE_FLAGS: if (!gjs_define_enumeration(context, in_object, (GIEnumInfo*) info)) return JS_FALSE; break; case GI_INFO_TYPE_CONSTANT: if (!gjs_define_constant(context, in_object, (GIConstantInfo*) info)) return JS_FALSE; break; case GI_INFO_TYPE_INTERFACE: gjs_define_interface_class(context, in_object, (GIInterfaceInfo*) info); break; default: gjs_throw(context, "API of type %s not implemented, cannot define %s.%s", gjs_info_type_name(g_base_info_get_type(info)), g_base_info_get_namespace(info), g_base_info_get_name(info)); return JS_FALSE; } return JS_TRUE; } /* Get the "unknown namespace", which should be used for unnamespaced types */ JSObject* gjs_lookup_private_namespace(JSContext *context) { jsid ns_name; ns_name = gjs_context_get_const_string(context, GJS_STRING_PRIVATE_NS_MARKER); return gjs_lookup_namespace_object_by_name(context, ns_name); } /* Get the namespace object that the GIBaseInfo should be inside */ JSObject* gjs_lookup_namespace_object(JSContext *context, GIBaseInfo *info) { const char *ns; jsid ns_name; ns = g_base_info_get_namespace(info); if (ns == NULL) { gjs_throw(context, "%s '%s' does not have a namespace", gjs_info_type_name(g_base_info_get_type(info)), g_base_info_get_name(info)); return NULL; } ns_name = gjs_intern_string_to_id(context, ns); return gjs_lookup_namespace_object_by_name(context, ns_name); } static JSObject* lookup_override_function(JSContext *context, jsid ns_name) { jsval importer; jsval overridespkg; jsval module; jsval function; jsid overrides_name, object_init_name; JS_BeginRequest(context); importer = gjs_get_global_slot(context, GJS_GLOBAL_SLOT_IMPORTS); g_assert(JSVAL_IS_OBJECT(importer)); overridespkg = JSVAL_VOID; overrides_name = gjs_context_get_const_string(context, GJS_STRING_GI_OVERRIDES); if (!gjs_object_require_property(context, JSVAL_TO_OBJECT(importer), "importer", overrides_name, &overridespkg) || !JSVAL_IS_OBJECT(overridespkg)) goto fail; module = JSVAL_VOID; if (!gjs_object_require_property(context, JSVAL_TO_OBJECT(overridespkg), "GI repository object", ns_name, &module) || !JSVAL_IS_OBJECT(module)) goto fail; object_init_name = gjs_context_get_const_string(context, GJS_STRING_GOBJECT_INIT); if (!gjs_object_require_property(context, JSVAL_TO_OBJECT(module), "override module", object_init_name, &function) || !JSVAL_IS_OBJECT(function)) goto fail; JS_EndRequest(context); return JSVAL_TO_OBJECT(function); fail: JS_ClearPendingException(context); JS_EndRequest(context); return NULL; } JSObject* gjs_lookup_namespace_object_by_name(JSContext *context, jsid ns_name) { JSObject *repo_obj; jsval importer; jsval girepository; jsval ns_obj; jsid gi_name; JS_BeginRequest(context); importer = gjs_get_global_slot(context, GJS_GLOBAL_SLOT_IMPORTS); g_assert(JSVAL_IS_OBJECT(importer)); girepository = JSVAL_VOID; gi_name = gjs_context_get_const_string(context, GJS_STRING_GI_MODULE); if (!gjs_object_require_property(context, JSVAL_TO_OBJECT(importer), "importer", gi_name, &girepository) || !JSVAL_IS_OBJECT(girepository)) { gjs_log_exception(context); gjs_throw(context, "No gi property in importer"); goto fail; } repo_obj = JSVAL_TO_OBJECT(girepository); if (!gjs_object_require_property(context, repo_obj, "GI repository object", ns_name, &ns_obj)) { goto fail; } if (!JSVAL_IS_OBJECT(ns_obj)) { char *name; gjs_get_string_id(context, ns_name, &name); gjs_throw(context, "Namespace '%s' is not an object?", name); g_free(name); goto fail; } JS_EndRequest(context); return JSVAL_TO_OBJECT(ns_obj); fail: JS_EndRequest(context); return NULL; } const char* gjs_info_type_name(GIInfoType type) { switch (type) { case GI_INFO_TYPE_INVALID: return "INVALID"; case GI_INFO_TYPE_FUNCTION: return "FUNCTION"; case GI_INFO_TYPE_CALLBACK: return "CALLBACK"; case GI_INFO_TYPE_STRUCT: return "STRUCT"; case GI_INFO_TYPE_BOXED: return "BOXED"; case GI_INFO_TYPE_ENUM: return "ENUM"; case GI_INFO_TYPE_FLAGS: return "FLAGS"; case GI_INFO_TYPE_OBJECT: return "OBJECT"; case GI_INFO_TYPE_INTERFACE: return "INTERFACE"; case GI_INFO_TYPE_CONSTANT: return "CONSTANT"; case GI_INFO_TYPE_UNION: return "UNION"; case GI_INFO_TYPE_VALUE: return "VALUE"; case GI_INFO_TYPE_SIGNAL: return "SIGNAL"; case GI_INFO_TYPE_VFUNC: return "VFUNC"; case GI_INFO_TYPE_PROPERTY: return "PROPERTY"; case GI_INFO_TYPE_FIELD: return "FIELD"; case GI_INFO_TYPE_ARG: return "ARG"; case GI_INFO_TYPE_TYPE: return "TYPE"; case GI_INFO_TYPE_UNRESOLVED: return "UNRESOLVED"; case GI_INFO_TYPE_INVALID_0: g_assert_not_reached(); break; } return "???"; } char* gjs_camel_from_hyphen(const char *hyphen_name) { GString *s; const char *p; gboolean next_upper; s = g_string_sized_new(strlen(hyphen_name) + 1); next_upper = FALSE; for (p = hyphen_name; *p; ++p) { if (*p == '-' || *p == '_') { next_upper = TRUE; } else { if (next_upper) { g_string_append_c(s, g_ascii_toupper(*p)); next_upper = FALSE; } else { g_string_append_c(s, *p); } } } return g_string_free(s, FALSE); } char* gjs_hyphen_from_camel(const char *camel_name) { GString *s; const char *p; /* four hyphens should be reasonable guess */ s = g_string_sized_new(strlen(camel_name) + 4 + 1); for (p = camel_name; *p; ++p) { if (g_ascii_isupper(*p)) { g_string_append_c(s, '-'); g_string_append_c(s, g_ascii_tolower(*p)); } else { g_string_append_c(s, *p); } } return g_string_free(s, FALSE); } JSObject * gjs_lookup_generic_prototype(JSContext *context, GIBaseInfo *info) { JSObject *in_object; JSObject *constructor; const char *constructor_name; jsval value; in_object = gjs_lookup_namespace_object(context, (GIBaseInfo*) info); constructor_name = g_base_info_get_name((GIBaseInfo*) info); if (G_UNLIKELY (!in_object)) return NULL; if (!JS_GetProperty(context, in_object, constructor_name, &value)) return NULL; if (G_UNLIKELY (!JSVAL_IS_OBJECT(value) || JSVAL_IS_NULL(value))) return NULL; constructor = JSVAL_TO_OBJECT(value); g_assert(constructor != NULL); if (!gjs_object_get_property_const(context, constructor, GJS_STRING_PROTOTYPE, &value)) return NULL; if (G_UNLIKELY (!JSVAL_IS_OBJECT(value))) return NULL; return JSVAL_TO_OBJECT(value); } cjs-2.8.0/gi/closure.h0000664000175000017500000000415112610172032013443 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_CLOSURE_H__ #define __GJS_CLOSURE_H__ #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS GClosure* gjs_closure_new (JSContext *context, JSObject *callable, const char *description, gboolean root_function); void gjs_closure_invoke (GClosure *closure, int argc, jsval *argv, jsval *retval); JSContext* gjs_closure_get_context (GClosure *closure); gboolean gjs_closure_is_valid (GClosure *closure); JSObject* gjs_closure_get_callable (GClosure *closure); void gjs_closure_trace (GClosure *closure, JSTracer *tracer); G_END_DECLS #endif /* __GJS_CLOSURE_H__ */ cjs-2.8.0/gi/keep-alive.cpp0000664000175000017500000003001112610172032014336 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "keep-alive.h" #include #include #include #include typedef struct { GjsUnrootedFunc notify; JSObject *child; void *data; } Child; typedef struct { GHashTable *children; unsigned int inside_finalize : 1; unsigned int inside_trace : 1; } KeepAlive; extern struct JSClass gjs_keep_alive_class; GJS_DEFINE_PRIV_FROM_JS(KeepAlive, gjs_keep_alive_class) static guint child_hash(gconstpointer v) { const Child *child = (const Child *) v; return GPOINTER_TO_UINT(child->notify) ^ GPOINTER_TO_UINT(child->child) ^ GPOINTER_TO_UINT(child->data); } static gboolean child_equal (gconstpointer v1, gconstpointer v2) { const Child *child1 = (const Child *) v1; const Child *child2 = (const Child *) v2; /* notify is most likely to be equal, so check it last */ return child1->data == child2->data && child1->child == child2->child && child1->notify == child2->notify; } static void child_free(void *data) { Child *child = (Child *) data; g_slice_free(Child, child); } GJS_NATIVE_CONSTRUCTOR_DEFINE_ABSTRACT(keep_alive) static void keep_alive_finalize(JSFreeOp *fop, JSObject *obj) { KeepAlive *priv; void *key; void *value; priv = (KeepAlive *) JS_GetPrivate(obj); gjs_debug_lifecycle(GJS_DEBUG_KEEP_ALIVE, "keep_alive finalizing, obj %p priv %p", obj, priv); if (priv == NULL) return; /* we are the prototype, not a real instance */ priv->inside_finalize = TRUE; while (gjs_g_hash_table_steal_one(priv->children, &key, &value)) { Child *child = (Child *) value; if (child->notify) (* child->notify) (child->child, child->data); child_free(child); } g_hash_table_destroy(priv->children); g_slice_free(KeepAlive, priv); } static void trace_foreach(void *key, void *value, void *data) { Child *child = (Child *) value; JSTracer *tracer = (JSTracer *) data; if (child->child != NULL) { jsval val; JS_SET_TRACING_DETAILS(tracer, NULL, "keep-alive", 0); val = OBJECT_TO_JSVAL(child->child); g_assert (JSVAL_TO_TRACEABLE (val)); JS_CallValueTracer(tracer, &val, "keep-alive::val"); } } static void keep_alive_trace(JSTracer *tracer, JSObject *obj) { KeepAlive *priv; priv = (KeepAlive *) JS_GetPrivate(obj); if (priv == NULL) /* prototype */ return; g_assert(!priv->inside_trace); priv->inside_trace = TRUE; g_hash_table_foreach(priv->children, trace_foreach, tracer); priv->inside_trace = FALSE; } /* The bizarre thing about this vtable is that it applies to both * instances of the object, and to the prototype that instances of the * class have. */ struct JSClass gjs_keep_alive_class = { "__private_GjsKeepAlive", /* means "new __private_GjsKeepAlive()" works */ JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, keep_alive_finalize, NULL, NULL, NULL, NULL, keep_alive_trace, }; JSPropertySpec gjs_keep_alive_proto_props[] = { { NULL } }; JSFunctionSpec gjs_keep_alive_proto_funcs[] = { { NULL } }; JSObject* gjs_keep_alive_new(JSContext *context) { KeepAlive *priv; JSObject *keep_alive = NULL; JSObject *global; JSBool found; /* This function creates an unattached KeepAlive object; following our * general strategy, we have a single KeepAlive class with a constructor * stored on our single "load global" pseudo-global object, and we create * instances with the load global as parent. */ g_assert(context != NULL); JS_BeginRequest(context); global = gjs_get_import_global(context); g_assert(global != NULL); if (!JS_HasProperty(context, global, gjs_keep_alive_class.name, &found)) goto out; if (!found) { JSObject *prototype; gjs_debug(GJS_DEBUG_KEEP_ALIVE, "Initializing keep-alive class in context %p global %p", context, global); prototype = JS_InitClass(context, global, /* parent prototype JSObject* for * prototype; NULL for * Object.prototype */ NULL, &gjs_keep_alive_class, /* constructor for instances (NULL for * none - just name the prototype like * Math - rarely correct) */ gjs_keep_alive_constructor, /* number of constructor args */ 0, /* props of prototype */ &gjs_keep_alive_proto_props[0], /* funcs of prototype */ &gjs_keep_alive_proto_funcs[0], /* props of constructor, MyConstructor.myprop */ NULL, /* funcs of constructor, MyConstructor.myfunc() */ NULL); if (prototype == NULL) g_error("Can't init class %s", gjs_keep_alive_class.name); gjs_debug(GJS_DEBUG_KEEP_ALIVE, "Initialized class %s prototype %p", gjs_keep_alive_class.name, prototype); } gjs_debug(GJS_DEBUG_KEEP_ALIVE, "Creating new keep-alive object for context %p global %p", context, global); keep_alive = JS_NewObject(context, &gjs_keep_alive_class, NULL, global); if (keep_alive == NULL) { gjs_log_exception(context); g_error("Failed to create keep_alive object"); } priv = g_slice_new0(KeepAlive); priv->children = g_hash_table_new_full(child_hash, child_equal, NULL, child_free); g_assert(priv_from_js(context, keep_alive) == NULL); JS_SetPrivate(keep_alive, priv); gjs_debug_lifecycle(GJS_DEBUG_KEEP_ALIVE, "keep_alive constructor, obj %p priv %p", keep_alive, priv); out: JS_EndRequest(context); return keep_alive; } void gjs_keep_alive_add_child(JSObject *keep_alive, GjsUnrootedFunc notify, JSObject *obj, void *data) { KeepAlive *priv; Child *child; g_assert(keep_alive != NULL); priv = (KeepAlive *) JS_GetPrivate(keep_alive); g_assert(priv != NULL); g_return_if_fail(!priv->inside_trace); g_return_if_fail(!priv->inside_finalize); child = g_slice_new0(Child); child->notify = notify; child->child = obj; child->data = data; /* this is sort of an expensive check, probably */ g_return_if_fail(g_hash_table_lookup(priv->children, child) == NULL); /* this overwrites any identical-by-value previous child, * but there should not be one. */ g_hash_table_replace(priv->children, child, child); } void gjs_keep_alive_remove_child(JSObject *keep_alive, GjsUnrootedFunc notify, JSObject *obj, void *data) { KeepAlive *priv; Child child; g_assert(keep_alive != NULL); priv = (KeepAlive *) JS_GetPrivate(keep_alive); g_assert(priv != NULL); g_return_if_fail(!priv->inside_trace); g_return_if_fail(!priv->inside_finalize); child.notify = notify; child.child = obj; child.data = data; g_hash_table_remove(priv->children, &child); } static JSObject* gjs_keep_alive_create(JSContext *context) { JSObject *keep_alive; JS_BeginRequest(context); keep_alive = gjs_keep_alive_new(context); if (!keep_alive) g_error("could not create keep_alive on global object, no memory?"); gjs_set_global_slot(context, GJS_GLOBAL_SLOT_KEEP_ALIVE, OBJECT_TO_JSVAL(keep_alive)); JS_EndRequest(context); return keep_alive; } JSObject* gjs_keep_alive_get_global_if_exists (JSContext *context) { jsval keep_alive; keep_alive = gjs_get_global_slot(context, GJS_GLOBAL_SLOT_KEEP_ALIVE); if (G_LIKELY (JSVAL_IS_OBJECT(keep_alive))) return JSVAL_TO_OBJECT(keep_alive); return NULL; } JSObject* gjs_keep_alive_get_global(JSContext *context) { JSObject *keep_alive = gjs_keep_alive_get_global_if_exists(context); if (G_LIKELY(keep_alive)) return keep_alive; return gjs_keep_alive_create(context); } void gjs_keep_alive_add_global_child(JSContext *context, GjsUnrootedFunc notify, JSObject *child, void *data) { JSObject *keep_alive; JS_BeginRequest(context); keep_alive = gjs_keep_alive_get_global(context); gjs_keep_alive_add_child(keep_alive, notify, child, data); JS_EndRequest(context); } void gjs_keep_alive_remove_global_child(JSContext *context, GjsUnrootedFunc notify, JSObject *child, void *data) { JSObject *keep_alive; JS_BeginRequest(context); keep_alive = gjs_keep_alive_get_global(context); if (!keep_alive) g_error("no keep_alive property on the global object, have you " "previously added this child?"); gjs_keep_alive_remove_child(keep_alive, notify, child, data); JS_EndRequest(context); } typedef struct { GHashTableIter hashiter; } GjsRealKeepAliveIter; void gjs_keep_alive_iterator_init (GjsKeepAliveIter *iter, JSObject *keep_alive) { GjsRealKeepAliveIter *real = (GjsRealKeepAliveIter*)iter; KeepAlive *priv = (KeepAlive *) JS_GetPrivate(keep_alive); g_assert(priv != NULL); g_hash_table_iter_init(&real->hashiter, priv->children); } gboolean gjs_keep_alive_iterator_next (GjsKeepAliveIter *iter, GjsUnrootedFunc notify_func, JSObject **out_child, void **out_data) { GjsRealKeepAliveIter *real = (GjsRealKeepAliveIter*)iter; gpointer k, v; gboolean ret = FALSE; while (g_hash_table_iter_next(&real->hashiter, &k, &v)) { Child *child = (Child*)k; if (child->notify != notify_func) continue; ret = TRUE; *out_child = child->child; *out_data = child->data; break; } return ret; } cjs-2.8.0/gi/fundamental.h0000664000175000017500000000606112610172032014267 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2013 Intel Corporation * Copyright (c) 2008-2010 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_FUNDAMENTAL_H__ #define __GJS_FUNDAMENTAL_H__ #include #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS JSBool gjs_define_fundamental_class (JSContext *context, JSObject *in_object, GIObjectInfo *info, JSObject **constructor_p, JSObject **prototype_p); JSObject* gjs_object_from_g_fundamental (JSContext *context, GIObjectInfo *info, void *fobj); void* gjs_g_fundamental_from_object (JSContext *context, JSObject *obj); JSObject *gjs_fundamental_from_g_value (JSContext *context, const GValue *value, GType gtype); JSBool gjs_typecheck_fundamental (JSContext *context, JSObject *object, GType expected_gtype, JSBool throw_error); JSBool gjs_typecheck_is_fundamental (JSContext *context, JSObject *object, JSBool throw_error); void* gjs_fundamental_ref (JSContext *context, void *fobj); void gjs_fundamental_unref (JSContext *context, void *fobj); G_END_DECLS #endif /* __GJS_FUNDAMENTAL_H__ */ cjs-2.8.0/gi/object.h0000664000175000017500000000465712610172032013250 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_OBJECT_H__ #define __GJS_OBJECT_H__ #include #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS void gjs_define_object_class (JSContext *context, JSObject *in_object, GIObjectInfo *info, GType gtype, JSObject **constructor_p); JSObject* gjs_object_from_g_object (JSContext *context, GObject *gobj); GObject* gjs_g_object_from_object (JSContext *context, JSObject *obj); JSBool gjs_typecheck_object (JSContext *context, JSObject *obj, GType expected_type, JSBool throw_error); JSBool gjs_typecheck_is_object (JSContext *context, JSObject *obj, JSBool throw_error); void gjs_object_prepare_shutdown (JSContext *context); G_END_DECLS #endif /* __GJS_OBJECT_H__ */ cjs-2.8.0/gi/foreign.cpp0000664000175000017500000001443012610172032013754 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2010 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include #include "arg.h" #include "foreign.h" static struct { char *gi_namespace; char *module; // relative to "imports." gboolean loaded; } foreign_modules[] = { { (char*)"cairo", (char*)"cairo", FALSE }, { NULL } }; static GHashTable* foreign_structs_table = NULL; static GHashTable* get_foreign_structs(void) { // FIXME: look into hasing on GITypeInfo instead. if (!foreign_structs_table) { foreign_structs_table = g_hash_table_new_full(g_str_hash, g_str_equal, (GDestroyNotify)g_free, NULL); } return foreign_structs_table; } static JSBool gjs_foreign_load_foreign_module(JSContext *context, const gchar *gi_namespace) { int i; for (i = 0; foreign_modules[i].gi_namespace; ++i) { int code; GError *error = NULL; char *script; if (strcmp(gi_namespace, foreign_modules[i].gi_namespace) != 0) continue; if (foreign_modules[i].loaded) { return JS_TRUE; } // FIXME: Find a way to check if a module is imported // and only execute this statement if isn't script = g_strdup_printf("imports.%s;", gi_namespace); if (!gjs_context_eval((GjsContext*) JS_GetContextPrivate(context), script, strlen(script), "", &code, &error)) { g_printerr("ERROR: %s\n", error->message); g_free(script); g_error_free(error); return JS_FALSE; } g_free(script); foreign_modules[i].loaded = TRUE; } return JS_TRUE; } JSBool gjs_struct_foreign_register(const char *gi_namespace, const char *type_name, GjsForeignInfo *info) { char *canonical_name; g_return_val_if_fail(info != NULL, JS_FALSE); g_return_val_if_fail(info->to_func != NULL, JS_FALSE); g_return_val_if_fail(info->from_func != NULL, JS_FALSE); canonical_name = g_strdup_printf("%s.%s", gi_namespace, type_name); g_hash_table_insert(get_foreign_structs(), canonical_name, info); return JS_TRUE; } static GjsForeignInfo * gjs_struct_foreign_lookup(JSContext *context, GIBaseInfo *interface_info) { GjsForeignInfo *retval = NULL; GHashTable *hash_table; char *key; key = g_strdup_printf("%s.%s", g_base_info_get_namespace(interface_info), g_base_info_get_name(interface_info)); hash_table = get_foreign_structs(); retval = (GjsForeignInfo*)g_hash_table_lookup(hash_table, key); if (!retval) { if (gjs_foreign_load_foreign_module(context, g_base_info_get_namespace(interface_info))) { retval = (GjsForeignInfo*)g_hash_table_lookup(hash_table, key); } } if (!retval) { gjs_throw(context, "Unable to find module implementing foreign type %s.%s", g_base_info_get_namespace(interface_info), g_base_info_get_name(interface_info)); } g_free(key); return retval; } JSBool gjs_struct_foreign_convert_to_g_argument(JSContext *context, jsval value, GIBaseInfo *interface_info, const char *arg_name, GjsArgumentType argument_type, GITransfer transfer, gboolean may_be_null, GArgument *arg) { GjsForeignInfo *foreign; foreign = gjs_struct_foreign_lookup(context, interface_info); if (!foreign) return JS_FALSE; if (!foreign->to_func(context, value, arg_name, argument_type, transfer, may_be_null, arg)) return JS_FALSE; return JS_TRUE; } JSBool gjs_struct_foreign_convert_from_g_argument(JSContext *context, jsval *value_p, GIBaseInfo *interface_info, GArgument *arg) { GjsForeignInfo *foreign; foreign = gjs_struct_foreign_lookup(context, interface_info); if (!foreign) return JS_FALSE; if (!foreign->from_func(context, value_p, arg)) return JS_FALSE; return JS_TRUE; } JSBool gjs_struct_foreign_release_g_argument(JSContext *context, GITransfer transfer, GIBaseInfo *interface_info, GArgument *arg) { GjsForeignInfo *foreign; foreign = gjs_struct_foreign_lookup(context, interface_info); if (!foreign) return JS_FALSE; if (!foreign->release_func) return JS_TRUE; if (!foreign->release_func(context, transfer, arg)) return JS_FALSE; return JS_TRUE; } cjs-2.8.0/gi/function.cpp0000664000175000017500000017765312610172032014171 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "function.h" #include "arg.h" #include "object.h" #include "fundamental.h" #include "boxed.h" #include "union.h" #include "gerror.h" #include #include #include #include #include #include #include #include #include /* We use guint8 for arguments; functions can't * have more than this. */ #define GJS_ARG_INDEX_INVALID G_MAXUINT8 typedef struct { GIFunctionInfo *info; GjsParamType *param_types; guint8 expected_js_argc; guint8 js_out_argc; GIFunctionInvoker invoker; } Function; extern struct JSClass gjs_function_class; /* Because we can't free the mmap'd data for a callback * while it's in use, this list keeps track of ones that * will be freed the next time we invoke a C function. */ static GSList *completed_trampolines = NULL; /* GjsCallbackTrampoline */ GJS_DEFINE_PRIV_FROM_JS(Function, gjs_function_class) void gjs_callback_trampoline_ref(GjsCallbackTrampoline *trampoline) { trampoline->ref_count++; } void gjs_callback_trampoline_unref(GjsCallbackTrampoline *trampoline) { /* Not MT-safe, like all the rest of GJS */ trampoline->ref_count--; if (trampoline->ref_count == 0) { JSContext *context = trampoline->context; if (!trampoline->is_vfunc) { JS_BeginRequest(context); JS_RemoveValueRoot(context, &trampoline->js_function); JS_EndRequest(context); } g_callable_info_free_closure(trampoline->info, trampoline->closure); g_base_info_unref( (GIBaseInfo*) trampoline->info); g_free (trampoline->param_types); g_slice_free(GjsCallbackTrampoline, trampoline); } } static void set_return_ffi_arg_from_giargument (GITypeInfo *ret_type, void *result, GIArgument *return_value) { switch (g_type_info_get_tag(ret_type)) { case GI_TYPE_TAG_INT8: *(ffi_sarg *) result = return_value->v_int8; break; case GI_TYPE_TAG_UINT8: *(ffi_arg *) result = return_value->v_uint8; break; case GI_TYPE_TAG_INT16: *(ffi_sarg *) result = return_value->v_int16; break; case GI_TYPE_TAG_UINT16: *(ffi_arg *) result = return_value->v_uint16; break; case GI_TYPE_TAG_INT32: *(ffi_sarg *) result = return_value->v_int32; break; case GI_TYPE_TAG_UINT32: case GI_TYPE_TAG_BOOLEAN: case GI_TYPE_TAG_UNICHAR: *(ffi_arg *) result = return_value->v_uint32; break; case GI_TYPE_TAG_INT64: *(ffi_sarg *) result = return_value->v_int64; break; case GI_TYPE_TAG_UINT64: *(ffi_arg *) result = return_value->v_uint64; break; case GI_TYPE_TAG_INTERFACE: { GIBaseInfo* interface_info; GIInfoType interface_type; interface_info = g_type_info_get_interface(ret_type); interface_type = g_base_info_get_type(interface_info); switch (interface_type) { case GI_INFO_TYPE_ENUM: case GI_INFO_TYPE_FLAGS: *(ffi_sarg *) result = return_value->v_long; break; default: *(ffi_arg *) result = (ffi_arg) return_value->v_pointer; break; } g_base_info_unref(interface_info); } default: *(ffi_arg *) result = (ffi_arg) return_value->v_uint64; break; } } /* This is our main entry point for ffi_closure callbacks. * ffi_prep_closure is doing pure magic and replaces the original * function call with this one which gives us the ffi arguments, * a place to store the return value and our use data. * In other words, everything we need to call the JS function and * getting the return value back. */ static void gjs_callback_closure(ffi_cif *cif, void *result, void **args, void *data) { JSContext *context; JSRuntime *runtime; JSObject *global; GjsCallbackTrampoline *trampoline; int i, n_args, n_jsargs, n_outargs; jsval *jsargs, rval; JSObject *this_object; GITypeInfo ret_type; gboolean success = FALSE; gboolean ret_type_is_void; trampoline = (GjsCallbackTrampoline *) data; g_assert(trampoline); gjs_callback_trampoline_ref(trampoline); context = trampoline->context; runtime = JS_GetRuntime(context); if (G_UNLIKELY (gjs_runtime_is_sweeping(runtime))) { g_critical("Attempting to call back into JSAPI during the sweeping phase of GC. " "This is most likely caused by not destroying a Clutter actor or Gtk+ " "widget with ::destroy signals connected, but can also be caused by " "using the destroy() or dispose() vfuncs. Because it would crash the " "application, it has been blocked and the JS callback not invoked."); /* A gjs_dumpstack() would be nice here, but we can't, because that works by creating a new Error object and reading the stack property, which is the worst possible idea during a GC session. */ gjs_callback_trampoline_unref(trampoline); return; } JS_BeginRequest(context); global = JS_GetGlobalObject(context); JSAutoCompartment ac(context, global); n_args = g_callable_info_get_n_args(trampoline->info); g_assert(n_args >= 0); n_outargs = 0; jsargs = (jsval*)g_newa(jsval, n_args); for (i = 0, n_jsargs = 0; i < n_args; i++) { GIArgInfo arg_info; GITypeInfo type_info; GjsParamType param_type; g_callable_info_load_arg(trampoline->info, i, &arg_info); g_arg_info_load_type(&arg_info, &type_info); /* Skip void * arguments */ if (g_type_info_get_tag(&type_info) == GI_TYPE_TAG_VOID) continue; if (g_arg_info_get_direction(&arg_info) == GI_DIRECTION_OUT) { n_outargs++; continue; } if (g_arg_info_get_direction(&arg_info) == GI_DIRECTION_INOUT) n_outargs++; param_type = trampoline->param_types[i]; switch (param_type) { case PARAM_SKIPPED: continue; case PARAM_ARRAY: { gint array_length_pos = g_type_info_get_array_length(&type_info); GIArgInfo array_length_arg; GITypeInfo arg_type_info; jsval length; g_callable_info_load_arg(trampoline->info, array_length_pos, &array_length_arg); g_arg_info_load_type(&array_length_arg, &arg_type_info); if (!gjs_value_from_g_argument(context, &length, &arg_type_info, (GArgument *) args[array_length_pos], TRUE)) goto out; if (!gjs_value_from_explicit_array(context, &jsargs[n_jsargs++], &type_info, (GArgument*) args[i], JSVAL_TO_INT(length))) goto out; break; } case PARAM_NORMAL: if (!gjs_value_from_g_argument(context, &jsargs[n_jsargs++], &type_info, (GArgument *) args[i], FALSE)) goto out; break; default: g_assert_not_reached(); } } if (trampoline->is_vfunc) { g_assert(n_args > 0); this_object = JSVAL_TO_OBJECT(jsargs[0]); jsargs++; n_jsargs--; } else { this_object = NULL; } if (!JS_CallFunctionValue(context, this_object, trampoline->js_function, n_jsargs, jsargs, &rval)) { goto out; } g_callable_info_load_return_type(trampoline->info, &ret_type); ret_type_is_void = g_type_info_get_tag (&ret_type) == GI_TYPE_TAG_VOID; if (n_outargs == 0 && !ret_type_is_void) { GIArgument argument; /* non-void return value, no out args. Should * be a single return value. */ if (!gjs_value_to_g_argument(context, rval, &ret_type, "callback", GJS_ARGUMENT_RETURN_VALUE, GI_TRANSFER_NOTHING, TRUE, &argument)) goto out; set_return_ffi_arg_from_giargument(&ret_type, result, &argument); } else if (n_outargs == 1 && ret_type_is_void) { /* void return value, one out args. Should * be a single return value. */ for (i = 0; i < n_args; i++) { GIArgInfo arg_info; GITypeInfo type_info; g_callable_info_load_arg(trampoline->info, i, &arg_info); if (g_arg_info_get_direction(&arg_info) == GI_DIRECTION_IN) continue; g_arg_info_load_type(&arg_info, &type_info); if (!gjs_value_to_g_argument(context, rval, &type_info, "callback", GJS_ARGUMENT_ARGUMENT, GI_TRANSFER_NOTHING, TRUE, *(GArgument **)args[i])) goto out; break; } } else { jsval elem; gsize elem_idx = 0; /* more than one of a return value or an out argument. * Should be an array of output values. */ if (!ret_type_is_void) { GIArgument argument; if (!JS_GetElement(context, JSVAL_TO_OBJECT(rval), elem_idx, &elem)) goto out; if (!gjs_value_to_g_argument(context, elem, &ret_type, "callback", GJS_ARGUMENT_ARGUMENT, GI_TRANSFER_NOTHING, TRUE, &argument)) goto out; set_return_ffi_arg_from_giargument(&ret_type, result, &argument); elem_idx++; } for (i = 0; i < n_args; i++) { GIArgInfo arg_info; GITypeInfo type_info; g_callable_info_load_arg(trampoline->info, i, &arg_info); if (g_arg_info_get_direction(&arg_info) == GI_DIRECTION_IN) continue; g_arg_info_load_type(&arg_info, &type_info); if (!JS_GetElement(context, JSVAL_TO_OBJECT(rval), elem_idx, &elem)) goto out; if (!gjs_value_to_g_argument(context, elem, &type_info, "callback", GJS_ARGUMENT_ARGUMENT, GI_TRANSFER_NOTHING, TRUE, *(GArgument **)args[i])) goto out; elem_idx++; } } success = TRUE; out: if (!success) { gjs_log_exception (context); /* Fill in the result with some hopefully neutral value */ g_callable_info_load_return_type(trampoline->info, &ret_type); gjs_g_argument_init_default (context, &ret_type, (GArgument *) result); } if (trampoline->scope == GI_SCOPE_TYPE_ASYNC) { completed_trampolines = g_slist_prepend(completed_trampolines, trampoline); } gjs_callback_trampoline_unref(trampoline); gjs_schedule_gc_if_needed(context); JS_EndRequest(context); } /* The global entry point for any invocations of GDestroyNotify; * look up the callback through the user_data and then free it. */ static void gjs_destroy_notify_callback(gpointer data) { GjsCallbackTrampoline *trampoline = (GjsCallbackTrampoline *) data; g_assert(trampoline); gjs_callback_trampoline_unref(trampoline); } GjsCallbackTrampoline* gjs_callback_trampoline_new(JSContext *context, jsval function, GICallableInfo *callable_info, GIScopeType scope, gboolean is_vfunc) { GjsCallbackTrampoline *trampoline; int n_args, i; if (JSVAL_IS_NULL(function)) { return NULL; } g_assert(JS_TypeOfValue(context, function) == JSTYPE_FUNCTION); trampoline = g_slice_new(GjsCallbackTrampoline); trampoline->ref_count = 1; trampoline->context = context; trampoline->info = callable_info; g_base_info_ref((GIBaseInfo*)trampoline->info); trampoline->js_function = function; if (!is_vfunc) JS_AddValueRoot(context, &trampoline->js_function); /* Analyze param types and directions, similarly to init_cached_function_data */ n_args = g_callable_info_get_n_args(trampoline->info); trampoline->param_types = g_new0(GjsParamType, n_args); for (i = 0; i < n_args; i++) { GIDirection direction; GIArgInfo arg_info; GITypeInfo type_info; GITypeTag type_tag; if (trampoline->param_types[i] == PARAM_SKIPPED) continue; g_callable_info_load_arg(trampoline->info, i, &arg_info); g_arg_info_load_type(&arg_info, &type_info); direction = g_arg_info_get_direction(&arg_info); type_tag = g_type_info_get_tag(&type_info); if (direction != GI_DIRECTION_IN) { /* INOUT and OUT arguments are handled differently. */ continue; } if (type_tag == GI_TYPE_TAG_INTERFACE) { GIBaseInfo* interface_info; GIInfoType interface_type; interface_info = g_type_info_get_interface(&type_info); interface_type = g_base_info_get_type(interface_info); if (interface_type == GI_INFO_TYPE_CALLBACK) { gjs_throw(context, "Callback accepts another callback as a parameter. This is not supported"); g_base_info_unref(interface_info); return NULL; } g_base_info_unref(interface_info); } else if (type_tag == GI_TYPE_TAG_ARRAY) { if (g_type_info_get_array_type(&type_info) == GI_ARRAY_TYPE_C) { int array_length_pos = g_type_info_get_array_length(&type_info); if (array_length_pos >= 0 && array_length_pos < n_args) { GIArgInfo length_arg_info; g_callable_info_load_arg(trampoline->info, array_length_pos, &length_arg_info); if (g_arg_info_get_direction(&length_arg_info) != direction) { gjs_throw(context, "Callback has an array with different-direction length arg, not supported"); return NULL; } trampoline->param_types[array_length_pos] = PARAM_SKIPPED; trampoline->param_types[i] = PARAM_ARRAY; } } } } trampoline->closure = g_callable_info_prepare_closure(callable_info, &trampoline->cif, gjs_callback_closure, trampoline); trampoline->scope = scope; trampoline->is_vfunc = is_vfunc; return trampoline; } /* an helper function to retrieve array lengths from a GArgument (letting the compiler generate good instructions in case of big endian machines) */ static unsigned long get_length_from_arg (GArgument *arg, GITypeTag tag) { switch (tag) { case GI_TYPE_TAG_INT8: return arg->v_int8; case GI_TYPE_TAG_UINT8: return arg->v_uint8; case GI_TYPE_TAG_INT16: return arg->v_int16; case GI_TYPE_TAG_UINT16: return arg->v_uint16; case GI_TYPE_TAG_INT32: return arg->v_int32; case GI_TYPE_TAG_UINT32: return arg->v_uint32; case GI_TYPE_TAG_INT64: return arg->v_int64; case GI_TYPE_TAG_UINT64: return arg->v_uint64; default: g_assert_not_reached (); } } static JSBool gjs_fill_method_instance (JSContext *context, JSObject *obj, Function *function, GIArgument *out_arg) { GIBaseInfo *container = g_base_info_get_container((GIBaseInfo *) function->info); GIInfoType type = g_base_info_get_type(container); GType gtype = g_registered_type_info_get_g_type ((GIRegisteredTypeInfo *)container); switch (type) { case GI_INFO_TYPE_STRUCT: case GI_INFO_TYPE_BOXED: /* GError must be special cased */ if (g_type_is_a(gtype, G_TYPE_ERROR)) { if (!gjs_typecheck_gerror(context, obj, JS_TRUE)) return JS_FALSE; out_arg->v_pointer = gjs_gerror_from_error(context, obj); } else { if (!gjs_typecheck_boxed(context, obj, container, gtype, JS_TRUE)) return JS_FALSE; out_arg->v_pointer = gjs_c_struct_from_boxed(context, obj); } break; case GI_INFO_TYPE_UNION: if (!gjs_typecheck_union(context, obj, container, gtype, JS_TRUE)) return JS_FALSE; out_arg->v_pointer = gjs_c_union_from_union(context, obj); break; case GI_INFO_TYPE_OBJECT: case GI_INFO_TYPE_INTERFACE: if (gjs_typecheck_is_object(context, obj, JS_FALSE)) { if (!gjs_typecheck_object(context, obj, gtype, JS_TRUE)) return JS_FALSE; out_arg->v_pointer = gjs_g_object_from_object(context, obj); } else if (gjs_typecheck_is_fundamental(context, obj, JS_FALSE)) { if (!gjs_typecheck_fundamental(context, obj, gtype, JS_TRUE)) return JS_FALSE; out_arg->v_pointer = gjs_g_fundamental_from_object(context, obj); } else { gjs_throw_custom(context, "TypeError", "%s.%s is not an object instance neither a fundamental instance of a supported type", g_base_info_get_namespace(container), g_base_info_get_name(container)); return JS_FALSE; } break; default: g_assert_not_reached(); } return JS_TRUE; } /* * This function can be called in 2 different ways. You can either use * it to create javascript objects by providing a @js_rval argument or * you can decide to keep the return values in #GArgument format by * providing a @r_value argument. */ static JSBool gjs_invoke_c_function(JSContext *context, Function *function, JSObject *obj, /* "this" object */ unsigned js_argc, jsval *js_argv, jsval *js_rval, GArgument *r_value) { /* These first four are arrays which hold argument pointers. * @in_arg_cvalues: C values which are passed on input (in or inout) * @out_arg_cvalues: C values which are returned as arguments (out or inout) * @inout_original_arg_cvalues: For the special case of (inout) args, we need to * keep track of the original values we passed into the function, in case we * need to free it. * @ffi_arg_pointers: For passing data to FFI, we need to create another layer * of indirection; this array is a pointer to an element in in_arg_cvalues * or out_arg_cvalues. * @return_value: The actual return value of the C function, i.e. not an (out) param */ GArgument *in_arg_cvalues; GArgument *out_arg_cvalues; GArgument *inout_original_arg_cvalues; gpointer *ffi_arg_pointers; GIFFIReturnValue return_value; gpointer return_value_p; /* Will point inside the union return_value */ GArgument return_gargument; guint8 processed_c_args = 0; guint8 gi_argc, gi_arg_pos; guint8 c_argc, c_arg_pos; guint8 js_arg_pos; gboolean can_throw_gerror; gboolean did_throw_gerror = FALSE; GError *local_error = NULL; gboolean failed, postinvoke_release_failed; gboolean is_method; GITypeInfo return_info; GITypeTag return_tag; jsval *return_values = NULL; guint8 next_rval = 0; /* index into return_values */ GSList *iter; /* Because we can't free a closure while we're in it, we defer * freeing until the next time a C function is invoked. What * we should really do instead is queue it for a GC thread. */ if (completed_trampolines) { for (iter = completed_trampolines; iter; iter = iter->next) { GjsCallbackTrampoline *trampoline = (GjsCallbackTrampoline *) iter->data; gjs_callback_trampoline_unref(trampoline); } g_slist_free(completed_trampolines); completed_trampolines = NULL; } is_method = g_callable_info_is_method(function->info); can_throw_gerror = g_callable_info_can_throw_gerror(function->info); c_argc = function->invoker.cif.nargs; gi_argc = g_callable_info_get_n_args( (GICallableInfo*) function->info); /* @c_argc is the number of arguments that the underlying C * function takes. @gi_argc is the number of arguments the * GICallableInfo describes (which does not include "this" or * GError**). @function->expected_js_argc is the number of * arguments we expect the JS function to take (which does not * include PARAM_SKIPPED args). * * @js_argc is the number of arguments that were actually passed; * we allow this to be larger than @expected_js_argc for * convenience, and simply ignore the extra arguments. But we * don't allow too few args, since that would break. */ if (js_argc < function->expected_js_argc) { gjs_throw(context, "Too few arguments to %s %s.%s expected %d got %d", is_method ? "method" : "function", g_base_info_get_namespace( (GIBaseInfo*) function->info), g_base_info_get_name( (GIBaseInfo*) function->info), function->expected_js_argc, js_argc); return JS_FALSE; } g_callable_info_load_return_type( (GICallableInfo*) function->info, &return_info); return_tag = g_type_info_get_tag(&return_info); in_arg_cvalues = g_newa(GArgument, c_argc); ffi_arg_pointers = g_newa(gpointer, c_argc); out_arg_cvalues = g_newa(GArgument, c_argc); inout_original_arg_cvalues = g_newa(GArgument, c_argc); failed = FALSE; c_arg_pos = 0; /* index into in_arg_cvalues, etc */ js_arg_pos = 0; /* index into argv */ if (is_method) { if (!gjs_fill_method_instance(context, obj, function, &in_arg_cvalues[0])) return JS_FALSE; ffi_arg_pointers[0] = &in_arg_cvalues[0]; ++c_arg_pos; } processed_c_args = c_arg_pos; for (gi_arg_pos = 0; gi_arg_pos < gi_argc; gi_arg_pos++, c_arg_pos++) { GIDirection direction; GIArgInfo arg_info; gboolean arg_removed = FALSE; /* gjs_debug(GJS_DEBUG_GFUNCTION, "gi_arg_pos: %d c_arg_pos: %d js_arg_pos: %d", gi_arg_pos, c_arg_pos, js_arg_pos); */ g_callable_info_load_arg( (GICallableInfo*) function->info, gi_arg_pos, &arg_info); direction = g_arg_info_get_direction(&arg_info); g_assert_cmpuint(c_arg_pos, <, c_argc); ffi_arg_pointers[c_arg_pos] = &in_arg_cvalues[c_arg_pos]; if (direction == GI_DIRECTION_OUT) { if (g_arg_info_is_caller_allocates(&arg_info)) { GITypeTag type_tag; GITypeInfo ainfo; g_arg_info_load_type(&arg_info, &ainfo); type_tag = g_type_info_get_tag(&ainfo); switch (type_tag) { case GI_TYPE_TAG_INTERFACE: { GIBaseInfo* interface_info; GIInfoType interface_type; gsize size; interface_info = g_type_info_get_interface(&ainfo); g_assert(interface_info != NULL); interface_type = g_base_info_get_type(interface_info); if (interface_type == GI_INFO_TYPE_STRUCT) { size = g_struct_info_get_size((GIStructInfo*)interface_info); } else if (interface_type == GI_INFO_TYPE_UNION) { size = g_union_info_get_size((GIUnionInfo*)interface_info); } else { failed = TRUE; } g_base_info_unref((GIBaseInfo*)interface_info); if (!failed) { in_arg_cvalues[c_arg_pos].v_pointer = g_slice_alloc0(size); out_arg_cvalues[c_arg_pos].v_pointer = in_arg_cvalues[c_arg_pos].v_pointer; } break; } default: failed = TRUE; } if (failed) gjs_throw(context, "Unsupported type %s for (out caller-allocates)", g_type_tag_to_string(type_tag)); } else { out_arg_cvalues[c_arg_pos].v_pointer = NULL; in_arg_cvalues[c_arg_pos].v_pointer = &out_arg_cvalues[c_arg_pos]; } } else { GArgument *in_value; GITypeInfo ainfo; GjsParamType param_type; g_arg_info_load_type(&arg_info, &ainfo); in_value = &in_arg_cvalues[c_arg_pos]; param_type = function->param_types[gi_arg_pos]; switch (param_type) { case PARAM_CALLBACK: { GICallableInfo *callable_info; GIScopeType scope = g_arg_info_get_scope(&arg_info); GjsCallbackTrampoline *trampoline; ffi_closure *closure; jsval value = js_argv[js_arg_pos]; if (JSVAL_IS_NULL(value) && g_arg_info_may_be_null(&arg_info)) { closure = NULL; trampoline = NULL; } else { if (!(JS_TypeOfValue(context, value) == JSTYPE_FUNCTION)) { gjs_throw(context, "Error invoking %s.%s: Expected function for callback argument %s, got %s", g_base_info_get_namespace( (GIBaseInfo*) function->info), g_base_info_get_name( (GIBaseInfo*) function->info), g_base_info_get_name( (GIBaseInfo*) &arg_info), JS_GetTypeName(context, JS_TypeOfValue(context, value))); failed = TRUE; break; } callable_info = (GICallableInfo*) g_type_info_get_interface(&ainfo); trampoline = gjs_callback_trampoline_new(context, value, callable_info, scope, FALSE); closure = trampoline->closure; g_base_info_unref(callable_info); } gint destroy_pos = g_arg_info_get_destroy(&arg_info); gint closure_pos = g_arg_info_get_closure(&arg_info); if (destroy_pos >= 0) { gint c_pos = is_method ? destroy_pos + 1 : destroy_pos; g_assert (function->param_types[destroy_pos] == PARAM_SKIPPED); in_arg_cvalues[c_pos].v_pointer = trampoline ? (gpointer) gjs_destroy_notify_callback : NULL; } if (closure_pos >= 0) { gint c_pos = is_method ? closure_pos + 1 : closure_pos; g_assert (function->param_types[closure_pos] == PARAM_SKIPPED); in_arg_cvalues[c_pos].v_pointer = trampoline; } if (trampoline && scope != GI_SCOPE_TYPE_CALL) { /* Add an extra reference that will be cleared when collecting async calls, or when GDestroyNotify is called */ gjs_callback_trampoline_ref(trampoline); } in_value->v_pointer = closure; break; } case PARAM_SKIPPED: arg_removed = TRUE; break; case PARAM_ARRAY: { GIArgInfo array_length_arg; gint array_length_pos = g_type_info_get_array_length(&ainfo); gsize length; if (!gjs_value_to_explicit_array(context, js_argv[js_arg_pos], &arg_info, in_value, &length)) { failed = TRUE; break; } g_callable_info_load_arg(function->info, array_length_pos, &array_length_arg); array_length_pos += is_method ? 1 : 0; if (!gjs_value_to_arg(context, INT_TO_JSVAL(length), &array_length_arg, in_arg_cvalues + array_length_pos)) { failed = TRUE; break; } /* Also handle the INOUT for the length here */ if (direction == GI_DIRECTION_INOUT) { if (in_value->v_pointer == NULL) { /* Special case where we were given JS null to * also pass null for length, and not a * pointer to an integer that derefs to 0. */ in_arg_cvalues[array_length_pos].v_pointer = NULL; out_arg_cvalues[array_length_pos].v_pointer = NULL; inout_original_arg_cvalues[array_length_pos].v_pointer = NULL; } else { out_arg_cvalues[array_length_pos] = inout_original_arg_cvalues[array_length_pos] = *(in_arg_cvalues + array_length_pos); in_arg_cvalues[array_length_pos].v_pointer = &out_arg_cvalues[array_length_pos]; } } break; } case PARAM_NORMAL: /* Ok, now just convert argument normally */ g_assert_cmpuint(js_arg_pos, <, js_argc); if (!gjs_value_to_arg(context, js_argv[js_arg_pos], &arg_info, in_value)) { failed = TRUE; break; } } if (direction == GI_DIRECTION_INOUT && !arg_removed && !failed) { out_arg_cvalues[c_arg_pos] = inout_original_arg_cvalues[c_arg_pos] = in_arg_cvalues[c_arg_pos]; in_arg_cvalues[c_arg_pos].v_pointer = &out_arg_cvalues[c_arg_pos]; } if (failed) { /* Exit from the loop */ break; } if (!failed && !arg_removed) ++js_arg_pos; } if (failed) break; processed_c_args++; } /* Did argument conversion fail? In that case, skip invocation and jump to release * processing. */ if (failed) { did_throw_gerror = FALSE; goto release; } if (can_throw_gerror) { g_assert_cmpuint(c_arg_pos, <, c_argc); in_arg_cvalues[c_arg_pos].v_pointer = &local_error; ffi_arg_pointers[c_arg_pos] = &(in_arg_cvalues[c_arg_pos]); c_arg_pos++; /* don't update processed_c_args as we deal with local_error * separately */ } g_assert_cmpuint(c_arg_pos, ==, c_argc); g_assert_cmpuint(gi_arg_pos, ==, gi_argc); /* See comment for GjsFFIReturnValue above */ if (return_tag == GI_TYPE_TAG_FLOAT) return_value_p = &return_value.v_float; else if (return_tag == GI_TYPE_TAG_DOUBLE) return_value_p = &return_value.v_double; else if (return_tag == GI_TYPE_TAG_INT64 || return_tag == GI_TYPE_TAG_UINT64) return_value_p = &return_value.v_uint64; else return_value_p = &return_value.v_long; ffi_call(&(function->invoker.cif), FFI_FN(function->invoker.native_address), return_value_p, ffi_arg_pointers); /* Return value and out arguments are valid only if invocation doesn't * return error. In arguments need to be released always. */ if (can_throw_gerror) { did_throw_gerror = local_error != NULL; } else { did_throw_gerror = FALSE; } if (js_rval) *js_rval = JSVAL_VOID; /* Only process return values if the function didn't throw */ if (function->js_out_argc > 0 && !did_throw_gerror) { return_values = g_newa(jsval, function->js_out_argc); gjs_set_values(context, return_values, function->js_out_argc, JSVAL_VOID); gjs_root_value_locations(context, return_values, function->js_out_argc); if (return_tag != GI_TYPE_TAG_VOID) { GITransfer transfer = g_callable_info_get_caller_owns((GICallableInfo*) function->info); gboolean arg_failed = FALSE; gint array_length_pos; g_assert_cmpuint(next_rval, <, function->js_out_argc); gi_type_info_extract_ffi_return_value(&return_info, &return_value, &return_gargument); array_length_pos = g_type_info_get_array_length(&return_info); if (array_length_pos >= 0) { GIArgInfo array_length_arg; GITypeInfo arg_type_info; jsval length; g_callable_info_load_arg(function->info, array_length_pos, &array_length_arg); g_arg_info_load_type(&array_length_arg, &arg_type_info); array_length_pos += is_method ? 1 : 0; arg_failed = !gjs_value_from_g_argument(context, &length, &arg_type_info, &out_arg_cvalues[array_length_pos], TRUE); if (!arg_failed && js_rval) { arg_failed = !gjs_value_from_explicit_array(context, &return_values[next_rval], &return_info, &return_gargument, JSVAL_TO_INT(length)); } if (!arg_failed && !r_value && !gjs_g_argument_release_out_array(context, transfer, &return_info, JSVAL_TO_INT(length), &return_gargument)) failed = TRUE; } else { if (js_rval) arg_failed = !gjs_value_from_g_argument(context, &return_values[next_rval], &return_info, &return_gargument, TRUE); /* Free GArgument, the jsval should have ref'd or copied it */ if (!arg_failed && !r_value && !gjs_g_argument_release(context, transfer, &return_info, &return_gargument)) failed = TRUE; } if (arg_failed) failed = TRUE; ++next_rval; } } release: /* We walk over all args, release in args (if allocated) and convert * all out args to JS */ c_arg_pos = is_method ? 1 : 0; postinvoke_release_failed = FALSE; for (gi_arg_pos = 0; gi_arg_pos < gi_argc && c_arg_pos < processed_c_args; gi_arg_pos++, c_arg_pos++) { GIDirection direction; GIArgInfo arg_info; GITypeInfo arg_type_info; GjsParamType param_type; g_callable_info_load_arg( (GICallableInfo*) function->info, gi_arg_pos, &arg_info); direction = g_arg_info_get_direction(&arg_info); g_arg_info_load_type(&arg_info, &arg_type_info); param_type = function->param_types[gi_arg_pos]; if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) { GArgument *arg; GITransfer transfer; if (direction == GI_DIRECTION_IN) { arg = &in_arg_cvalues[c_arg_pos]; transfer = g_arg_info_get_ownership_transfer(&arg_info); } else { arg = &inout_original_arg_cvalues[c_arg_pos]; /* For inout, transfer refers to what we get back from the function; for * the temporary C value we allocated, clearly we're responsible for * freeing it. */ transfer = GI_TRANSFER_NOTHING; } if (param_type == PARAM_CALLBACK) { ffi_closure *closure = (ffi_closure *) arg->v_pointer; if (closure) { GjsCallbackTrampoline *trampoline = (GjsCallbackTrampoline *) closure->user_data; /* CallbackTrampolines are refcounted because for notified/async closures it is possible to destroy it while in call, and therefore we cannot check its scope at this point */ gjs_callback_trampoline_unref(trampoline); arg->v_pointer = NULL; } } else if (param_type == PARAM_ARRAY) { gsize length; GIArgInfo array_length_arg; GITypeInfo array_length_type; gint array_length_pos = g_type_info_get_array_length(&arg_type_info); g_assert(array_length_pos >= 0); g_callable_info_load_arg(function->info, array_length_pos, &array_length_arg); g_arg_info_load_type(&array_length_arg, &array_length_type); array_length_pos += is_method ? 1 : 0; length = get_length_from_arg(in_arg_cvalues + array_length_pos, g_type_info_get_tag(&array_length_type)); if (!gjs_g_argument_release_in_array(context, transfer, &arg_type_info, length, arg)) { postinvoke_release_failed = TRUE; } } else if (param_type == PARAM_NORMAL) { if (!gjs_g_argument_release_in_arg(context, transfer, &arg_type_info, arg)) { postinvoke_release_failed = TRUE; } } } /* Don't free out arguments if function threw an exception or we failed * earlier - note "postinvoke_release_failed" is separate from "failed". We * sync them up after this loop. */ if (did_throw_gerror || failed) continue; if ((direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) && param_type != PARAM_SKIPPED) { GArgument *arg; gboolean arg_failed = FALSE; gint array_length_pos; jsval array_length; GITransfer transfer; g_assert(next_rval < function->js_out_argc); arg = &out_arg_cvalues[c_arg_pos]; array_length_pos = g_type_info_get_array_length(&arg_type_info); if (js_rval) { if (array_length_pos >= 0) { GIArgInfo array_length_arg; GITypeInfo array_length_type_info; g_callable_info_load_arg(function->info, array_length_pos, &array_length_arg); g_arg_info_load_type(&array_length_arg, &array_length_type_info); array_length_pos += is_method ? 1 : 0; arg_failed = !gjs_value_from_g_argument(context, &array_length, &array_length_type_info, &out_arg_cvalues[array_length_pos], TRUE); if (!arg_failed) { arg_failed = !gjs_value_from_explicit_array(context, &return_values[next_rval], &arg_type_info, arg, JSVAL_TO_INT(array_length)); } } else { arg_failed = !gjs_value_from_g_argument(context, &return_values[next_rval], &arg_type_info, arg, TRUE); } } if (arg_failed) postinvoke_release_failed = TRUE; /* For caller-allocates, what happens here is we allocate * a structure above, then gjs_value_from_g_argument calls * g_boxed_copy on it, and takes ownership of that. So * here we release the memory allocated above. It would be * better to special case this and directly hand JS the boxed * object and tell gjs_boxed it owns the memory, but for now * this works OK. We could also alloca() the structure instead * of slice allocating. */ if (g_arg_info_is_caller_allocates(&arg_info)) { GITypeTag type_tag; GIBaseInfo* interface_info; GIInfoType interface_type; gsize size; type_tag = g_type_info_get_tag(&arg_type_info); g_assert(type_tag == GI_TYPE_TAG_INTERFACE); interface_info = g_type_info_get_interface(&arg_type_info); interface_type = g_base_info_get_type(interface_info); if (interface_type == GI_INFO_TYPE_STRUCT) { size = g_struct_info_get_size((GIStructInfo*)interface_info); } else if (interface_type == GI_INFO_TYPE_UNION) { size = g_union_info_get_size((GIUnionInfo*)interface_info); } else { g_assert_not_reached(); } g_slice_free1(size, out_arg_cvalues[c_arg_pos].v_pointer); g_base_info_unref((GIBaseInfo*)interface_info); } /* Free GArgument, the jsval should have ref'd or copied it */ transfer = g_arg_info_get_ownership_transfer(&arg_info); if (!arg_failed) { if (array_length_pos >= 0) { gjs_g_argument_release_out_array(context, transfer, &arg_type_info, JSVAL_TO_INT(array_length), arg); } else { gjs_g_argument_release(context, transfer, &arg_type_info, arg); } } ++next_rval; } } if (postinvoke_release_failed) failed = TRUE; g_assert(failed || did_throw_gerror || next_rval == (guint8)function->js_out_argc); g_assert_cmpuint(c_arg_pos, ==, processed_c_args); if (function->js_out_argc > 0 && (!failed && !did_throw_gerror)) { /* if we have 1 return value or out arg, return that item * on its own, otherwise return a JavaScript array with * [return value, out arg 1, out arg 2, ...] */ if (js_rval) { if (function->js_out_argc == 1) { *js_rval = return_values[0]; } else { JSObject *array; array = JS_NewArrayObject(context, function->js_out_argc, return_values); if (array == NULL) { failed = TRUE; } else { *js_rval = OBJECT_TO_JSVAL(array); } } } gjs_unroot_value_locations(context, return_values, function->js_out_argc); if (r_value) { *r_value = return_gargument; } } if (!failed && did_throw_gerror) { gjs_throw_g_error(context, local_error); return JS_FALSE; } else if (failed) { return JS_FALSE; } else { return JS_TRUE; } } static JSBool function_call(JSContext *context, unsigned js_argc, jsval *vp) { jsval *js_argv = JS_ARGV(context, vp); JSObject *object = JS_THIS_OBJECT(context, vp); JSObject *callee = JSVAL_TO_OBJECT(JS_CALLEE(context, vp)); JSBool success; Function *priv; jsval retval; priv = priv_from_js(context, callee); gjs_debug_marshal(GJS_DEBUG_GFUNCTION, "Call callee %p priv %p this obj %p %s", callee, priv, obj, JS_GetTypeName(context, JS_TypeOfValue(context, OBJECT_TO_JSVAL(object)))); if (priv == NULL) return JS_TRUE; /* we are the prototype, or have the wrong class */ success = gjs_invoke_c_function(context, priv, object, js_argc, js_argv, &retval, NULL); if (success) JS_SET_RVAL(context, vp, retval); return success; } GJS_NATIVE_CONSTRUCTOR_DEFINE_ABSTRACT(function) /* Does not actually free storage for structure, just * reverses init_cached_function_data */ static void uninit_cached_function_data (Function *function) { if (function->info) g_base_info_unref( (GIBaseInfo*) function->info); if (function->param_types) g_free(function->param_types); g_function_invoker_destroy(&function->invoker); } static void function_finalize(JSFreeOp *fop, JSObject *obj) { Function *priv; priv = (Function *) JS_GetPrivate(obj); gjs_debug_lifecycle(GJS_DEBUG_GFUNCTION, "finalize, obj %p priv %p", obj, priv); if (priv == NULL) return; /* we are the prototype, not a real instance, so constructor never called */ uninit_cached_function_data(priv); GJS_DEC_COUNTER(function); g_slice_free(Function, priv); } static JSBool get_num_arguments (JSContext *context, JSObject **obj, jsid *id, jsval *vp) { int n_args, n_jsargs, i; jsval retval; Function *priv; priv = priv_from_js(context, *obj); if (priv == NULL) return JS_FALSE; n_args = g_callable_info_get_n_args(priv->info); n_jsargs = 0; for (i = 0; i < n_args; i++) { GIArgInfo arg_info; if (priv->param_types[i] == PARAM_SKIPPED) continue; g_callable_info_load_arg(priv->info, i, &arg_info); if (g_arg_info_get_direction(&arg_info) == GI_DIRECTION_OUT) continue; } retval = INT_TO_JSVAL(n_jsargs); JS_SET_RVAL(context, vp, retval); return JS_TRUE; } static JSBool function_to_string (JSContext *context, guint argc, jsval *vp) { Function *priv; gchar *string; gboolean free; JSObject *self; jsval retval; JSBool ret = JS_FALSE; int i, n_args, n_jsargs; GString *arg_names_str; gchar *arg_names; self = JS_THIS_OBJECT(context, vp); if (!self) { gjs_throw(context, "this cannot be null"); return JS_FALSE; } priv = priv_from_js (context, self); if (priv == NULL) { string = (gchar *) "function () {\n}"; free = FALSE; goto out; } free = TRUE; n_args = g_callable_info_get_n_args(priv->info); n_jsargs = 0; arg_names_str = g_string_new(""); for (i = 0; i < n_args; i++) { GIArgInfo arg_info; if (priv->param_types[i] == PARAM_SKIPPED) continue; g_callable_info_load_arg(priv->info, i, &arg_info); if (g_arg_info_get_direction(&arg_info) == GI_DIRECTION_OUT) continue; if (n_jsargs > 0) g_string_append(arg_names_str, ", "); n_jsargs++; g_string_append(arg_names_str, g_base_info_get_name(&arg_info)); } arg_names = g_string_free(arg_names_str, FALSE); if (g_base_info_get_type(priv->info) == GI_INFO_TYPE_FUNCTION) { string = g_strdup_printf("function %s(%s) {\n\t/* proxy for native symbol %s(); */\n}", g_base_info_get_name ((GIBaseInfo *) priv->info), arg_names, g_function_info_get_symbol ((GIFunctionInfo *) priv->info)); } else { string = g_strdup_printf("function %s(%s) {\n\t/* proxy for native symbol */\n}", g_base_info_get_name ((GIBaseInfo *) priv->info), arg_names); } g_free(arg_names); out: if (gjs_string_from_utf8(context, string, -1, &retval)) { JS_SET_RVAL(context, vp, retval); ret = JS_TRUE; } if (free) g_free(string); return ret; } /* The bizarre thing about this vtable is that it applies to both * instances of the object, and to the prototype that instances of the * class have. */ struct JSClass gjs_function_class = { "GIRepositoryFunction", /* means "new GIRepositoryFunction()" works */ JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, function_finalize, NULL, function_call, NULL, NULL, NULL }; JSPropertySpec gjs_function_proto_props[] = { { "length", 0, (JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED), JSOP_WRAPPER((JSPropertyOp)get_num_arguments), JSOP_WRAPPER(JS_StrictPropertyStub) }, { NULL } }; /* The original Function.prototype.toString complains when given a GIRepository function as an argument */ JSFunctionSpec gjs_function_proto_funcs[] = { JS_FN("toString", function_to_string, 0, 0), JS_FS_END }; static gboolean init_cached_function_data (JSContext *context, Function *function, GType gtype, GICallableInfo *info) { guint8 i, n_args; int array_length_pos; GError *error = NULL; GITypeInfo return_type; GIInfoType info_type; info_type = g_base_info_get_type((GIBaseInfo *)info); if (info_type == GI_INFO_TYPE_FUNCTION) { if (!g_function_info_prep_invoker((GIFunctionInfo *)info, &(function->invoker), &error)) { gjs_throw_g_error(context, error); return FALSE; } } else if (info_type == GI_INFO_TYPE_VFUNC) { gpointer addr; addr = g_vfunc_info_get_address((GIVFuncInfo *)info, gtype, &error); if (error != NULL) { if (error->code != G_INVOKE_ERROR_SYMBOL_NOT_FOUND) gjs_throw_g_error(context, error); g_clear_error(&error); return FALSE; } if (!g_function_invoker_new_for_address(addr, info, &(function->invoker), &error)) { gjs_throw_g_error(context, error); return FALSE; } } g_callable_info_load_return_type((GICallableInfo*)info, &return_type); if (g_type_info_get_tag(&return_type) != GI_TYPE_TAG_VOID) function->js_out_argc += 1; n_args = g_callable_info_get_n_args((GICallableInfo*) info); function->param_types = g_new0(GjsParamType, n_args); array_length_pos = g_type_info_get_array_length(&return_type); if (array_length_pos >= 0 && array_length_pos < n_args) function->param_types[array_length_pos] = PARAM_SKIPPED; for (i = 0; i < n_args; i++) { GIDirection direction; GIArgInfo arg_info; GITypeInfo type_info; int destroy = -1; int closure = -1; GITypeTag type_tag; if (function->param_types[i] == PARAM_SKIPPED) continue; g_callable_info_load_arg((GICallableInfo*) info, i, &arg_info); g_arg_info_load_type(&arg_info, &type_info); direction = g_arg_info_get_direction(&arg_info); type_tag = g_type_info_get_tag(&type_info); if (type_tag == GI_TYPE_TAG_INTERFACE) { GIBaseInfo* interface_info; GIInfoType interface_type; interface_info = g_type_info_get_interface(&type_info); interface_type = g_base_info_get_type(interface_info); if (interface_type == GI_INFO_TYPE_CALLBACK) { if (strcmp(g_base_info_get_name(interface_info), "DestroyNotify") == 0 && strcmp(g_base_info_get_namespace(interface_info), "GLib") == 0) { /* Skip GDestroyNotify if they appear before the respective callback */ function->param_types[i] = PARAM_SKIPPED; } else { function->param_types[i] = PARAM_CALLBACK; function->expected_js_argc += 1; destroy = g_arg_info_get_destroy(&arg_info); closure = g_arg_info_get_closure(&arg_info); if (destroy >= 0 && destroy < n_args) function->param_types[destroy] = PARAM_SKIPPED; if (closure >= 0 && closure < n_args) function->param_types[closure] = PARAM_SKIPPED; if (destroy >= 0 && closure < 0) { gjs_throw(context, "Function %s.%s has a GDestroyNotify but no user_data, not supported", g_base_info_get_namespace( (GIBaseInfo*) info), g_base_info_get_name( (GIBaseInfo*) info)); g_base_info_unref(interface_info); return JS_FALSE; } } } g_base_info_unref(interface_info); } else if (type_tag == GI_TYPE_TAG_ARRAY) { if (g_type_info_get_array_type(&type_info) == GI_ARRAY_TYPE_C) { array_length_pos = g_type_info_get_array_length(&type_info); if (array_length_pos >= 0 && array_length_pos < n_args) { GIArgInfo length_arg_info; g_callable_info_load_arg((GICallableInfo*) info, array_length_pos, &length_arg_info); if (g_arg_info_get_direction(&length_arg_info) != direction) { gjs_throw(context, "Function %s.%s has an array with different-direction length arg, not supported", g_base_info_get_namespace( (GIBaseInfo*) info), g_base_info_get_name( (GIBaseInfo*) info)); return JS_FALSE; } function->param_types[array_length_pos] = PARAM_SKIPPED; function->param_types[i] = PARAM_ARRAY; if (array_length_pos < i) { /* we already collected array_length_pos, remove it */ if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) function->expected_js_argc -= 1; if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) function->js_out_argc -= 1; } } } } if (function->param_types[i] == PARAM_NORMAL || function->param_types[i] == PARAM_ARRAY) { if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) function->expected_js_argc += 1; if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) function->js_out_argc += 1; } } function->info = info; g_base_info_ref((GIBaseInfo*) function->info); return JS_TRUE; } static JSObject* function_new(JSContext *context, GType gtype, GICallableInfo *info) { JSObject *function; JSObject *global; Function *priv; JSBool found; /* put constructor for GIRepositoryFunction() in the global namespace */ global = gjs_get_import_global(context); if (!JS_HasProperty(context, global, gjs_function_class.name, &found)) return NULL; if (!found) { JSObject *prototype; JSObject *parent_proto; jsval native_function; JS_GetProperty(context, global, "Function", &native_function); /* We take advantage from that fact that Function.__proto__ is Function.prototype */ JS_GetPrototype(context, JSVAL_TO_OBJECT(native_function), &parent_proto); prototype = JS_InitClass(context, global, /* parent prototype JSObject* for * prototype; NULL for * Object.prototype */ parent_proto, &gjs_function_class, /* constructor for instances (NULL for * none - just name the prototype like * Math - rarely correct) */ gjs_function_constructor, /* number of constructor args */ 0, /* props of prototype */ &gjs_function_proto_props[0], /* funcs of prototype */ &gjs_function_proto_funcs[0], /* props of constructor, MyConstructor.myprop */ NULL, /* funcs of constructor, MyConstructor.myfunc() */ NULL); if (prototype == NULL) g_error("Can't init class %s", gjs_function_class.name); gjs_debug(GJS_DEBUG_GFUNCTION, "Initialized class %s prototype %p", gjs_function_class.name, prototype); } function = JS_NewObject(context, &gjs_function_class, NULL, global); if (function == NULL) { gjs_debug(GJS_DEBUG_GFUNCTION, "Failed to construct function"); return NULL; } priv = g_slice_new0(Function); GJS_INC_COUNTER(function); g_assert(priv_from_js(context, function) == NULL); JS_SetPrivate(function, priv); gjs_debug_lifecycle(GJS_DEBUG_GFUNCTION, "function constructor, obj %p priv %p", function, priv); if (!init_cached_function_data(context, priv, gtype, (GICallableInfo *)info)) return NULL; return function; } JSObject* gjs_define_function(JSContext *context, JSObject *in_object, GType gtype, GICallableInfo *info) { JSObject *function = NULL; GIInfoType info_type; gchar *name; gboolean free_name; info_type = g_base_info_get_type((GIBaseInfo *)info); JS_BeginRequest(context); function = function_new(context, gtype, info); if (function == NULL) { gjs_move_exception(context, context); goto out; } if (info_type == GI_INFO_TYPE_FUNCTION) { name = (gchar *) g_base_info_get_name((GIBaseInfo*) info); free_name = FALSE; } else if (info_type == GI_INFO_TYPE_VFUNC) { name = g_strdup_printf("vfunc_%s", g_base_info_get_name((GIBaseInfo*) info)); free_name = TRUE; } else { g_assert_not_reached (); } if (!JS_DefineProperty(context, in_object, name, OBJECT_TO_JSVAL(function), NULL, NULL, GJS_MODULE_PROP_FLAGS)) { gjs_debug(GJS_DEBUG_GFUNCTION, "Failed to define function"); function = NULL; } if (free_name) g_free(name); out: JS_EndRequest(context); return function; } JSBool gjs_invoke_c_function_uncached (JSContext *context, GIFunctionInfo *info, JSObject *obj, unsigned argc, jsval *argv, jsval *rval) { Function function; JSBool result; memset (&function, 0, sizeof (Function)); if (!init_cached_function_data (context, &function, 0, info)) return JS_FALSE; result = gjs_invoke_c_function (context, &function, obj, argc, argv, rval, NULL); uninit_cached_function_data (&function); return result; } JSBool gjs_invoke_constructor_from_c (JSContext *context, JSObject *constructor, JSObject *obj, unsigned argc, jsval *argv, GArgument *rvalue) { Function *priv; priv = priv_from_js(context, constructor); return gjs_invoke_c_function(context, priv, obj, argc, argv, NULL, rvalue); } cjs-2.8.0/gi/gerror.cpp0000664000175000017500000004151612610172032013630 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include "boxed.h" #include "enumeration.h" #include "repo.h" #include "gerror.h" #include #include typedef struct { GIEnumInfo *info; GQuark domain; GError *gerror; /* NULL if we are the prototype and not an instance */ } Error; enum { PROP_0, PROP_DOMAIN, PROP_CODE, PROP_MESSAGE }; extern struct JSClass gjs_error_class; static void define_error_properties(JSContext *, JSObject *); GJS_DEFINE_PRIV_FROM_JS(Error, gjs_error_class) GJS_NATIVE_CONSTRUCTOR_DECLARE(error) { GJS_NATIVE_CONSTRUCTOR_VARIABLES(error) Error *priv; Error *proto_priv; JSObject *proto; jsid message_name, code_name; jsval v_message, v_code; gchar *message; /* Check early to avoid allocating memory for nothing */ if (argc != 1 || !JSVAL_IS_OBJECT(argv[0])) { gjs_throw(context, "Invalid parameters passed to GError constructor, expected one object"); return JS_FALSE; } GJS_NATIVE_CONSTRUCTOR_PRELUDE(error); priv = g_slice_new0(Error); GJS_INC_COUNTER(gerror); g_assert(priv_from_js(context, object) == NULL); JS_SetPrivate(object, priv); gjs_debug_lifecycle(GJS_DEBUG_GERROR, "GError constructor, obj %p priv %p", object, priv); JS_GetPrototype(context, object, &proto); gjs_debug_lifecycle(GJS_DEBUG_GERROR, "GError instance __proto__ is %p", proto); /* If we're the prototype, then post-construct we'll fill in priv->info. * If we are not the prototype, though, then we'll get ->info from the * prototype and then create a GObject if we don't have one already. */ proto_priv = priv_from_js(context, proto); if (proto_priv == NULL) { gjs_debug(GJS_DEBUG_GERROR, "Bad prototype set on GError? Must match JSClass of object. JS error should have been reported."); return JS_FALSE; } priv->info = proto_priv->info; g_base_info_ref( (GIBaseInfo*) priv->info); priv->domain = proto_priv->domain; message_name = gjs_context_get_const_string(context, GJS_STRING_MESSAGE); code_name = gjs_context_get_const_string(context, GJS_STRING_CODE); if (!gjs_object_require_property(context, JSVAL_TO_OBJECT(argv[0]), "GError constructor", message_name, &v_message)) return JS_FALSE; if (!gjs_object_require_property(context, JSVAL_TO_OBJECT(argv[0]), "GError constructor", code_name, &v_code)) return JS_FALSE; if (!gjs_string_to_utf8 (context, v_message, &message)) return JS_FALSE; priv->gerror = g_error_new_literal(priv->domain, JSVAL_TO_INT(v_code), message); g_free (message); /* We assume this error will be thrown in the same line as the constructor */ define_error_properties(context, object); GJS_NATIVE_CONSTRUCTOR_FINISH(boxed); return JS_TRUE; } static void error_finalize(JSFreeOp *fop, JSObject *obj) { Error *priv; priv = (Error*) JS_GetPrivate(obj); gjs_debug_lifecycle(GJS_DEBUG_GERROR, "finalize, obj %p priv %p", obj, priv); if (priv == NULL) return; /* wrong class? */ g_clear_error (&priv->gerror); if (priv->info) { g_base_info_unref( (GIBaseInfo*) priv->info); priv->info = NULL; } GJS_DEC_COUNTER(gerror); g_slice_free(Error, priv); } static JSBool error_get_domain(JSContext *context, JSObject **obj, jsid *id, jsval *vp) { Error *priv; priv = priv_from_js(context, *obj); if (priv == NULL) return JS_FALSE; *vp = INT_TO_JSVAL(priv->domain); return JS_TRUE; } static JSBool error_get_message(JSContext *context, JSObject **obj, jsid *id, jsval *vp) { Error *priv; priv = priv_from_js(context, *obj); if (priv == NULL) return JS_FALSE; if (priv->gerror == NULL) { /* Object is prototype, not instance */ gjs_throw(context, "Can't get a field from a GError prototype"); return JS_FALSE; } return gjs_string_from_utf8(context, priv->gerror->message, -1, vp); } static JSBool error_get_code(JSContext *context, JSObject **obj, jsid *id, jsval *vp) { Error *priv; priv = priv_from_js(context, *obj); if (priv == NULL) return JS_FALSE; if (priv->gerror == NULL) { /* Object is prototype, not instance */ gjs_throw(context, "Can't get a field from a GError prototype"); return JS_FALSE; } *vp = INT_TO_JSVAL(priv->gerror->code); return JS_TRUE; } static JSBool error_to_string(JSContext *context, unsigned argc, jsval *vp) { jsval v_self; JSObject *self; Error *priv; jsval v_out; gchar *descr; JSBool retval; v_self = JS_THIS(context, vp); if (!JSVAL_IS_OBJECT(v_self)) { /* Lie a bit here... */ gjs_throw(context, "GLib.Error.prototype.toString() called on a non object"); return JS_FALSE; } self = JSVAL_TO_OBJECT(v_self); priv = priv_from_js(context, self); if (priv == NULL) return JS_FALSE; v_out = JSVAL_VOID; retval = JS_FALSE; /* We follow the same pattern as standard JS errors, at the expense of hiding some useful information */ if (priv->gerror == NULL) { descr = g_strdup_printf("%s.%s", g_base_info_get_namespace(priv->info), g_base_info_get_name(priv->info)); if (!gjs_string_from_utf8(context, descr, -1, &v_out)) goto out; } else { descr = g_strdup_printf("%s.%s: %s", g_base_info_get_namespace(priv->info), g_base_info_get_name(priv->info), priv->gerror->message); if (!gjs_string_from_utf8(context, descr, -1, &v_out)) goto out; } JS_SET_RVAL(context, vp, v_out); retval = JS_TRUE; out: g_free(descr); return retval; } static JSBool error_constructor_value_of(JSContext *context, unsigned argc, jsval *vp) { jsval v_self, v_prototype; Error *priv; jsval v_out; jsid prototype_name; v_self = JS_THIS(context, vp); if (!JSVAL_IS_OBJECT(v_self)) { /* Lie a bit here... */ gjs_throw(context, "GLib.Error.valueOf() called on a non object"); return JS_FALSE; } prototype_name = gjs_context_get_const_string(context, GJS_STRING_PROTOTYPE); if (!gjs_object_require_property(context, JSVAL_TO_OBJECT(v_self), "constructor", prototype_name, &v_prototype)) return JS_FALSE; if (!JSVAL_IS_OBJECT(v_prototype)) { gjs_throw(context, "GLib.Error.valueOf() called on something that is not" " a constructor"); return JS_FALSE; } priv = priv_from_js(context, JSVAL_TO_OBJECT(v_prototype)); if (priv == NULL) return JS_FALSE; v_out = INT_TO_JSVAL(priv->domain); JS_SET_RVAL(context, vp, v_out); return TRUE; } /* The bizarre thing about this vtable is that it applies to both * instances of the object, and to the prototype that instances of the * class have. */ struct JSClass gjs_error_class = { "GLib_Error", JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE, JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, error_finalize, NULL, NULL, NULL, NULL, NULL }; /* We need to shadow all fields of GError, to prevent calling the getter from GBoxed (which would trash memory accessing the instance private data) */ JSPropertySpec gjs_error_proto_props[] = { { "domain", PROP_DOMAIN, GJS_MODULE_PROP_FLAGS | JSPROP_READONLY, JSOP_WRAPPER((JSPropertyOp)error_get_domain), JSOP_WRAPPER(JS_StrictPropertyStub) }, { "code", PROP_CODE, GJS_MODULE_PROP_FLAGS | JSPROP_READONLY, JSOP_WRAPPER((JSPropertyOp)error_get_code), JSOP_WRAPPER(JS_StrictPropertyStub) }, { "message", PROP_MESSAGE, GJS_MODULE_PROP_FLAGS | JSPROP_READONLY, JSOP_WRAPPER((JSPropertyOp)error_get_message), JSOP_WRAPPER(JS_StrictPropertyStub) }, { NULL } }; JSFunctionSpec gjs_error_proto_funcs[] = { { "toString", JSOP_WRAPPER((JSNative)error_to_string), 0, GJS_MODULE_PROP_FLAGS }, JS_FS_END }; static JSFunctionSpec gjs_error_constructor_funcs[] = { { "valueOf", JSOP_WRAPPER((JSNative)error_constructor_value_of), 0, GJS_MODULE_PROP_FLAGS }, JS_FS_END }; void gjs_define_error_class(JSContext *context, JSObject *in_object, GIEnumInfo *info) { const char *constructor_name; GIBoxedInfo *glib_error_info; JSObject *prototype, *parent_proto; JSObject *constructor; Error *priv; /* See the comment in gjs_define_boxed_class() for an * explanation of how this all works; Error is pretty much the * same as Boxed (except that we inherit from GLib.Error). */ constructor_name = g_base_info_get_name( (GIBaseInfo*) info); g_irepository_require(NULL, "GLib", "2.0", (GIRepositoryLoadFlags) 0, NULL); glib_error_info = (GIBoxedInfo*) g_irepository_find_by_name(NULL, "GLib", "Error"); parent_proto = gjs_lookup_generic_prototype(context, glib_error_info); g_base_info_unref((GIBaseInfo*)glib_error_info); if (!gjs_init_class_dynamic(context, in_object, parent_proto, g_base_info_get_namespace( (GIBaseInfo*) info), constructor_name, &gjs_error_class, gjs_error_constructor, 1, /* props of prototype */ &gjs_error_proto_props[0], /* funcs of prototype */ &gjs_error_proto_funcs[0], /* props of constructor, MyConstructor.myprop */ NULL, /* funcs of constructor, MyConstructor.myfunc() */ &gjs_error_constructor_funcs[0], &prototype, &constructor)) { gjs_log_exception(context); g_error("Can't init class %s", constructor_name); } GJS_INC_COUNTER(gerror); priv = g_slice_new0(Error); priv->info = info; g_base_info_ref( (GIBaseInfo*) priv->info); priv->domain = g_quark_from_string (g_enum_info_get_error_domain(priv->info)); JS_SetPrivate(prototype, priv); gjs_debug(GJS_DEBUG_GBOXED, "Defined class %s prototype is %p class %p in object %p", constructor_name, prototype, JS_GetClass(prototype), in_object); gjs_define_enum_values(context, constructor, priv->info); gjs_define_enum_static_methods(context, constructor, priv->info); } static GIEnumInfo * find_error_domain_info(GQuark domain) { GIEnumInfo *info; /* first an attempt without loading extra libraries */ info = g_irepository_find_by_error_domain(NULL, domain); if (info) return info; /* load standard stuff */ g_irepository_require(NULL, "GLib", "2.0", (GIRepositoryLoadFlags) 0, NULL); g_irepository_require(NULL, "GObject", "2.0", (GIRepositoryLoadFlags) 0, NULL); g_irepository_require(NULL, "Gio", "2.0", (GIRepositoryLoadFlags) 0, NULL); info = g_irepository_find_by_error_domain(NULL, domain); if (info) return info; /* last attempt: load GIRepository (for invoke errors, rarely needed) */ g_irepository_require(NULL, "GIRepository", "1.0", (GIRepositoryLoadFlags) 0, NULL); info = g_irepository_find_by_error_domain(NULL, domain); return info; } /* define properties that JS Error() expose, such as fileName, lineNumber and stack */ static void define_error_properties(JSContext *context, JSObject *obj) { jsid stack_name, filename_name, linenumber_name; jsval stack, fileName, lineNumber; if (!gjs_context_get_frame_info (context, &stack, &fileName, &lineNumber)) return; stack_name = gjs_context_get_const_string(context, GJS_STRING_STACK); filename_name = gjs_context_get_const_string(context, GJS_STRING_FILENAME); linenumber_name = gjs_context_get_const_string(context, GJS_STRING_LINE_NUMBER); JS_DefinePropertyById(context, obj, stack_name, stack, NULL, NULL, JSPROP_ENUMERATE); JS_DefinePropertyById(context, obj, filename_name, fileName, NULL, NULL, JSPROP_ENUMERATE); JS_DefinePropertyById(context, obj, linenumber_name, lineNumber, NULL, NULL, JSPROP_ENUMERATE); } JSObject* gjs_error_from_gerror(JSContext *context, GError *gerror, gboolean add_stack) { JSObject *obj; JSObject *proto; Error *priv; Error *proto_priv; GIEnumInfo *info; if (gerror == NULL) return NULL; info = find_error_domain_info(gerror->domain); if (!info) { /* We don't have error domain metadata */ /* Marshal the error as a plain GError */ GIBaseInfo *glib_boxed; JSObject *retval; glib_boxed = g_irepository_find_by_name(NULL, "GLib", "Error"); retval = gjs_boxed_from_c_struct(context, glib_boxed, gerror, (GjsBoxedCreationFlags) 0); g_base_info_unref(glib_boxed); return retval; } gjs_debug_marshal(GJS_DEBUG_GBOXED, "Wrapping struct %s %p with JSObject", g_base_info_get_name((GIBaseInfo *)info), gboxed); proto = gjs_lookup_generic_prototype(context, info); proto_priv = priv_from_js(context, proto); obj = JS_NewObjectWithGivenProto(context, JS_GetClass(proto), proto, gjs_get_import_global (context)); GJS_INC_COUNTER(gerror); priv = g_slice_new0(Error); JS_SetPrivate(obj, priv); priv->info = info; priv->domain = proto_priv->domain; g_base_info_ref( (GIBaseInfo*) priv->info); priv->gerror = g_error_copy(gerror); if (add_stack) define_error_properties(context, obj); return obj; } GError* gjs_gerror_from_error(JSContext *context, JSObject *obj) { Error *priv; if (obj == NULL) return NULL; /* If this is a plain GBoxed (i.e. a GError without metadata), delegate marshalling. */ if (gjs_typecheck_boxed (context, obj, NULL, G_TYPE_ERROR, JS_FALSE)) return (GError*) gjs_c_struct_from_boxed (context, obj); priv = priv_from_js(context, obj); if (priv == NULL) return NULL; if (priv->gerror == NULL) { gjs_throw(context, "Object is %s.%s.prototype, not an object instance - cannot convert to a boxed instance", g_base_info_get_namespace( (GIBaseInfo*) priv->info), g_base_info_get_name( (GIBaseInfo*) priv->info)); return NULL; } return priv->gerror; } JSBool gjs_typecheck_gerror (JSContext *context, JSObject *obj, JSBool throw_error) { if (gjs_typecheck_boxed (context, obj, NULL, G_TYPE_ERROR, JS_FALSE)) return TRUE; return do_base_typecheck(context, obj, throw_error); } cjs-2.8.0/gi/keep-alive.h0000664000175000017500000001036112610172032014011 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_KEEP_ALIVE_H__ #define __GJS_KEEP_ALIVE_H__ #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS /* This is an alternative to JS_AddRoot(). * * This "keep alive" object holds a collection of child objects and * traces them when GC occurs. If the keep alive object is collected, * it calls a notification function on all the child objects. * * The "global keep alive" is stuck on the global object as a property, * so its children only get notified when the entire JSContext is * blown away (or its global object replaced, I suppose, but that * won't happen afaik). * * The problem with JS_AddRoot() is that it has no notification when the * JSContext is destroyed. Also, it can be annoying to wrap a C type purely * to put a finalizer on it, this lets you avoid that. * * All three fields (notify, child, and data) are optional, so you can have * no JSObject - just notification+data - and you can have no notifier, * only the keep-alive capability. */ typedef void (* GjsUnrootedFunc) (JSObject *obj, void *data); JSObject* gjs_keep_alive_new (JSContext *context); void gjs_keep_alive_add_child (JSObject *keep_alive, GjsUnrootedFunc notify, JSObject *child, void *data); void gjs_keep_alive_remove_child (JSObject *keep_alive, GjsUnrootedFunc notify, JSObject *child, void *data); JSObject* gjs_keep_alive_get_global (JSContext *context); JSObject* gjs_keep_alive_get_global_if_exists (JSContext *context); void gjs_keep_alive_add_global_child (JSContext *context, GjsUnrootedFunc notify, JSObject *child, void *data); void gjs_keep_alive_remove_global_child (JSContext *context, GjsUnrootedFunc notify, JSObject *child, void *data); typedef struct GjsKeepAliveIter GjsKeepAliveIter; struct GjsKeepAliveIter { gpointer dummy[4]; guint v; GHashTableIter dummyhiter; }; void gjs_keep_alive_iterator_init (GjsKeepAliveIter *iter, JSObject *keep_alive); gboolean gjs_keep_alive_iterator_next (GjsKeepAliveIter *iter, GjsUnrootedFunc notify_func, JSObject **out_child, void **out_data); G_END_DECLS #endif /* __GJS_KEEP_ALIVE_H__ */ cjs-2.8.0/gi/proxyutils.h0000664000175000017500000000327712610172032014241 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_PROXYUTILS_H__ #define __GJS_PROXYUTILS_H__ #include "cjs/jsapi-util.h" #include G_BEGIN_DECLS JSBool _gjs_proxy_to_string_func(JSContext *context, JSObject *this_obj, const char *objtype, GIBaseInfo *info, GType gtype, gpointer native_address, jsval *ret); G_END_DECLS #endif /* __GJS_OBJECT_H__ */ cjs-2.8.0/gi/fundamental.cpp0000664000175000017500000007733112610172032014632 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2013 Intel Corporation * Copyright (c) 2008-2010 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include "fundamental.h" #include "arg.h" #include "object.h" #include "boxed.h" #include "repo.h" #include "function.h" #include "gtype.h" #include "proxyutils.h" #include #include #include #include /* * Structure allocated for prototypes. */ typedef struct _Fundamental { /* instance info */ void *gfundamental; struct _Fundamental *prototype; /* NULL if prototype */ /* prototype info */ GIObjectInfo *info; GType gtype; GIObjectInfoRefFunction ref_function; GIObjectInfoUnrefFunction unref_function; GIObjectInfoGetValueFunction get_value_function; GIObjectInfoSetValueFunction set_value_function; jsid constructor_name; GICallableInfo *constructor_info; } Fundamental; /* * Structure allocated for instances. */ typedef struct { void *gfundamental; struct _Fundamental *prototype; } FundamentalInstance; extern struct JSClass gjs_fundamental_instance_class; GJS_DEFINE_PRIV_FROM_JS(FundamentalInstance, gjs_fundamental_instance_class) static GQuark gjs_fundamental_table_quark (void) { static GQuark val = 0; if (!val) val = g_quark_from_static_string("gjs::fundamental-table"); return val; } static GHashTable * _ensure_mapping_table(GjsContext *context) { GHashTable *table = (GHashTable *) g_object_get_qdata ((GObject *) context, gjs_fundamental_table_quark()); if (G_UNLIKELY(table == NULL)) { table = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, NULL); g_object_set_qdata_full((GObject *) context, gjs_fundamental_table_quark(), table, (GDestroyNotify) g_hash_table_unref); } return table; } static void _fundamental_add_object(void *native_object, JSObject *js_object) { GHashTable *table = _ensure_mapping_table(gjs_context_get_current()); g_hash_table_insert(table, native_object, js_object); } static void _fundamental_remove_object(void *native_object) { GHashTable *table = _ensure_mapping_table(gjs_context_get_current()); g_hash_table_remove(table, native_object); } static JSObject * _fundamental_lookup_object(void *native_object) { GHashTable *table = _ensure_mapping_table(gjs_context_get_current()); return (JSObject *) g_hash_table_lookup(table, native_object); } /**/ static inline Fundamental * proto_priv_from_js(JSContext *context, JSObject *obj) { JSObject *proto; JS_GetPrototype(context, obj, &proto); return (Fundamental*) priv_from_js(context, proto); } static FundamentalInstance * init_fundamental_instance(JSContext *context, JSObject *object) { Fundamental *proto_priv; FundamentalInstance *priv; JS_BeginRequest(context); priv = g_slice_new0(FundamentalInstance); GJS_INC_COUNTER(fundamental); g_assert(priv_from_js(context, object) == NULL); JS_SetPrivate(object, priv); gjs_debug_lifecycle(GJS_DEBUG_GFUNDAMENTAL, "fundamental instance constructor, obj %p priv %p proto %p ", object, priv, JS_GetPrototype (object)); proto_priv = proto_priv_from_js(context, object); g_assert(proto_priv != NULL); priv->prototype = proto_priv; JS_EndRequest(context); return priv; } static void associate_js_instance_to_fundamental(JSContext *context, JSObject *object, void *gfundamental, gboolean owned_ref) { FundamentalInstance *priv; priv = priv_from_js(context, object); priv->gfundamental = gfundamental; g_assert(_fundamental_lookup_object(gfundamental) == NULL); _fundamental_add_object(gfundamental, object); gjs_debug_lifecycle(GJS_DEBUG_GFUNDAMENTAL, "associated JSObject %p with fundamental %p", object, gfundamental); if (!owned_ref) priv->prototype->ref_function(gfundamental); } /**/ /* Find the first constructor */ static GIFunctionInfo * find_fundamental_constructor(JSContext *context, GIObjectInfo *info, jsid *constructor_name) { int i, n_methods; n_methods = g_object_info_get_n_methods(info); for (i = 0; i < n_methods; ++i) { GIFunctionInfo *func_info; GIFunctionInfoFlags flags; func_info = g_object_info_get_method(info, i); flags = g_function_info_get_flags(func_info); if ((flags & GI_FUNCTION_IS_CONSTRUCTOR) != 0) { const char *name; name = g_base_info_get_name((GIBaseInfo *) func_info); *constructor_name = gjs_intern_string_to_id(context, name); return func_info; } g_base_info_unref((GIBaseInfo *) func_info); } return NULL; } /**/ static JSBool gjs_define_static_methods(JSContext *context, JSObject *constructor, GType gtype, GIObjectInfo *object_info) { int i; int n_methods; n_methods = g_object_info_get_n_methods(object_info); for (i = 0; i < n_methods; i++) { GIFunctionInfo *meth_info; GIFunctionInfoFlags flags; meth_info = g_object_info_get_method(object_info, i); flags = g_function_info_get_flags(meth_info); /* Anything that isn't a method we put on the prototype of the * constructor. This includes introspection * methods, as well as the forthcoming "static methods" * support. We may want to change this to use * GI_FUNCTION_IS_CONSTRUCTOR and GI_FUNCTION_IS_STATIC or the * like in the near future. */ if (!(flags & GI_FUNCTION_IS_METHOD)) { gjs_define_function(context, constructor, gtype, (GICallableInfo *)meth_info); } g_base_info_unref((GIBaseInfo *) meth_info); } return JS_TRUE; } static JSBool fundamental_instance_new_resolve_interface(JSContext *context, JSObject *obj, JSObject **objp, Fundamental *proto_priv, char *name) { GIFunctionInfo *method_info; JSBool ret; GType *interfaces; guint n_interfaces; guint i; ret = JS_TRUE; interfaces = g_type_interfaces(proto_priv->gtype, &n_interfaces); for (i = 0; i < n_interfaces; i++) { GIBaseInfo *base_info; GIInterfaceInfo *iface_info; base_info = g_irepository_find_by_gtype(g_irepository_get_default(), interfaces[i]); if (base_info == NULL) continue; /* An interface GType ought to have interface introspection info */ g_assert(g_base_info_get_type(base_info) == GI_INFO_TYPE_INTERFACE); iface_info = (GIInterfaceInfo *) base_info; method_info = g_interface_info_find_method(iface_info, name); g_base_info_unref(base_info); if (method_info != NULL) { if (gjs_define_function(context, obj, proto_priv->gtype, (GICallableInfo *) method_info)) { *objp = obj; } else { ret = JS_FALSE; } g_base_info_unref((GIBaseInfo *) method_info); } } g_free(interfaces); return ret; } /* * Like JSResolveOp, but flags provide contextual information as follows: * * JSRESOLVE_QUALIFIED a qualified property id: obj.id or obj[id], not id * JSRESOLVE_ASSIGNING obj[id] is on the left-hand side of an assignment * JSRESOLVE_DETECTING 'if (o.p)...' or similar detection opcode sequence * JSRESOLVE_DECLARING var, const, or fundamental prolog declaration opcode * JSRESOLVE_CLASSNAME class name used when constructing * * The *objp out parameter, on success, should be null to indicate that id * was not resolved; and non-null, referring to obj or one of its prototypes, * if id was resolved. */ static JSBool fundamental_instance_new_resolve(JSContext *context, JSObject **obj, jsid *id, unsigned flags, JSObject **objp) { FundamentalInstance *priv; char *name; JSBool ret = JS_FALSE; *objp = NULL; if (!gjs_get_string_id(context, *id, &name)) return JS_TRUE; /* not resolved, but no error */ priv = priv_from_js(context, *obj); gjs_debug_jsprop(GJS_DEBUG_GFUNDAMENTAL, "Resolve prop '%s' hook obj %p priv %p", name, *obj, priv); if (priv == NULL) goto out; /* wrong class */ if (priv->prototype == NULL) { /* We are the prototype, so look for methods and other class properties */ Fundamental *proto_priv = (Fundamental *) priv; GIFunctionInfo *method_info; method_info = g_object_info_find_method((GIStructInfo*) proto_priv->info, name); if (method_info != NULL) { const char *method_name; #if GJS_VERBOSE_ENABLE_GI_USAGE _gjs_log_info_usage((GIBaseInfo *) method_info); #endif method_name = g_base_info_get_name((GIBaseInfo *) method_info); /* we do not define deprecated methods in the prototype */ if (g_base_info_is_deprecated((GIBaseInfo *) method_info)) { gjs_debug(GJS_DEBUG_GFUNDAMENTAL, "Ignoring definition of deprecated method %s in prototype %s.%s", method_name, g_base_info_get_namespace((GIBaseInfo *) proto_priv->info), g_base_info_get_name((GIBaseInfo *) proto_priv->info)); g_base_info_unref((GIBaseInfo *) method_info); ret = JS_TRUE; goto out; } gjs_debug(GJS_DEBUG_GFUNDAMENTAL, "Defining method %s in prototype for %s.%s", method_name, g_base_info_get_namespace((GIBaseInfo *) proto_priv->info), g_base_info_get_name((GIBaseInfo *) proto_priv->info)); if (gjs_define_function(context, *obj, proto_priv->gtype, method_info) == NULL) { g_base_info_unref((GIBaseInfo *) method_info); goto out; } *objp = *obj; g_base_info_unref((GIBaseInfo *) method_info); } ret = fundamental_instance_new_resolve_interface(context, *obj, objp, proto_priv, name); } else { /* We are an instance, not a prototype, so look for * per-instance props that we want to define on the * JSObject. Generally we do not want to cache these in JS, we * want to always pull them from the C object, or JS would not * see any changes made from C. So we use the get/set prop * hooks, not this resolve hook. */ } ret = JS_TRUE; out: g_free(name); return ret; } static JSBool fundamental_invoke_constructor(FundamentalInstance *priv, JSContext *context, JSObject *obj, unsigned argc, jsval *argv, GArgument *rvalue) { jsval js_constructor, js_constructor_func; jsid constructor_const; JSBool ret = JS_FALSE; constructor_const = gjs_context_get_const_string(context, GJS_STRING_CONSTRUCTOR); if (!gjs_object_require_property(context, obj, NULL, constructor_const, &js_constructor) || priv->prototype->constructor_name == JSID_VOID) { gjs_throw (context, "Couldn't find a constructor for type %s.%s", g_base_info_get_namespace((GIBaseInfo*) priv->prototype->info), g_base_info_get_name((GIBaseInfo*) priv->prototype->info)); goto end; } if (!gjs_object_require_property(context, JSVAL_TO_OBJECT(js_constructor), NULL, priv->prototype->constructor_name, &js_constructor_func)) { gjs_throw (context, "Couldn't find a constructor for type %s.%s", g_base_info_get_namespace((GIBaseInfo*) priv->prototype->info), g_base_info_get_name((GIBaseInfo*) priv->prototype->info)); goto end; } ret = gjs_invoke_constructor_from_c(context, JSVAL_TO_OBJECT(js_constructor_func), obj, argc, argv, rvalue); end: return ret; } /* If we set JSCLASS_CONSTRUCT_PROTOTYPE flag, then this is called on * the prototype in addition to on each instance. When called on the * prototype, "obj" is the prototype, and "retval" is the prototype * also, but can be replaced with another object to use instead as the * prototype. If we don't set JSCLASS_CONSTRUCT_PROTOTYPE we can * identify the prototype as an object of our class with NULL private * data. */ GJS_NATIVE_CONSTRUCTOR_DECLARE(fundamental_instance) { GJS_NATIVE_CONSTRUCTOR_VARIABLES(fundamental_instance) FundamentalInstance *priv; GArgument ret_value; GITypeInfo return_info; GJS_NATIVE_CONSTRUCTOR_PRELUDE(fundamental_instance); priv = init_fundamental_instance(context, object); gjs_debug_lifecycle(GJS_DEBUG_GFUNDAMENTAL, "fundamental constructor, obj %p priv %p", object, priv); if (!fundamental_invoke_constructor(priv, context, object, argc, argv, &ret_value)) return JS_FALSE; associate_js_instance_to_fundamental(context, object, ret_value.v_pointer, FALSE); g_callable_info_load_return_type((GICallableInfo*) priv->prototype->constructor_info, &return_info); if (!gjs_g_argument_release (context, g_callable_info_get_caller_owns((GICallableInfo*) priv->prototype->constructor_info), &return_info, &ret_value)) return JS_FALSE; GJS_NATIVE_CONSTRUCTOR_FINISH(fundamental_instance); return JS_TRUE; } static void fundamental_finalize(JSFreeOp *fop, JSObject *obj) { FundamentalInstance *priv; priv = (FundamentalInstance *) JS_GetPrivate(obj); gjs_debug_lifecycle(GJS_DEBUG_GFUNDAMENTAL, "finalize, obj %p priv %p", obj, priv); if (priv == NULL) return; /* wrong class? */ if (priv->prototype) { if (priv->gfundamental) { _fundamental_remove_object(priv->gfundamental); priv->prototype->unref_function(priv->gfundamental); priv->gfundamental = NULL; } g_slice_free(FundamentalInstance, priv); GJS_DEC_COUNTER(fundamental); } else { Fundamental *proto_priv = (Fundamental *) priv; /* Only unref infos when freeing the prototype */ if (proto_priv->constructor_info) g_base_info_unref (proto_priv->constructor_info); proto_priv->constructor_info = NULL; if (proto_priv->info) g_base_info_unref((GIBaseInfo *) proto_priv->info); proto_priv->info = NULL; g_slice_free(Fundamental, proto_priv); } } static JSBool to_string_func(JSContext *context, unsigned argc, jsval *vp) { JSObject *obj = JS_THIS_OBJECT(context, vp); FundamentalInstance *priv; JSBool ret = JS_FALSE; jsval retval; if (!priv_from_js_with_typecheck(context, obj, &priv)) goto out; if (!priv->prototype) { Fundamental *proto_priv = (Fundamental *) priv; if (!_gjs_proxy_to_string_func(context, obj, "fundamental", (GIBaseInfo *) proto_priv->info, proto_priv->gtype, proto_priv->gfundamental, &retval)) goto out; } else { if (!_gjs_proxy_to_string_func(context, obj, "fundamental", (GIBaseInfo *) priv->prototype->info, priv->prototype->gtype, priv->gfundamental, &retval)) goto out; } ret = JS_TRUE; JS_SET_RVAL(context, vp, retval); out: return ret; } /* The bizarre thing about this vtable is that it applies to both * instances of the object, and to the prototype that instances of the * class have. * * Also, there's a constructor field in here, but as far as I can * tell, it would only be used if no constructor were provided to * JS_InitClass. The constructor from JS_InitClass is not applied to * the prototype unless JSCLASS_CONSTRUCT_PROTOTYPE is in flags. * * We allocate 1 reserved slot; this is typically unused, but if the * fundamental is for a nested structure inside a parent structure, the * reserved slot is used to hold onto the parent Javascript object and * make sure it doesn't get freed. */ struct JSClass gjs_fundamental_instance_class = { "GFundamental_Object", JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE, JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, (JSResolveOp) fundamental_instance_new_resolve, /* needs cast since it's the new resolve signature */ JS_ConvertStub, fundamental_finalize, NULL, NULL, NULL, NULL, NULL }; static JSPropertySpec gjs_fundamental_instance_proto_props[] = { { NULL } }; static JSFunctionSpec gjs_fundamental_instance_proto_funcs[] = { { "toString", JSOP_WRAPPER((JSNative)to_string_func), 0, 0 }, { NULL } }; static JSObject * gjs_lookup_fundamental_prototype(JSContext *context, GIObjectInfo *info, GType gtype) { JSObject *in_object; JSObject *constructor; const char *constructor_name; jsval value; if (info) { in_object = gjs_lookup_namespace_object(context, (GIBaseInfo*) info); constructor_name = g_base_info_get_name((GIBaseInfo*) info); } else { in_object = gjs_lookup_private_namespace(context); constructor_name = g_type_name(gtype); } if (G_UNLIKELY (!in_object)) return NULL; if (!JS_GetProperty(context, in_object, constructor_name, &value)) return NULL; if (JSVAL_IS_VOID(value)) { /* In case we're looking for a private type, and we don't find it, we need to define it first. */ gjs_define_fundamental_class(context, in_object, info, &constructor, NULL); } else { if (G_UNLIKELY (!JSVAL_IS_OBJECT(value) || JSVAL_IS_NULL(value))) return NULL; constructor = JSVAL_TO_OBJECT(value); } g_assert(constructor != NULL); if (!gjs_object_get_property_const(context, constructor, GJS_STRING_PROTOTYPE, &value)) return NULL; if (G_UNLIKELY (!JSVAL_IS_OBJECT(value))) return NULL; return JSVAL_TO_OBJECT(value); } static JSObject* gjs_lookup_fundamental_prototype_from_gtype(JSContext *context, GType gtype) { GIObjectInfo *info; JSObject *proto; info = (GIObjectInfo *) g_irepository_find_by_gtype(g_irepository_get_default(), gtype); proto = gjs_lookup_fundamental_prototype(context, info, gtype); if (info) g_base_info_unref((GIBaseInfo*)info); return proto; } JSBool gjs_define_fundamental_class(JSContext *context, JSObject *in_object, GIObjectInfo *info, JSObject **constructor_p, JSObject **prototype_p) { const char *constructor_name; JSObject *prototype; jsval value; jsid js_constructor_name = JSID_VOID; JSObject *parent_proto; Fundamental *priv; JSObject *constructor; GType parent_gtype; GType gtype; GIFunctionInfo *constructor_info; /* See the comment in gjs_define_object_class() for an explanation * of how this all works; Fundamental is pretty much the same as * Object. */ constructor_name = g_base_info_get_name((GIBaseInfo *) info); constructor_info = find_fundamental_constructor(context, info, &js_constructor_name); gtype = g_registered_type_info_get_g_type (info); parent_gtype = g_type_parent(gtype); parent_proto = NULL; if (parent_gtype != G_TYPE_INVALID) parent_proto = gjs_lookup_fundamental_prototype_from_gtype(context, parent_gtype); if (!gjs_init_class_dynamic(context, in_object, /* parent prototype JSObject* for * prototype; NULL for * Object.prototype */ parent_proto, g_base_info_get_namespace((GIBaseInfo *) info), constructor_name, &gjs_fundamental_instance_class, gjs_fundamental_instance_constructor, /* number of constructor args (less can be passed) */ constructor_info != NULL ? g_callable_info_get_n_args((GICallableInfo *) constructor_info) : 0, /* props of prototype */ parent_proto ? NULL : &gjs_fundamental_instance_proto_props[0], /* funcs of prototype */ parent_proto ? NULL : &gjs_fundamental_instance_proto_funcs[0], /* props of constructor, MyConstructor.myprop */ NULL, /* funcs of constructor, MyConstructor.myfunc() */ NULL, &prototype, &constructor)) { gjs_log_exception(context); g_error("Can't init class %s", constructor_name); } /* Put the info in the prototype */ priv = g_slice_new0(Fundamental); g_assert(priv != NULL); g_assert(priv->info == NULL); priv->info = g_base_info_ref((GIBaseInfo *) info); priv->gtype = gtype; priv->constructor_name = js_constructor_name; priv->constructor_info = constructor_info; priv->ref_function = g_object_info_get_ref_function_pointer(info); g_assert(priv->ref_function != NULL); priv->unref_function = g_object_info_get_unref_function_pointer(info); g_assert(priv->unref_function != NULL); priv->set_value_function = g_object_info_get_set_value_function_pointer(info); g_assert(priv->set_value_function != NULL); priv->get_value_function = g_object_info_get_get_value_function_pointer(info); g_assert(priv->get_value_function != NULL); JS_SetPrivate(prototype, priv); gjs_debug(GJS_DEBUG_GFUNDAMENTAL, "Defined class %s prototype is %p class %p in object %p constructor %s.%s.%s", constructor_name, prototype, JS_GetClass(prototype), in_object, constructor_info != NULL ? g_base_info_get_namespace(constructor_info) : "unknown", constructor_info != NULL ? g_base_info_get_name(g_base_info_get_container(constructor_info)) : "unknown", constructor_info != NULL ? g_base_info_get_name(constructor_info) : "unknown"); if (g_object_info_get_n_fields(priv->info) > 0) { gjs_debug(GJS_DEBUG_GFUNDAMENTAL, "Fundamental type '%s.%s' apparently has accessible fields. " "Gjs has no support for this yet, ignoring these.", g_base_info_get_namespace((GIBaseInfo *)priv->info), g_base_info_get_name ((GIBaseInfo *)priv->info)); } gjs_define_static_methods(context, constructor, gtype, info); value = OBJECT_TO_JSVAL(gjs_gtype_create_gtype_wrapper(context, gtype)); JS_DefineProperty(context, constructor, "$gtype", value, NULL, NULL, JSPROP_PERMANENT); if (constructor_p) *constructor_p = constructor; if (prototype_p) *prototype_p = prototype; return JS_TRUE; } JSObject* gjs_object_from_g_fundamental(JSContext *context, GIObjectInfo *info, void *gfundamental) { JSObject *proto; JSObject *object; if (gfundamental == NULL) return NULL; object = _fundamental_lookup_object(gfundamental); if (object) return object; gjs_debug_marshal(GJS_DEBUG_GFUNDAMENTAL, "Wrapping fundamental %s.%s %p with JSObject", g_base_info_get_namespace((GIBaseInfo *) info), g_base_info_get_name((GIBaseInfo *) info), gfundamental); proto = gjs_lookup_fundamental_prototype_from_gtype(context, G_TYPE_FROM_INSTANCE(gfundamental)); object = JS_NewObjectWithGivenProto(context, JS_GetClass(proto), proto, gjs_get_import_global(context)); if (object == NULL) goto out; init_fundamental_instance(context, object); associate_js_instance_to_fundamental(context, object, gfundamental, FALSE); out: return object; } JSObject * gjs_fundamental_from_g_value(JSContext *context, const GValue *value, GType gtype) { JSObject *proto; Fundamental *proto_priv; void *fobj; proto = gjs_lookup_fundamental_prototype_from_gtype(context, gtype); if (!proto) return NULL; proto_priv = (Fundamental *) priv_from_js(context, proto); fobj = proto_priv->get_value_function(value); if (!fobj) { gjs_throw(context, "Failed to convert GValue to a fundamental instance"); return NULL; } return gjs_object_from_g_fundamental(context, proto_priv->info, fobj); } void* gjs_g_fundamental_from_object(JSContext *context, JSObject *obj) { FundamentalInstance *priv; if (obj == NULL) return NULL; priv = priv_from_js(context, obj); if (priv == NULL) { gjs_throw(context, "No introspection information for %p", obj); return NULL; } if (priv->gfundamental == NULL) { gjs_throw(context, "Object is %s.%s.prototype, not an object instance - cannot convert to a fundamental instance", g_base_info_get_namespace((GIBaseInfo *) priv->prototype->info), g_base_info_get_name((GIBaseInfo *) priv->prototype->info)); return NULL; } return priv->gfundamental; } JSBool gjs_typecheck_is_fundamental(JSContext *context, JSObject *object, JSBool throw_error) { return do_base_typecheck(context, object, throw_error); } JSBool gjs_typecheck_fundamental(JSContext *context, JSObject *object, GType expected_gtype, JSBool throw_error) { FundamentalInstance *priv; JSBool result; if (!do_base_typecheck(context, object, throw_error)) return JS_FALSE; priv = priv_from_js(context, object); g_assert(priv != NULL); if (priv->gfundamental == NULL) { if (throw_error) { Fundamental *proto_priv = (Fundamental *) priv; gjs_throw(context, "Object is %s.%s.prototype, not an fundamental instance - cannot convert to void*", proto_priv->info ? g_base_info_get_namespace((GIBaseInfo *) proto_priv->info) : "", proto_priv->info ? g_base_info_get_name((GIBaseInfo *) proto_priv->info) : g_type_name(proto_priv->gtype)); } return JS_FALSE; } if (expected_gtype != G_TYPE_NONE) result = g_type_is_a(priv->prototype->gtype, expected_gtype); else result = JS_TRUE; if (!result && throw_error) { if (priv->prototype->info) { gjs_throw_custom(context, "TypeError", "Object is of type %s.%s - cannot convert to %s", g_base_info_get_namespace((GIBaseInfo *) priv->prototype->info), g_base_info_get_name((GIBaseInfo *) priv->prototype->info), g_type_name(expected_gtype)); } else { gjs_throw_custom(context, "TypeError", "Object is of type %s - cannot convert to %s", g_type_name(priv->prototype->gtype), g_type_name(expected_gtype)); } } return result; } void * gjs_fundamental_ref(JSContext *context, void *gfundamental) { JSObject *proto; Fundamental *proto_priv; proto = gjs_lookup_fundamental_prototype_from_gtype(context, G_TYPE_FROM_INSTANCE(gfundamental)); proto_priv = (Fundamental *) priv_from_js(context, proto); return proto_priv->ref_function(gfundamental); } void gjs_fundamental_unref(JSContext *context, void *gfundamental) { JSObject *proto; Fundamental *proto_priv; proto = gjs_lookup_fundamental_prototype_from_gtype(context, G_TYPE_FROM_INSTANCE(gfundamental)); proto_priv = (Fundamental *) priv_from_js(context, proto); proto_priv->unref_function(gfundamental); } cjs-2.8.0/gi/boxed.cpp0000664000175000017500000012617112610172032013432 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include "boxed.h" #include "arg.h" #include "object.h" #include #include #include "repo.h" #include "proxyutils.h" #include "function.h" #include "gtype.h" #include #include typedef struct { /* prototype info */ GIBoxedInfo *info; GType gtype; gint zero_args_constructor; /* -1 if none */ jsid zero_args_constructor_name; gint default_constructor; /* -1 if none */ jsid default_constructor_name; /* instance info */ void *gboxed; /* NULL if we are the prototype and not an instance */ guint can_allocate_directly : 1; guint allocated_directly : 1; guint not_owning_gboxed : 1; /* if set, the JS wrapper does not own the reference to the C gboxed */ } Boxed; static gboolean struct_is_simple(GIStructInfo *info); static JSBool boxed_set_field_from_value(JSContext *context, Boxed *priv, GIFieldInfo *field_info, jsval value); extern struct JSClass gjs_boxed_class; GJS_DEFINE_PRIV_FROM_JS(Boxed, gjs_boxed_class) static JSBool gjs_define_static_methods(JSContext *context, JSObject *constructor, GType gtype, GIStructInfo *boxed_info) { int i; int n_methods; n_methods = g_struct_info_get_n_methods(boxed_info); for (i = 0; i < n_methods; i++) { GIFunctionInfo *meth_info; GIFunctionInfoFlags flags; meth_info = g_struct_info_get_method (boxed_info, i); flags = g_function_info_get_flags (meth_info); /* Anything that isn't a method we put on the prototype of the * constructor. This includes introspection * methods, as well as the forthcoming "static methods" * support. We may want to change this to use * GI_FUNCTION_IS_CONSTRUCTOR and GI_FUNCTION_IS_STATIC or the * like in the near future. */ if (!(flags & GI_FUNCTION_IS_METHOD)) { gjs_define_function(context, constructor, gtype, (GICallableInfo *)meth_info); } g_base_info_unref((GIBaseInfo*) meth_info); } return JS_TRUE; } /* * Like JSResolveOp, but flags provide contextual information as follows: * * JSRESOLVE_QUALIFIED a qualified property id: obj.id or obj[id], not id * JSRESOLVE_ASSIGNING obj[id] is on the left-hand side of an assignment * JSRESOLVE_DETECTING 'if (o.p)...' or similar detection opcode sequence * JSRESOLVE_DECLARING var, const, or boxed prolog declaration opcode * JSRESOLVE_CLASSNAME class name used when constructing * * The *objp out parameter, on success, should be null to indicate that id * was not resolved; and non-null, referring to obj or one of its prototypes, * if id was resolved. */ static JSBool boxed_new_resolve(JSContext *context, JSObject **obj, jsid *id, unsigned flags, JSObject **objp) { Boxed *priv; char *name; JSBool ret = JS_FALSE; *objp = NULL; if (!gjs_get_string_id(context, *id, &name)) return JS_TRUE; /* not resolved, but no error */ priv = priv_from_js(context, *obj); gjs_debug_jsprop(GJS_DEBUG_GBOXED, "Resolve prop '%s' hook obj %p priv %p", name, *obj, priv); if (priv == NULL) goto out; /* wrong class */ if (priv->gboxed == NULL) { /* We are the prototype, so look for methods and other class properties */ GIFunctionInfo *method_info; method_info = g_struct_info_find_method((GIStructInfo*) priv->info, name); if (method_info != NULL) { JSObject *boxed_proto; const char *method_name; #if GJS_VERBOSE_ENABLE_GI_USAGE _gjs_log_info_usage((GIBaseInfo*) method_info); #endif method_name = g_base_info_get_name( (GIBaseInfo*) method_info); gjs_debug(GJS_DEBUG_GBOXED, "Defining method %s in prototype for %s.%s", method_name, g_base_info_get_namespace( (GIBaseInfo*) priv->info), g_base_info_get_name( (GIBaseInfo*) priv->info)); boxed_proto = *obj; if (gjs_define_function(context, boxed_proto, priv->gtype, (GICallableInfo *)method_info) == NULL) { g_base_info_unref( (GIBaseInfo*) method_info); goto out; } *objp = boxed_proto; /* we defined the prop in object_proto */ g_base_info_unref( (GIBaseInfo*) method_info); } } else { /* We are an instance, not a prototype, so look for * per-instance props that we want to define on the * JSObject. Generally we do not want to cache these in JS, we * want to always pull them from the C object, or JS would not * see any changes made from C. So we use the get/set prop * hooks, not this resolve hook. */ } ret = JS_TRUE; out: g_free(name); return ret; } /* Check to see if jsval passed in is another Boxed object of the same, * and if so, retrieves the Boxed private structure for it. */ static JSBool boxed_get_copy_source(JSContext *context, Boxed *priv, jsval value, Boxed **source_priv_out) { Boxed *source_priv; if (!JSVAL_IS_OBJECT(value)) return JS_FALSE; if (!priv_from_js_with_typecheck(context, JSVAL_TO_OBJECT(value), &source_priv)) return JS_FALSE; if (!g_base_info_equal((GIBaseInfo*) priv->info, (GIBaseInfo*) source_priv->info)) return JS_FALSE; *source_priv_out = source_priv; return JS_TRUE; } static void boxed_new_direct(Boxed *priv) { g_assert(priv->can_allocate_directly); priv->gboxed = g_slice_alloc0(g_struct_info_get_size (priv->info)); priv->allocated_directly = TRUE; gjs_debug_lifecycle(GJS_DEBUG_GBOXED, "JSObject created by directly allocating %s", g_base_info_get_name ((GIBaseInfo *)priv->info)); } /* When initializing a boxed object from a hash of properties, we don't want * to do n O(n) lookups, so put temporarily put the fields into a hash table * for fast lookup. We could also do this ahead of time and store it on proto->priv. */ static GHashTable * get_field_map(GIStructInfo *struct_info) { GHashTable *result; int n_fields; int i; result = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, (GDestroyNotify)g_base_info_unref); n_fields = g_struct_info_get_n_fields(struct_info); for (i = 0; i < n_fields; i++) { GIFieldInfo *field_info = g_struct_info_get_field(struct_info, i); g_hash_table_insert(result, (char *)g_base_info_get_name((GIBaseInfo *)field_info), field_info); } return result; } /* Initialize a newly created Boxed from an object that is a "hash" of * properties to set as fieds of the object. We don't require that every field * of the object be set. */ static JSBool boxed_init_from_props(JSContext *context, JSObject *obj, Boxed *priv, jsval props_value) { JSObject *props; JSObject *iter; jsid prop_id; GHashTable *field_map; gboolean success; success = FALSE; if (!JSVAL_IS_OBJECT(props_value)) { gjs_throw(context, "argument should be a hash with fields to set"); return JS_FALSE; } props = JSVAL_TO_OBJECT(props_value); iter = JS_NewPropertyIterator(context, props); if (iter == NULL) { gjs_throw(context, "Failed to create property iterator for fields hash"); return JS_FALSE; } field_map = get_field_map(priv->info); prop_id = JSID_VOID; if (!JS_NextProperty(context, iter, &prop_id)) goto out; while (!JSID_IS_VOID(prop_id)) { GIFieldInfo *field_info; char *name; jsval value; if (!gjs_get_string_id(context, prop_id, &name)) goto out; field_info = (GIFieldInfo *) g_hash_table_lookup(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); goto out; } if (!gjs_object_require_property(context, props, "property list", prop_id, &value)) { g_free(name); goto out; } g_free(name); if (!boxed_set_field_from_value(context, priv, field_info, value)) goto out; prop_id = JSID_VOID; if (!JS_NextProperty(context, iter, &prop_id)) goto out; } success = TRUE; out: g_hash_table_destroy(field_map); return success; } static JSBool boxed_invoke_constructor(JSContext *context, JSObject *obj, jsid constructor_name, unsigned argc, jsval *argv, jsval *rval) { jsval js_constructor, js_constructor_func; jsid constructor_const; constructor_const = gjs_context_get_const_string(context, GJS_STRING_CONSTRUCTOR); if (!gjs_object_require_property(context, obj, NULL, constructor_const, &js_constructor)) return JS_FALSE; if (!gjs_object_require_property(context, JSVAL_TO_OBJECT(js_constructor), NULL, constructor_name, &js_constructor_func)) return JS_FALSE; return gjs_call_function_value(context, NULL, js_constructor_func, argc, argv, rval); } static JSBool boxed_new(JSContext *context, JSObject *obj, /* "this" for constructor */ Boxed *priv, unsigned argc, jsval *argv, jsval *rval) { if (priv->gtype == G_TYPE_VARIANT) { jsid constructor_name; /* Short-circuit construction for GVariants by calling into the JS packing function */ constructor_name = gjs_context_get_const_string(context, GJS_STRING_NEW_INTERNAL); return boxed_invoke_constructor (context, obj, constructor_name, argc, argv, rval); } /* If the structure is registered as a boxed, we can create a new instance by * looking for a zero-args constructor and calling it. * Constructors don't really make sense for non-boxed types, since there is no * memory management for the return value, and zero_args_constructor and * default_constructor are always -1 for them. * * For backward compatibility, we choose the zero args constructor if one * exists, otherwise we choose the internal slice allocator if possible; * finally, we fallback on the default constructor */ if (priv->zero_args_constructor >= 0) { GIFunctionInfo *func_info = g_struct_info_get_method (priv->info, priv->zero_args_constructor); GIArgument rval; GError *error = NULL; if (!g_function_info_invoke(func_info, NULL, 0, NULL, 0, &rval, &error)) { gjs_throw(context, "Failed to invoke boxed constructor: %s", error->message); g_clear_error(&error); g_base_info_unref((GIBaseInfo*) func_info); return JS_FALSE; } g_base_info_unref((GIBaseInfo*) func_info); priv->gboxed = rval.v_pointer; gjs_debug_lifecycle(GJS_DEBUG_GBOXED, "JSObject created with boxed instance %p type %s", priv->gboxed, g_type_name(priv->gtype)); } else if (priv->can_allocate_directly) { boxed_new_direct(priv); } else if (priv->default_constructor >= 0) { JSBool retval; /* for simplicity, we simply delegate all the work to the actual JS constructor function (which we retrieve from the JS constructor, that is, Namespace.BoxedType, or object.constructor, given that object was created with the right prototype */ retval = boxed_invoke_constructor(context, obj, priv->default_constructor_name, argc, argv, rval); return retval; } else { gjs_throw(context, "Unable to construct struct type %s since it has no default constructor and cannot be allocated directly", g_base_info_get_name((GIBaseInfo*) priv->info)); return JS_FALSE; } /* If we reach this code, we need to init from a map of fields */ if (argc == 0) return JS_TRUE; if (argc > 1) { gjs_throw(context, "Constructor with multiple arguments not supported for %s", g_base_info_get_name((GIBaseInfo *)priv->info)); return JS_FALSE; } return boxed_init_from_props (context, obj, priv, argv[0]); } GJS_NATIVE_CONSTRUCTOR_DECLARE(boxed) { GJS_NATIVE_CONSTRUCTOR_VARIABLES(boxed) Boxed *priv; Boxed *proto_priv; JSObject *proto; Boxed *source_priv; jsval actual_rval; JSBool retval; GJS_NATIVE_CONSTRUCTOR_PRELUDE(boxed); priv = g_slice_new0(Boxed); GJS_INC_COUNTER(boxed); g_assert(priv_from_js(context, object) == NULL); JS_SetPrivate(object, priv); gjs_debug_lifecycle(GJS_DEBUG_GBOXED, "boxed constructor, obj %p priv %p", object, priv); JS_GetPrototype(context, object, &proto); gjs_debug_lifecycle(GJS_DEBUG_GBOXED, "boxed instance __proto__ is %p", proto); /* If we're the prototype, then post-construct we'll fill in priv->info. * If we are not the prototype, though, then we'll get ->info from the * prototype and then create a GObject if we don't have one already. */ proto_priv = priv_from_js(context, proto); if (proto_priv == NULL) { gjs_debug(GJS_DEBUG_GBOXED, "Bad prototype set on boxed? Must match JSClass of object. JS error should have been reported."); return JS_FALSE; } *priv = *proto_priv; g_base_info_ref( (GIBaseInfo*) priv->info); /* Short-circuit copy-construction in the case where we can use g_boxed_copy or memcpy */ if (argc == 1 && boxed_get_copy_source(context, priv, argv[0], &source_priv)) { if (g_type_is_a (priv->gtype, G_TYPE_BOXED)) { priv->gboxed = g_boxed_copy(priv->gtype, source_priv->gboxed); GJS_NATIVE_CONSTRUCTOR_FINISH(boxed); return JS_TRUE; } else if (priv->can_allocate_directly) { boxed_new_direct (priv); memcpy(priv->gboxed, source_priv->gboxed, g_struct_info_get_size (priv->info)); GJS_NATIVE_CONSTRUCTOR_FINISH(boxed); return JS_TRUE; } } /* we may need to return a value different from object (for example because we delegate to another constructor) prepare the location for that */ actual_rval = JSVAL_VOID; JS_AddValueRoot(context, &actual_rval); retval = boxed_new(context, object, priv, argc, argv, &actual_rval); if (retval) { if (!JSVAL_IS_VOID (actual_rval)) JS_SET_RVAL(context, vp, actual_rval); else GJS_NATIVE_CONSTRUCTOR_FINISH(boxed); } JS_RemoveValueRoot(context, &actual_rval); return retval; } static void boxed_finalize(JSFreeOp *fop, JSObject *obj) { Boxed *priv; priv = (Boxed *) JS_GetPrivate(obj); gjs_debug_lifecycle(GJS_DEBUG_GBOXED, "finalize, obj %p priv %p", obj, priv); if (priv == NULL) return; /* wrong class? */ if (priv->gboxed && !priv->not_owning_gboxed) { if (priv->allocated_directly) { g_slice_free1(g_struct_info_get_size (priv->info), priv->gboxed); } else { if (g_type_is_a (priv->gtype, G_TYPE_BOXED)) g_boxed_free (priv->gtype, priv->gboxed); else if (g_type_is_a (priv->gtype, G_TYPE_VARIANT)) g_variant_unref ((GVariant *) priv->gboxed); else g_assert_not_reached (); } priv->gboxed = NULL; } if (priv->info) { g_base_info_unref( (GIBaseInfo*) priv->info); priv->info = NULL; } GJS_DEC_COUNTER(boxed); g_slice_free(Boxed, priv); } static GIFieldInfo * get_field_info (JSContext *context, Boxed *priv, jsid id) { int field_index; jsval id_val; if (!JS_IdToValue(context, id, &id_val)) return JS_FALSE; if (!JSVAL_IS_INT (id_val)) { gjs_throw(context, "Field index for %s is not an integer", g_base_info_get_name ((GIBaseInfo *)priv->info)); return NULL; } field_index = JSVAL_TO_INT(id_val); if (field_index < 0 || field_index >= g_struct_info_get_n_fields (priv->info)) { gjs_throw(context, "Bad field index %d for %s", field_index, g_base_info_get_name ((GIBaseInfo *)priv->info)); return NULL; } return g_struct_info_get_field (priv->info, field_index); } static JSBool get_nested_interface_object (JSContext *context, JSObject *parent_obj, Boxed *parent_priv, GIFieldInfo *field_info, GITypeInfo *type_info, GIBaseInfo *interface_info, jsval *value) { JSObject *obj; JSObject *proto; int offset; Boxed *priv; Boxed *proto_priv; if (!struct_is_simple ((GIStructInfo *)interface_info)) { gjs_throw(context, "Reading field %s.%s is not supported", g_base_info_get_name ((GIBaseInfo *)parent_priv->info), g_base_info_get_name ((GIBaseInfo *)field_info)); return JS_FALSE; } proto = gjs_lookup_generic_prototype(context, (GIBoxedInfo*) interface_info); proto_priv = priv_from_js(context, proto); offset = g_field_info_get_offset (field_info); obj = JS_NewObjectWithGivenProto(context, JS_GetClass(proto), proto, gjs_get_import_global (context)); if (obj == NULL) return JS_FALSE; GJS_INC_COUNTER(boxed); priv = g_slice_new0(Boxed); JS_SetPrivate(obj, priv); priv->info = (GIBoxedInfo*) interface_info; g_base_info_ref( (GIBaseInfo*) priv->info); priv->gtype = g_registered_type_info_get_g_type ((GIRegisteredTypeInfo*) interface_info); priv->can_allocate_directly = proto_priv->can_allocate_directly; /* A structure nested inside a parent object; doesn't have an independent allocation */ priv->gboxed = ((char *)parent_priv->gboxed) + offset; priv->not_owning_gboxed = TRUE; /* We never actually read the reserved slot, but we put the parent object * into it to hold onto the parent object. */ JS_SetReservedSlot(obj, 0, OBJECT_TO_JSVAL (parent_obj)); *value = OBJECT_TO_JSVAL(obj); return JS_TRUE; } static JSBool boxed_field_getter (JSContext *context, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleValue value) { Boxed *priv; GIFieldInfo *field_info; GITypeInfo *type_info; GArgument arg; gboolean success = FALSE; priv = priv_from_js(context, obj); if (!priv) return JS_FALSE; field_info = get_field_info(context, priv, id); if (!field_info) return JS_FALSE; type_info = g_field_info_get_type (field_info); if (priv->gboxed == NULL) { /* direct access to proto field */ gjs_throw(context, "Can't get field %s.%s from a prototype", g_base_info_get_name ((GIBaseInfo *)priv->info), g_base_info_get_name ((GIBaseInfo *)field_info)); goto out; } if (!g_type_info_is_pointer (type_info) && g_type_info_get_tag (type_info) == GI_TYPE_TAG_INTERFACE) { GIBaseInfo *interface_info = g_type_info_get_interface(type_info); if (g_base_info_get_type (interface_info) == GI_INFO_TYPE_STRUCT || g_base_info_get_type (interface_info) == GI_INFO_TYPE_BOXED) { success = get_nested_interface_object (context, obj, priv, field_info, type_info, interface_info, value.address()); g_base_info_unref ((GIBaseInfo *)interface_info); goto out; } g_base_info_unref ((GIBaseInfo *)interface_info); } if (!g_field_info_get_field (field_info, priv->gboxed, &arg)) { gjs_throw(context, "Reading field %s.%s is not supported", g_base_info_get_name ((GIBaseInfo *)priv->info), g_base_info_get_name ((GIBaseInfo *)field_info)); goto out; } if (!gjs_value_from_g_argument (context, value.address(), type_info, &arg, TRUE)) goto out; success = TRUE; out: g_base_info_unref ((GIBaseInfo *)field_info); g_base_info_unref ((GIBaseInfo *)type_info); return success; } static JSBool set_nested_interface_object (JSContext *context, Boxed *parent_priv, GIFieldInfo *field_info, GITypeInfo *type_info, GIBaseInfo *interface_info, jsval value) { JSObject *proto; int offset; Boxed *proto_priv; Boxed *source_priv; if (!struct_is_simple ((GIStructInfo *)interface_info)) { gjs_throw(context, "Writing field %s.%s is not supported", g_base_info_get_name ((GIBaseInfo *)parent_priv->info), g_base_info_get_name ((GIBaseInfo *)field_info)); return JS_FALSE; } proto = gjs_lookup_generic_prototype(context, (GIBoxedInfo*) interface_info); proto_priv = priv_from_js(context, proto); /* If we can't directly copy from the source object we need * to construct a new temporary object. */ if (!boxed_get_copy_source(context, proto_priv, value, &source_priv)) { JSObject *tmp_object = gjs_construct_object_dynamic(context, proto, 1, &value); if (!tmp_object) return JS_FALSE; source_priv = priv_from_js(context, tmp_object); if (!source_priv) return JS_FALSE; } offset = g_field_info_get_offset (field_info); memcpy(((char *)parent_priv->gboxed) + offset, source_priv->gboxed, g_struct_info_get_size (source_priv->info)); return JS_TRUE; } static JSBool boxed_set_field_from_value(JSContext *context, Boxed *priv, GIFieldInfo *field_info, jsval value) { GITypeInfo *type_info; GArgument arg; gboolean success = FALSE; gboolean need_release = FALSE; type_info = g_field_info_get_type (field_info); if (!g_type_info_is_pointer (type_info) && g_type_info_get_tag (type_info) == GI_TYPE_TAG_INTERFACE) { GIBaseInfo *interface_info = g_type_info_get_interface(type_info); if (g_base_info_get_type (interface_info) == GI_INFO_TYPE_STRUCT || g_base_info_get_type (interface_info) == GI_INFO_TYPE_BOXED) { success = set_nested_interface_object (context, priv, field_info, type_info, interface_info, value); g_base_info_unref ((GIBaseInfo *)interface_info); goto out; } g_base_info_unref ((GIBaseInfo *)interface_info); } if (!gjs_value_to_g_argument(context, value, type_info, g_base_info_get_name ((GIBaseInfo *)field_info), GJS_ARGUMENT_FIELD, GI_TRANSFER_NOTHING, TRUE, &arg)) goto out; need_release = TRUE; if (!g_field_info_set_field (field_info, priv->gboxed, &arg)) { gjs_throw(context, "Writing field %s.%s is not supported", g_base_info_get_name ((GIBaseInfo *)priv->info), g_base_info_get_name ((GIBaseInfo *)field_info)); goto out; } success = TRUE; out: if (need_release) gjs_g_argument_release (context, GI_TRANSFER_NOTHING, type_info, &arg); g_base_info_unref ((GIBaseInfo *)type_info); return success; } static JSBool boxed_field_setter (JSContext *context, JS::HandleObject obj, JS::HandleId id, JSBool strict, JS::MutableHandleValue value) { Boxed *priv; GIFieldInfo *field_info; gboolean success = FALSE; priv = priv_from_js(context, obj); if (!priv) return JS_FALSE; field_info = get_field_info(context, priv, id); if (!field_info) return JS_FALSE; if (priv->gboxed == NULL) { /* direct access to proto field */ gjs_throw(context, "Can't set field %s.%s on prototype", g_base_info_get_name ((GIBaseInfo *)priv->info), g_base_info_get_name ((GIBaseInfo *)field_info)); goto out; } success = boxed_set_field_from_value (context, priv, field_info, value); out: g_base_info_unref ((GIBaseInfo *)field_info); return success; } static JSBool define_boxed_class_fields (JSContext *context, Boxed *priv, JSObject *proto) { int n_fields = g_struct_info_get_n_fields (priv->info); int i; /* We identify properties with a 'TinyId': a 8-bit numeric value * that can be retrieved in the property getter/setter. Using it * allows us to avoid a hash-table lookup or linear search. * It does restrict us to a maximum of 256 fields per type. * * We define all fields as read/write so that the user gets an * error message. If we omitted fields or defined them read-only * we'd: * * - Storing a new property for a non-accessible field * - Silently do nothing when writing a read-only field * * Which is pretty confusing if the only reason a field isn't * writable is language binding or memory-management restrictions. * * We just go ahead and define the fields immediately for the * class; doing it lazily in boxed_new_resolve() would be possible * as well if doing it ahead of time caused to much start-up * memory overhead. */ if (n_fields > 256) { g_warning("Only defining the first 256 fields in boxed type '%s'", g_base_info_get_name ((GIBaseInfo *)priv->info)); n_fields = 256; } for (i = 0; i < n_fields; i++) { GIFieldInfo *field = g_struct_info_get_field (priv->info, i); const char *field_name = g_base_info_get_name ((GIBaseInfo *)field); gboolean result; result = JS_DefinePropertyWithTinyId(context, proto, field_name, i, JSVAL_NULL, boxed_field_getter, boxed_field_setter, JSPROP_PERMANENT | JSPROP_SHARED); g_base_info_unref ((GIBaseInfo *)field); if (!result) return JS_FALSE; } return JS_TRUE; } static JSBool to_string_func(JSContext *context, unsigned argc, jsval *vp) { JSObject *obj = JS_THIS_OBJECT(context, vp); Boxed *priv; JSBool ret = JS_FALSE; jsval retval; if (!priv_from_js_with_typecheck(context, obj, &priv)) goto out; if (!_gjs_proxy_to_string_func(context, obj, "boxed", (GIBaseInfo*)priv->info, priv->gtype, priv->gboxed, &retval)) goto out; ret = JS_TRUE; JS_SET_RVAL(context, vp, retval); out: return ret; } /* The bizarre thing about this vtable is that it applies to both * instances of the object, and to the prototype that instances of the * class have. * * We allocate 1 reserved slot; this is typically unused, but if the * boxed is for a nested structure inside a parent structure, the * reserved slot is used to hold onto the parent Javascript object and * make sure it doesn't get freed. */ struct JSClass gjs_boxed_class = { "GObject_Boxed", JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(1), JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, (JSResolveOp) boxed_new_resolve, /* needs cast since it's the new resolve signature */ JS_ConvertStub, boxed_finalize, NULL, NULL, NULL, NULL, NULL }; JSPropertySpec gjs_boxed_proto_props[] = { { NULL } }; JSFunctionSpec gjs_boxed_proto_funcs[] = { { "toString", JSOP_WRAPPER((JSNative)to_string_func), 0, 0 }, { NULL } }; static gboolean type_can_be_allocated_directly(GITypeInfo *type_info) { gboolean is_simple = TRUE; if (g_type_info_is_pointer(type_info)) { if (g_type_info_get_tag(type_info) == GI_TYPE_TAG_ARRAY && g_type_info_get_array_type(type_info) == GI_ARRAY_TYPE_C) { GITypeInfo *param_info; param_info = g_type_info_get_param_type(type_info, 0); is_simple = type_can_be_allocated_directly(param_info); g_base_info_unref((GIBaseInfo*)param_info); } else { is_simple = FALSE; } } else { switch (g_type_info_get_tag(type_info)) { case GI_TYPE_TAG_BOOLEAN: case GI_TYPE_TAG_INT8: case GI_TYPE_TAG_UINT8: case GI_TYPE_TAG_INT16: case GI_TYPE_TAG_UINT16: case GI_TYPE_TAG_INT32: case GI_TYPE_TAG_UINT32: case GI_TYPE_TAG_INT64: case GI_TYPE_TAG_UINT64: case GI_TYPE_TAG_FLOAT: case GI_TYPE_TAG_DOUBLE: case GI_TYPE_TAG_UNICHAR: break; case GI_TYPE_TAG_VOID: case GI_TYPE_TAG_GTYPE: case GI_TYPE_TAG_ERROR: case GI_TYPE_TAG_UTF8: case GI_TYPE_TAG_FILENAME: case GI_TYPE_TAG_ARRAY: case GI_TYPE_TAG_GLIST: case GI_TYPE_TAG_GSLIST: case GI_TYPE_TAG_GHASH: break; case GI_TYPE_TAG_INTERFACE: { GIBaseInfo *interface = g_type_info_get_interface(type_info); switch (g_base_info_get_type(interface)) { case GI_INFO_TYPE_BOXED: case GI_INFO_TYPE_STRUCT: if (!struct_is_simple((GIStructInfo *)interface)) is_simple = FALSE; break; case GI_INFO_TYPE_UNION: /* FIXME: Need to implement */ is_simple = FALSE; break; case GI_INFO_TYPE_ENUM: case GI_INFO_TYPE_FLAGS: break; case GI_INFO_TYPE_OBJECT: case GI_INFO_TYPE_VFUNC: case GI_INFO_TYPE_CALLBACK: case GI_INFO_TYPE_INVALID: case GI_INFO_TYPE_INTERFACE: case GI_INFO_TYPE_FUNCTION: case GI_INFO_TYPE_CONSTANT: case GI_INFO_TYPE_VALUE: case GI_INFO_TYPE_SIGNAL: case GI_INFO_TYPE_PROPERTY: case GI_INFO_TYPE_FIELD: case GI_INFO_TYPE_ARG: case GI_INFO_TYPE_TYPE: case GI_INFO_TYPE_UNRESOLVED: is_simple = FALSE; break; case GI_INFO_TYPE_INVALID_0: g_assert_not_reached(); break; } g_base_info_unref(interface); break; } } } return is_simple; } /* Check if the type of the boxed is "simple" - every field is a non-pointer * type that we know how to assign to. If so, then we can allocate and free * instances without needing a constructor. */ static gboolean struct_is_simple(GIStructInfo *info) { int n_fields = g_struct_info_get_n_fields(info); gboolean is_simple = TRUE; int i; /* If it's opaque, it's not simple */ if (n_fields == 0) return FALSE; for (i = 0; i < n_fields && is_simple; i++) { GIFieldInfo *field_info = g_struct_info_get_field(info, i); GITypeInfo *type_info = g_field_info_get_type(field_info); is_simple = type_can_be_allocated_directly(type_info); g_base_info_unref((GIBaseInfo *)field_info); g_base_info_unref((GIBaseInfo *)type_info); } return is_simple; } static void boxed_fill_prototype_info(JSContext *context, Boxed *priv) { int i, n_methods; int first_constructor = -1; jsid first_constructor_name = JSID_VOID; priv->gtype = g_registered_type_info_get_g_type( (GIRegisteredTypeInfo*) priv->info); priv->zero_args_constructor = -1; priv->default_constructor = -1; if (priv->gtype != G_TYPE_NONE) { /* If the structure is registered as a boxed, we can create a new instance by * looking for a zero-args constructor and calling it; constructors don't * really make sense for non-boxed types, since there is no memory management * for the return value. */ n_methods = g_struct_info_get_n_methods(priv->info); for (i = 0; i < n_methods; ++i) { GIFunctionInfo *func_info; GIFunctionInfoFlags flags; func_info = g_struct_info_get_method(priv->info, i); flags = g_function_info_get_flags(func_info); if ((flags & GI_FUNCTION_IS_CONSTRUCTOR) != 0) { if (first_constructor < 0) { const char *name; name = g_base_info_get_name((GIBaseInfo*) func_info); first_constructor = i; first_constructor_name = gjs_intern_string_to_id(context, name); } if (priv->zero_args_constructor < 0 && g_callable_info_get_n_args((GICallableInfo*) func_info) == 0) { const char *name; name = g_base_info_get_name((GIBaseInfo*) func_info); priv->zero_args_constructor = i; priv->zero_args_constructor_name = gjs_intern_string_to_id(context, name); } if (priv->default_constructor < 0 && strcmp(g_base_info_get_name ((GIBaseInfo*) func_info), "new") == 0) { priv->default_constructor = i; priv->default_constructor_name = gjs_context_get_const_string(context, GJS_STRING_NEW); } } g_base_info_unref((GIBaseInfo*) func_info); } if (priv->default_constructor < 0) { priv->default_constructor = priv->zero_args_constructor; priv->default_constructor_name = priv->zero_args_constructor_name; } if (priv->default_constructor < 0) { priv->default_constructor = first_constructor; priv->default_constructor_name = first_constructor_name; } } } void gjs_define_boxed_class(JSContext *context, JSObject *in_object, GIBoxedInfo *info) { const char *constructor_name; JSObject *prototype; JSObject *constructor; jsval value; Boxed *priv; /* See the comment in gjs_define_object_class() for an * explanation of how this all works; Boxed is pretty much the * same as Object. */ constructor_name = g_base_info_get_name( (GIBaseInfo*) info); if (!gjs_init_class_dynamic(context, in_object, NULL, /* parent prototype */ g_base_info_get_namespace( (GIBaseInfo*) info), constructor_name, &gjs_boxed_class, gjs_boxed_constructor, 1, /* props of prototype */ &gjs_boxed_proto_props[0], /* funcs of prototype */ &gjs_boxed_proto_funcs[0], /* props of constructor, MyConstructor.myprop */ NULL, /* funcs of constructor, MyConstructor.myfunc() */ NULL, &prototype, &constructor)) { gjs_log_exception(context); g_error("Can't init class %s", constructor_name); } GJS_INC_COUNTER(boxed); priv = g_slice_new0(Boxed); priv->info = info; boxed_fill_prototype_info(context, priv); g_base_info_ref( (GIBaseInfo*) priv->info); priv->gtype = g_registered_type_info_get_g_type ((GIRegisteredTypeInfo*) priv->info); JS_SetPrivate(prototype, priv); gjs_debug(GJS_DEBUG_GBOXED, "Defined class %s prototype is %p class %p in object %p", constructor_name, prototype, JS_GetClass(prototype), in_object); priv->can_allocate_directly = struct_is_simple (priv->info); define_boxed_class_fields (context, priv, prototype); gjs_define_static_methods (context, constructor, priv->gtype, priv->info); value = OBJECT_TO_JSVAL(gjs_gtype_create_gtype_wrapper(context, priv->gtype)); JS_DefineProperty(context, constructor, "$gtype", value, NULL, NULL, JSPROP_PERMANENT); } JSObject* gjs_boxed_from_c_struct(JSContext *context, GIStructInfo *info, void *gboxed, GjsBoxedCreationFlags flags) { JSObject *obj; JSObject *proto; Boxed *priv; Boxed *proto_priv; if (gboxed == NULL) return NULL; gjs_debug_marshal(GJS_DEBUG_GBOXED, "Wrapping struct %s %p with JSObject", g_base_info_get_name((GIBaseInfo *)info), gboxed); proto = gjs_lookup_generic_prototype(context, info); proto_priv = priv_from_js(context, proto); obj = JS_NewObjectWithGivenProto(context, JS_GetClass(proto), proto, gjs_get_import_global (context)); GJS_INC_COUNTER(boxed); priv = g_slice_new0(Boxed); *priv = *proto_priv; g_base_info_ref( (GIBaseInfo*) priv->info); JS_SetPrivate(obj, priv); if ((flags & GJS_BOXED_CREATION_NO_COPY) != 0) { /* we need to create a JS Boxed which references the * original C struct, not a copy of it. Used for * G_SIGNAL_TYPE_STATIC_SCOPE */ priv->gboxed = gboxed; priv->not_owning_gboxed = TRUE; } else { if (priv->gtype != G_TYPE_NONE && g_type_is_a (priv->gtype, G_TYPE_BOXED)) { priv->gboxed = g_boxed_copy(priv->gtype, gboxed); } else if (priv->gtype == G_TYPE_VARIANT) { priv->gboxed = g_variant_ref_sink ((GVariant *) gboxed); } else if (priv->can_allocate_directly) { boxed_new_direct(priv); memcpy(priv->gboxed, gboxed, g_struct_info_get_size (priv->info)); } else { gjs_throw(context, "Can't create a Javascript object for %s; no way to copy", g_base_info_get_name( (GIBaseInfo*) priv->info)); } } return obj; } void* gjs_c_struct_from_boxed(JSContext *context, JSObject *obj) { Boxed *priv; if (obj == NULL) return NULL; priv = priv_from_js(context, obj); if (priv == NULL) return NULL; return priv->gboxed; } JSBool gjs_typecheck_boxed(JSContext *context, JSObject *object, GIStructInfo *expected_info, GType expected_type, JSBool throw_error) { Boxed *priv; JSBool result; if (!do_base_typecheck(context, object, throw_error)) return JS_FALSE; priv = priv_from_js(context, object); if (priv->gboxed == NULL) { if (throw_error) { gjs_throw_custom(context, "TypeError", "Object is %s.%s.prototype, not an object instance - cannot convert to a boxed instance", g_base_info_get_namespace( (GIBaseInfo*) priv->info), g_base_info_get_name( (GIBaseInfo*) priv->info)); } return JS_FALSE; } if (expected_type != G_TYPE_NONE) result = g_type_is_a (priv->gtype, expected_type); else if (expected_info != NULL) result = g_base_info_equal((GIBaseInfo*) priv->info, (GIBaseInfo*) expected_info); else result = JS_TRUE; if (!result && throw_error) { if (expected_info != NULL) { gjs_throw_custom(context, "TypeError", "Object is of type %s.%s - cannot convert to %s.%s", g_base_info_get_namespace((GIBaseInfo*) priv->info), g_base_info_get_name((GIBaseInfo*) priv->info), g_base_info_get_namespace((GIBaseInfo*) expected_info), g_base_info_get_name((GIBaseInfo*) expected_info)); } else { gjs_throw_custom(context, "TypeError", "Object is of type %s.%s - cannot convert to %s", g_base_info_get_namespace((GIBaseInfo*) priv->info), g_base_info_get_name((GIBaseInfo*) priv->info), g_type_name(expected_type)); } } return result; } cjs-2.8.0/gi/gerror.h0000664000175000017500000000415312610172032013271 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_ERROR_H__ #define __GJS_ERROR_H__ #include #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS void gjs_define_error_class (JSContext *context, JSObject *in_object, GIEnumInfo *info); GError* gjs_gerror_from_error (JSContext *context, JSObject *obj); JSObject* gjs_error_from_gerror (JSContext *context, GError *gerror, gboolean add_stack); JSBool gjs_typecheck_gerror (JSContext *context, JSObject *obj, JSBool throw_error); G_END_DECLS #endif /* __GJS_ERROR_H__ */ cjs-2.8.0/gi/object.cpp0000664000175000017500000025021012610172032013567 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include "object.h" #include "gtype.h" #include "arg.h" #include "repo.h" #include "gtype.h" #include "function.h" #include "proxyutils.h" #include "param.h" #include "value.h" #include "keep-alive.h" #include "closure.h" #include "gjs_gi_trace.h" #include #include #include #include #include #include #include typedef struct { GIObjectInfo *info; GObject *gobj; /* NULL if we are the prototype and not an instance */ JSObject *keep_alive; /* NULL if we are not added to it */ GType gtype; /* a list of all signal connections, used when tracing */ GList *signals; /* the GObjectClass wrapped by this JS Object (only used for prototypes) */ GTypeClass *klass; } ObjectInstance; typedef struct { ObjectInstance *obj; GList *link; GClosure *closure; } ConnectData; typedef enum { TOGGLE_DOWN, TOGGLE_UP, } ToggleDirection; typedef struct { GObject *gobj; ToggleDirection direction; guint needs_unref : 1; } ToggleRefNotifyOperation; enum { PROP_0, PROP_JS_HANDLED, }; static GSList *object_init_list; static GHashTable *class_init_properties; extern struct JSClass gjs_object_instance_class; static GThread *gjs_eval_thread; static volatile gint pending_idle_toggles; GJS_DEFINE_PRIV_FROM_JS(ObjectInstance, gjs_object_instance_class) static JSObject* peek_js_obj (GObject *gobj); static void set_js_obj (GObject *gobj, JSObject *obj); static void disassociate_js_gobject (GObject *gobj); static void invalidate_all_signals (ObjectInstance *priv); typedef enum { SOME_ERROR_OCCURRED = JS_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; if (G_UNLIKELY (!val)) val = g_quark_from_static_string ("gjs::private"); return val; } static GQuark gjs_toggle_down_quark (void) { static GQuark val = 0; if (G_UNLIKELY (!val)) val = g_quark_from_static_string ("gjs::toggle-down-quark"); return val; } static GQuark gjs_toggle_up_quark (void) { static GQuark val = 0; if (G_UNLIKELY (!val)) val = g_quark_from_static_string ("gjs::toggle-up-quark"); return val; } /* Plain g_type_query fails and leaves @query uninitialized for dynamic types. See https://bugzilla.gnome.org/show_bug.cgi?id=687184 and https://bugzilla.gnome.org/show_bug.cgi?id=687211 */ static void g_type_query_dynamic_safe (GType type, GTypeQuery *query) { while (g_type_get_qdata(type, gjs_is_custom_type_quark())) type = g_type_parent(type); g_type_query(type, query); } static void throw_priv_is_null_error(JSContext *context) { gjs_throw(context, "This JS object wrapper isn't wrapping a GObject." " If this is a custom subclass, are you sure you chained" " up to the parent _init properly?"); } static ValueFromPropertyResult init_g_param_from_property(JSContext *context, const char *js_prop_name, jsval js_value, GType gtype, GParameter *parameter, gboolean constructing) { char *gname; GParamSpec *param_spec; void *klass; gname = gjs_hyphen_from_camel(js_prop_name); gjs_debug_jsprop(GJS_DEBUG_GOBJECT, "Hyphen name %s on %s", gname, g_type_name(gtype)); klass = g_type_class_ref(gtype); param_spec = g_object_class_find_property(G_OBJECT_CLASS(klass), gname); g_type_class_unref(klass); g_free(gname); if (param_spec == NULL) { /* not a GObject prop, so nothing else to do */ return NO_SUCH_G_PROPERTY; } /* Do not set JS overridden properties through GObject, to avoid * infinite recursion (but set them when constructing) */ if (!constructing && g_param_spec_get_qdata(param_spec, gjs_is_custom_property_quark())) return NO_SUCH_G_PROPERTY; if ((param_spec->flags & G_PARAM_WRITABLE) == 0) { /* prevent setting the prop even in JS */ gjs_throw(context, "Property %s (GObject %s) is not writable", js_prop_name, param_spec->name); return SOME_ERROR_OCCURRED; } gjs_debug_jsprop(GJS_DEBUG_GOBJECT, "Syncing %s to GObject prop %s", js_prop_name, param_spec->name); g_value_init(¶meter->value, G_PARAM_SPEC_VALUE_TYPE(param_spec)); if (!gjs_value_to_g_value(context, js_value, ¶meter->value)) { g_value_unset(¶meter->value); return SOME_ERROR_OCCURRED; } parameter->name = param_spec->name; return VALUE_WAS_SET; } static inline ObjectInstance * proto_priv_from_js(JSContext *context, JSObject *obj) { JSObject *proto; JS_GetPrototype(context, obj, &proto); return priv_from_js(context, proto); } /* a hook on getting a property; set value_p to override property's value. * Return value is JS_FALSE on OOM/exception. */ static JSBool object_instance_get_prop(JSContext *context, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleValue value_p) { ObjectInstance *priv; char *name; char *gname; GParamSpec *param; GValue gvalue = { 0, }; JSBool ret = JS_TRUE; if (!gjs_get_string_id(context, id, &name)) return JS_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, obj, priv); if (priv == NULL) { /* 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; } if (priv->gobj == NULL) /* prototype, not an instance. */ goto out; gname = gjs_hyphen_from_camel(name); param = g_object_class_find_property(G_OBJECT_GET_CLASS(priv->gobj), gname); g_free(gname); if (param == NULL) { /* leave value_p as it was */ goto out; } /* Do not fetch JS overridden properties from GObject, to avoid * infinite recursion. */ if (g_param_spec_get_qdata(param, gjs_is_custom_property_quark())) goto out; if ((param->flags & G_PARAM_READABLE) == 0) goto out; gjs_debug_jsprop(GJS_DEBUG_GOBJECT, "Overriding %s with GObject prop %s", name, param->name); g_value_init(&gvalue, G_PARAM_SPEC_VALUE_TYPE(param)); g_object_get_property(priv->gobj, param->name, &gvalue); if (!gjs_value_from_g_value(context, value_p.address(), &gvalue)) { g_value_unset(&gvalue); ret = JS_FALSE; goto out; } g_value_unset(&gvalue); out: g_free(name); return ret; } /* a hook on setting a property; set value_p to override property value to * be set. Return value is JS_FALSE on OOM/exception. */ static JSBool object_instance_set_prop(JSContext *context, JS::HandleObject obj, JS::HandleId id, JSBool strict, JS::MutableHandleValue value_p) { ObjectInstance *priv; char *name; GParameter param = { NULL, { 0, }}; JSBool ret = JS_TRUE; if (!gjs_get_string_id(context, id, &name)) return JS_TRUE; /* not resolved, but no error */ priv = priv_from_js(context, obj); gjs_debug_jsprop(GJS_DEBUG_GOBJECT, "Set prop '%s' hook obj %p priv %p", name, obj, priv); if (priv == NULL) { /* see the comment in object_instance_get_prop() on this */ goto out; } if (priv->gobj == NULL) /* prototype, not an instance. */ goto out; switch (init_g_param_from_property(context, name, value_p, G_TYPE_FROM_INSTANCE(priv->gobj), ¶m, FALSE /* constructing */)) { case SOME_ERROR_OCCURRED: ret = JS_FALSE; case NO_SUCH_G_PROPERTY: goto out; case VALUE_WAS_SET: break; } g_object_set_property(priv->gobj, param.name, ¶m.value); g_value_unset(¶m.value); /* 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; } static gboolean is_vfunc_unchanged(GIVFuncInfo *info, GType gtype) { GType ptype = g_type_parent(gtype); GError *error = NULL; gpointer addr1, addr2; addr1 = g_vfunc_info_get_address(info, gtype, &error); if (error) { g_clear_error(&error); return FALSE; } addr2 = g_vfunc_info_get_address(info, ptype, &error); if (error) { g_clear_error(&error); return FALSE; } return addr1 == addr2; } static GIVFuncInfo * find_vfunc_on_parents(GIObjectInfo *info, gchar *name, gboolean *out_defined_by_parent) { GIVFuncInfo *vfunc = NULL; GIObjectInfo *parent; gboolean defined_by_parent = FALSE; /* ref the first info so that we don't destroy * it when unrefing parents later */ g_base_info_ref(info); parent = info; /* Since it isn't possible to override a vfunc on * an interface without reimplementing it, we don't need * to search the parent types when looking for a vfunc. */ vfunc = g_object_info_find_vfunc_using_interfaces(parent, name, NULL); while (!vfunc && parent) { GIObjectInfo *tmp = parent; parent = g_object_info_get_parent(tmp); g_base_info_unref(tmp); if (parent) vfunc = g_object_info_find_vfunc(parent, name); defined_by_parent = TRUE; } if (parent) g_base_info_unref(parent); if (out_defined_by_parent) *out_defined_by_parent = defined_by_parent; return vfunc; } static JSBool object_instance_new_resolve_no_info(JSContext *context, JSObject *obj, JSObject **objp, ObjectInstance *priv, char *name) { GIFunctionInfo *method_info; JSBool ret; GType *interfaces; guint n_interfaces; guint i; ret = JS_TRUE; interfaces = g_type_interfaces(priv->gtype, &n_interfaces); for (i = 0; i < n_interfaces; i++) { GIBaseInfo *base_info; GIInterfaceInfo *iface_info; base_info = g_irepository_find_by_gtype(g_irepository_get_default(), interfaces[i]); if (base_info == NULL) continue; /* An interface GType ought to have interface introspection info */ g_assert (g_base_info_get_type(base_info) == GI_INFO_TYPE_INTERFACE); iface_info = (GIInterfaceInfo*) base_info; method_info = g_interface_info_find_method(iface_info, name); g_base_info_unref(base_info); if (method_info != NULL) { if (gjs_define_function(context, obj, priv->gtype, (GICallableInfo *)method_info)) { *objp = obj; } else { ret = JS_FALSE; } g_base_info_unref( (GIBaseInfo*) method_info); } } g_free(interfaces); return ret; } /* * Like JSResolveOp, but flags provide contextual information as follows: * * JSRESOLVE_QUALIFIED a qualified property id: obj.id or obj[id], not id * JSRESOLVE_ASSIGNING obj[id] is on the left-hand side of an assignment * JSRESOLVE_DETECTING 'if (o.p)...' or similar detection opcode sequence * JSRESOLVE_DECLARING var, const, or object prolog declaration opcode * JSRESOLVE_CLASSNAME class name used when constructing * * The *objp out parameter, on success, should be null to indicate that id * was not resolved; and non-null, referring to obj or one of its prototypes, * if id was resolved. */ static JSBool object_instance_new_resolve(JSContext *context, JSObject **obj, jsid *id, unsigned flags, JSObject **objp) { GIFunctionInfo *method_info; ObjectInstance *priv; char *name; JSBool ret = JS_FALSE; *objp = NULL; if (!gjs_get_string_id(context, *id, &name)) return JS_TRUE; /* not resolved, but no error */ priv = priv_from_js(context, *obj); gjs_debug_jsprop(GJS_DEBUG_GOBJECT, "Resolve prop '%s' hook obj %p priv %p (%s.%s) gobj %p %s", name, *obj, priv, priv && priv->info ? g_base_info_get_namespace (priv->info) : "", priv && priv->info ? g_base_info_get_name (priv->info) : "", priv ? priv->gobj : NULL, (priv && priv->gobj) ? g_type_name_from_instance((GTypeInstance*) priv->gobj) : "(type unknown)"); if (priv == NULL) { /* We won't have a private until the initializer is called, so * just defer to prototype chains in this case. * * This isn't too bad: either you get undefined if the field * doesn't exist on any of the prototype chains, or whatever code * will run afterwards will fail because of the "priv == NULL" * check there. */ ret = JS_TRUE; goto out; } if (priv->gobj != NULL) { ret = JS_TRUE; goto out; } /* If we have no GIRepository information (we're a JS GObject subclass), * we need to look at exposing interfaces. Look up our interfaces through * GType data, and then hope that *those* are introspectable. */ if (priv->info == NULL) { ret = object_instance_new_resolve_no_info(context, *obj, objp, priv, name); goto out; } if (g_str_has_prefix (name, "vfunc_")) { /* The only time we find a vfunc info is when we're the base * class that defined the vfunc. If we let regular prototype * chaining resolve this, we'd have the implementation for the base's * vfunc on the base class, without any other "real" implementations * in the way. If we want to expose a "real" vfunc implementation, * we need to go down to the parent infos and look at their VFuncInfos. * * This is good, but it's memory-hungry -- we would define every * possible vfunc on every possible object, even if it's the same * "real" vfunc underneath. Instead, only expose vfuncs that are * different from their parent, and let prototype chaining do the * rest. */ gchar *name_without_vfunc_ = &name[6]; GIVFuncInfo *vfunc; gboolean defined_by_parent; vfunc = find_vfunc_on_parents(priv->info, name_without_vfunc_, &defined_by_parent); if (vfunc != NULL) { /* In the event that the vfunc is unchanged, let regular * prototypal inheritance take over. */ if (defined_by_parent && is_vfunc_unchanged(vfunc, priv->gtype)) { g_base_info_unref((GIBaseInfo *)vfunc); ret = JS_TRUE; goto out; } gjs_define_function(context, *obj, priv->gtype, vfunc); *objp = *obj; g_base_info_unref((GIBaseInfo *)vfunc); ret = JS_TRUE; goto out; } /* If the vfunc wasn't found, fall through, back to normal * method resolution. */ } /* find_method does not look at methods on parent classes, * we rely on javascript to walk up the __proto__ chain * and find those and define them in the right prototype. * * Note that if it isn't a method on the object, since JS * lacks multiple inheritance, we're sticking the iface * methods in the object prototype, which means there are many * copies of the iface methods (one per object class node that * introduces the iface) */ method_info = g_object_info_find_method_using_interfaces(priv->info, name, NULL); /** * Search through any interfaces implemented by the GType; * this could be done better. See * https://bugzilla.gnome.org/show_bug.cgi?id=632922 */ if (method_info == NULL) { ret = object_instance_new_resolve_no_info(context, *obj, objp, priv, name); goto out; } else { #if GJS_VERBOSE_ENABLE_GI_USAGE _gjs_log_info_usage((GIBaseInfo*) method_info); #endif gjs_debug(GJS_DEBUG_GOBJECT, "Defining method %s in prototype for %s (%s.%s)", g_base_info_get_name( (GIBaseInfo*) method_info), g_type_name(priv->gtype), g_base_info_get_namespace( (GIBaseInfo*) priv->info), g_base_info_get_name( (GIBaseInfo*) priv->info)); if (gjs_define_function(context, *obj, priv->gtype, method_info) == NULL) { g_base_info_unref( (GIBaseInfo*) method_info); goto out; } *objp = *obj; /* we defined the prop in obj */ g_base_info_unref( (GIBaseInfo*) method_info); } ret = JS_TRUE; out: g_free(name); return ret; } static void free_g_params(GParameter *params, int n_params) { int i; for (i = 0; i < n_params; ++i) { g_value_unset(¶ms[i].value); } g_free(params); } /* Set properties from args to constructor (argv[0] is supposed to be * a hash) */ static JSBool object_instance_props_to_g_parameters(JSContext *context, JSObject *obj, unsigned argc, jsval *argv, GType gtype, GParameter **gparams_p, int *n_gparams_p) { JSObject *props; JSObject *iter; jsid prop_id; GArray *gparams; if (gparams_p) *gparams_p = NULL; if (n_gparams_p) *n_gparams_p = 0; gparams = g_array_new(/* nul term */ FALSE, /* clear */ TRUE, sizeof(GParameter)); if (argc == 0 || JSVAL_IS_VOID(argv[0])) goto out; if (!JSVAL_IS_OBJECT(argv[0])) { gjs_throw(context, "argument should be a hash with props to set"); goto free_array_and_fail; } props = JSVAL_TO_OBJECT(argv[0]); iter = JS_NewPropertyIterator(context, props); if (iter == NULL) { gjs_throw(context, "Failed to create property iterator for object props hash"); goto free_array_and_fail; } prop_id = JSID_VOID; if (!JS_NextProperty(context, iter, &prop_id)) goto free_array_and_fail; while (!JSID_IS_VOID(prop_id)) { char *name = NULL; jsval value; GParameter gparam = { NULL, { 0, }}; if (!gjs_object_require_property(context, props, "property list", prop_id, &value)) { goto free_array_and_fail; } if (!gjs_get_string_id(context, prop_id, &name)) goto free_array_and_fail; switch (init_g_param_from_property(context, name, value, gtype, &gparam, TRUE /* constructing */)) { case NO_SUCH_G_PROPERTY: gjs_throw(context, "No property %s on this GObject %s", name, g_type_name(gtype)); case SOME_ERROR_OCCURRED: g_free(name); goto free_array_and_fail; case VALUE_WAS_SET: break; } g_free(name); g_array_append_val(gparams, gparam); prop_id = JSID_VOID; if (!JS_NextProperty(context, iter, &prop_id)) goto free_array_and_fail; } out: if (n_gparams_p) *n_gparams_p = gparams->len; if (gparams_p) *gparams_p = (GParameter*) g_array_free(gparams, FALSE); return JS_TRUE; free_array_and_fail: { GParameter *to_free; int count; count = gparams->len; to_free = (GParameter*) g_array_free(gparams, FALSE); free_g_params(to_free, count); } return JS_FALSE; } #define DEBUG_DISPOSE 0 #if DEBUG_DISPOSE static void wrapped_gobj_dispose_notify(gpointer data, GObject *where_the_object_was) { gjs_debug(GJS_DEBUG_GOBJECT, "JSObject %p GObject %p disposed", data, where_the_object_was); } #endif static void gobj_no_longer_kept_alive_func(JSObject *obj, void *data) { ObjectInstance *priv; priv = (ObjectInstance *) data; gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "GObject wrapper %p will no longer be kept alive, eligible for collection", obj); priv->keep_alive = NULL; } static GQuark get_qdata_key_for_toggle_direction(ToggleDirection direction) { GQuark quark; switch (direction) { case TOGGLE_UP: quark = gjs_toggle_up_quark(); break; case TOGGLE_DOWN: quark = gjs_toggle_down_quark(); break; default: g_assert_not_reached(); } return quark; } static gboolean clear_toggle_idle_source(GObject *gobj, ToggleDirection direction) { GQuark qdata_key; qdata_key = get_qdata_key_for_toggle_direction(direction); return g_object_steal_qdata(gobj, qdata_key) != NULL; } static gboolean toggle_idle_source_is_queued(GObject *gobj, ToggleDirection direction) { GQuark qdata_key; qdata_key = get_qdata_key_for_toggle_direction(direction); return g_object_get_qdata(gobj, qdata_key) != NULL; } static gboolean cancel_toggle_idle(GObject *gobj, ToggleDirection direction) { GQuark qdata_key; GSource *source; qdata_key = get_qdata_key_for_toggle_direction(direction); source = (GSource*) g_object_steal_qdata(gobj, qdata_key); if (source) g_source_destroy(source); return source != 0; } static void handle_toggle_down(GObject *gobj) { ObjectInstance *priv; JSObject *obj; obj = peek_js_obj(gobj); priv = (ObjectInstance *) JS_GetPrivate(obj); gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Toggle notify gobj %p obj %p is_last_ref TRUE keep-alive %p", gobj, obj, priv->keep_alive); /* Change to weak ref so the wrapper-wrappee pair can be * collected by the GC */ if (priv->keep_alive != NULL) { gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Removing object from keep alive"); gjs_keep_alive_remove_child(priv->keep_alive, gobj_no_longer_kept_alive_func, obj, priv); priv->keep_alive = NULL; } } static void handle_toggle_up(GObject *gobj) { ObjectInstance *priv; JSObject *obj; /* We need to root the JSObject associated with the passed in GObject so it * doesn't get garbage collected (and lose any associated javascript state * such as custom properties). */ obj = peek_js_obj(gobj); if (!obj) /* Object already GC'd */ return; priv = (ObjectInstance *) JS_GetPrivate(obj); gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Toggle notify gobj %p obj %p is_last_ref FALSEd keep-alive %p", gobj, obj, priv->keep_alive); /* Change to strong ref so the wrappee keeps the wrapper alive * in case the wrapper has data in it that the app cares about */ if (priv->keep_alive == NULL) { /* FIXME: thread the context through somehow. Maybe by looking up * the compartment that obj belongs to. */ GjsContext *context = gjs_context_get_current(); gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Adding object to keep alive"); priv->keep_alive = gjs_keep_alive_get_global((JSContext*) gjs_context_get_native_context(context)); gjs_keep_alive_add_child(priv->keep_alive, gobj_no_longer_kept_alive_func, obj, priv); } } static gboolean idle_handle_toggle(gpointer data) { ToggleRefNotifyOperation *operation = (ToggleRefNotifyOperation *) data; if (!clear_toggle_idle_source(operation->gobj, operation->direction)) { /* Already cleared, the JSObject is going away, abort mission */ goto out; } switch (operation->direction) { case TOGGLE_UP: handle_toggle_up(operation->gobj); break; case TOGGLE_DOWN: handle_toggle_down(operation->gobj); break; default: g_assert_not_reached(); } out: return FALSE; } static void toggle_ref_notify_operation_free(ToggleRefNotifyOperation *operation) { if (operation->needs_unref) g_object_unref (operation->gobj); g_slice_free(ToggleRefNotifyOperation, operation); g_atomic_int_add(&pending_idle_toggles, -1); } static void queue_toggle_idle(GObject *gobj, ToggleDirection direction) { ToggleRefNotifyOperation *operation; GQuark qdata_key; GSource *source; operation = g_slice_new0(ToggleRefNotifyOperation); operation->direction = direction; switch (direction) { case TOGGLE_UP: /* If we're toggling up we take a reference to the object now, * so it won't toggle down before we process it. This ensures we * only ever have at most two toggle notifications queued. * (either only up, or down-up) */ operation->gobj = (GObject*) g_object_ref(gobj); operation->needs_unref = TRUE; break; case TOGGLE_DOWN: /* If we're toggling down, we don't need to take a reference since * the associated JSObject already has one, and that JSObject won't * get finalized until we've completed toggling (since it's rooted, * until we unroot it when we dispatch the toggle down idle). * * Taking a reference now would be bad anyway, since it would force * the object to toggle back up again. */ operation->gobj = gobj; break; default: g_assert_not_reached(); } qdata_key = get_qdata_key_for_toggle_direction(direction); source = g_idle_source_new(); g_source_set_priority(source, G_PRIORITY_HIGH); g_source_set_callback(source, idle_handle_toggle, operation, (GDestroyNotify) toggle_ref_notify_operation_free); g_atomic_int_inc(&pending_idle_toggles); g_object_set_qdata (gobj, qdata_key, source); g_source_attach (source, NULL); /* object qdata is piggy-backing off the main loop's ref of the source */ g_source_unref (source); } static void wrapped_gobj_toggle_notify(gpointer data, GObject *gobj, gboolean is_last_ref) { gboolean is_main_thread, is_sweeping; gboolean toggle_up_queued, toggle_down_queued; GjsContext *context; JSContext *js_context; context = gjs_context_get_current(); if (_gjs_context_destroying(context)) { /* Do nothing here - we're in the process of disassociating * the objects. */ return; } /* We only want to touch javascript from one thread. * If we're not in that thread, then we need to defer processing * to it. * In case we're toggling up (and thus rooting the JS object) we * also need to take care if GC is running. The marking side * of it is taken care by JS::Heap, which we use in KeepAlive, * so we're safe. As for sweeping, it is too late: the JS object * is dead, and attempting to keep it alive would soon crash * the process. Plus, if we touch the JSAPI, libmozjs aborts in * the first BeginRequest. * Thus, in the toggleup+sweeping case we deassociate the object * and the wrapper and let the wrapper die. Then, if the object * appears again, we log a critical. * In practice, a toggle up during JS finalize can only happen * for temporary refs/unrefs of objects that are garbage anyway, * because JS code is never invoked while the finalizers run * and C code needs to clean after itself before it returns * from dispose()/finalize(). * On the other hand, toggling down is a lot simpler, because * we're creating more garbage. So we just add the object to * the keep alive and wait for the next GC cycle. * * Note that one would think that toggling up only happens * in the main thread (because toggling up is the result of * the JS object, previously visible only to JS code, becoming * visible to the refcounted C world), but because of weird * weak singletons like g_bus_get_sync() objects can see toggle-ups * from different threads too. * We could lock the keep alive structure and avoid the idle maybe, * but there aren't many peculiar objects like that and it's * not a big deal. */ is_main_thread = (gjs_eval_thread == g_thread_self()); if (is_main_thread) { js_context = (JSContext*) gjs_context_get_native_context(context); is_sweeping = gjs_runtime_is_sweeping(JS_GetRuntime(js_context)); } else { is_sweeping = FALSE; } toggle_up_queued = toggle_idle_source_is_queued(gobj, TOGGLE_UP); toggle_down_queued = toggle_idle_source_is_queued(gobj, TOGGLE_DOWN); if (is_last_ref) { /* We've transitions from 2 -> 1 references, * The JSObject is rooted and we need to unroot it so it * can be garbage collected */ if (is_main_thread) { if (G_UNLIKELY (toggle_up_queued || toggle_down_queued)) { g_error("toggling down object %s that's already queued to toggle %s\n", G_OBJECT_TYPE_NAME(gobj), toggle_up_queued && toggle_down_queued? "up and down" : toggle_up_queued? "up" : "down"); } handle_toggle_down(gobj); } else { queue_toggle_idle(gobj, TOGGLE_DOWN); } } else { /* We've transitioned from 1 -> 2 references. * * The JSObject associated with the gobject is not rooted, * but it needs to be. We'll root it. */ if (is_main_thread && !toggle_down_queued) { if (G_UNLIKELY (toggle_up_queued)) { g_error("toggling up object %s that's already queued to toggle up\n", G_OBJECT_TYPE_NAME(gobj)); } if (is_sweeping) { JSObject *object; object = peek_js_obj(gobj); if (JS_IsAboutToBeFinalized(&object)) { /* Ouch, the JS object is dead already. Disassociate the GObject * and hope the GObject dies too. */ disassociate_js_gobject(gobj); } } else { handle_toggle_up(gobj); } } else { queue_toggle_idle(gobj, TOGGLE_UP); } } } static void release_native_object (ObjectInstance *priv) { set_js_obj(priv->gobj, NULL); g_object_remove_toggle_ref(priv->gobj, wrapped_gobj_toggle_notify, NULL); priv->gobj = NULL; } /* At shutdown, we need to ensure we've cleared the context of any * pending toggle references. */ void gjs_object_prepare_shutdown (JSContext *context) { JSObject *keep_alive = gjs_keep_alive_get_global_if_exists (context); GjsKeepAliveIter kiter; JSObject *child; void *data; if (!keep_alive) return; /* First, get rid of anything left over on the main context */ while (g_main_context_pending(NULL) && g_atomic_int_get(&pending_idle_toggles) > 0) { g_main_context_iteration(NULL, FALSE); } /* Now, we iterate over all of the objects, breaking the JS <-> C * associaton. We avoid the potential recursion implied in: * toggle ref removal -> gobj dispose -> toggle ref notify * by simply ignoring toggle ref notifications during this process. */ gjs_keep_alive_iterator_init(&kiter, keep_alive); while (gjs_keep_alive_iterator_next(&kiter, gobj_no_longer_kept_alive_func, &child, &data)) { ObjectInstance *priv = (ObjectInstance*)data; release_native_object(priv); } } static ObjectInstance * init_object_private (JSContext *context, JSObject *object) { ObjectInstance *proto_priv; ObjectInstance *priv; JS_BeginRequest(context); priv = g_slice_new0(ObjectInstance); GJS_INC_COUNTER(object); g_assert(priv_from_js(context, object) == NULL); JS_SetPrivate(object, priv); gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "obj instance constructor, obj %p priv %p", object, priv); proto_priv = proto_priv_from_js(context, object); g_assert(proto_priv != NULL); priv->gtype = proto_priv->gtype; priv->info = proto_priv->info; if (priv->info) g_base_info_ref( (GIBaseInfo*) priv->info); JS_EndRequest(context); return priv; } static void associate_js_gobject (JSContext *context, JSObject *object, GObject *gobj) { ObjectInstance *priv; priv = priv_from_js(context, object); priv->gobj = gobj; g_assert(peek_js_obj(gobj) == NULL); set_js_obj(gobj, object); #if DEBUG_DISPOSE g_object_weak_ref(gobj, wrapped_gobj_dispose_notify, object); #endif /* OK, here is where things get complicated. We want the * wrapped gobj to keep the JSObject* wrapper alive, because * people might set properties on the JSObject* that they care * about. Therefore, whenever the refcount on the wrapped gobj * is >1, i.e. whenever something other than the wrapper is * referencing the wrapped gobj, the wrapped gobj has a strong * ref (gc-roots the wrapper). When the refcount on the * wrapped gobj is 1, then we change to a weak ref to allow * the wrapper to be garbage collected (and thus unref the * wrappee). */ priv->keep_alive = gjs_keep_alive_get_global(context); gjs_keep_alive_add_child(priv->keep_alive, gobj_no_longer_kept_alive_func, object, priv); g_object_add_toggle_ref(gobj, wrapped_gobj_toggle_notify, NULL); } static void disassociate_js_gobject (GObject *gobj) { JSObject *object; ObjectInstance *priv; object = peek_js_obj(gobj); priv = (ObjectInstance*) JS_GetPrivate(object); /* Idles are already checked in the only caller of this function, the toggle ref notify, but let's check again... */ g_assert(!cancel_toggle_idle(gobj, TOGGLE_UP)); g_assert(!cancel_toggle_idle(gobj, TOGGLE_DOWN)); invalidate_all_signals(priv); release_native_object(priv); /* Use -1 to mark that a JS object once existed, but it doesn't any more */ set_js_obj(gobj, (JSObject*)(-1)); #if DEBUG_DISPOSE g_object_weak_unref(gobj, wrapped_gobj_dispose_notify, object); #endif } static JSBool object_instance_init (JSContext *context, JSObject **object, unsigned argc, jsval *argv) { ObjectInstance *priv; GType gtype; GParameter *params; int n_params; GTypeQuery query; JSObject *old_jsobj; GObject *gobj; priv = init_object_private(context, *object); gtype = priv->gtype; g_assert(gtype != G_TYPE_NONE); if (G_TYPE_IS_ABSTRACT(gtype)) { gjs_throw(context, "Cannont instantiate abstract class %s", g_type_name(gtype)); return JS_FALSE; } if (!object_instance_props_to_g_parameters(context, *object, argc, argv, gtype, ¶ms, &n_params)) { return JS_FALSE; } /* Mark this object in the construction stack, it will be popped in gjs_object_custom_init() later down. */ if (g_type_get_qdata(gtype, gjs_is_custom_type_quark())) object_init_list = g_slist_prepend(object_init_list, *object); gobj = (GObject*) g_object_newv(gtype, n_params, params); free_g_params(params, n_params); old_jsobj = peek_js_obj(gobj); if (old_jsobj != NULL && old_jsobj != *object) { /* g_object_newv returned an object that's already tracked by a JS * object. Let's assume this is a singleton like IBus.IBus and return * the existing JS wrapper object. * * 'object' has a value that was originally created by * JS_NewObjectForConstructor in GJS_NATIVE_CONSTRUCTOR_PRELUDE, but * we're not actually using it, so just let it get collected. Avoiding * this would require a non-trivial amount of work. * */ *object = old_jsobj; g_object_unref(gobj); /* We already own a reference */ gobj = NULL; goto out; } g_type_query_dynamic_safe(gtype, &query); if (G_LIKELY (query.type)) JS_updateMallocCounter(context, query.instance_size); if (G_IS_INITIALLY_UNOWNED(gobj) && !g_object_is_floating(gobj)) { /* GtkWindow does not return a ref to caller of g_object_new. * Need a flag in gobject-introspection to tell us this. */ gjs_debug(GJS_DEBUG_GOBJECT, "Newly-created object is initially unowned but we did not get the " "floating ref, probably GtkWindow, using hacky workaround"); g_object_ref(gobj); } else if (g_object_is_floating(gobj)) { g_object_ref_sink(gobj); } else { /* we should already have a ref */ } if (priv->gobj == NULL) associate_js_gobject(context, *object, gobj); /* We now have both a ref and a toggle ref, we only want the * toggle ref. This may immediately remove the GC root * we just added, since refcount may drop to 1. */ g_object_unref(gobj); gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "JSObject created with GObject %p %s", priv->gobj, g_type_name_from_instance((GTypeInstance*) priv->gobj)); TRACE(GJS_OBJECT_PROXY_NEW(priv, priv->gobj, priv->info ? g_base_info_get_namespace((GIBaseInfo*) priv->info) : "_gjs_private", priv->info ? g_base_info_get_name((GIBaseInfo*) priv->info) : g_type_name(gtype))); out: return JS_TRUE; } GJS_NATIVE_CONSTRUCTOR_DECLARE(object_instance) { GJS_NATIVE_CONSTRUCTOR_VARIABLES(object_instance) JSBool ret; jsval initer; jsval rval; jsid object_init_name; GJS_NATIVE_CONSTRUCTOR_PRELUDE(object_instance); object_init_name = gjs_context_get_const_string(context, GJS_STRING_GOBJECT_INIT); if (!gjs_object_require_property(context, object, "GObject instance", object_init_name, &initer)) return JS_FALSE; rval = JSVAL_VOID; ret = gjs_call_function_value(context, object, initer, argc, argv, &rval); if (JSVAL_IS_VOID(rval)) rval = OBJECT_TO_JSVAL(object); JS_SET_RVAL(context, vp, rval); return ret; } static void invalidate_all_signals(ObjectInstance *priv) { GList *iter, *next; for (iter = priv->signals; iter; ) { ConnectData *cd = (ConnectData*) iter->data; next = iter->next; /* This will also free cd and iter, through the closure invalidation mechanism */ g_closure_invalidate(cd->closure); iter = next; } } static void object_instance_trace(JSTracer *tracer, JSObject *obj) { ObjectInstance *priv; GList *iter; priv = (ObjectInstance *) JS_GetPrivate(obj); for (iter = priv->signals; iter; iter = iter->next) { ConnectData *cd = (ConnectData *) iter->data; gjs_closure_trace(cd->closure, tracer); } } static void object_instance_finalize(JSFreeOp *fop, JSObject *obj) { ObjectInstance *priv; priv = (ObjectInstance *) JS_GetPrivate(obj); gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "finalize obj %p priv %p gtype %s gobj %p", obj, priv, (priv && priv->gobj) ? g_type_name_from_instance( (GTypeInstance*) priv->gobj) : "", priv ? priv->gobj : NULL); g_assert (priv != NULL); TRACE(GJS_OBJECT_PROXY_FINALIZE(priv, priv->gobj, priv->info ? g_base_info_get_namespace((GIBaseInfo*) priv->info) : "_gjs_private", priv->info ? g_base_info_get_name((GIBaseInfo*) priv->info) : g_type_name(priv->gtype))); if (priv->gobj) { gboolean had_toggle_up; gboolean had_toggle_down; invalidate_all_signals (priv); if (G_UNLIKELY (priv->gobj->ref_count <= 0)) { g_error("Finalizing proxy for an already freed object of type: %s.%s\n", priv->info ? g_base_info_get_namespace((GIBaseInfo*) priv->info) : "", priv->info ? g_base_info_get_name((GIBaseInfo*) priv->info) : g_type_name(priv->gtype)); } had_toggle_up = cancel_toggle_idle(priv->gobj, TOGGLE_UP); had_toggle_down = cancel_toggle_idle(priv->gobj, TOGGLE_DOWN); if (!had_toggle_up && had_toggle_down) { g_error("Finalizing proxy for an object that's scheduled to be unrooted: %s.%s\n", priv->info ? g_base_info_get_namespace((GIBaseInfo*) priv->info) : "", priv->info ? g_base_info_get_name((GIBaseInfo*) priv->info) : g_type_name(priv->gtype)); } release_native_object(priv); } if (priv->keep_alive != NULL) { /* This happens when the refcount on the object is still >1, * for example with global objects GDK never frees like GdkDisplay, * when we close down the JS runtime. */ gjs_debug(GJS_DEBUG_GOBJECT, "Wrapper was finalized despite being kept alive, has refcount >1"); gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Removing from keep alive"); gjs_keep_alive_remove_child(priv->keep_alive, gobj_no_longer_kept_alive_func, obj, priv); } if (priv->info) { g_base_info_unref( (GIBaseInfo*) priv->info); priv->info = NULL; } if (priv->klass) { g_type_class_unref (priv->klass); priv->klass = NULL; } GJS_DEC_COUNTER(object); g_slice_free(ObjectInstance, priv); } static JSObject * gjs_lookup_object_prototype_from_info(JSContext *context, GIObjectInfo *info, GType gtype) { JSObject *in_object; JSObject *constructor; const char *constructor_name; jsval value; if (info) { in_object = gjs_lookup_namespace_object(context, (GIBaseInfo*) info); constructor_name = g_base_info_get_name((GIBaseInfo*) info); } else { in_object = gjs_lookup_private_namespace(context); constructor_name = g_type_name(gtype); } if (G_UNLIKELY (!in_object)) return NULL; if (!JS_GetProperty(context, in_object, constructor_name, &value)) return NULL; if (JSVAL_IS_VOID(value)) { /* In case we're looking for a private type, and we don't find it, we need to define it first. */ gjs_define_object_class(context, in_object, NULL, gtype, &constructor); } else { if (G_UNLIKELY (!JSVAL_IS_OBJECT(value) || JSVAL_IS_NULL(value))) return NULL; constructor = JSVAL_TO_OBJECT(value); } g_assert(constructor != NULL); if (!gjs_object_get_property_const(context, constructor, GJS_STRING_PROTOTYPE, &value)) return NULL; if (G_UNLIKELY (!JSVAL_IS_OBJECT(value))) return NULL; return JSVAL_TO_OBJECT(value); } static JSObject * gjs_lookup_object_prototype(JSContext *context, GType gtype) { GIObjectInfo *info; JSObject *proto; info = (GIObjectInfo*)g_irepository_find_by_gtype(g_irepository_get_default(), gtype); proto = gjs_lookup_object_prototype_from_info(context, info, gtype); if (info) g_base_info_unref((GIBaseInfo*)info); return proto; } static void signal_connection_invalidated (gpointer user_data, GClosure *closure) { ConnectData *connect_data = (ConnectData *) user_data; connect_data->obj->signals = g_list_delete_link(connect_data->obj->signals, connect_data->link); g_slice_free(ConnectData, connect_data); } static JSBool real_connect_func(JSContext *context, unsigned argc, jsval *vp, gboolean after) { JSObject *obj = JS_THIS_OBJECT(context, vp); jsval *argv = JS_ARGV(context, vp); ObjectInstance *priv; GClosure *closure; gulong id; guint signal_id; char *signal_name; GQuark signal_detail; jsval retval; ConnectData *connect_data; JSBool ret = JS_FALSE; if (!do_base_typecheck(context, obj, JS_TRUE)) return JS_FALSE; priv = priv_from_js(context, obj); gjs_debug_gsignal("connect obj %p priv %p argc %d", obj, priv, argc); if (priv == NULL) { throw_priv_is_null_error(context); return JS_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 JS_FALSE; } /* Best I can tell, there is no way to know if argv[1] is really * callable other than to just try it. Checking whether it's a * function will not detect native objects that provide * JSClass::call, for example. */ if (argc != 2 || !JSVAL_IS_STRING(argv[0]) || !JSVAL_IS_OBJECT(argv[1])) { gjs_throw(context, "connect() takes two args, the signal name and the callback"); return JS_FALSE; } if (!gjs_string_to_utf8(context, argv[0], &signal_name)) { return JS_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, g_type_name(G_OBJECT_TYPE(priv->gobj))); goto out; } closure = gjs_closure_new_for_signal(context, JSVAL_TO_OBJECT(argv[1]), "signal callback", signal_id); if (closure == NULL) goto out; connect_data = g_slice_new(ConnectData); priv->signals = g_list_prepend(priv->signals, connect_data); connect_data->obj = priv; connect_data->link = priv->signals; /* This is a weak reference, and will be cleared when the closure is invalidated */ connect_data->closure = closure; g_closure_add_invalidate_notifier(closure, connect_data, signal_connection_invalidated); id = g_signal_connect_closure_by_id(priv->gobj, signal_id, signal_detail, closure, after); if (!JS_NewNumberValue(context, id, &retval)) { g_signal_handler_disconnect(priv->gobj, id); goto out; } JS_SET_RVAL(context, vp, retval); ret = JS_TRUE; out: g_free(signal_name); return ret; } static JSBool connect_after_func(JSContext *context, unsigned argc, jsval *vp) { return real_connect_func(context, argc, vp, TRUE); } static JSBool connect_func(JSContext *context, unsigned argc, jsval *vp) { return real_connect_func(context, argc, vp, FALSE); } static JSBool emit_func(JSContext *context, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(context, vp); JSObject *obj = JS_THIS_OBJECT(context, vp); ObjectInstance *priv; guint signal_id; GQuark signal_detail; GSignalQuery signal_query; char *signal_name; GValue *instance_and_args; GValue rvalue = G_VALUE_INIT; unsigned int i; gboolean failed; jsval retval; JSBool ret = JS_FALSE; if (!do_base_typecheck(context, obj, JS_TRUE)) return JS_FALSE; priv = priv_from_js(context, obj); gjs_debug_gsignal("emit obj %p priv %p argc %d", obj, priv, argc); if (priv == NULL) { throw_priv_is_null_error(context); return JS_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 JS_FALSE; } if (argc < 1 || !JSVAL_IS_STRING(argv[0])) { gjs_throw(context, "emit() first arg is the signal name"); return JS_FALSE; } if (!gjs_string_to_utf8(context, argv[0], &signal_name)) return JS_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, g_type_name(G_OBJECT_TYPE(priv->gobj))); goto out; } 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; } if (signal_query.return_type != G_TYPE_NONE) { g_value_init(&rvalue, signal_query.return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE); } instance_and_args = g_newa(GValue, signal_query.n_params + 1); memset(instance_and_args, 0, sizeof(GValue) * (signal_query.n_params + 1)); g_value_init(&instance_and_args[0], G_TYPE_FROM_INSTANCE(priv->gobj)); g_value_set_instance(&instance_and_args[0], priv->gobj); failed = FALSE; for (i = 0; i < signal_query.n_params; ++i) { GValue *value; value = &instance_and_args[i + 1]; g_value_init(value, signal_query.param_types[i] & ~G_SIGNAL_TYPE_STATIC_SCOPE); if ((signal_query.param_types[i] & G_SIGNAL_TYPE_STATIC_SCOPE) != 0) failed = !gjs_value_to_g_value_no_copy(context, argv[i+1], value); else failed = !gjs_value_to_g_value(context, argv[i+1], value); if (failed) break; } if (!failed) { g_signal_emitv(instance_and_args, signal_id, signal_detail, &rvalue); } if (signal_query.return_type != G_TYPE_NONE) { if (!gjs_value_from_g_value(context, &retval, &rvalue)) failed = TRUE; g_value_unset(&rvalue); } else { retval = JSVAL_VOID; } for (i = 0; i < (signal_query.n_params + 1); ++i) { g_value_unset(&instance_and_args[i]); } if (!failed) JS_SET_RVAL(context, vp, retval); ret = !failed; out: g_free(signal_name); return ret; } static JSBool to_string_func(JSContext *context, unsigned argc, jsval *vp) { JSObject *obj = JS_THIS_OBJECT(context, vp); ObjectInstance *priv; JSBool ret = JS_FALSE; jsval retval; if (!do_base_typecheck(context, obj, JS_TRUE)) goto out; priv = priv_from_js(context, obj); if (priv == NULL) { throw_priv_is_null_error(context); goto out; /* wrong class passed in */ } if (!_gjs_proxy_to_string_func(context, obj, "object", (GIBaseInfo*)priv->info, priv->gtype, priv->gobj, &retval)) goto out; ret = JS_TRUE; JS_SET_RVAL(context, vp, retval); out: return ret; } struct JSClass gjs_object_instance_class = { "GObject_Object", JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE, JS_PropertyStub, JS_DeletePropertyStub, object_instance_get_prop, object_instance_set_prop, JS_EnumerateStub, (JSResolveOp) object_instance_new_resolve, /* needs cast since it's the new resolve signature */ JS_ConvertStub, object_instance_finalize, NULL, NULL, NULL, NULL, object_instance_trace, }; static JSBool init_func (JSContext *context, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(context, vp); JSObject *obj = JS_THIS_OBJECT(context, vp); JSBool ret; if (!do_base_typecheck(context, obj, TRUE)) return FALSE; ret = object_instance_init(context, &obj, argc, argv); if (ret) JS_SET_RVAL(context, vp, OBJECT_TO_JSVAL(obj)); return ret; } JSPropertySpec gjs_object_instance_proto_props[] = { { NULL } }; JSFunctionSpec gjs_object_instance_proto_funcs[] = { { "_init", JSOP_WRAPPER((JSNative)init_func), 0, 0 }, { "connect", JSOP_WRAPPER((JSNative)connect_func), 0, 0 }, { "connect_after", JSOP_WRAPPER((JSNative)connect_after_func), 0, 0 }, { "emit", JSOP_WRAPPER((JSNative)emit_func), 0, 0 }, { "toString", JSOP_WRAPPER((JSNative)to_string_func), 0, 0 }, { NULL } }; static JSBool gjs_define_static_methods(JSContext *context, JSObject *constructor, GType gtype, GIObjectInfo *object_info) { int i; int n_methods; n_methods = g_object_info_get_n_methods(object_info); for (i = 0; i < n_methods; i++) { GIFunctionInfo *meth_info; GIFunctionInfoFlags flags; meth_info = g_object_info_get_method(object_info, i); flags = g_function_info_get_flags (meth_info); /* Anything that isn't a method we put on the prototype of the * constructor. This includes introspection * methods, as well as the forthcoming "static methods" * support. We may want to change this to use * GI_FUNCTION_IS_CONSTRUCTOR and GI_FUNCTION_IS_STATIC or the * like in the near future. */ if (!(flags & GI_FUNCTION_IS_METHOD)) { gjs_define_function(context, constructor, gtype, (GICallableInfo *)meth_info); } g_base_info_unref((GIBaseInfo*) meth_info); } return JS_TRUE; } void gjs_define_object_class(JSContext *context, JSObject *in_object, GIObjectInfo *info, GType gtype, JSObject **constructor_p) { const char *constructor_name; JSObject *prototype; JSObject *constructor; JSObject *parent_proto; JSObject *global; jsval value; ObjectInstance *priv; const char *ns; GType parent_type; g_assert(in_object != NULL); g_assert(gtype != G_TYPE_INVALID); /* http://egachine.berlios.de/embedding-sm-best-practice/apa.html * http://www.sitepoint.com/blogs/2006/01/17/javascript-inheritance/ * http://www.cs.rit.edu/~atk/JavaScript/manuals/jsobj/ * * What we want is: * * repoobj.Gtk.Window is constructor for a GtkWindow wrapper JSObject * (gjs_define_object_constructor() is supposed to define Window in Gtk) * * Window.prototype contains the methods on Window, e.g. set_default_size() * mywindow.__proto__ is Window.prototype * mywindow.__proto__.__proto__ is Bin.prototype * mywindow.__proto__.__proto__.__proto__ is Container.prototype * * Because Window.prototype is an instance of Window in a sense, * Window.prototype.__proto__ is Window.prototype, just as * mywindow.__proto__ is Window.prototype * * If we do "mywindow = new Window()" then we should get: * mywindow.__proto__ == Window.prototype * which means "mywindow instanceof Window" is true. * * Remember "Window.prototype" is "the __proto__ of stuff * constructed with new Window()" * * __proto__ is used to search for properties if you do "this.foo" * while __parent__ defines the scope to search if you just have * "foo". * * __proto__ is used to look up properties, while .prototype is only * relevant for constructors and is used to set __proto__ on new'd * objects. So .prototype only makes sense on constructors. * * JS_SetPrototype() and JS_GetPrototype() are for __proto__. * To set/get .prototype, just use the normal property accessors, * or JS_InitClass() sets it up automatically. * * JavaScript is SO AWESOME */ parent_proto = NULL; parent_type = g_type_parent(gtype); if (parent_type != G_TYPE_INVALID) parent_proto = gjs_lookup_object_prototype(context, parent_type); /* ns is only used to set the JSClass->name field (exposed by Object.prototype.toString). * We can safely set "unknown" if there is no info, as in that case * the name is globally unique (it's a GType name). */ if (info) { ns = g_base_info_get_namespace((GIBaseInfo*) info); constructor_name = g_base_info_get_name((GIBaseInfo*) info); } else { ns = "unknown"; constructor_name = g_type_name(gtype); } global = JS_GetGlobalObject(context); JSAutoCompartment ac(context, global); if (!gjs_init_class_dynamic(context, in_object, parent_proto, ns, constructor_name, &gjs_object_instance_class, gjs_object_instance_constructor, 0, /* props of prototype */ parent_proto ? NULL : &gjs_object_instance_proto_props[0], /* funcs of prototype */ parent_proto ? NULL : &gjs_object_instance_proto_funcs[0], /* props of constructor, MyConstructor.myprop */ NULL, /* funcs of constructor, MyConstructor.myfunc() */ NULL, &prototype, &constructor)) { g_error("Can't init class %s", constructor_name); } GJS_INC_COUNTER(object); priv = g_slice_new0(ObjectInstance); priv->info = info; if (info) g_base_info_ref((GIBaseInfo*) info); priv->gtype = gtype; priv->klass = (GTypeClass*) g_type_class_ref (gtype); JS_SetPrivate(prototype, priv); gjs_debug(GJS_DEBUG_GOBJECT, "Defined class %s prototype %p class %p in object %p", constructor_name, prototype, JS_GetClass(prototype), in_object); if (info) gjs_define_static_methods(context, constructor, gtype, info); value = OBJECT_TO_JSVAL(gjs_gtype_create_gtype_wrapper(context, gtype)); JS_DefineProperty(context, constructor, "$gtype", value, NULL, NULL, JSPROP_PERMANENT); if (constructor_p) *constructor_p = constructor; } static JSObject* peek_js_obj(GObject *gobj) { JSObject *object = (JSObject*) g_object_get_qdata(gobj, gjs_object_priv_quark()); if (G_UNLIKELY (object == (JSObject*)(-1))) { g_critical ("Object %p (a %s) resurfaced after the JS wrapper was finalized. " "This is some library doing dubious memory management inside dispose()", gobj, g_type_name(G_TYPE_FROM_INSTANCE(object))); return NULL; /* return null to associate again with a new wrapper */ } return object; } static void set_js_obj(GObject *gobj, JSObject *obj) { g_object_set_qdata(gobj, gjs_object_priv_quark(), obj); } JSObject* gjs_object_from_g_object(JSContext *context, GObject *gobj) { JSObject *obj; JSObject *global; if (gobj == NULL) return NULL; obj = peek_js_obj(gobj); if (obj == NULL) { /* We have to create a wrapper */ JSObject *proto; GType gtype; gjs_debug_marshal(GJS_DEBUG_GOBJECT, "Wrapping %s with JSObject", g_type_name_from_instance((GTypeInstance*) gobj)); gtype = G_TYPE_FROM_INSTANCE(gobj); proto = gjs_lookup_object_prototype(context, gtype); JS_BeginRequest(context); global = JS_GetGlobalObject(context); JSAutoCompartment ac(context, global); obj = JS_NewObjectWithGivenProto(context, JS_GetClass(proto), proto, gjs_get_import_global (context)); JS_EndRequest(context); if (obj == NULL) goto out; init_object_private(context, obj); g_object_ref_sink(gobj); associate_js_gobject(context, obj, gobj); /* see the comment in init_object_instance() for this */ g_object_unref(gobj); g_assert(peek_js_obj(gobj) == obj); } out: return obj; } GObject* gjs_g_object_from_object(JSContext *context, JSObject *obj) { ObjectInstance *priv; if (obj == NULL) return NULL; priv = priv_from_js(context, obj); return priv->gobj; } JSBool gjs_typecheck_is_object(JSContext *context, JSObject *object, JSBool throw_error) { return do_base_typecheck(context, object, throw_error); } JSBool gjs_typecheck_object(JSContext *context, JSObject *object, GType expected_type, JSBool throw_error) { ObjectInstance *priv; JSBool result; if (!do_base_typecheck(context, object, throw_error)) return JS_FALSE; priv = priv_from_js(context, object); if (priv == NULL) { if (throw_error) { gjs_throw(context, "Object instance or prototype has not been properly initialized yet. " "Did you forget to chain-up from _init()?"); } return JS_FALSE; } if (priv->gobj == NULL) { if (throw_error) { gjs_throw(context, "Object is %s.%s.prototype, not an object instance - cannot convert to GObject*", priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "", priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype)); } return JS_FALSE; } g_assert(priv->gtype == G_OBJECT_TYPE(priv->gobj)); if (expected_type != G_TYPE_NONE) result = g_type_is_a (priv->gtype, expected_type); else result = JS_TRUE; if (!result && throw_error) { if (priv->info) { gjs_throw_custom(context, "TypeError", "Object is of type %s.%s - cannot convert to %s", g_base_info_get_namespace((GIBaseInfo*) priv->info), g_base_info_get_name((GIBaseInfo*) priv->info), g_type_name(expected_type)); } else { gjs_throw_custom(context, "TypeError", "Object is of type %s - cannot convert to %s", g_type_name(priv->gtype), g_type_name(expected_type)); } } return result; } static void find_vfunc_info (JSContext *context, GType implementor_gtype, GIBaseInfo *vfunc_info, gchar *vfunc_name, gpointer *implementor_vtable_ret, GIFieldInfo **field_info_ret) { GType ancestor_gtype; int length, i; GIBaseInfo *ancestor_info; GIStructInfo *struct_info; gpointer implementor_class; gboolean is_interface; *field_info_ret = NULL; *implementor_vtable_ret = NULL; ancestor_info = g_base_info_get_container(vfunc_info); ancestor_gtype = g_registered_type_info_get_g_type((GIRegisteredTypeInfo*)ancestor_info); is_interface = g_base_info_get_type(ancestor_info) == GI_INFO_TYPE_INTERFACE; implementor_class = g_type_class_ref(implementor_gtype); if (is_interface) { GTypeInstance *implementor_iface_class; implementor_iface_class = (GTypeInstance*) g_type_interface_peek(implementor_class, ancestor_gtype); if (implementor_iface_class == NULL) { g_type_class_unref(implementor_class); gjs_throw (context, "Couldn't find GType of implementor of interface %s.", g_type_name(ancestor_gtype)); return; } *implementor_vtable_ret = implementor_iface_class; struct_info = g_interface_info_get_iface_struct((GIInterfaceInfo*)ancestor_info); } else { struct_info = g_object_info_get_class_struct((GIObjectInfo*)ancestor_info); *implementor_vtable_ret = implementor_class; } g_type_class_unref(implementor_class); length = g_struct_info_get_n_fields(struct_info); for (i = 0; i < length; i++) { GIFieldInfo *field_info; GITypeInfo *type_info; field_info = g_struct_info_get_field(struct_info, i); if (strcmp(g_base_info_get_name((GIBaseInfo*)field_info), vfunc_name) != 0) { g_base_info_unref(field_info); continue; } type_info = g_field_info_get_type(field_info); if (g_type_info_get_tag(type_info) != GI_TYPE_TAG_INTERFACE) { /* We have a field with the same name, but it's not a callback. * There's no hope of being another field with a correct name, * so just abort early. */ g_base_info_unref(type_info); g_base_info_unref(field_info); break; } else { g_base_info_unref(type_info); *field_info_ret = field_info; break; } } g_base_info_unref(struct_info); } static JSBool gjs_hook_up_vfunc(JSContext *cx, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(cx, vp); gchar *name; JSObject *object; JSObject *function; ObjectInstance *priv; GType gtype, info_gtype; GIObjectInfo *info; GIVFuncInfo *vfunc; gpointer implementor_vtable; GIFieldInfo *field_info; if (!gjs_parse_args(cx, "hook_up_vfunc", "oso", argc, argv, "object", &object, "name", &name, "function", &function)) return JS_FALSE; if (!do_base_typecheck(cx, object, JS_TRUE)) return JS_FALSE; priv = priv_from_js(cx, object); gtype = priv->gtype; info = priv->info; /* find the first class that actually has repository information */ info_gtype = gtype; while (!info && info_gtype != G_TYPE_OBJECT) { info_gtype = g_type_parent(info_gtype); info = g_irepository_find_by_gtype(g_irepository_get_default(), info_gtype); } /* If we don't have 'info', we don't have the base class (GObject). * This is awful, so abort now. */ g_assert(info != NULL); JS_SET_RVAL(cx, vp, JSVAL_VOID); vfunc = find_vfunc_on_parents(info, name, NULL); if (!vfunc) { guint i, n_interfaces; GType *interface_list; GIInterfaceInfo *interface; interface_list = g_type_interfaces(gtype, &n_interfaces); for (i = 0; i < n_interfaces; i++) { interface = (GIInterfaceInfo*)g_irepository_find_by_gtype(g_irepository_get_default(), interface_list[i]); /* The interface doesn't have to exist -- it could be private * or dynamic. */ if (interface) vfunc = g_interface_info_find_vfunc(interface, name); g_base_info_unref((GIBaseInfo*)interface); if (vfunc) break; } g_free(interface_list); } if (!vfunc) { gjs_throw(cx, "Could not find definition of virtual function %s", name); g_free(name); return JS_FALSE; } find_vfunc_info(cx, gtype, vfunc, name, &implementor_vtable, &field_info); if (field_info != NULL) { GITypeInfo *type_info; GIBaseInfo *interface_info; GICallbackInfo *callback_info; gint offset; gpointer method_ptr; GjsCallbackTrampoline *trampoline; type_info = g_field_info_get_type(field_info); interface_info = g_type_info_get_interface(type_info); callback_info = (GICallbackInfo*)interface_info; offset = g_field_info_get_offset(field_info); method_ptr = G_STRUCT_MEMBER_P(implementor_vtable, offset); trampoline = gjs_callback_trampoline_new(cx, OBJECT_TO_JSVAL(function), callback_info, GI_SCOPE_TYPE_NOTIFIED, TRUE); *((ffi_closure **)method_ptr) = trampoline->closure; g_base_info_unref(interface_info); g_base_info_unref(type_info); g_base_info_unref(field_info); } g_base_info_unref(vfunc); g_free(name); return JS_TRUE; } static gchar * hyphen_to_underscore (gchar *string) { gchar *str, *s; str = s = g_strdup(string); while (*(str++) != '\0') { if (*str == '-') *str = '_'; } return s; } static void gjs_object_get_gproperty (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GjsContext *gjs_context; JSContext *context; JSObject *js_obj; jsval jsvalue; gchar *underscore_name; gjs_context = gjs_context_get_current(); context = (JSContext*) gjs_context_get_native_context(gjs_context); js_obj = peek_js_obj(object); underscore_name = hyphen_to_underscore((gchar *)pspec->name); JS_GetProperty(context, js_obj, underscore_name, &jsvalue); g_free (underscore_name); if (!gjs_value_to_g_value(context, jsvalue, value)) return; } static void gjs_object_set_gproperty (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GjsContext *gjs_context; JSContext *context; JSObject *js_obj; jsval jsvalue; gchar *underscore_name; gjs_context = gjs_context_get_current(); context = (JSContext*) gjs_context_get_native_context(gjs_context); js_obj = peek_js_obj(object); if (!gjs_value_from_g_value(context, &jsvalue, value)) return; underscore_name = hyphen_to_underscore((gchar *)pspec->name); JS_SetProperty(context, js_obj, underscore_name, &jsvalue); g_free (underscore_name); } static void gjs_object_class_init(GObjectClass *klass, gpointer user_data) { GPtrArray *properties; GType gtype; guint i; gtype = G_OBJECT_CLASS_TYPE (klass); klass->set_property = gjs_object_set_gproperty; klass->get_property = gjs_object_get_gproperty; gjs_eval_thread = g_thread_self(); properties = (GPtrArray*) gjs_hash_table_for_gsize_lookup (class_init_properties, gtype); if (properties != NULL) { for (i = 0; i < properties->len; i++) { GParamSpec *pspec = (GParamSpec*) properties->pdata[i]; g_param_spec_set_qdata(pspec, gjs_is_custom_property_quark(), GINT_TO_POINTER(1)); g_object_class_install_property (klass, i+1, pspec); } gjs_hash_table_for_gsize_remove (class_init_properties, gtype); } } static void gjs_object_custom_init(GTypeInstance *instance, gpointer klass) { GjsContext *gjs_context; JSContext *context; JSObject *object; ObjectInstance *priv; object = (JSObject*) object_init_list->data; priv = (ObjectInstance*) JS_GetPrivate(object); if (priv->gtype != G_TYPE_FROM_INSTANCE (instance)) { /* This is not the most derived instance_init function, do nothing. */ return; } object_init_list = g_slist_delete_link(object_init_list, object_init_list); gjs_context = gjs_context_get_current(); context = (JSContext*) gjs_context_get_native_context(gjs_context); associate_js_gobject(context, object, G_OBJECT (instance)); } static inline void gjs_add_interface(GType instance_type, GType interface_type) { static GInterfaceInfo interface_vtable = { NULL, NULL, NULL }; g_type_add_interface_static(instance_type, interface_type, &interface_vtable); } static JSBool gjs_register_type(JSContext *cx, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(cx, vp); gchar *name; JSObject *parent, *constructor, *interfaces, *properties, *module; GType instance_type, parent_type; GTypeQuery query; GTypeModule *type_module; ObjectInstance *parent_priv; GTypeInfo type_info = { 0, /* class_size */ (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) gjs_object_class_init, (GClassFinalizeFunc) NULL, NULL, /* class_data */ 0, /* instance_size */ 0, /* n_preallocs */ gjs_object_custom_init, }; guint32 i, n_interfaces, n_properties; GPtrArray *properties_native = NULL; GType *iface_types; JSBool retval = JS_FALSE; JS_BeginRequest(cx); if (!gjs_parse_args(cx, "register_type", "osoo", argc, argv, "parent", &parent, "name", &name, "interfaces", &interfaces, "properties", &properties)) goto out; if (!parent) goto out; if (!do_base_typecheck(cx, parent, JS_TRUE)) goto out; if (!JS_IsArrayObject(cx, interfaces)) { gjs_throw(cx, "Invalid parameter interfaces (expected Array)"); goto out; } if (!JS_GetArrayLength(cx, interfaces, &n_interfaces)) goto out; if (!JS_IsArrayObject(cx, properties)) { gjs_throw(cx, "Invalid parameter properties (expected Array)"); goto out; } if (!JS_GetArrayLength(cx, properties, &n_properties)) goto out; iface_types = (GType*) g_alloca(sizeof(GType) * n_interfaces); /* We do interface addition in two passes so that any failure is caught early, before registering the GType (which we can't undo) */ for (i = 0; i < n_interfaces; i++) { jsval iface_val; GType iface_type; if (!JS_GetElement(cx, interfaces, i, &iface_val)) goto out; if (!JSVAL_IS_OBJECT(iface_val) || ((iface_type = gjs_gtype_get_actual_gtype(cx, JSVAL_TO_OBJECT(iface_val))) == G_TYPE_INVALID)) { gjs_throw(cx, "Invalid parameter interfaces (element %d was not a GType)", i); goto out; } iface_types[i] = iface_type; } if (g_type_from_name(name) != G_TYPE_INVALID) { gjs_throw (cx, "Type name %s is already registered", name); goto out; } parent_priv = priv_from_js(cx, parent); /* We checked parent above, in do_base_typecheck() */ g_assert(parent_priv != NULL); parent_type = parent_priv->gtype; g_type_query_dynamic_safe(parent_type, &query); if (G_UNLIKELY (query.type == 0)) { gjs_throw (cx, "Cannot inherit from a non-gjs dynamic type [bug 687184]"); goto out; } type_info.class_size = query.class_size; type_info.instance_size = query.instance_size; type_module = G_TYPE_MODULE (gjs_type_module_get()); instance_type = g_type_module_register_type(type_module, parent_type, name, &type_info, (GTypeFlags) 0); g_free(name); g_type_set_qdata (instance_type, gjs_is_custom_type_quark(), GINT_TO_POINTER (1)); if (!class_init_properties) class_init_properties = gjs_hash_table_new_for_gsize ((GDestroyNotify)g_ptr_array_unref); properties_native = g_ptr_array_new_with_free_func ((GDestroyNotify)g_param_spec_unref); for (i = 0; i < n_properties; i++) { jsval prop_val; JSObject *prop_obj; if (!JS_GetElement(cx, properties, i, &prop_val)) goto out; if (!JSVAL_IS_OBJECT(prop_val)) { gjs_throw (cx, "Invalid parameter, expected object"); goto out; } prop_obj = JSVAL_TO_OBJECT(prop_val); if (!gjs_typecheck_param(cx, prop_obj, G_TYPE_NONE, JS_TRUE)) goto out; g_ptr_array_add (properties_native, g_param_spec_ref (gjs_g_param_from_param (cx, prop_obj))); } gjs_hash_table_for_gsize_insert (class_init_properties, (gsize)instance_type, g_ptr_array_ref (properties_native)); for (i = 0; i < n_interfaces; i++) gjs_add_interface(instance_type, iface_types[i]); /* create a custom JSClass */ module = gjs_lookup_private_namespace(cx); gjs_define_object_class(cx, module, NULL, instance_type, &constructor); JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(constructor)); retval = JS_TRUE; out: g_clear_pointer(&properties_native, g_ptr_array_unref); JS_EndRequest(cx); return retval; } static JSBool gjs_signal_new(JSContext *cx, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(cx, vp); JSObject *obj; GType gtype; gchar *signal_name = NULL; GSignalAccumulator accumulator; gint signal_id; guint i, n_parameters; GType *params; JSBool ret; if (argc != 6) return JS_FALSE; JS_BeginRequest(cx); if (!gjs_string_to_utf8(cx, argv[1], &signal_name)) { ret = JS_FALSE; goto out; } obj = JSVAL_TO_OBJECT(argv[0]); if (!gjs_typecheck_gtype(cx, obj, JS_TRUE)) return JS_FALSE; /* we only support standard accumulators for now */ switch (JSVAL_TO_INT(argv[3])) { case 1: accumulator = g_signal_accumulator_first_wins; break; case 2: accumulator = g_signal_accumulator_true_handled; break; case 0: default: accumulator = NULL; } if (accumulator == g_signal_accumulator_true_handled && JSVAL_TO_INT(argv[4]) != G_TYPE_BOOLEAN) { gjs_throw (cx, "GObject.SignalAccumulator.TRUE_HANDLED can only be used with boolean signals"); ret = JS_FALSE; goto out; } if (!JS_GetArrayLength(cx, JSVAL_TO_OBJECT(argv[5]), &n_parameters)) { ret = JS_FALSE; goto out; } params = g_newa(GType, n_parameters); for (i = 0; i < n_parameters; i++) { jsval gtype_val; if (!JS_GetElement(cx, JSVAL_TO_OBJECT(argv[5]), i, >ype_val) || !JSVAL_IS_OBJECT(gtype_val)) { gjs_throw(cx, "Invalid signal parameter number %d", i); ret = JS_FALSE; goto out; } params[i] = gjs_gtype_get_actual_gtype(cx, JSVAL_TO_OBJECT(gtype_val)); } gtype = gjs_gtype_get_actual_gtype(cx, obj); signal_id = g_signal_newv(signal_name, gtype, (GSignalFlags) JSVAL_TO_INT(argv[2]), /* signal_flags */ NULL, /* class closure */ accumulator, NULL, /* accu_data */ g_cclosure_marshal_generic, gjs_gtype_get_actual_gtype(cx, JSVAL_TO_OBJECT(argv[4])), /* return type */ n_parameters, params); JS_SET_RVAL(cx, vp, INT_TO_JSVAL(signal_id)); ret = JS_TRUE; out: JS_EndRequest(cx); free (signal_name); return ret; } static JSFunctionSpec module_funcs[] = { { "register_type", JSOP_WRAPPER ((JSNative) gjs_register_type), 4, GJS_MODULE_PROP_FLAGS }, { "add_interface", JSOP_WRAPPER ((JSNative) gjs_add_interface), 2, GJS_MODULE_PROP_FLAGS }, { "hook_up_vfunc", JSOP_WRAPPER ((JSNative) gjs_hook_up_vfunc), 3, GJS_MODULE_PROP_FLAGS }, { "signal_new", JSOP_WRAPPER ((JSNative) gjs_signal_new), 6, GJS_MODULE_PROP_FLAGS }, { NULL }, }; JSBool gjs_define_private_gi_stuff(JSContext *context, JSObject **module_out) { JSObject *module; module = JS_NewObject (context, NULL, NULL, NULL); if (!JS_DefineFunctions(context, module, &module_funcs[0])) return JS_FALSE; *module_out = module; return JS_TRUE; } cjs-2.8.0/gi/function.h0000664000175000017500000000641112610172032013615 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_FUNCTION_H__ #define __GJS_FUNCTION_H__ #include #include "cjs/jsapi-util.h" #include #include G_BEGIN_DECLS typedef enum { PARAM_NORMAL, PARAM_SKIPPED, PARAM_ARRAY, PARAM_CALLBACK } GjsParamType; typedef struct { gint ref_count; JSContext *context; GICallableInfo *info; jsval js_function; ffi_cif cif; ffi_closure *closure; GIScopeType scope; gboolean is_vfunc; GjsParamType *param_types; } GjsCallbackTrampoline; GjsCallbackTrampoline* gjs_callback_trampoline_new(JSContext *context, jsval function, GICallableInfo *callable_info, GIScopeType scope, gboolean is_vfunc); void gjs_callback_trampoline_unref(GjsCallbackTrampoline *trampoline); void gjs_callback_trampoline_ref(GjsCallbackTrampoline *trampoline); JSObject* gjs_define_function (JSContext *context, JSObject *in_object, GType gtype, GICallableInfo *info); JSBool gjs_invoke_c_function_uncached (JSContext *context, GIFunctionInfo *info, JSObject *obj, unsigned argc, jsval *argv, jsval *rval); JSBool gjs_invoke_constructor_from_c (JSContext *context, JSObject *constructor, JSObject *obj, unsigned argc, jsval *argv, GArgument *rvalue); void gjs_init_cinvoke_profiling (void); G_END_DECLS #endif /* __GJS_FUNCTION_H__ */ cjs-2.8.0/gi/closure.cpp0000664000175000017500000002755412610172032014012 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include "closure.h" #include "keep-alive.h" #include #include typedef struct { GClosure base; JSRuntime *runtime; JSContext *context; JSObject *obj; guint unref_on_global_object_finalized : 1; } Closure; /* * Memory management of closures is "interesting" because we're keeping around * a JSContext* and then trying to use it spontaneously from the main loop. * I don't think that's really quite kosher, and perhaps the problem is that * (in xulrunner) we just need to save a different context. * * Or maybe the right fix is to create our own context just for this? * * But for the moment, we save the context that was used to create the closure. * * Here's the problem: this context can be destroyed. AFTER the * context is destroyed, or at least potentially after, the objects in * the context's global object may be garbage collected. Remember that * JSObject* belong to a runtime, not a context. * * There is apparently no robust way to track context destruction in * SpiderMonkey, because the context can be destroyed without running * the garbage collector, and xulrunner takes over the JS_SetContextCallback() * callback. So there's no callback for us. * * So, when we go to use our context, we iterate the contexts in the runtime * and see if ours is still in the valid list, and decide to invalidate * the closure if it isn't. * * The closure can thus be destroyed in several cases: * - invalidation by unref, e.g. when a signal is disconnected, closure is unref'd * - invalidation because we were invoked while the context was dead * - invalidation through finalization (we were garbage collected) * * These don't have to happen in the same order; garbage collection can * be either before, or after, context destruction. * */ static void invalidate_js_pointers(Closure *c) { if (c->obj == NULL) return; c->obj = NULL; c->context = NULL; c->runtime = NULL; /* Notify any closure reference holders they * may want to drop references. */ g_closure_invalidate(&c->base); } static void global_context_finalized(JSObject *obj, void *data) { Closure *c; gboolean need_unref; c = (Closure *) data; gjs_debug_closure("Context global object destroy notifier on closure %p " "which calls object %p", c, c->obj); /* invalidate_js_pointers() could free us so check flag now to avoid * invalid memory access */ need_unref = c->unref_on_global_object_finalized; c->unref_on_global_object_finalized = FALSE; if (c->obj != NULL) { g_assert(c->obj == obj); invalidate_js_pointers(c); } if (need_unref) { g_closure_unref(&c->base); } } static void check_context_valid(Closure *c) { JSContext *a_context; JSContext *iter; if (c->runtime == NULL) return; iter = NULL; while ((a_context = JS_ContextIterator(c->runtime, &iter)) != NULL) { if (a_context == c->context) { return; } } gjs_debug_closure("Context %p no longer exists, invalidating " "closure %p which calls object %p", c->context, c, c->obj); /* Did not find the context. */ invalidate_js_pointers(c); } /* Invalidation is like "dispose" - it is guaranteed to happen at * finalize, but may happen before finalize. Normally, g_closure_invalidate() * is called when the "target" of the closure becomes invalid, so that the * source (the signal connection, say can be removed.) The usage above * in invalidate_js_pointers() is typical. Since the target of the closure * is under our control, it's unlikely that g_closure_invalidate() will ever * be called by anyone else, but in case it ever does, it's slightly better * to remove the "keep alive" here rather than in the finalize notifier. * * Unlike "dispose" invalidation only happens once. */ static void closure_invalidated(gpointer data, GClosure *closure) { Closure *c; c = (Closure*) closure; GJS_DEC_COUNTER(closure); gjs_debug_closure("Invalidating closure %p which calls object %p", closure, c->obj); if (c->obj == NULL) { gjs_debug_closure(" (closure %p already dead, nothing to do)", closure); return; } /* this will set c->obj to null if the context is dead */ check_context_valid(c); if (c->obj == NULL) { /* Context is dead here. This happens if, as a side effect of * tearing down the context, the closure was invalidated, * say be some other finalized object that had a ref to * the closure dropping said ref. * * Because c->obj was not NULL at the start of * closure_invalidated, we know that * global_context_finalized() has not been called. So we know * we are not being invalidated from inside * global_context_finalized(). * * That means global_context_finalized() has yet to be called, * but we know it will be called, because the context is dead * and thus its global object should be finalized. * * We can't call gjs_keep_alive_remove_global_child() because * the context is invalid memory and we can't get to the * global object that stores the keep alive. * * So global_context_finalized() could be called on an * already-finalized closure. To avoid this, we temporarily * ref ourselves, and set a flag to remove this ref * in global_context_finalized(). */ gjs_debug_closure(" (closure %p's context was dead, holding ref " "until global object finalize)", closure); c->unref_on_global_object_finalized = TRUE; g_closure_ref(&c->base); } else { /* If the context still exists, then remove our destroy * notifier. Otherwise we would call the destroy notifier on * an already-freed closure. * * This happens in the normal case, when the closure is * invalidated for some reason other than destruction of the * JSContext. */ gjs_debug_closure(" (closure %p's context was alive, " "removing our destroy notifier on global object)", closure); gjs_keep_alive_remove_global_child(c->context, global_context_finalized, c->obj, c); c->obj = NULL; c->context = NULL; c->runtime = NULL; } } static void closure_set_invalid(gpointer data, GClosure *closure) { Closure *self = (Closure*) closure; self->obj = NULL; self->context = NULL; self->runtime = NULL; GJS_DEC_COUNTER(closure); } void gjs_closure_invoke(GClosure *closure, int argc, jsval *argv, jsval *retval) { Closure *c; JSContext *context; JSObject *global; c = (Closure*) closure; check_context_valid(c); if (c->obj == NULL) { /* We were destroyed; become a no-op */ c->context = NULL; return; } context = c->context; JS_BeginRequest(context); global = JS_GetGlobalObject(context); JSAutoCompartment ac(context, global); if (JS_IsExceptionPending(context)) { gjs_debug_closure("Exception was pending before invoking callback??? " "Not expected"); gjs_log_exception(context); } if (!gjs_call_function_value(context, NULL, /* "this" object; NULL is some kind of default presumably */ OBJECT_TO_JSVAL(c->obj), argc, argv, retval)) { /* Exception thrown... */ gjs_debug_closure("Closure invocation failed (exception should " "have been thrown) closure %p callable %p", closure, c->obj); if (!gjs_log_exception(context)) gjs_debug_closure("Closure invocation failed but no exception was set?"); goto out; } if (gjs_log_exception(context)) { gjs_debug_closure("Closure invocation succeeded but an exception was set"); } out: JS_EndRequest(context); } gboolean gjs_closure_is_valid(GClosure *closure) { Closure *c; c = (Closure*) closure; return c->context != NULL; } JSContext* gjs_closure_get_context(GClosure *closure) { Closure *c; c = (Closure*) closure; return c->context; } JSObject* gjs_closure_get_callable(GClosure *closure) { Closure *c; c = (Closure*) closure; return c->obj; } void gjs_closure_trace(GClosure *closure, JSTracer *tracer) { Closure *c; c = (Closure*) closure; if (c->obj == NULL) return; JS_CallObjectTracer(tracer, &c->obj, "signal connection"); } GClosure* gjs_closure_new(JSContext *context, JSObject *callable, const char *description, gboolean root_function) { Closure *c; c = (Closure*) g_closure_new_simple(sizeof(Closure), NULL); c->runtime = JS_GetRuntime(context); /* The saved context is used for lifetime management, so that the closure will * be torn down with the context that created it. The context could be attached to * the default context of the runtime using if we wanted the closure to survive * the context that created it. */ c->context = context; JS_BeginRequest(context); c->obj = callable; c->unref_on_global_object_finalized = FALSE; GJS_INC_COUNTER(closure); if (root_function) { /* Fully manage closure lifetime if so asked */ gjs_keep_alive_add_global_child(context, global_context_finalized, c->obj, c); g_closure_add_invalidate_notifier(&c->base, NULL, closure_invalidated); } else { /* Only mark the closure as invalid if memory is managed outside (i.e. by object.c for signals) */ g_closure_add_invalidate_notifier(&c->base, NULL, closure_set_invalid); } gjs_debug_closure("Create closure %p which calls object %p '%s'", c, c->obj, description); JS_EndRequest(context); return &c->base; } cjs-2.8.0/gi/foreign.h0000664000175000017500000000771012610172032013424 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2010 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_OVERRIDE_H__ #define __GJS_OVERRIDE_H__ #include #include #include "arg.h" typedef JSBool (*GjsArgOverrideToGArgumentFunc) (JSContext *context, jsval value, const char *arg_name, GjsArgumentType argument_type, GITransfer transfer, gboolean may_be_null, GArgument *arg); typedef JSBool (*GjsArgOverrideFromGArgumentFunc) (JSContext *context, jsval *value_p, GArgument *arg); typedef JSBool (*GjsArgOverrideReleaseGArgumentFunc) (JSContext *context, GITransfer transfer, GArgument *arg); typedef struct { GjsArgOverrideToGArgumentFunc to_func; GjsArgOverrideFromGArgumentFunc from_func; GjsArgOverrideReleaseGArgumentFunc release_func; } GjsForeignInfo; JSBool gjs_struct_foreign_register (const char *gi_namespace, const char *type_name, GjsForeignInfo *info); JSBool gjs_struct_foreign_convert_to_g_argument (JSContext *context, jsval value, GIBaseInfo *interface_info, const char *arg_name, GjsArgumentType argument_type, GITransfer transfer, gboolean may_be_null, GArgument *arg); JSBool gjs_struct_foreign_convert_from_g_argument (JSContext *context, jsval *value_p, GIBaseInfo *interface_info, GArgument *arg); JSBool gjs_struct_foreign_release_g_argument (JSContext *context, GITransfer transfer, GIBaseInfo *interface_info, GArgument *arg); #endif /* __GJS_OVERRIDE_H__ */ cjs-2.8.0/gi/proxyutils.cpp0000664000175000017500000000516512610172032014572 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2012 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include "proxyutils.h" /* Default spidermonkey toString is worthless. Replace it * with something that gives us both the introspection name * and a memory address. */ JSBool _gjs_proxy_to_string_func(JSContext *context, JSObject *this_obj, const char *objtype, GIBaseInfo *info, GType gtype, gpointer native_address, jsval *rval) { GString *buf; JSBool ret = JS_FALSE; buf = g_string_new(""); g_string_append_c(buf, '['); g_string_append(buf, objtype); if (native_address == NULL) g_string_append(buf, " prototype of"); else g_string_append(buf, " instance proxy"); if (info != NULL) { g_string_append_printf(buf, " GIName:%s.%s", g_base_info_get_namespace(info), g_base_info_get_name(info)); } else { g_string_append(buf, " GType:"); g_string_append(buf, g_type_name(gtype)); } g_string_append_printf(buf, " jsobj@%p", this_obj); if (native_address != NULL) g_string_append_printf(buf, " native@%p", native_address); g_string_append_c(buf, ']'); if (!gjs_string_from_utf8 (context, buf->str, -1, rval)) goto out; ret = JS_TRUE; out: g_string_free (buf, TRUE); return ret; } cjs-2.8.0/gi/value.cpp0000664000175000017500000007626512610172032013455 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include "foreign.h" #include "value.h" #include "closure.h" #include "arg.h" #include "param.h" #include "object.h" #include "fundamental.h" #include "boxed.h" #include "union.h" #include "gtype.h" #include "gerror.h" #include #include #include static JSBool gjs_value_from_g_value_internal(JSContext *context, jsval *value_p, const GValue *gvalue, gboolean no_copy, GSignalQuery *signal_query, gint arg_n); static void closure_marshal(GClosure *closure, GValue *return_value, guint n_param_values, const GValue *param_values, gpointer invocation_hint, gpointer marshal_data) { JSContext *context; JSObject *global; JSRuntime *runtime; int argc; jsval *argv; jsval rval; int i; GSignalQuery signal_query = { 0, }; gjs_debug_marshal(GJS_DEBUG_GCLOSURE, "Marshal closure %p", closure); if (!gjs_closure_is_valid(closure)) { /* We were destroyed; become a no-op */ return; } context = gjs_closure_get_context(closure); runtime = JS_GetRuntime(context); if (G_UNLIKELY (gjs_runtime_is_sweeping(runtime))) { GSignalInvocationHint *hint = (GSignalInvocationHint*) invocation_hint; g_critical("Attempting to call back into JSAPI during the sweeping phase of GC. " "This is most likely caused by not destroying a Clutter actor or Gtk+ " "widget with ::destroy signals connected, but can also be caused by " "using the destroy() or dispose() vfuncs. Because it would crash the " "application, it has been blocked and the JS callback not invoked."); if (hint) { gpointer instance; g_signal_query(hint->signal_id, &signal_query); instance = g_value_peek_pointer(¶m_values[0]); g_critical("The offending signal was %s on %s %p.", signal_query.signal_name, g_type_name(G_TYPE_FROM_INSTANCE(instance)), instance); } /* A gjs_dumpstack() would be nice here, but we can't, because that works by creating a new Error object and reading the stack property, which is the worst possible idea during a GC session. */ return; } JS_BeginRequest(context); global = JS_GetGlobalObject(context); JSAutoCompartment ac(context, global); argc = n_param_values; rval = JSVAL_VOID; if (argc > 0) { argv = g_newa(jsval, n_param_values); gjs_set_values(context, argv, argc, JSVAL_VOID); gjs_root_value_locations(context, argv, argc); } else { /* squash a compiler warning */ argv = NULL; } JS_AddValueRoot(context, &rval); if (marshal_data) { /* we are used for a signal handler */ guint signal_id; signal_id = GPOINTER_TO_UINT(marshal_data); g_signal_query(signal_id, &signal_query); if (!signal_query.signal_id) { gjs_debug(GJS_DEBUG_GCLOSURE, "Signal handler being called on invalid signal"); goto cleanup; } if (signal_query.n_params + 1 != n_param_values) { gjs_debug(GJS_DEBUG_GCLOSURE, "Signal handler being called with wrong number of parameters"); goto cleanup; } } for (i = 0; i < argc; ++i) { const GValue *gval = ¶m_values[i]; gboolean no_copy; no_copy = FALSE; if (i >= 1 && signal_query.signal_id) { no_copy = (signal_query.param_types[i - 1] & G_SIGNAL_TYPE_STATIC_SCOPE) != 0; } if (!gjs_value_from_g_value_internal(context, &argv[i], gval, no_copy, &signal_query, i)) { gjs_debug(GJS_DEBUG_GCLOSURE, "Unable to convert arg %d in order to invoke closure", i); gjs_log_exception(context); goto cleanup; } } gjs_closure_invoke(closure, argc, argv, &rval); if (return_value != NULL) { if (JSVAL_IS_VOID(rval)) { /* something went wrong invoking, error should be set already */ goto cleanup; } if (!gjs_value_to_g_value(context, rval, return_value)) { gjs_debug(GJS_DEBUG_GCLOSURE, "Unable to convert return value when invoking closure"); gjs_log_exception(context); goto cleanup; } } cleanup: if (argc > 0) gjs_unroot_value_locations(context, argv, argc); JS_RemoveValueRoot(context, &rval); JS_EndRequest(context); } GClosure* gjs_closure_new_for_signal(JSContext *context, JSObject *callable, const char *description, guint signal_id) { GClosure *closure; closure = gjs_closure_new(context, callable, description, FALSE); g_closure_set_meta_marshal(closure, GUINT_TO_POINTER(signal_id), closure_marshal); return closure; } GClosure* gjs_closure_new_marshaled (JSContext *context, JSObject *callable, const char *description) { GClosure *closure; closure = gjs_closure_new(context, callable, description, TRUE); g_closure_set_marshal(closure, closure_marshal); return closure; } static GType gjs_value_guess_g_type(JSContext *context, jsval value) { if (JSVAL_IS_NULL(value)) return G_TYPE_POINTER; if (JSVAL_IS_STRING(value)) return G_TYPE_STRING; if (JSVAL_IS_INT(value)) return G_TYPE_INT; if (JSVAL_IS_DOUBLE(value)) return G_TYPE_DOUBLE; if (JSVAL_IS_BOOLEAN(value)) return G_TYPE_BOOLEAN; if (JSVAL_IS_OBJECT(value)) return gjs_gtype_get_actual_gtype(context, JSVAL_TO_OBJECT(value)); return G_TYPE_INVALID; } static JSBool gjs_value_to_g_value_internal(JSContext *context, jsval value, GValue *gvalue, gboolean no_copy) { GType gtype; gtype = G_VALUE_TYPE(gvalue); if (gtype == 0) { gtype = gjs_value_guess_g_type(context, value); if (gtype == G_TYPE_INVALID) { gjs_throw(context, "Could not guess unspecified GValue type"); return JS_FALSE; } gjs_debug_marshal(GJS_DEBUG_GCLOSURE, "Guessed GValue type %s from JS Value", g_type_name(gtype)); g_value_init(gvalue, gtype); } gjs_debug_marshal(GJS_DEBUG_GCLOSURE, "Converting jsval to gtype %s", g_type_name(gtype)); if (gtype == G_TYPE_STRING) { /* Don't use ValueToString since we don't want to just toString() * everything automatically */ if (JSVAL_IS_NULL(value)) { g_value_set_string(gvalue, NULL); } else if (JSVAL_IS_STRING(value)) { gchar *utf8_string; if (!gjs_string_to_utf8(context, value, &utf8_string)) return JS_FALSE; g_value_take_string(gvalue, utf8_string); } else { gjs_throw(context, "Wrong type %s; string expected", gjs_get_type_name(value)); return JS_FALSE; } } else if (gtype == G_TYPE_CHAR) { gint32 i; if (JS_ValueToInt32(context, value, &i) && i >= SCHAR_MIN && i <= SCHAR_MAX) { g_value_set_schar(gvalue, (signed char)i); } else { gjs_throw(context, "Wrong type %s; char expected", gjs_get_type_name(value)); return JS_FALSE; } } else if (gtype == G_TYPE_UCHAR) { guint16 i; if (JS_ValueToUint16(context, value, &i) && i <= UCHAR_MAX) { g_value_set_uchar(gvalue, (unsigned char)i); } else { gjs_throw(context, "Wrong type %s; unsigned char expected", gjs_get_type_name(value)); return JS_FALSE; } } else if (gtype == G_TYPE_INT) { gint32 i; if (JS_ValueToInt32(context, value, &i)) { g_value_set_int(gvalue, i); } else { gjs_throw(context, "Wrong type %s; integer expected", gjs_get_type_name(value)); return JS_FALSE; } } else if (gtype == G_TYPE_DOUBLE) { gdouble d; if (JS_ValueToNumber(context, value, &d)) { g_value_set_double(gvalue, d); } else { gjs_throw(context, "Wrong type %s; double expected", gjs_get_type_name(value)); return JS_FALSE; } } else if (gtype == G_TYPE_FLOAT) { gdouble d; if (JS_ValueToNumber(context, value, &d)) { g_value_set_float(gvalue, d); } else { gjs_throw(context, "Wrong type %s; float expected", gjs_get_type_name(value)); return JS_FALSE; } } else if (gtype == G_TYPE_UINT) { guint32 i; if (JS_ValueToECMAUint32(context, value, &i)) { g_value_set_uint(gvalue, i); } else { gjs_throw(context, "Wrong type %s; unsigned integer expected", gjs_get_type_name(value)); return JS_FALSE; } } else if (gtype == G_TYPE_BOOLEAN) { JSBool b; /* JS_ValueToBoolean() pretty much always succeeds, * which is maybe surprising sometimes, but could * be handy also... */ if (JS_ValueToBoolean(context, value, &b)) { g_value_set_boolean(gvalue, b); } else { gjs_throw(context, "Wrong type %s; boolean expected", gjs_get_type_name(value)); return JS_FALSE; } } else if (g_type_is_a(gtype, G_TYPE_OBJECT) || g_type_is_a(gtype, G_TYPE_INTERFACE)) { GObject *gobj; gobj = NULL; if (JSVAL_IS_NULL(value)) { /* nothing to do */ } else if (JSVAL_IS_OBJECT(value)) { JSObject *obj; obj = JSVAL_TO_OBJECT(value); if (!gjs_typecheck_object(context, obj, gtype, JS_TRUE)) return JS_FALSE; gobj = gjs_g_object_from_object(context, obj); } else { gjs_throw(context, "Wrong type %s; object %s expected", gjs_get_type_name(value), g_type_name(gtype)); return JS_FALSE; } g_value_set_object(gvalue, gobj); } else if (gtype == G_TYPE_STRV) { jsid length_name; JSBool found_length; length_name = gjs_context_get_const_string(context, GJS_STRING_LENGTH); if (JSVAL_IS_NULL(value)) { /* do nothing */ } else if (JS_HasPropertyById(context, JSVAL_TO_OBJECT(value), length_name, &found_length) && found_length) { jsval length_value; guint32 length; if (!gjs_object_require_property(context, JSVAL_TO_OBJECT(value), NULL, length_name, &length_value) || !JS_ValueToECMAUint32(context, length_value, &length)) { gjs_throw(context, "Wrong type %s; strv expected", gjs_get_type_name(value)); return JS_FALSE; } else { void *result; char **strv; if (!gjs_array_to_strv (context, value, length, &result)) return JS_FALSE; /* cast to strv in a separate step to avoid type-punning */ strv = (char**) result; g_value_take_boxed (gvalue, strv); } } else { gjs_throw(context, "Wrong type %s; strv expected", gjs_get_type_name(value)); return JS_FALSE; } } else if (g_type_is_a(gtype, G_TYPE_BOXED)) { void *gboxed; gboxed = NULL; if (JSVAL_IS_NULL(value)) { /* nothing to do */ } else if (JSVAL_IS_OBJECT(value)) { JSObject *obj; obj = JSVAL_TO_OBJECT(value); if (g_type_is_a(gtype, G_TYPE_ERROR)) { /* special case GError */ if (!gjs_typecheck_gerror(context, obj, JS_TRUE)) return JS_FALSE; gboxed = gjs_gerror_from_error(context, obj); } else { GIBaseInfo *registered = g_irepository_find_by_gtype (NULL, gtype); /* We don't necessarily have the typelib loaded when we first see the structure... */ if (registered) { GIInfoType info_type = g_base_info_get_type (registered); if (info_type == GI_INFO_TYPE_STRUCT && g_struct_info_is_foreign ((GIStructInfo*)registered)) { GArgument arg; if (!gjs_struct_foreign_convert_to_g_argument (context, value, registered, NULL, GJS_ARGUMENT_ARGUMENT, GI_TRANSFER_NOTHING, TRUE, &arg)) return FALSE; gboxed = arg.v_pointer; } } /* First try a union, if that fails, assume a boxed struct. Distinguishing which one is expected would require checking the associated GIBaseInfo, which is not necessary possible, if e.g. we see the GType without loading the typelib. */ if (!gboxed) { if (gjs_typecheck_union(context, obj, NULL, gtype, JS_FALSE)) { gboxed = gjs_c_union_from_union(context, obj); } else { if (!gjs_typecheck_boxed(context, obj, NULL, gtype, JS_TRUE)) return JS_FALSE; gboxed = gjs_c_struct_from_boxed(context, obj); } } } } else { gjs_throw(context, "Wrong type %s; boxed type %s expected", gjs_get_type_name(value), g_type_name(gtype)); return JS_FALSE; } if (no_copy) g_value_set_static_boxed(gvalue, gboxed); else g_value_set_boxed(gvalue, gboxed); } else if (g_type_is_a(gtype, G_TYPE_VARIANT)) { GVariant *variant = NULL; if (JSVAL_IS_NULL(value)) { /* nothing to do */ } else if (JSVAL_IS_OBJECT(value)) { JSObject *obj = JSVAL_TO_OBJECT(value); if (!gjs_typecheck_boxed(context, obj, NULL, G_TYPE_VARIANT, JS_TRUE)) return JS_FALSE; variant = (GVariant*) gjs_c_struct_from_boxed(context, obj); } else { gjs_throw(context, "Wrong type %s; boxed type %s expected", gjs_get_type_name(value), g_type_name(gtype)); return JS_FALSE; } g_value_set_variant (gvalue, variant); } else if (g_type_is_a(gtype, G_TYPE_ENUM)) { gint64 value_int64; if (gjs_value_to_int64 (context, value, &value_int64)) { GEnumValue *v; gpointer gtype_class = g_type_class_ref(gtype); /* See arg.c:_gjs_enum_to_int() */ v = g_enum_get_value(G_ENUM_CLASS(gtype_class), (int)value_int64); g_type_class_unref(gtype_class); if (v == NULL) { gjs_throw(context, "%d is not a valid value for enumeration %s", JSVAL_TO_INT(value), g_type_name(gtype)); return JS_FALSE; } g_value_set_enum(gvalue, v->value); } else { gjs_throw(context, "Wrong type %s; enum %s expected", gjs_get_type_name(value), g_type_name(gtype)); return JS_FALSE; } } else if (g_type_is_a(gtype, G_TYPE_FLAGS)) { gint64 value_int64; if (gjs_value_to_int64 (context, value, &value_int64)) { if (!_gjs_flags_value_is_valid(context, gtype, value_int64)) return JS_FALSE; /* See arg.c:_gjs_enum_to_int() */ g_value_set_flags(gvalue, (int)value_int64); } else { gjs_throw(context, "Wrong type %s; flags %s expected", gjs_get_type_name(value), g_type_name(gtype)); return JS_FALSE; } } else if (g_type_is_a(gtype, G_TYPE_PARAM)) { void *gparam; gparam = NULL; if (JSVAL_IS_NULL(value)) { /* nothing to do */ } else if (JSVAL_IS_OBJECT(value)) { JSObject *obj; obj = JSVAL_TO_OBJECT(value); if (!gjs_typecheck_param(context, obj, gtype, JS_TRUE)) return JS_FALSE; gparam = gjs_g_param_from_param(context, obj); } else { gjs_throw(context, "Wrong type %s; param type %s expected", gjs_get_type_name(value), g_type_name(gtype)); return JS_FALSE; } g_value_set_param(gvalue, (GParamSpec*) gparam); } else if (g_type_is_a(gtype, G_TYPE_GTYPE)) { GType type; if (!JSVAL_IS_OBJECT(value)) { gjs_throw(context, "Wrong type %s; expect a GType object", gjs_get_type_name(value)); return JS_FALSE; } type = gjs_gtype_get_actual_gtype(context, JSVAL_TO_OBJECT(value)); g_value_set_gtype(gvalue, type); } else if (g_type_is_a(gtype, G_TYPE_POINTER)) { if (JSVAL_IS_NULL(value)) { /* Nothing to do */ } else { gjs_throw(context, "Cannot convert non-null JS value to G_POINTER"); return JS_FALSE; } } else if (JSVAL_IS_NUMBER(value) && g_value_type_transformable(G_TYPE_INT, gtype)) { /* Only do this crazy gvalue transform stuff after we've * exhausted everything else. Adding this for * e.g. ClutterUnit. */ gint32 i; if (JS_ValueToInt32(context, value, &i)) { GValue int_value = { 0, }; g_value_init(&int_value, G_TYPE_INT); g_value_set_int(&int_value, i); g_value_transform(&int_value, gvalue); } else { gjs_throw(context, "Wrong type %s; integer expected", gjs_get_type_name(value)); return JS_FALSE; } } else { gjs_debug(GJS_DEBUG_GCLOSURE, "jsval is number %d gtype fundamental %d transformable to int %d from int %d", JSVAL_IS_NUMBER(value), G_TYPE_IS_FUNDAMENTAL(gtype), g_value_type_transformable(gtype, G_TYPE_INT), g_value_type_transformable(G_TYPE_INT, gtype)); gjs_throw(context, "Don't know how to convert JavaScript object to GType %s", g_type_name(gtype)); return JS_FALSE; } return JS_TRUE; } JSBool gjs_value_to_g_value(JSContext *context, jsval value, GValue *gvalue) { return gjs_value_to_g_value_internal(context, value, gvalue, FALSE); } JSBool gjs_value_to_g_value_no_copy(JSContext *context, jsval value, GValue *gvalue) { return gjs_value_to_g_value_internal(context, value, gvalue, TRUE); } static JSBool convert_int_to_enum (JSContext *context, jsval *value_p, GType gtype, int v) { double v_double; if (v > 0 && v < G_MAXINT) { /* Optimize the unambiguous case */ v_double = v; } else { GIBaseInfo *info; /* Need to distinguish between negative integers and unsigned integers */ info = g_irepository_find_by_gtype(g_irepository_get_default(), gtype); g_assert (info); v_double = _gjs_enum_from_int ((GIEnumInfo *)info, v); g_base_info_unref(info); } return JS_NewNumberValue(context, v_double, value_p); } static JSBool gjs_value_from_g_value_internal(JSContext *context, jsval *value_p, const GValue *gvalue, gboolean no_copy, GSignalQuery *signal_query, gint arg_n) { GType gtype; gtype = G_VALUE_TYPE(gvalue); gjs_debug_marshal(GJS_DEBUG_GCLOSURE, "Converting gtype %s to jsval", g_type_name(gtype)); if (gtype == G_TYPE_STRING) { const char *v; v = g_value_get_string(gvalue); if (v == NULL) { gjs_debug_marshal(GJS_DEBUG_GCLOSURE, "Converting NULL string to JSVAL_NULL"); *value_p = JSVAL_NULL; } else { if (!gjs_string_from_utf8(context, v, -1, value_p)) return JS_FALSE; } } else if (gtype == G_TYPE_CHAR) { char v; v = g_value_get_schar(gvalue); *value_p = INT_TO_JSVAL(v); } else if (gtype == G_TYPE_UCHAR) { unsigned char v; v = g_value_get_uchar(gvalue); *value_p = INT_TO_JSVAL(v); } else if (gtype == G_TYPE_INT) { int v; v = g_value_get_int(gvalue); return JS_NewNumberValue(context, v, value_p); } else if (gtype == G_TYPE_UINT) { guint v; v = g_value_get_uint(gvalue); return JS_NewNumberValue(context, v, value_p); } else if (gtype == G_TYPE_DOUBLE) { double d; d = g_value_get_double(gvalue); return JS_NewNumberValue(context, d, value_p); } else if (gtype == G_TYPE_FLOAT) { double d; d = g_value_get_float(gvalue); return JS_NewNumberValue(context, d, value_p); } else if (gtype == G_TYPE_BOOLEAN) { gboolean v; v = g_value_get_boolean(gvalue); *value_p = BOOLEAN_TO_JSVAL(!!v); } else if (g_type_is_a(gtype, G_TYPE_OBJECT) || g_type_is_a(gtype, G_TYPE_INTERFACE)) { GObject *gobj; JSObject *obj; gobj = (GObject*) g_value_get_object(gvalue); obj = gjs_object_from_g_object(context, gobj); *value_p = OBJECT_TO_JSVAL(obj); } else if (gtype == G_TYPE_STRV) { if (!gjs_array_from_strv (context, value_p, (const char**) g_value_get_boxed (gvalue))) { gjs_throw(context, "Failed to convert strv to array"); return JS_FALSE; } } else if (g_type_is_a(gtype, G_TYPE_HASH_TABLE) || g_type_is_a(gtype, G_TYPE_ARRAY) || g_type_is_a(gtype, G_TYPE_BYTE_ARRAY) || g_type_is_a(gtype, G_TYPE_PTR_ARRAY)) { gjs_throw(context, "Unable to introspect element-type of container in GValue"); return JS_FALSE; } else if (g_type_is_a(gtype, G_TYPE_BOXED) || g_type_is_a(gtype, G_TYPE_VARIANT)) { GjsBoxedCreationFlags boxed_flags; GIBaseInfo *info; void *gboxed; JSObject *obj; if (g_type_is_a(gtype, G_TYPE_BOXED)) gboxed = g_value_get_boxed(gvalue); else gboxed = g_value_get_variant(gvalue); boxed_flags = GJS_BOXED_CREATION_NONE; /* special case GError */ if (g_type_is_a(gtype, G_TYPE_ERROR)) { obj = gjs_error_from_gerror(context, (GError*) gboxed, FALSE); *value_p = OBJECT_TO_JSVAL(obj); return TRUE; } /* The only way to differentiate unions and structs is from * their g-i info as both GBoxed */ info = g_irepository_find_by_gtype(g_irepository_get_default(), gtype); if (info == NULL) { gjs_throw(context, "No introspection information found for %s", g_type_name(gtype)); return JS_FALSE; } if (g_base_info_get_type(info) == GI_INFO_TYPE_STRUCT && g_struct_info_is_foreign((GIStructInfo*)info)) { JSBool ret; GIArgument arg; arg.v_pointer = gboxed; ret = gjs_struct_foreign_convert_from_g_argument(context, value_p, info, &arg); g_base_info_unref(info); return ret; } switch (g_base_info_get_type(info)) { case GI_INFO_TYPE_BOXED: case GI_INFO_TYPE_STRUCT: if (no_copy) boxed_flags = (GjsBoxedCreationFlags) (boxed_flags | GJS_BOXED_CREATION_NO_COPY); obj = gjs_boxed_from_c_struct(context, (GIStructInfo *)info, gboxed, boxed_flags); break; case GI_INFO_TYPE_UNION: obj = gjs_union_from_c_union(context, (GIUnionInfo *)info, gboxed); break; default: gjs_throw(context, "Unexpected introspection type %d for %s", g_base_info_get_type(info), g_type_name(gtype)); g_base_info_unref(info); return JS_FALSE; } *value_p = OBJECT_TO_JSVAL(obj); g_base_info_unref(info); } else if (g_type_is_a(gtype, G_TYPE_ENUM)) { return convert_int_to_enum(context, value_p, gtype, g_value_get_enum(gvalue)); } else if (g_type_is_a(gtype, G_TYPE_PARAM)) { GParamSpec *gparam; JSObject *obj; gparam = g_value_get_param(gvalue); obj = gjs_param_from_g_param(context, gparam); *value_p = OBJECT_TO_JSVAL(obj); } else if (signal_query && g_type_is_a(gtype, G_TYPE_POINTER)) { JSBool res; GArgument arg; GIArgInfo *arg_info; GIBaseInfo *obj; GISignalInfo *signal_info; GITypeInfo type_info; obj = g_irepository_find_by_gtype(NULL, signal_query->itype); if (!obj) { gjs_throw(context, "Signal argument with GType %s isn't introspectable", g_type_name(signal_query->itype)); return JS_FALSE; } signal_info = g_object_info_find_signal((GIObjectInfo*)obj, signal_query->signal_name); if (!signal_info) { gjs_throw(context, "Unknown signal."); g_base_info_unref((GIBaseInfo*)obj); return JS_FALSE; } arg_info = g_callable_info_get_arg(signal_info, arg_n - 1); g_arg_info_load_type(arg_info, &type_info); arg.v_pointer = g_value_get_pointer(gvalue); res = gjs_value_from_g_argument(context, value_p, &type_info, &arg, TRUE); g_base_info_unref((GIBaseInfo*)arg_info); g_base_info_unref((GIBaseInfo*)signal_info); g_base_info_unref((GIBaseInfo*)obj); return res; } else if (g_type_is_a(gtype, G_TYPE_POINTER)) { gpointer pointer; pointer = g_value_get_pointer(gvalue); if (pointer == NULL) { *value_p = JSVAL_NULL; } else { gjs_throw(context, "Can't convert non-null pointer to JS value"); return JS_FALSE; } } else if (g_value_type_transformable(gtype, G_TYPE_DOUBLE)) { GValue double_value = { 0, }; double v; g_value_init(&double_value, G_TYPE_DOUBLE); g_value_transform(gvalue, &double_value); v = g_value_get_double(&double_value); return JS_NewNumberValue(context, v, value_p); } else if (g_value_type_transformable(gtype, G_TYPE_INT)) { GValue int_value = { 0, }; int v; g_value_init(&int_value, G_TYPE_INT); g_value_transform(gvalue, &int_value); v = g_value_get_int(&int_value); return JS_NewNumberValue(context, v, value_p); } else if (G_TYPE_IS_INSTANTIATABLE(gtype)) { /* The gtype is none of the above, it should be a custom fundamental type. */ JSObject *obj; obj = gjs_fundamental_from_g_value(context, (const GValue*)gvalue, gtype); if (obj == NULL) return JS_FALSE; else *value_p = OBJECT_TO_JSVAL(obj); } else { gjs_throw(context, "Don't know how to convert GType %s to JavaScript object", g_type_name(gtype)); return JS_FALSE; } return JS_TRUE; } JSBool gjs_value_from_g_value(JSContext *context, jsval *value_p, const GValue *gvalue) { return gjs_value_from_g_value_internal(context, value_p, gvalue, FALSE, NULL, 0); } cjs-2.8.0/gi/enumeration.h0000664000175000017500000000404312610172032014315 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_ENUMERATION_H__ #define __GJS_ENUMERATION_H__ #include #include "cjs/jsapi-util.h" #include G_BEGIN_DECLS JSBool gjs_define_enum_values (JSContext *context, JSObject *in_object, GIEnumInfo *info); JSBool gjs_define_enum_static_methods(JSContext *context, JSObject *constructor, GIEnumInfo *enum_info); JSBool gjs_define_enumeration (JSContext *context, JSObject *in_object, GIEnumInfo *info); JSObject* gjs_lookup_enumeration (JSContext *context, GIEnumInfo *info); G_END_DECLS #endif /* __GJS_ENUMERATION_H__ */ cjs-2.8.0/gi/union.h0000664000175000017500000000427112610172032013122 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_UNION_H__ #define __GJS_UNION_H__ #include #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS JSBool gjs_define_union_class (JSContext *context, JSObject *in_object, GIUnionInfo *info); void* gjs_c_union_from_union (JSContext *context, JSObject *obj); JSObject* gjs_union_from_c_union (JSContext *context, GIUnionInfo *info, void *gboxed); JSBool gjs_typecheck_union (JSContext *context, JSObject *obj, GIStructInfo *expected_info, GType expected_type, JSBool throw_error); G_END_DECLS #endif /* __GJS_UNION_H__ */ cjs-2.8.0/gi/gtype.cpp0000664000175000017500000001410312610172032013450 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * Copyright (c) 2012 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "gtype.h" #include #include #include #include GJS_DEFINE_PROTO_ABSTRACT("GIRepositoryGType", gtype); /* priv_from_js adds a "*", so this returns "void *" */ GJS_DEFINE_PRIV_FROM_JS(void, gjs_gtype_class); static GQuark gjs_get_gtype_wrapper_quark(void) { static gsize once_init = 0; static GQuark value = 0; if (g_once_init_enter(&once_init)) { value = g_quark_from_string("gjs-gtype-wrapper"); g_once_init_leave(&once_init, 1); } return value; } static void gjs_gtype_finalize(JSFreeOp *fop, JSObject *obj) { GType gtype = GPOINTER_TO_SIZE(JS_GetPrivate(obj)); /* proto doesn't have a private set */ if (G_UNLIKELY(gtype == 0)) return; g_type_set_qdata(gtype, gjs_get_gtype_wrapper_quark(), NULL); } static JSBool to_string_func(JSContext *context, unsigned argc, jsval *vp) { JSObject *obj = JS_THIS_OBJECT(context, vp); GType gtype; gchar *strval; JSBool ret; jsval retval; gtype = GPOINTER_TO_SIZE(priv_from_js(context, obj)); if (gtype == 0) strval = g_strdup("[object GType prototype]"); else strval = g_strdup_printf("[object GType for '%s']", g_type_name(gtype)); ret = gjs_string_from_utf8(context, strval, -1, &retval); if (ret) JS_SET_RVAL(context, vp, retval); g_free(strval); return ret; } static JSBool get_name_func (JSContext *context, JSObject **obj, jsid *id, jsval *vp) { GType gtype; JSBool ret; jsval retval; gtype = GPOINTER_TO_SIZE(priv_from_js(context, *obj)); if (gtype == 0) { JS_SET_RVAL(context, vp, JSVAL_NULL); return TRUE; } else { ret = gjs_string_from_utf8(context, g_type_name(gtype), -1, &retval); if (ret) JS_SET_RVAL(context, vp, retval); return ret; } } /* Properties */ JSPropertySpec gjs_gtype_proto_props[] = { { "name", 0, JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED, JSOP_WRAPPER((JSPropertyOp)get_name_func), JSOP_WRAPPER(JS_StrictPropertyStub) }, { NULL }, }; /* Functions */ JSFunctionSpec gjs_gtype_proto_funcs[] = { { "toString", JSOP_WRAPPER((JSNative)to_string_func), 0, 0 }, { NULL } }; JSObject * gjs_gtype_create_gtype_wrapper (JSContext *context, GType gtype) { JSObject *object; JSObject *global; JS_BeginRequest(context); /* put constructor for GIRepositoryGType() in the global namespace */ global = gjs_get_import_global(context); gjs_gtype_create_proto(context, global, "GIRepositoryGType", NULL); object = (JSObject*) g_type_get_qdata(gtype, gjs_get_gtype_wrapper_quark()); if (object != NULL) goto out; object = JS_NewObject(context, &gjs_gtype_class, NULL, NULL); if (object == NULL) goto out; JS_SetPrivate(object, GSIZE_TO_POINTER(gtype)); g_type_set_qdata(gtype, gjs_get_gtype_wrapper_quark(), object); out: JS_EndRequest(context); return object; } static GType _gjs_gtype_get_actual_gtype (JSContext *context, JSObject *object, int recurse) { GType gtype = G_TYPE_INVALID; jsval gtype_val = JSVAL_VOID; JS_BeginRequest(context); if (JS_InstanceOf(context, object, &gjs_gtype_class, NULL)) { gtype = GPOINTER_TO_SIZE(priv_from_js(context, object)); goto out; } /* OK, we don't have a GType wrapper object -- grab the "$gtype" * property on that and hope it's a GType wrapper object */ if (!JS_GetProperty(context, object, "$gtype", >ype_val) || !JSVAL_IS_OBJECT(gtype_val)) { /* OK, so we're not a class. But maybe we're an instance. Check for "constructor" and recurse on that. */ if (!JS_GetProperty(context, object, "constructor", >ype_val)) goto out; } if (recurse > 0 && JSVAL_IS_OBJECT(gtype_val)) gtype = _gjs_gtype_get_actual_gtype(context, JSVAL_TO_OBJECT(gtype_val), recurse - 1); out: JS_EndRequest(context); return gtype; } GType gjs_gtype_get_actual_gtype (JSContext *context, JSObject *object) { /* 2 means: recurse at most three times (including this call). The levels are calculated considering that, in the worst case we need to go from instance to class, from class to GType object and from GType object to GType value. */ return _gjs_gtype_get_actual_gtype(context, object, 2); } JSBool gjs_typecheck_gtype (JSContext *context, JSObject *obj, JSBool throw_error) { return do_base_typecheck(context, obj, throw_error); } cjs-2.8.0/cjs/0000775000175000017500000000000012610172032011775 5ustar fabiofabiocjs-2.8.0/cjs/runtime.cpp0000664000175000017500000001676112610172032014177 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2013 Giovanni Campagna * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "compat.h" #include "runtime.h" struct RuntimeData { JSBool in_gc_sweep; }; JSBool gjs_runtime_is_sweeping (JSRuntime *runtime) { RuntimeData *data = (RuntimeData*) JS_GetRuntimePrivate(runtime); return data->in_gc_sweep; } /* Implementations of locale-specific operations; these are used * in the implementation of String.localeCompare(), Date.toLocaleDateString(), * and so forth. We take the straight-forward approach of converting * to UTF-8, using the appropriate GLib functions, and converting * back if necessary. */ static JSBool gjs_locale_to_upper_case (JSContext *context, JS::HandleString src, JS::MutableHandleValue retval) { JSBool success = JS_FALSE; char *utf8 = NULL; char *upper_case_utf8 = NULL; if (!gjs_string_to_utf8(context, STRING_TO_JSVAL(src), &utf8)) goto out; upper_case_utf8 = g_utf8_strup (utf8, -1); if (!gjs_string_from_utf8(context, upper_case_utf8, -1, retval.address())) goto out; success = JS_TRUE; out: g_free(utf8); g_free(upper_case_utf8); return success; } static JSBool gjs_locale_to_lower_case (JSContext *context, JS::HandleString src, JS::MutableHandleValue retval) { JSBool success = JS_FALSE; char *utf8 = NULL; char *lower_case_utf8 = NULL; if (!gjs_string_to_utf8(context, STRING_TO_JSVAL(src), &utf8)) goto out; lower_case_utf8 = g_utf8_strdown (utf8, -1); if (!gjs_string_from_utf8(context, lower_case_utf8, -1, retval.address())) goto out; success = JS_TRUE; out: g_free(utf8); g_free(lower_case_utf8); return success; } static JSBool gjs_locale_compare (JSContext *context, JS::HandleString src_1, JS::HandleString src_2, JS::MutableHandleValue retval) { JSBool success = JS_FALSE; char *utf8_1 = NULL, *utf8_2 = NULL; int result; if (!gjs_string_to_utf8(context, STRING_TO_JSVAL(src_1), &utf8_1) || !gjs_string_to_utf8(context, STRING_TO_JSVAL(src_2), &utf8_2)) goto out; result = g_utf8_collate (utf8_1, utf8_2); retval.set(INT_TO_JSVAL(result)); success = JS_TRUE; out: g_free(utf8_1); g_free(utf8_2); return success; } static JSBool gjs_locale_to_unicode (JSContext *context, const char *src, JS::MutableHandleValue retval) { JSBool success; char *utf8; GError *error = NULL; utf8 = g_locale_to_utf8(src, -1, NULL, NULL, &error); if (!utf8) { gjs_throw(context, "Failed to convert locale string to UTF8: %s", error->message); g_error_free(error); return JS_FALSE; } success = gjs_string_from_utf8(context, utf8, -1, retval.address()); g_free (utf8); return success; } static void destroy_runtime(gpointer data) { JSRuntime *runtime = (JSRuntime *) data; RuntimeData *rtdata = (RuntimeData *) JS_GetRuntimePrivate(runtime); g_free(rtdata); JS_DestroyRuntime(runtime); } static GPrivate thread_runtime = G_PRIVATE_INIT(destroy_runtime); static JSLocaleCallbacks gjs_locale_callbacks = { gjs_locale_to_upper_case, gjs_locale_to_lower_case, gjs_locale_compare, gjs_locale_to_unicode }; void gjs_finalize_callback(JSFreeOp *fop, JSFinalizeStatus status, JSBool isCompartment) { JSRuntime *runtime; RuntimeData *data; runtime = fop->runtime(); data = (RuntimeData*) JS_GetRuntimePrivate(runtime); /* Implementation note for mozjs 24: sweeping happens in two phases, in the first phase all GC things from the allocation arenas are queued for sweeping, then the actual sweeping happens. The first phase is marked by JSFINALIZE_GROUP_START, the second one by JSFINALIZE_GROUP_END, and finally we will see JSFINALIZE_COLLECTION_END at the end of all GC. (see jsgc.cpp, BeginSweepPhase/BeginSweepingZoneGroup and SweepPhase, all called from IncrementalCollectSlice). Incremental GC muds the waters, because BeginSweepPhase is always run to entirety, but SweepPhase can be run incrementally and mixed with JS code runs or even native code, when MaybeGC/IncrementalGC return. Luckily for us, objects are treated specially, and are not really queued for deferred incremental finalization (unless they are marked for background sweeping). Instead, they are finalized immediately during phase 1, so the following guarantees are true (and we rely on them) - phase 1 of GC will begin and end in the same JSAPI call (ie, our callback will be called with GROUP_START and the triggering JSAPI call will not return until we see a GROUP_END) - object finalization will begin and end in the same JSAPI call - therefore, if there is a finalizer frame somewhere in the stack, gjs_runtime_is_sweeping() will return TRUE. Comments in mozjs24 imply that this behavior might change in the future, but it hasn't changed in mozilla-central as of 2014-02-23. In addition to that, the mozilla-central version has a huge comment in a different portion of the file, explaining why finalization of objects can't be mixed with JS code, so we can probably rely on this behavior. */ if (status == JSFINALIZE_GROUP_START) data->in_gc_sweep = JS_TRUE; else if (status == JSFINALIZE_GROUP_END) data->in_gc_sweep = JS_FALSE; } JSRuntime * gjs_runtime_for_current_thread(void) { JSRuntime *runtime = (JSRuntime *) g_private_get(&thread_runtime); RuntimeData *data; if (!runtime) { runtime = JS_NewRuntime(32*1024*1024 /* max bytes */, JS_USE_HELPER_THREADS); if (runtime == NULL) g_error("Failed to create javascript runtime"); data = g_new0(RuntimeData, 1); JS_SetRuntimePrivate(runtime, data); JS_SetNativeStackQuota(runtime, 1024*1024); JS_SetGCParameter(runtime, JSGC_MAX_BYTES, 0xffffffff); JS_SetLocaleCallbacks(runtime, &gjs_locale_callbacks); JS_SetFinalizeCallback(runtime, gjs_finalize_callback); g_private_set(&thread_runtime, runtime); } return runtime; } cjs-2.8.0/cjs/gjs.h0000664000175000017500000000237712610172032012742 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_GJS_H__ #define __GJS_GJS_H__ #include #endif /* __GJS_GJS_H__ */ cjs-2.8.0/cjs/byteArray.cpp0000664000175000017500000006326312610172032014455 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2010 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include "byteArray.h" #include "../gi/boxed.h" #include #include #include #include typedef struct { GByteArray *array; GBytes *bytes; } ByteArrayInstance; extern struct JSClass gjs_byte_array_class; GJS_DEFINE_PRIV_FROM_JS(ByteArrayInstance, gjs_byte_array_class) static JSBool byte_array_get_prop (JSContext *context, JSObject **obj, jsid *id, jsval *value_p); static JSBool byte_array_set_prop (JSContext *context, JSObject **obj, jsid *id, JSBool strict, jsval *value_p); GJS_NATIVE_CONSTRUCTOR_DECLARE(byte_array); static void byte_array_finalize (JSFreeOp *fop, JSObject *obj); struct JSClass gjs_byte_array_class = { "ByteArray", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_DeletePropertyStub, (JSPropertyOp)byte_array_get_prop, (JSStrictPropertyOp)byte_array_set_prop, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, byte_array_finalize, NULL, NULL, NULL, NULL, NULL }; JSBool gjs_typecheck_bytearray(JSContext *context, JSObject *object, JSBool throw_error) { return do_base_typecheck(context, object, throw_error); } static JSBool gjs_value_from_gsize(JSContext *context, gsize v, jsval *value_p) { if (v > (gsize) JSVAL_INT_MAX) { *value_p = INT_TO_JSVAL(v); return JS_TRUE; } else { return JS_NewNumberValue(context, v, value_p); } } static void byte_array_ensure_array (ByteArrayInstance *priv) { if (priv->bytes) { priv->array = g_bytes_unref_to_array(priv->bytes); priv->bytes = NULL; } else { g_assert(priv->array); } } static void byte_array_ensure_gbytes (ByteArrayInstance *priv) { if (priv->array) { priv->bytes = g_byte_array_free_to_bytes(priv->array); priv->array = NULL; } else { g_assert(priv->bytes); } } static JSBool gjs_value_to_gsize(JSContext *context, jsval value, gsize *v_p) { guint32 val32; /* Just JS_ValueToECMAUint32() would work. However, * we special case ints for two reasons: * - JS_ValueToECMAUint32() always goes via a double which is slow * - nicer error message on negative indices */ if (JSVAL_IS_INT(value)) { int i = JSVAL_TO_INT(value); if (i < 0) { gjs_throw(context, "Negative length or index %d is not allowed for ByteArray", i); return JS_FALSE; } *v_p = i; return JS_TRUE; } else { JSBool ret; /* This is pretty liberal (it converts about anything to * a number) but it's what we use elsewhere in gjs too. */ ret = JS_ValueToECMAUint32(context, value, &val32); *v_p = val32; return ret; } } static JSBool gjs_value_to_byte(JSContext *context, jsval value, guint8 *v_p) { gsize v; if (!gjs_value_to_gsize(context, value, &v)) return JS_FALSE; if (v >= 256) { gjs_throw(context, "Value %" G_GSIZE_FORMAT " is not a valid byte; must be in range [0,255]", v); return JS_FALSE; } *v_p = v; return JS_TRUE; } static JSBool byte_array_get_index(JSContext *context, JSObject *obj, ByteArrayInstance *priv, gsize idx, jsval *value_p) { gsize len; guint8 *data; gjs_byte_array_peek_data(context, obj, &data, &len); if (idx >= len) { gjs_throw(context, "Index %" G_GSIZE_FORMAT " is out of range for ByteArray length %lu", idx, (unsigned long)len); return JS_FALSE; } *value_p = INT_TO_JSVAL(data[idx]); return JS_TRUE; } /* a hook on getting a property; set value_p to override property's value. * Return value is JS_FALSE on OOM/exception. */ static JSBool byte_array_get_prop(JSContext *context, JSObject **obj, jsid *id, jsval *value_p) { ByteArrayInstance *priv; jsval id_value; priv = priv_from_js(context, *obj); if (priv == NULL) return JS_TRUE; /* prototype, not an instance. */ if (!JS_IdToValue(context, *id, &id_value)) return JS_FALSE; /* First handle array indexing */ if (JSVAL_IS_NUMBER(id_value)) { gsize idx; if (!gjs_value_to_gsize(context, id_value, &idx)) return JS_FALSE; return byte_array_get_index(context, *obj, priv, idx, value_p); } /* We don't special-case anything else for now. Regular JS arrays * allow string versions of ints for the index, we don't bother. */ return JS_TRUE; } static JSBool byte_array_length_getter(JSContext *context, JSObject **obj, jsid *id, jsval *value_p) { ByteArrayInstance *priv; gsize len = 0; priv = priv_from_js(context, *obj); if (priv == NULL) return JS_TRUE; /* prototype, not an instance. */ if (priv->array != NULL) len = priv->array->len; else if (priv->bytes != NULL) len = g_bytes_get_size (priv->bytes); return gjs_value_from_gsize(context, len, value_p); } static JSBool byte_array_length_setter(JSContext *context, JSObject **obj, jsid *id, JSBool strict, jsval *value_p) { ByteArrayInstance *priv; gsize len = 0; priv = priv_from_js(context, *obj); if (priv == NULL) return JS_TRUE; /* prototype, not instance */ byte_array_ensure_array(priv); if (!gjs_value_to_gsize(context, *value_p, &len)) { gjs_throw(context, "Can't set ByteArray length to non-integer"); return JS_FALSE; } g_byte_array_set_size(priv->array, len); return JS_TRUE; } static JSBool byte_array_set_index(JSContext *context, JSObject *obj, ByteArrayInstance *priv, gsize idx, jsval *value_p) { guint8 v; if (!gjs_value_to_byte(context, *value_p, &v)) { return JS_FALSE; } byte_array_ensure_array(priv); /* grow the array if necessary */ if (idx >= priv->array->len) { g_byte_array_set_size(priv->array, idx + 1); } g_array_index(priv->array, guint8, idx) = v; /* Stop JS from storing a copy of the value */ *value_p = JSVAL_VOID; return JS_TRUE; } /* a hook on setting a property; set value_p to override property value to * be set. Return value is JS_FALSE on OOM/exception. */ static JSBool byte_array_set_prop(JSContext *context, JSObject **obj, jsid *id, JSBool strict, jsval *value_p) { ByteArrayInstance *priv; jsval id_value; priv = priv_from_js(context, *obj); if (priv == NULL) return JS_TRUE; /* prototype, not an instance. */ if (!JS_IdToValue(context, *id, &id_value)) return JS_FALSE; /* First handle array indexing */ if (JSVAL_IS_NUMBER(id_value)) { gsize idx; if (!gjs_value_to_gsize(context, id_value, &idx)) return JS_FALSE; return byte_array_set_index(context, *obj, priv, idx, value_p); } /* We don't special-case anything else for now */ return JS_TRUE; } static GByteArray * gjs_g_byte_array_new(int preallocated_length) { GByteArray *array; /* can't use g_byte_array_new() because we need to clear to zero. * We nul-terminate too for ease of toString() and for security * paranoia. */ array = (GByteArray*) g_array_sized_new (TRUE, /* nul-terminated */ TRUE, /* clear to zero */ 1, /* element size */ preallocated_length); if (preallocated_length > 0) { /* we want to not only allocate the size, but have it * already be the array's length. */ g_byte_array_set_size(array, preallocated_length); } return array; } GJS_NATIVE_CONSTRUCTOR_DECLARE(byte_array) { GJS_NATIVE_CONSTRUCTOR_VARIABLES(byte_array) ByteArrayInstance *priv; gsize preallocated_length; GJS_NATIVE_CONSTRUCTOR_PRELUDE(byte_array); preallocated_length = 0; if (argc >= 1) { if (!gjs_value_to_gsize(context, argv[0], &preallocated_length)) { gjs_throw(context, "Argument to ByteArray constructor should be a positive number for array length"); return JS_FALSE; } } priv = g_slice_new0(ByteArrayInstance); priv->array = gjs_g_byte_array_new(preallocated_length); g_assert(priv_from_js(context, object) == NULL); JS_SetPrivate(object, priv); GJS_NATIVE_CONSTRUCTOR_FINISH(byte_array); return JS_TRUE; } static void byte_array_finalize(JSFreeOp *fop, JSObject *obj) { ByteArrayInstance *priv; priv = (ByteArrayInstance*) JS_GetPrivate(obj); if (priv == NULL) return; /* prototype, not instance */ if (priv->array) { g_byte_array_free(priv->array, TRUE); priv->array = NULL; } else if (priv->bytes) { g_clear_pointer(&priv->bytes, g_bytes_unref); } g_slice_free(ByteArrayInstance, priv); } /* implement toString() with an optional encoding arg */ static JSBool to_string_func(JSContext *context, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(context, vp); JSObject *object = JS_THIS_OBJECT(context, vp); ByteArrayInstance *priv; char *encoding; gboolean encoding_is_utf8; gchar *data; priv = priv_from_js(context, object); if (priv == NULL) return JS_TRUE; /* prototype, not instance */ byte_array_ensure_array(priv); if (argc >= 1 && JSVAL_IS_STRING(argv[0])) { if (!gjs_string_to_utf8(context, argv[0], &encoding)) return JS_FALSE; /* maybe we should be smarter about utf8 synonyms here. * 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; } } else { encoding_is_utf8 = TRUE; } if (priv->array->len == 0) /* the internal data pointer could be NULL in this case */ data = (gchar*)""; else data = (gchar*)priv->array->data; if (encoding_is_utf8) { /* optimization, avoids iconv overhead and runs * libmozjs hardwired utf8-to-utf16 */ jsval retval; JSBool ok; ok = gjs_string_from_utf8(context, data, priv->array->len, &retval); if (ok) JS_SET_RVAL(context, vp, retval); return ok; } else { JSBool ok = JS_FALSE; gsize bytes_written; GError *error; JSString *s; char *u16_str; error = NULL; u16_str = g_convert(data, priv->array->len, "UTF-16", encoding, NULL, /* bytes read */ &bytes_written, &error); g_free(encoding); if (u16_str == NULL) { /* frees the GError */ gjs_throw_g_error(context, error); return JS_FALSE; } /* bytes_written should be bytes in a UTF-16 string so * should be a multiple of 2 */ g_assert((bytes_written % 2) == 0); s = JS_NewUCStringCopyN(context, (jschar*) u16_str, bytes_written / 2); if (s != NULL) { ok = JS_TRUE; JS_SET_RVAL(context, vp, STRING_TO_JSVAL(s)); } g_free(u16_str); return ok; } } static JSBool to_gbytes_func(JSContext *context, unsigned argc, jsval *vp) { JSObject *object = JS_THIS_OBJECT(context, vp); ByteArrayInstance *priv; JSObject *ret_bytes_obj; GIBaseInfo *gbytes_info; priv = priv_from_js(context, object); if (priv == NULL) return JS_TRUE; /* prototype, not instance */ byte_array_ensure_gbytes(priv); gbytes_info = g_irepository_find_by_gtype(NULL, G_TYPE_BYTES); ret_bytes_obj = gjs_boxed_from_c_struct(context, (GIStructInfo*)gbytes_info, priv->bytes, GJS_BOXED_CREATION_NONE); JS_SET_RVAL(context, vp, OBJECT_TO_JSVAL(ret_bytes_obj)); return JS_TRUE; } /* Ensure that the module and class objects exists, and that in turn * ensures that JS_InitClass has been called. */ static JSObject * byte_array_get_prototype(JSContext *context) { jsval retval; JSObject *prototype; retval = gjs_get_global_slot (context, GJS_GLOBAL_SLOT_BYTE_ARRAY_PROTOTYPE); if (!JSVAL_IS_OBJECT (retval)) { if (!gjs_eval_with_scope(context, NULL, "imports.byteArray.ByteArray.prototype;", -1, "", &retval)) g_error ("Could not import byte array prototype\n"); } return JSVAL_TO_OBJECT(retval); } static JSObject* byte_array_new(JSContext *context) { JSObject *array; ByteArrayInstance *priv; array = JS_NewObject(context, &gjs_byte_array_class, byte_array_get_prototype(context), NULL); priv = g_slice_new0(ByteArrayInstance); g_assert(priv_from_js(context, array) == NULL); JS_SetPrivate(array, priv); return array; } /* fromString() function implementation */ static JSBool from_string_func(JSContext *context, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(context, vp); ByteArrayInstance *priv; char *encoding; gboolean encoding_is_utf8; JSObject *obj; JSBool retval = JS_FALSE; obj = byte_array_new(context); if (obj == NULL) return JS_FALSE; JS_AddObjectRoot(context, &obj); priv = priv_from_js(context, obj); g_assert (priv != NULL); g_assert(argc > 0); /* because we specified min args 1 */ priv->array = gjs_g_byte_array_new(0); if (!JSVAL_IS_STRING(argv[0])) { gjs_throw(context, "byteArray.fromString() called with non-string as first arg"); goto out; } if (argc > 1 && JSVAL_IS_STRING(argv[1])) { if (!gjs_string_to_utf8(context, argv[1], &encoding)) goto out; /* maybe we should be smarter about utf8 synonyms here. * 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; } } else { encoding_is_utf8 = TRUE; } if (encoding_is_utf8) { /* optimization? avoids iconv overhead and runs * libmozjs hardwired utf16-to-utf8. */ char *utf8 = NULL; if (!gjs_string_to_utf8(context, argv[0], &utf8)) goto out; g_byte_array_set_size(priv->array, 0); g_byte_array_append(priv->array, (guint8*) utf8, strlen(utf8)); g_free(utf8); } else { char *encoded; gsize bytes_written; GError *error; const jschar *u16_chars; gsize u16_len; u16_chars = JS_GetStringCharsAndLength(context, JSVAL_TO_STRING(argv[0]), &u16_len); if (u16_chars == NULL) goto out; error = NULL; encoded = g_convert((char*) u16_chars, u16_len * 2, encoding, /* to_encoding */ "UTF-16", /* from_encoding */ NULL, /* bytes read */ &bytes_written, &error); g_free(encoding); if (encoded == NULL) { /* frees the GError */ gjs_throw_g_error(context, error); goto out; } g_byte_array_set_size(priv->array, 0); g_byte_array_append(priv->array, (guint8*) encoded, bytes_written); g_free(encoded); } JS_SET_RVAL(context, vp, OBJECT_TO_JSVAL(obj)); retval = JS_TRUE; out: JS_RemoveObjectRoot(context, &obj); return retval; } /* fromArray() function implementation */ static JSBool from_array_func(JSContext *context, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(context, vp); ByteArrayInstance *priv; guint32 len; guint32 i; JSObject *obj; JSBool ret = JS_FALSE; obj = byte_array_new(context); if (obj == NULL) return JS_FALSE; JS_AddObjectRoot(context, &obj); priv = priv_from_js(context, obj); g_assert (priv != NULL); g_assert(argc > 0); /* because we specified min args 1 */ priv->array = gjs_g_byte_array_new(0); if (!JS_IsArrayObject(context, JSVAL_TO_OBJECT(argv[0]))) { gjs_throw(context, "byteArray.fromArray() called with non-array as first arg"); goto out; } if (!JS_GetArrayLength(context, JSVAL_TO_OBJECT(argv[0]), &len)) { gjs_throw(context, "byteArray.fromArray() can't get length of first array arg"); goto out; } g_byte_array_set_size(priv->array, len); for (i = 0; i < len; ++i) { jsval elem; guint8 b; elem = JSVAL_VOID; if (!JS_GetElement(context, JSVAL_TO_OBJECT(argv[0]), i, &elem)) { /* this means there was an exception, while elem == JSVAL_VOID * means no element found */ goto out; } if (JSVAL_IS_VOID(elem)) continue; if (!gjs_value_to_byte(context, elem, &b)) goto out; g_array_index(priv->array, guint8, i) = b; } ret = JS_TRUE; JS_SET_RVAL(context, vp, OBJECT_TO_JSVAL(obj)); out: JS_RemoveObjectRoot(context, &obj); return ret; } static JSBool from_gbytes_func(JSContext *context, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(context, vp); JSObject *bytes_obj; GBytes *gbytes; ByteArrayInstance *priv; JSObject *obj; JSBool ret = JS_FALSE; if (!gjs_parse_args(context, "overrides_gbytes_to_array", "o", argc, argv, "bytes", &bytes_obj)) return JS_FALSE; if (!gjs_typecheck_boxed(context, bytes_obj, NULL, G_TYPE_BYTES, TRUE)) return JS_FALSE; gbytes = (GBytes*) gjs_c_struct_from_boxed(context, bytes_obj); obj = byte_array_new(context); if (obj == NULL) return JS_FALSE; priv = priv_from_js(context, obj); g_assert (priv != NULL); priv->bytes = g_bytes_ref(gbytes); ret = JS_TRUE; JS_SET_RVAL(context, vp, OBJECT_TO_JSVAL(obj)); return ret; } JSObject * gjs_byte_array_from_byte_array (JSContext *context, GByteArray *array) { JSObject *object; ByteArrayInstance *priv; g_return_val_if_fail(context != NULL, NULL); g_return_val_if_fail(array != NULL, NULL); object = JS_NewObject(context, &gjs_byte_array_class, byte_array_get_prototype(context), NULL); if (!object) { gjs_throw(context, "failed to create byte array"); return NULL; } priv = g_slice_new0(ByteArrayInstance); g_assert(priv_from_js(context, object) == NULL); JS_SetPrivate(object, priv); priv->array = g_byte_array_new(); priv->array->data = (guint8*) g_memdup(array->data, array->len); priv->array->len = array->len; return object; } JSObject * gjs_byte_array_from_bytes (JSContext *context, GBytes *bytes) { JSObject *object; ByteArrayInstance *priv; g_return_val_if_fail(context != NULL, NULL); g_return_val_if_fail(bytes != NULL, NULL); object = JS_NewObject(context, &gjs_byte_array_class, byte_array_get_prototype(context), NULL); if (!object) { gjs_throw(context, "failed to create byte array"); return NULL; } priv = g_slice_new0(ByteArrayInstance); g_assert(priv_from_js(context, object) == NULL); JS_SetPrivate(object, priv); priv->bytes = g_bytes_ref (bytes); return object; } GBytes * gjs_byte_array_get_bytes (JSContext *context, JSObject *object) { ByteArrayInstance *priv; priv = priv_from_js(context, object); g_assert(priv != NULL); byte_array_ensure_gbytes(priv); return g_bytes_ref (priv->bytes); } GByteArray * gjs_byte_array_get_byte_array (JSContext *context, JSObject *obj) { ByteArrayInstance *priv; priv = priv_from_js(context, obj); g_assert(priv != NULL); byte_array_ensure_array(priv); return g_byte_array_ref (priv->array); } void gjs_byte_array_peek_data (JSContext *context, JSObject *obj, guint8 **out_data, gsize *out_len) { ByteArrayInstance *priv; priv = priv_from_js(context, obj); g_assert(priv != NULL); if (priv->array != NULL) { *out_data = (guint8*)priv->array->data; *out_len = (gsize)priv->array->len; } else if (priv->bytes != NULL) { *out_data = (guint8*)g_bytes_get_data(priv->bytes, out_len); } else { g_assert_not_reached(); } } /* no idea what this is used for. examples in * spidermonkey use -1, -2, -3, etc. for tinyids. */ enum ByteArrayTinyId { BYTE_ARRAY_TINY_ID_LENGTH = -1 }; JSPropertySpec gjs_byte_array_proto_props[] = { { "length", BYTE_ARRAY_TINY_ID_LENGTH, JSPROP_PERMANENT, JSOP_WRAPPER ((JSPropertyOp) byte_array_length_getter), JSOP_WRAPPER ((JSStrictPropertyOp) byte_array_length_setter), }, { NULL } }; JSFunctionSpec gjs_byte_array_proto_funcs[] = { { "toString", JSOP_WRAPPER ((JSNative) to_string_func), 0, 0 }, { "toGBytes", JSOP_WRAPPER ((JSNative) to_gbytes_func), 0, 0 }, { NULL } }; static JSFunctionSpec gjs_byte_array_module_funcs[] = { { "fromString", JSOP_WRAPPER (from_string_func), 1, 0 }, { "fromArray", JSOP_WRAPPER (from_array_func), 1, 0 }, { "fromGBytes", JSOP_WRAPPER (from_gbytes_func), 1, 0 }, { NULL } }; JSBool gjs_define_byte_array_stuff(JSContext *context, JSObject **module_out) { JSObject *module; JSObject *prototype; module = JS_NewObject (context, NULL, NULL, NULL); prototype = JS_InitClass(context, module, NULL, &gjs_byte_array_class, gjs_byte_array_constructor, 0, &gjs_byte_array_proto_props[0], &gjs_byte_array_proto_funcs[0], NULL, NULL); if (!JS_DefineFunctions(context, module, &gjs_byte_array_module_funcs[0])) return JS_FALSE; g_assert(JSVAL_IS_VOID(gjs_get_global_slot(context, GJS_GLOBAL_SLOT_BYTE_ARRAY_PROTOTYPE))); gjs_set_global_slot(context, GJS_GLOBAL_SLOT_BYTE_ARRAY_PROTOTYPE, OBJECT_TO_JSVAL(prototype)); *module_out = module; return JS_TRUE; } cjs-2.8.0/cjs/gjs-module.h0000664000175000017500000000262112610172032014215 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2010 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_GJS_MODULE_H__ #define __GJS_GJS_MODULE_H__ #include #include #include #include #include #include #endif /* __GJS_GJS_umodule_H__ */ cjs-2.8.0/cjs/context.h0000664000175000017500000000765412610172032013646 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_CONTEXT_H__ #define __GJS_CONTEXT_H__ #if !defined (__GJS_GJS_H__) && !defined (GJS_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS typedef struct _GjsContext GjsContext; typedef struct _GjsContextClass GjsContextClass; #define GJS_TYPE_CONTEXT (gjs_context_get_type ()) #define GJS_CONTEXT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GJS_TYPE_CONTEXT, GjsContext)) #define GJS_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GJS_TYPE_CONTEXT, GjsContextClass)) #define GJS_IS_CONTEXT(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GJS_TYPE_CONTEXT)) #define GJS_IS_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GJS_TYPE_CONTEXT)) #define GJS_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GJS_TYPE_CONTEXT, GjsContextClass)) GType gjs_context_get_type (void) G_GNUC_CONST; GjsContext* gjs_context_new (void); GjsContext* gjs_context_new_with_search_path (char **search_path); gboolean gjs_context_eval_file (GjsContext *js_context, const char *filename, int *exit_status_p, GError **error); gboolean gjs_context_eval (GjsContext *js_context, const char *script, gssize script_len, const char *filename, int *exit_status_p, GError **error); gboolean gjs_context_define_string_array (GjsContext *js_context, const char *array_name, gssize array_length, const char **array_values, GError **error); GList* gjs_context_get_all (void); GjsContext *gjs_context_get_current (void); void gjs_context_make_current (GjsContext *js_context); void* gjs_context_get_native_context (GjsContext *js_context); void gjs_context_print_stack_stderr (GjsContext *js_context); void gjs_context_maybe_gc (GjsContext *context); void gjs_context_gc (GjsContext *context); void gjs_dumpstack (void); G_END_DECLS #endif /* __GJS_CONTEXT_H__ */ cjs-2.8.0/cjs/type-module.cpp0000664000175000017500000000436212610172032014752 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2012 Giovanni Campagna * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "type-module.h" struct _GjsTypeModule { GTypeModule parent; }; struct _GjsTypeModuleClass { GTypeModuleClass parent_class; }; G_DEFINE_TYPE (GjsTypeModule, gjs_type_module, G_TYPE_TYPE_MODULE) static GjsTypeModule *global_type_module; GjsTypeModule * gjs_type_module_get () { if (global_type_module == NULL) { global_type_module = (GjsTypeModule *) g_object_new (GJS_TYPE_TYPE_MODULE, NULL); } return global_type_module; } static gboolean gjs_type_module_load (GTypeModule *self) { return TRUE; } static void gjs_type_module_unload (GTypeModule *self) { g_assert_not_reached (); } static void gjs_type_module_class_init (GjsTypeModuleClass *klass) { GTypeModuleClass *type_module_class; type_module_class = G_TYPE_MODULE_CLASS (klass); type_module_class->load = gjs_type_module_load; type_module_class->unload = gjs_type_module_unload; } static void gjs_type_module_init (GjsTypeModule *self) { /* Prevent the use count from ever dropping to zero */ g_type_module_use (G_TYPE_MODULE (self)); } cjs-2.8.0/cjs/jsapi-dynamic-class.cpp0000664000175000017500000001712512610172032016342 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * 2012 Giovanni Campagna * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include "jsapi-util.h" #include "compat.h" #include "jsapi-private.h" #include #include /* * JS 1.8.5 has JS_NewObjectForConstructor, but it attempts * to retrieve the JSClass from private fields in the constructor function, * which fails for our "dynamic classes". * This is the version included in SpiderMonkey 1.9 and later, to be * used until we rebase on a newer libmozjs. */ JSObject * gjs_new_object_for_constructor(JSContext *context, JSClass *clasp, jsval *vp) { jsval callee; JSObject *parent; jsval prototype; callee = JS_CALLEE(context, vp); parent = JS_GetParent(JSVAL_TO_OBJECT (callee)); if (!gjs_object_get_property_const(context, JSVAL_TO_OBJECT(callee), GJS_STRING_PROTOTYPE, &prototype)) return NULL; return JS_NewObjectWithGivenProto(context, clasp, JSVAL_TO_OBJECT(prototype), parent); } JSBool gjs_init_class_dynamic(JSContext *context, JSObject *in_object, JSObject *parent_proto, const char *ns_name, const char *class_name, JSClass *clasp, JSNative constructor_native, unsigned nargs, JSPropertySpec *proto_ps, JSFunctionSpec *proto_fs, JSPropertySpec *static_ps, JSFunctionSpec *static_fs, JSObject **prototype_p, JSObject **constructor_p) { JSObject *global; /* Force these variables on the stack, so the conservative GC will find them */ JSObject * volatile prototype; JSObject * volatile constructor; JSFunction * volatile constructor_fun; char *full_function_name = NULL; JSBool res = JS_FALSE; /* Without a name, JS_NewObject fails */ g_assert (clasp->name != NULL); /* gjs_init_class_dynamic only makes sense for instantiable classes, use JS_InitClass for static classes like Math */ g_assert (constructor_native != NULL); JS_BeginRequest(context); global = gjs_get_import_global(context); /* Class initalization consists of three parts: - building a prototype - defining prototype properties and functions - building a constructor and definining it on the right object - defining constructor properties and functions - linking the constructor and the prototype, so that JS_NewObjectForConstructor can find it */ /* * JS_NewObject will try to search for clasp prototype in the global * object if parent_proto is NULL, which is wrong, but it's not * a problem because it will fallback to Object.prototype if the clasp's * constructor is not found (and it won't be found, because we never call * JS_InitClass). */ prototype = JS_NewObject(context, clasp, parent_proto, global); if (!prototype) goto out; if (proto_ps && !JS_DefineProperties(context, prototype, proto_ps)) goto out; if (proto_fs && !JS_DefineFunctions(context, prototype, proto_fs)) goto out; full_function_name = g_strdup_printf("%s_%s", ns_name, class_name); constructor_fun = JS_NewFunction(context, constructor_native, nargs, JSFUN_CONSTRUCTOR, global, full_function_name); if (!constructor_fun) goto out; constructor = JS_GetFunctionObject(constructor_fun); if (static_ps && !JS_DefineProperties(context, constructor, static_ps)) goto out; if (static_fs && !JS_DefineFunctions(context, constructor, static_fs)) goto out; if (!JS_DefineProperty(context, constructor, "prototype", OBJECT_TO_JSVAL(prototype), JS_PropertyStub, JS_StrictPropertyStub, JSPROP_PERMANENT | JSPROP_READONLY)) goto out; if (!JS_DefineProperty(context, prototype, "constructor", OBJECT_TO_JSVAL(constructor), JS_PropertyStub, JS_StrictPropertyStub, 0)) goto out; /* The constructor defined by JS_InitClass has no property attributes, but this is a more useful default for gjs */ if (!JS_DefineProperty(context, in_object, class_name, OBJECT_TO_JSVAL(constructor), JS_PropertyStub, JS_StrictPropertyStub, GJS_MODULE_PROP_FLAGS)) goto out; if (constructor_p) *constructor_p = constructor; if (prototype_p) *prototype_p = prototype; res = JS_TRUE; prototype = NULL; constructor_fun = NULL; constructor = NULL; out: JS_EndRequest(context); g_free(full_function_name); return res; } static const char* format_dynamic_class_name (const char *name) { if (g_str_has_prefix(name, "_private_")) return name + strlen("_private_"); else return name; } JSBool gjs_typecheck_instance(JSContext *context, JSObject *obj, JSClass *static_clasp, JSBool throw_error) { if (!JS_InstanceOf(context, obj, static_clasp, NULL)) { if (throw_error) { JSClass *obj_class = JS_GetClass(obj); gjs_throw_custom(context, "TypeError", "Object %p is not a subclass of %s, it's a %s", obj, static_clasp->name, format_dynamic_class_name (obj_class->name)); } return JS_FALSE; } return JS_TRUE; } JSObject* gjs_construct_object_dynamic(JSContext *context, JSObject *proto, unsigned argc, jsval *argv) { JSObject *constructor; JSObject *result = NULL; jsval value; jsid constructor_name; JS_BeginRequest(context); constructor_name = gjs_context_get_const_string(context, GJS_STRING_CONSTRUCTOR); if (!gjs_object_require_property(context, proto, "prototype", constructor_name, &value)) goto out; constructor = JSVAL_TO_OBJECT(value); result = JS_New(context, constructor, argc, argv); out: JS_EndRequest(context); return result; } cjs-2.8.0/cjs/context.cpp0000664000175000017500000005330212610172032014170 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include "context-private.h" #include "importer.h" #include "jsapi-private.h" #include "jsapi-util.h" #include "native.h" #include "byteArray.h" #include "compat.h" #include "runtime.h" #include "gi.h" #include "gi/object.h" #include #include #include #include #include static void gjs_context_dispose (GObject *object); static void gjs_context_finalize (GObject *object); static void gjs_context_constructed (GObject *object); static void gjs_context_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); static void gjs_context_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); struct _GjsContext { GObject parent; JSRuntime *runtime; JSContext *context; JSObject *global; char *program_name; char **search_path; gboolean destroying; guint auto_gc_id; jsid const_strings[GJS_STRING_LAST]; }; /* Keep this consistent with GjsConstString */ static const char *const_strings[] = { "constructor", "prototype", "length", "imports", "__parentModule__", "__init__", "searchPath", "__gjsKeepAlive", "__gjsPrivateNS", "gi", "versions", "overrides", "_init", "_new_internal", "new", "message", "code", "stack", "fileName", "lineNumber", "name", }; G_STATIC_ASSERT(G_N_ELEMENTS(const_strings) == GJS_STRING_LAST); struct _GjsContextClass { GObjectClass parent; }; G_DEFINE_TYPE(GjsContext, gjs_context, G_TYPE_OBJECT); enum { PROP_0, PROP_SEARCH_PATH, PROP_PROGRAM_NAME, }; static GMutex contexts_lock; static GList *all_contexts = NULL; static JSBool gjs_log(JSContext *context, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(context, vp); char *s; JSExceptionState *exc_state; JSString *jstr; if (argc != 1) { gjs_throw(context, "Must pass a single argument to log()"); return JS_FALSE; } JS_BeginRequest(context); /* JS_ValueToString might throw, in which we will only *log that the value could be converted to string */ exc_state = JS_SaveExceptionState(context); jstr = JS_ValueToString(context, argv[0]); if (jstr != NULL) argv[0] = STRING_TO_JSVAL(jstr); // GC root JS_RestoreExceptionState(context, exc_state); if (jstr == NULL) { g_message("JS LOG: "); JS_EndRequest(context); return JS_TRUE; } if (!gjs_string_to_utf8(context, STRING_TO_JSVAL(jstr), &s)) { JS_EndRequest(context); return JS_FALSE; } g_message("JS LOG: %s", s); g_free(s); JS_EndRequest(context); JS_SET_RVAL(context, vp, JSVAL_VOID); return JS_TRUE; } static JSBool gjs_log_error(JSContext *context, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(context, vp); JSExceptionState *exc_state; JSString *jstr; if ((argc != 1 && argc != 2) || !JSVAL_IS_OBJECT (argv[0])) { gjs_throw(context, "Must pass an exception and optionally a message to logError()"); return JS_FALSE; } JS_BeginRequest(context); if (argc == 2) { /* JS_ValueToString might throw, in which we will only *log that the value could be converted to string */ exc_state = JS_SaveExceptionState(context); jstr = JS_ValueToString(context, argv[1]); if (jstr != NULL) argv[1] = STRING_TO_JSVAL(jstr); // GC root JS_RestoreExceptionState(context, exc_state); } else { jstr = NULL; } gjs_log_exception_full(context, argv[0], jstr); JS_EndRequest(context); JS_SET_RVAL(context, vp, JSVAL_VOID); return JS_TRUE; } static JSBool gjs_print_parse_args(JSContext *context, unsigned argc, jsval *argv, char **buffer) { GString *str; gchar *s; guint n; JS_BeginRequest(context); str = g_string_new(""); for (n = 0; n < argc; ++n) { JSExceptionState *exc_state; JSString *jstr; /* JS_ValueToString might throw, in which we will only * log that the value could be converted to string */ exc_state = JS_SaveExceptionState(context); jstr = JS_ValueToString(context, argv[n]); if (jstr != NULL) argv[n] = STRING_TO_JSVAL(jstr); // GC root JS_RestoreExceptionState(context, exc_state); if (jstr != NULL) { if (!gjs_string_to_utf8(context, STRING_TO_JSVAL(jstr), &s)) { JS_EndRequest(context); g_string_free(str, TRUE); return JS_FALSE; } g_string_append(str, s); g_free(s); if (n < (argc-1)) g_string_append_c(str, ' '); } else { JS_EndRequest(context); *buffer = g_string_free(str, TRUE); if (!*buffer) *buffer = g_strdup(""); return JS_TRUE; } } *buffer = g_string_free(str, FALSE); JS_EndRequest(context); return JS_TRUE; } static JSBool gjs_print(JSContext *context, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(context, vp); char *buffer; if (!gjs_print_parse_args(context, argc, argv, &buffer)) { return FALSE; } g_print("%s\n", buffer); g_free(buffer); JS_SET_RVAL(context, vp, JSVAL_VOID); return JS_TRUE; } static JSBool gjs_printerr(JSContext *context, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(context, vp); char *buffer; if (!gjs_print_parse_args(context, argc, argv, &buffer)) { return FALSE; } g_printerr("%s\n", buffer); g_free(buffer); JS_SET_RVAL(context, vp, JSVAL_VOID); return JS_TRUE; } static void gjs_context_init(GjsContext *js_context) { gjs_context_make_current(js_context); } static void gjs_context_class_init(GjsContextClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GParamSpec *pspec; object_class->dispose = gjs_context_dispose; object_class->finalize = gjs_context_finalize; object_class->constructed = gjs_context_constructed; object_class->get_property = gjs_context_get_property; object_class->set_property = gjs_context_set_property; pspec = g_param_spec_boxed("search-path", "Search path", "Path where modules to import should reside", G_TYPE_STRV, (GParamFlags) (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property(object_class, PROP_SEARCH_PATH, pspec); pspec = g_param_spec_string("program-name", "Program Name", "The filename of the launched JS program", "", (GParamFlags) (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property(object_class, PROP_PROGRAM_NAME, pspec); /* For GjsPrivate */ { char *priv_typelib_dir = g_build_filename (PKGLIBDIR, "girepository-1.0", NULL); g_irepository_prepend_search_path(priv_typelib_dir); g_free (priv_typelib_dir); } gjs_register_native_module("byteArray", gjs_define_byte_array_stuff); gjs_register_native_module("_gi", gjs_define_private_gi_stuff); gjs_register_native_module("gi", gjs_define_gi_stuff); gjs_register_static_modules(); } static void gjs_context_dispose(GObject *object) { GjsContext *js_context; js_context = GJS_CONTEXT(object); if (js_context->global != NULL) { js_context->global = NULL; } if (js_context->context != NULL) { gjs_debug(GJS_DEBUG_CONTEXT, "Destroying JS context"); JS_BeginRequest(js_context->context); /* Do a full GC here before tearing down, since once we do * that we may not have the JS_GetPrivate() to access the * context */ JS_GC(js_context->runtime); JS_EndRequest(js_context->context); js_context->destroying = TRUE; /* Now, release all native objects, to avoid recursion between * the JS teardown and the C teardown. The JSObject proxies * still exist, but point to NULL. */ gjs_object_prepare_shutdown(js_context->context); if (js_context->auto_gc_id > 0) { g_source_remove (js_context->auto_gc_id); js_context->auto_gc_id = 0; } /* Tear down JS */ JS_DestroyContext(js_context->context); js_context->context = NULL; js_context->runtime = NULL; } G_OBJECT_CLASS(gjs_context_parent_class)->dispose(object); } static void gjs_context_finalize(GObject *object) { GjsContext *js_context; js_context = GJS_CONTEXT(object); if (js_context->search_path != NULL) { g_strfreev(js_context->search_path); js_context->search_path = NULL; } if (js_context->program_name != NULL) { g_free(js_context->program_name); js_context->program_name = NULL; } if (gjs_context_get_current() == (GjsContext*)object) gjs_context_make_current(NULL); g_mutex_lock(&contexts_lock); all_contexts = g_list_remove(all_contexts, object); g_mutex_unlock(&contexts_lock); G_OBJECT_CLASS(gjs_context_parent_class)->finalize(object); } static JSFunctionSpec global_funcs[] = { { "log", JSOP_WRAPPER (gjs_log), 1, GJS_MODULE_PROP_FLAGS }, { "logError", JSOP_WRAPPER (gjs_log_error), 2, GJS_MODULE_PROP_FLAGS }, { "print", JSOP_WRAPPER (gjs_print), 0, GJS_MODULE_PROP_FLAGS }, { "printerr", JSOP_WRAPPER (gjs_printerr), 0, GJS_MODULE_PROP_FLAGS }, { NULL }, }; static void gjs_context_constructed(GObject *object) { GjsContext *js_context = GJS_CONTEXT(object); int i; G_OBJECT_CLASS(gjs_context_parent_class)->constructed(object); js_context->runtime = gjs_runtime_for_current_thread(); js_context->context = JS_NewContext(js_context->runtime, 8192 /* stack chunk size */); if (js_context->context == NULL) g_error("Failed to create javascript context"); for (i = 0; i < GJS_STRING_LAST; i++) js_context->const_strings[i] = gjs_intern_string_to_id(js_context->context, const_strings[i]); JS_BeginRequest(js_context->context); /* set ourselves as the private data */ JS_SetContextPrivate(js_context->context, js_context); if (!gjs_init_context_standard(js_context->context)) g_error("Failed to initialize context"); js_context->global = JS_GetGlobalObject(js_context->context); JSAutoCompartment ac(js_context->context, js_context->global); if (!JS_DefineProperty(js_context->context, js_context->global, "window", OBJECT_TO_JSVAL(js_context->global), NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT)) g_error("No memory to export global object as 'window'"); if (!JS_DefineFunctions(js_context->context, js_context->global, &global_funcs[0])) g_error("Failed to define properties on the global object"); /* We create the global-to-runtime root importer with the * passed-in search path. If someone else already created * the root importer, this is a no-op. */ if (!gjs_create_root_importer(js_context->context, js_context->search_path ? (const char**) js_context->search_path : NULL, TRUE)) g_error("Failed to create root importer"); /* Now copy the global root importer (which we just created, * if it didn't exist) to our global object */ if (!gjs_define_root_importer(js_context->context, js_context->global)) g_error("Failed to point 'imports' property at root importer"); JS_EndRequest(js_context->context); g_mutex_lock (&contexts_lock); all_contexts = g_list_prepend(all_contexts, object); g_mutex_unlock (&contexts_lock); } static void gjs_context_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GjsContext *js_context; js_context = GJS_CONTEXT (object); switch (prop_id) { case PROP_PROGRAM_NAME: g_value_set_string(value, js_context->program_name); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gjs_context_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GjsContext *js_context; js_context = GJS_CONTEXT (object); switch (prop_id) { case PROP_SEARCH_PATH: js_context->search_path = (char**) g_value_dup_boxed(value); break; case PROP_PROGRAM_NAME: js_context->program_name = g_value_dup_string(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } GjsContext* gjs_context_new(void) { return (GjsContext*) g_object_new (GJS_TYPE_CONTEXT, NULL); } GjsContext* gjs_context_new_with_search_path(char** search_path) { return (GjsContext*) g_object_new (GJS_TYPE_CONTEXT, "search-path", search_path, NULL); } gboolean _gjs_context_destroying (GjsContext *context) { return context->destroying; } static gboolean trigger_gc_if_needed (gpointer user_data) { GjsContext *js_context = GJS_CONTEXT(user_data); js_context->auto_gc_id = 0; gjs_gc_if_needed(js_context->context); return FALSE; } void _gjs_context_schedule_gc_if_needed (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); } /** * gjs_context_maybe_gc: * @context: a #GjsContext * * Similar to the Spidermonkey JS_MaybeGC() call which * heuristically looks at JS runtime memory usage and * may initiate a garbage collection. * * This function always unconditionally invokes JS_MaybeGC(), but * additionally looks at memory usage from the system malloc() * when available, and if the delta has grown since the last run * significantly, also initiates a full JavaScript garbage * collection. The idea is that since GJS is a bridge between * JavaScript and system libraries, and JS objects act as proxies * for these system memory objects, GJS consumers need a way to * hint to the runtime that it may be a good idea to try a * collection. * * A good time to call this function is when your application * transitions to an idle state. */ void gjs_context_maybe_gc (GjsContext *context) { gjs_maybe_gc(context->context); } /** * gjs_context_gc: * @context: a #GjsContext * * Initiate a full GC; may or may not block until complete. This * function just calls Spidermonkey JS_GC(). */ void gjs_context_gc (GjsContext *context) { JS_GC(context->runtime); } /** * gjs_context_get_all: * * Returns a newly-allocated list containing all known instances of #GjsContext. * This is useful for operating on the contexts from a process-global situation * such as a debugger. * * Return value: (element-type GjsContext) (transfer full): Known #GjsContext instances */ GList* gjs_context_get_all(void) { GList *result; GList *iter; g_mutex_lock (&contexts_lock); result = g_list_copy(all_contexts); for (iter = result; iter; iter = iter->next) g_object_ref((GObject*)iter->data); g_mutex_unlock (&contexts_lock); return result; } /** * gjs_context_get_native_context: * * Returns a pointer to the underlying native context. For SpiderMonkey, this * is a JSContext * */ void* gjs_context_get_native_context (GjsContext *js_context) { g_return_val_if_fail(GJS_IS_CONTEXT(js_context), NULL); return js_context->context; } gboolean gjs_context_eval(GjsContext *js_context, const char *script, gssize script_len, const char *filename, int *exit_status_p, GError **error) { gboolean ret = FALSE; jsval retval; JSAutoCompartment ac(js_context->context, js_context->global); JSAutoRequest ar(js_context->context); g_object_ref(G_OBJECT(js_context)); if (!gjs_eval_with_scope(js_context->context, NULL, script, script_len, filename, &retval)) { gjs_log_exception(js_context->context); g_set_error(error, GJS_ERROR, GJS_ERROR_FAILED, "JS_EvaluateScript() failed"); goto out; } if (exit_status_p) { if (JSVAL_IS_INT(retval)) { int code; if (JS_ValueToInt32(js_context->context, retval, &code)) { gjs_debug(GJS_DEBUG_CONTEXT, "Script returned integer code %d", code); *exit_status_p = code; } } else { /* Assume success if no integer was returned */ *exit_status_p = 0; } } ret = TRUE; out: g_object_unref(G_OBJECT(js_context)); return ret; } gboolean gjs_context_eval_file(GjsContext *js_context, const char *filename, int *exit_status_p, GError **error) { char *script = NULL; gsize script_len; gboolean ret = TRUE; GFile *file = g_file_new_for_commandline_arg(filename); if (!g_file_query_exists(file, NULL)) { ret = FALSE; goto out; } if (!g_file_load_contents(file, NULL, &script, &script_len, NULL, error)) { ret = FALSE; goto out; } if (!gjs_context_eval(js_context, script, script_len, filename, exit_status_p, error)) { ret = FALSE; goto out; } out: g_free(script); g_object_unref(file); return ret; } gboolean gjs_context_define_string_array(GjsContext *js_context, const char *array_name, gssize array_length, const char **array_values, GError **error) { JSAutoCompartment ac(js_context->context, js_context->global); if (!gjs_define_string_array(js_context->context, js_context->global, array_name, array_length, array_values, JSPROP_READONLY | JSPROP_PERMANENT)) { gjs_log_exception(js_context->context); g_set_error(error, GJS_ERROR, GJS_ERROR_FAILED, "gjs_define_string_array() failed"); return FALSE; } return TRUE; } static GjsContext *current_context; GjsContext * gjs_context_get_current (void) { return current_context; } void gjs_context_make_current (GjsContext *context) { g_assert (context == NULL || current_context == NULL); current_context = context; } jsid gjs_context_get_const_string(JSContext *context, GjsConstString name) { GjsContext *gjs_context = (GjsContext *) JS_GetContextPrivate(context); return gjs_context->const_strings[name]; } gboolean gjs_object_get_property_const(JSContext *context, JSObject *obj, GjsConstString property_name, jsval *value_p) { jsid pname; pname = gjs_context_get_const_string(context, property_name); return JS_GetPropertyById(context, obj, pname, value_p); } cjs-2.8.0/cjs/coverage.cpp0000664000175000017500000012021412610172032014274 0ustar fabiofabio/* * Copyright © 2014 Endless Mobile, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Authored By: Sam Spilsbury */ #include #include #include "gjs-module.h" #include "coverage.h" #include "util/error.h" typedef struct _GjsDebugHooks GjsDebugHooks; typedef struct _GjsCoverageBranchData GjsCoverageBranchData; struct _GjsCoveragePrivate { gchar **covered_paths; GjsContext *context; JSObject *coverage_statistics; }; G_DEFINE_TYPE_WITH_PRIVATE(GjsCoverage, gjs_coverage, G_TYPE_OBJECT) enum { PROP_0, PROP_COVERAGE_PATHS, PROP_CONTEXT, PROP_N }; static GParamSpec *properties[PROP_N] = { NULL, }; struct _GjsCoverageBranchData { GArray *branch_alternatives; GArray *branch_alternatives_taken; unsigned int branch_point; unsigned int last_branch_exit; gboolean branch_hit; }; typedef struct _GjsCoverageBranchExit { unsigned int line; unsigned int hit_count; } GjsCoverageBranchExit; typedef struct _GjsCoverageBranch { GArray *exits; unsigned int point; gboolean hit; } GjsCoverageBranch; typedef struct _GjsCoverageFunction { char *key; unsigned int hit_count; } GjsCoverageFunction; static void write_string_into_stream(GOutputStream *stream, const gchar *string) { g_output_stream_write(stream, (gconstpointer) string, strlen(string) * sizeof(gchar), NULL, NULL); } static void write_source_file_header(GOutputStream *stream, const gchar *source_file_path) { write_string_into_stream(stream, "SF:"); write_string_into_stream(stream, source_file_path); write_string_into_stream(stream, "\n"); } typedef struct _FunctionHitCountData { GOutputStream *stream; unsigned int *n_functions_found; unsigned int *n_functions_hit; } FunctionHitCountData; static void write_function_hit_count(GOutputStream *stream, const char *function_name, unsigned int hit_count, unsigned int *n_functions_found, unsigned int *n_functions_hit) { char *line = g_strdup_printf("FNDA:%i,%s\n", hit_count, function_name); (*n_functions_found)++; if (hit_count > 0) (*n_functions_hit)++; write_string_into_stream(stream, line); g_free(line); } static void write_functions_hit_counts(GOutputStream *stream, GArray *functions, unsigned int *n_functions_found, unsigned int *n_functions_hit) { unsigned int i = 0; for (; i < functions->len; ++i) { GjsCoverageFunction *function = &(g_array_index(functions, GjsCoverageFunction, i)); write_function_hit_count(stream, function->key, function->hit_count, n_functions_found, n_functions_hit); } } static void write_function_foreach_func(gpointer value, gpointer user_data) { GOutputStream *stream = (GOutputStream *) user_data; GjsCoverageFunction *function = (GjsCoverageFunction *) value; write_string_into_stream(stream, "FN:"); write_string_into_stream(stream, function->key); write_string_into_stream(stream, "\n"); } static void for_each_element_in_array(GArray *array, GFunc func, gpointer user_data) { const gsize element_size = g_array_get_element_size(array); unsigned int i; char *current_array_pointer = (char *) array->data; for (i = 0; i < array->len; ++i, current_array_pointer += element_size) (*func)(current_array_pointer, user_data); } static void write_functions(GOutputStream *data_stream, GArray *functions) { for_each_element_in_array(functions, write_function_foreach_func, data_stream); } static void write_uint32_into_stream(GOutputStream *stream, unsigned int integer) { char buf[32]; g_snprintf(buf, 32, "%u", integer); g_output_stream_write(stream, (gconstpointer) buf, strlen(buf) * sizeof(char), NULL, NULL); } static void write_int32_into_stream(GOutputStream *stream, int integer) { char buf[32]; g_snprintf(buf, 32, "%i", integer); g_output_stream_write(stream, (gconstpointer) buf, strlen(buf) * sizeof(char), NULL, NULL); } static void write_function_coverage(GOutputStream *data_stream, unsigned int n_found_functions, unsigned int n_hit_functions) { write_string_into_stream(data_stream, "FNF:"); write_uint32_into_stream(data_stream, n_found_functions); write_string_into_stream(data_stream, "\n"); write_string_into_stream(data_stream, "FNH:"); write_uint32_into_stream(data_stream, n_hit_functions); write_string_into_stream(data_stream, "\n"); } typedef struct _WriteAlternativeData { unsigned int *n_branch_alternatives_found; unsigned int *n_branch_alternatives_hit; GOutputStream *output_stream; gpointer *all_alternatives; gboolean branch_point_was_hit; } WriteAlternativeData; typedef struct _WriteBranchInfoData { unsigned int *n_branch_exits_found; unsigned int *n_branch_exits_hit; GOutputStream *output_stream; } WriteBranchInfoData; static void write_individual_branch(gpointer branch_ptr, gpointer user_data) { GjsCoverageBranch *branch = (GjsCoverageBranch *) branch_ptr; WriteBranchInfoData *data = (WriteBranchInfoData *) user_data; /* This line is not a branch, don't write anything */ if (!branch->point) return; unsigned int i = 0; for (; i < branch->exits->len; ++i) { GjsCoverageBranchExit *exit = &(g_array_index(branch->exits, GjsCoverageBranchExit, i)); unsigned int alternative_counter = exit->hit_count; unsigned int branch_point = branch->point; char *hit_count_string = NULL; if (!branch->hit) hit_count_string = g_strdup_printf("-"); else hit_count_string = g_strdup_printf("%i", alternative_counter); char *branch_alternative_line = g_strdup_printf("BRDA:%i,0,%i,%s\n", branch_point, i, hit_count_string); write_string_into_stream(data->output_stream, branch_alternative_line); g_free(hit_count_string); g_free(branch_alternative_line); ++(*data->n_branch_exits_found); if (alternative_counter > 0) ++(*data->n_branch_exits_hit); } } static void write_branch_coverage(GOutputStream *stream, GArray *branches, unsigned int *n_branch_exits_found, unsigned int *n_branch_exits_hit) { /* Write individual branches and pass-out the totals */ WriteBranchInfoData data = { n_branch_exits_found, n_branch_exits_hit, stream }; for_each_element_in_array(branches, write_individual_branch, &data); } static void write_branch_totals(GOutputStream *stream, unsigned int n_branch_exits_found, unsigned int n_branch_exits_hit) { write_string_into_stream(stream, "BRF:"); write_uint32_into_stream(stream, n_branch_exits_found); write_string_into_stream(stream, "\n"); write_string_into_stream(stream, "BRH:"); write_uint32_into_stream(stream, n_branch_exits_hit); write_string_into_stream(stream, "\n"); } static void write_line_coverage(GOutputStream *stream, GArray *stats, unsigned int *lines_hit_count, unsigned int *executable_lines_count) { unsigned int i = 0; for (i = 0; i < stats->len; ++i) { int hit_count_for_line = g_array_index(stats, int, i); if (hit_count_for_line == -1) continue; write_string_into_stream(stream, "DA:"); write_uint32_into_stream(stream, i); write_string_into_stream(stream, ","); write_int32_into_stream(stream, hit_count_for_line); write_string_into_stream(stream, "\n"); if (hit_count_for_line > 0) ++(*lines_hit_count); ++(*executable_lines_count); } } static void write_line_totals(GOutputStream *stream, unsigned int lines_hit_count, unsigned int executable_lines_count) { write_string_into_stream(stream, "LH:"); write_uint32_into_stream(stream, lines_hit_count); write_string_into_stream(stream, "\n"); write_string_into_stream(stream, "LF:"); write_uint32_into_stream(stream, executable_lines_count); write_string_into_stream(stream, "\n"); } static void write_end_of_record(GOutputStream *stream) { write_string_into_stream(stream, "end_of_record\n"); } static void copy_source_file_to_coverage_output(const char *source, const char *destination) { GFile *source_file = g_file_new_for_commandline_arg(source); GFile *destination_file = g_file_new_for_commandline_arg(destination); GError *error = NULL; /* We also need to recursively make the directory we * want to copy to, as g_file_copy doesn't do that */ gchar *destination_dirname = g_path_get_dirname(destination); g_mkdir_with_parents(destination_dirname, S_IRWXU); if (!g_file_copy(source_file, destination_file, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, &error)) { g_critical("Failed to copy source file %s to destination %s: %s\n", source, destination, error->message); } g_clear_error(&error); g_free(destination_dirname); g_object_unref(destination_file); g_object_unref(source_file); } typedef struct _StatisticsPrintUserData { GjsContext *reflection_context; GFileOutputStream *ostream; const gchar *output_directory; JSContext *context; JSObject *object; } StatisticsPrintUserData; /* This function will strip a URI scheme and return * the string with the URI scheme stripped or NULL * if the path was not a valid URI */ static const char * strip_uri_scheme(const char *potential_uri) { char *uri_header = g_uri_parse_scheme(potential_uri); if (uri_header) { gsize offset = strlen(uri_header); g_free(uri_header); /* g_uri_parse_scheme only parses the name * of the scheme, we also need to strip the * characters '://' */ return potential_uri + offset + 3; } return NULL; } /* This function will return a string of pathname * components from the first directory indicating * where two directories diverge. For instance: * * child_path: /a/b/c/d/e * parent_path: /a/b/d/ * * Will return: c/d/e * * If the directories are not at all similar then * the full dirname of the child_path effectively * be returned. * * As a special case, child paths that are a URI * automatically return the full URI path with * the URI scheme stripped out. */ static char * find_diverging_child_components(const char *child_path, const char *parent_path) { const char *stripped_uri = strip_uri_scheme(child_path); if (stripped_uri) return g_strdup(stripped_uri); char **child_path_components = g_strsplit(child_path, "/", -1); char **parent_path_components = g_strsplit(parent_path, "/", -1); char **child_path_component_iterator = child_path_components; char **parent_path_component_iterator = parent_path_components; for (; *child_path_component_iterator != NULL && *parent_path_component_iterator != NULL; ++child_path_component_iterator, ++parent_path_component_iterator) { if (g_strcmp0(*child_path_component_iterator, *parent_path_component_iterator)) break; } /* Paste the child path components back together */ char *diverged = g_strjoinv("/", child_path_component_iterator); g_strfreev(child_path_components); g_strfreev(parent_path_components); return diverged; } /* The coverage output directory could be a relative path * so we need to get an absolute path */ static char * get_absolute_path(const char *path) { char *absolute_path = NULL; if (!g_path_is_absolute(path)) { char *current_dir = g_get_current_dir(); absolute_path = g_build_filename(current_dir, path, NULL); g_free(current_dir); } else { absolute_path = g_strdup(path); } return absolute_path; } typedef gboolean (*ConvertAndInsertJSVal) (GArray *array, JSContext *context, jsval *element); static gboolean get_array_from_js_value(JSContext *context, jsval *value, size_t array_element_size, GDestroyNotify element_clear_func, ConvertAndInsertJSVal inserter, GArray **out_array) { g_return_val_if_fail(out_array != NULL, FALSE); g_return_val_if_fail(*out_array == NULL, FALSE); JSObject *js_array = JSVAL_TO_OBJECT(*value); if (!JS_IsArrayObject(context, js_array)) { g_critical("Returned object from is not an array"); return FALSE; } /* We're not preallocating any space here at the moment until * we have some profiling data that suggests a good size to * preallocate to. */ GArray *c_side_array = g_array_new(TRUE, TRUE, array_element_size); u_int32_t js_array_len; if (element_clear_func) g_array_set_clear_func(c_side_array, element_clear_func); if (JS_GetArrayLength(context, js_array, &js_array_len)) { u_int32_t i = 0; for (; i < js_array_len; ++i) { jsval element; if (!JS_GetElement(context, js_array, i, &element)) { g_array_unref(c_side_array); gjs_throw(context, "Failed to get function names array element %i", i); return FALSE; } if (!(inserter(c_side_array, context, &element))) { g_array_unref(c_side_array); gjs_throw(context, "Failed to convert array element %i", i); return FALSE; } } } *out_array = c_side_array; return TRUE; } static gboolean convert_and_insert_unsigned_int(GArray *array, JSContext *context, jsval *element) { if (!JSVAL_IS_INT(*element) && !JSVAL_IS_VOID(*element) && !JSVAL_IS_NULL(*element)) { g_critical("Array element is not an integer or undefined or null"); return FALSE; } if (JSVAL_IS_INT(*element)) { unsigned int element_integer = JSVAL_TO_INT(*element); g_array_append_val(array, element_integer); } else { int not_executable = -1; g_array_append_val(array, not_executable); } return TRUE; } static GArray * get_executed_lines_for(GjsCoverage *coverage, jsval *filename_value) { GjsCoveragePrivate *priv = (GjsCoveragePrivate *) gjs_coverage_get_instance_private(coverage); JSContext *context = (JSContext *) gjs_context_get_native_context(priv->context); GArray *array = NULL; jsval rval; if (!JS_CallFunctionName(context, priv->coverage_statistics, "getExecutedLinesFor", 1, filename_value, &rval)) { gjs_log_exception(context); return NULL; } if (!get_array_from_js_value(context, &rval, sizeof (unsigned int), NULL, convert_and_insert_unsigned_int, &array)) { gjs_log_exception(context); return NULL; } return array; } static void init_covered_function(GjsCoverageFunction *function, char *key, unsigned int hit_count) { function->key = key; function->hit_count = hit_count; } static void clear_coverage_function(gpointer info_location) { GjsCoverageFunction *info = (GjsCoverageFunction *) info_location; g_free(info->key); } static gboolean convert_and_insert_function_decl(GArray *array, JSContext *context, jsval *element) { JSObject *object = JSVAL_TO_OBJECT(*element); if (!object) { gjs_throw(context, "Converting element to object failed"); return FALSE; } jsval function_name_property_value; if (!JS_GetProperty(context, object, "name", &function_name_property_value)) { gjs_throw(context, "Failed to get name property for function object"); return FALSE; } char *utf8_string; if (JSVAL_IS_STRING(function_name_property_value)) { if (!gjs_string_to_utf8(context, function_name_property_value, &utf8_string)) { gjs_throw(context, "Failed to convert function_name to string"); return FALSE; } } else if (JSVAL_IS_NULL(function_name_property_value)) { utf8_string = NULL; } else { gjs_throw(context, "Unexpected type for function_name"); return FALSE; } jsval hit_count_property_value; if (!JS_GetProperty(context, object, "hitCount", &hit_count_property_value) || !JSVAL_IS_INT(hit_count_property_value)) { gjs_throw(context, "Failed to get hitCount property for function object"); return FALSE; } unsigned int line_number = JSVAL_TO_INT(hit_count_property_value); GjsCoverageFunction info; init_covered_function(&info, utf8_string, line_number); g_array_append_val(array, info); return TRUE; } static GArray * get_functions_for(GjsCoverage *coverage, jsval *filename_value) { GjsCoveragePrivate *priv = (GjsCoveragePrivate *) gjs_coverage_get_instance_private(coverage); JSContext *context = (JSContext *) gjs_context_get_native_context(priv->context); GArray *array = NULL; jsval rval; if (!JS_CallFunctionName(context, priv->coverage_statistics, "getFunctionsFor", 1, filename_value, &rval)) { gjs_log_exception(context); return NULL; } if (!get_array_from_js_value(context, &rval, sizeof (GjsCoverageFunction), clear_coverage_function, convert_and_insert_function_decl, &array)) { gjs_log_exception(context); return NULL; } return array; } static void init_covered_branch(GjsCoverageBranch *branch, unsigned int point, JSBool was_hit, GArray *exits) { branch->point = point; branch->hit = !!was_hit; branch->exits = exits; } static void clear_coverage_branch(gpointer branch_location) { GjsCoverageBranch *branch = (GjsCoverageBranch *) branch_location; g_array_unref(branch->exits); } static gboolean convert_and_insert_branch_exit(GArray *array, JSContext *context, jsval *element) { if (!JSVAL_IS_OBJECT(*element)) { gjs_throw(context, "Branch exit array element is not an object"); return FALSE; } JSObject *object = JSVAL_TO_OBJECT(*element); if (!object) { gjs_throw(context, "Converting element to object failed"); return FALSE; } jsval line_value; int32_t line; if (!JS_GetProperty(context, object, "line", &line_value) || !JSVAL_IS_INT(line_value)) { gjs_throw(context, "Failed to get line property from element"); return FALSE; } line = JSVAL_TO_INT(line_value); jsval hit_count_value; int32_t hit_count; if (!JS_GetProperty(context, object, "hitCount", &hit_count_value) || !JSVAL_IS_INT(hit_count_value)) { gjs_throw(context, "Failed to get hitCount property from element"); return FALSE; } hit_count = JSVAL_TO_INT(hit_count_value); GjsCoverageBranchExit exit = { (unsigned int) line, (unsigned int) hit_count }; g_array_append_val(array, exit); return TRUE; } static gboolean convert_and_insert_branch_info(GArray *array, JSContext *context, jsval *element) { if (!JSVAL_IS_OBJECT(*element) && !JSVAL_IS_VOID(*element)) { gjs_throw(context, "Branch array element is not an object or undefined"); return FALSE; } if (JSVAL_IS_OBJECT(*element)) { JSObject *object = JSVAL_TO_OBJECT(*element); if (!object) { gjs_throw(context, "Converting element to object failed"); return FALSE; } jsval branch_point_value; int32_t branch_point; if (!JS_GetProperty(context, object, "point", &branch_point_value) || !JSVAL_IS_INT(branch_point_value)) { gjs_throw(context, "Failed to get point property from element"); return FALSE; } branch_point = JSVAL_TO_INT(branch_point_value); jsval was_hit_value; JSBool was_hit; if (!JS_GetProperty(context, object, "hit", &was_hit_value) || !JSVAL_IS_BOOLEAN(was_hit_value)) { gjs_throw(context, "Failed to get point property from element"); return FALSE; } was_hit = JSVAL_TO_BOOLEAN(was_hit_value); jsval branch_exits_value; GArray *branch_exits_array = NULL; if (!JS_GetProperty(context, object, "exits", &branch_exits_value) || !JSVAL_IS_OBJECT(branch_exits_value)) { gjs_throw(context, "Failed to get exits property from element"); return FALSE; } if (!get_array_from_js_value(context, &branch_exits_value, sizeof(GjsCoverageBranchExit), NULL, convert_and_insert_branch_exit, &branch_exits_array)) { /* Already logged the exception, no need to do anything here */ return FALSE; } GjsCoverageBranch branch; init_covered_branch(&branch, branch_point, was_hit, branch_exits_array); g_array_append_val(array, branch); } return TRUE; } static GArray * get_branches_for(GjsCoverage *coverage, jsval *filename_value) { GjsCoveragePrivate *priv = (GjsCoveragePrivate *) gjs_coverage_get_instance_private(coverage); JSContext *context = (JSContext *) gjs_context_get_native_context(priv->context); GArray *array = NULL; jsval rval; if (!JS_CallFunctionName(context, priv->coverage_statistics, "getBranchesFor", 1, filename_value, &rval)) { gjs_log_exception(context); return NULL; } if (!get_array_from_js_value(context, &rval, sizeof (GjsCoverageBranch), clear_coverage_branch, convert_and_insert_branch_info, &array)) { gjs_log_exception(context); return NULL; } return array; } static void print_statistics_for_file(GjsCoverage *coverage, char *filename, const char *output_directory, GOutputStream *ostream) { GjsCoveragePrivate *priv = (GjsCoveragePrivate *) gjs_coverage_get_instance_private(coverage); char *absolute_output_directory = get_absolute_path(output_directory); char *diverged_paths = find_diverging_child_components(filename, absolute_output_directory); char *destination_filename = g_build_filename(absolute_output_directory, diverged_paths, NULL); JSContext *context = (JSContext *) gjs_context_get_native_context(priv->context); JSAutoCompartment compartment(context, priv->coverage_statistics); JSAutoRequest ar(context); JSString *filename_jsstr = JS_NewStringCopyZ(context, filename); jsval filename_jsval = STRING_TO_JSVAL(filename_jsstr); GArray *lines = get_executed_lines_for(coverage, &filename_jsval); GArray *functions = get_functions_for(coverage, &filename_jsval); GArray *branches = get_branches_for(coverage, &filename_jsval); if (!lines || !functions || !branches) { if (lines) { g_array_unref(lines); } if (functions) { g_array_unref(functions); } if (branches) { g_array_unref(branches); } g_free(diverged_paths); g_free(destination_filename); g_free(absolute_output_directory); return; } copy_source_file_to_coverage_output(filename, destination_filename); write_source_file_header(ostream, (const char *) destination_filename); write_functions(ostream, functions); unsigned int functions_hit_count = 0; unsigned int functions_found_count = 0; write_functions_hit_counts(ostream, functions, &functions_found_count, &functions_hit_count); write_function_coverage(ostream, functions_found_count, functions_hit_count); unsigned int branches_hit_count = 0; unsigned int branches_found_count = 0; write_branch_coverage(ostream, branches, &branches_found_count, &branches_hit_count); write_branch_totals(ostream, branches_found_count, branches_hit_count); unsigned int lines_hit_count = 0; unsigned int executable_lines_count = 0; write_line_coverage(ostream, lines, &lines_hit_count, &executable_lines_count); write_line_totals(ostream, lines_hit_count, executable_lines_count); write_end_of_record(ostream); g_array_unref(lines); g_array_unref(functions); g_array_unref(branches); g_free(diverged_paths); g_free(destination_filename); g_free(absolute_output_directory); } void gjs_coverage_write_statistics(GjsCoverage *coverage, const char *output_directory) { GjsCoveragePrivate *priv = (GjsCoveragePrivate *) gjs_coverage_get_instance_private(coverage); GError *error = NULL; /* Create output_directory if it doesn't exist */ g_mkdir_with_parents(output_directory, 0755); char *output_file_path = g_build_filename(output_directory, "coverage.lcov", NULL); GFile *output_file = g_file_new_for_commandline_arg(output_file_path); g_free (output_file_path); GOutputStream *ostream = G_OUTPUT_STREAM(g_file_append_to(output_file, G_FILE_CREATE_NONE, NULL, &error)); char **file_iter = priv->covered_paths; while (*file_iter) { print_statistics_for_file(coverage, *file_iter, output_directory, ostream); ++file_iter; } g_object_unref(ostream); g_object_unref(output_file); } static void gjs_coverage_init(GjsCoverage *self) { } static JSClass coverage_global_class = { "GjsCoverageGlobal", JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(GJS_GLOBAL_SLOT_LAST), JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL, NULL /* checkAccess */, NULL /* call */, NULL /* hasInstance */, NULL /* construct */, NULL, { NULL } }; static gboolean gjs_context_eval_file_in_compartment(GjsContext *context, const char *filename, JSObject *compartment_object, GError **error) { char *script = NULL; gsize script_len = 0; GFile *file = g_file_new_for_commandline_arg(filename); if (!g_file_load_contents(file, NULL, &script, &script_len, NULL, error)) return FALSE; jsval return_value; JSContext *js_context = (JSContext *) gjs_context_get_native_context(context); JSAutoCompartment compartment(js_context, compartment_object); if (!gjs_eval_with_scope(js_context, compartment_object, script, script_len, filename, &return_value)) { gjs_log_exception(js_context); g_set_error(error, GJS_ERROR, GJS_ERROR_FAILED, "Failed to evaluate %s", filename); return FALSE; } g_free(script); return TRUE; } static JSBool coverage_warning(JSContext *context, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(context, vp); char *s; JSExceptionState *exc_state; JSString *jstr; if (argc != 1) { gjs_throw(context, "Must pass a single argument to warning()"); return JS_FALSE; } JS_BeginRequest(context); /* JS_ValueToString might throw, in which we will only *log that the value could be converted to string */ exc_state = JS_SaveExceptionState(context); jstr = JS_ValueToString(context, argv[0]); if (jstr != NULL) argv[0] = STRING_TO_JSVAL(jstr); // GC root JS_RestoreExceptionState(context, exc_state); if (jstr == NULL) { g_message("JS LOG: "); JS_EndRequest(context); return JS_TRUE; } if (!gjs_string_to_utf8(context, STRING_TO_JSVAL(jstr), &s)) { JS_EndRequest(context); return JS_FALSE; } g_message("JS COVERAGE WARNING: %s", s); g_free(s); JS_EndRequest(context); JS_SET_RVAL(context, vp, JSVAL_VOID); return JS_TRUE; } static JSBool coverage_get_file_contents(JSContext *context, unsigned argc, jsval *vp) { JSBool ret = JS_FALSE; JS::CallArgs args = JS::CallArgsFromVp(argc, vp); char *filename = NULL; GFile *file = NULL; char *script = NULL; gsize script_len; JSString *script_jsstr; GError *error = NULL; if (!gjs_parse_call_args(context, "getFileContents", "s", args, "filename", &filename)) goto out; file = g_file_new_for_commandline_arg(filename); if (!g_file_load_contents(file, NULL, &script, &script_len, NULL, &error)) { gjs_throw(context, "Failed to load contents for filename %s: %s", filename, error->message); goto out; } args.rval().setString(JS_NewStringCopyN(context, script, script_len)); ret = JS_TRUE; out: g_clear_error(&error); if (file) g_object_unref(file); g_free(filename); g_free(script); return ret; } static JSFunctionSpec coverage_funcs[] = { { "warning", JSOP_WRAPPER (coverage_warning), 1, GJS_MODULE_PROP_FLAGS }, { "getFileContents", JSOP_WRAPPER (coverage_get_file_contents), 1, GJS_MODULE_PROP_FLAGS }, { NULL }, }; static gboolean bootstrap_coverage(GjsCoverage *coverage) { static const char *coverage_script = "resource:///org/gnome/gjs/modules/coverage.js"; GjsCoveragePrivate *priv = (GjsCoveragePrivate *) gjs_coverage_get_instance_private(coverage); GError *error = NULL; JSContext *context = (JSContext *) gjs_context_get_native_context(priv->context); JSAutoRequest ar(context); JSObject *debuggee = JS_GetGlobalObject(context); JS::CompartmentOptions options; options.setVersion(JSVERSION_LATEST); JSObject *debugger_compartment = JS_NewGlobalObject(context, &coverage_global_class, NULL, options); { JSAutoCompartment compartment(context, debugger_compartment); JS::RootedObject debuggeeWrapper(context, debuggee); if (!JS_WrapObject(context, debuggeeWrapper.address())) { gjs_throw(context, "Failed to wrap debugeee"); return FALSE; } JS::RootedValue debuggeeWrapperValue(context, JS::ObjectValue(*debuggeeWrapper)); if (!JS_SetProperty(context, debugger_compartment, "debuggee", debuggeeWrapperValue.address())) { gjs_throw(context, "Failed to set debuggee property"); return FALSE; } if (!JS_InitStandardClasses(context, debugger_compartment)) { gjs_throw(context, "Failed to init standard classes"); return FALSE; } if (!JS_InitReflect(context, debugger_compartment)) { gjs_throw(context, "Failed to init Reflect"); return FALSE; } if (!JS_DefineDebuggerObject(context, debugger_compartment)) { gjs_throw(context, "Failed to init Debugger"); return FALSE; } if (!JS_DefineFunctions(context, debugger_compartment, &coverage_funcs[0])) g_error("Failed to init coverage"); if (!gjs_context_eval_file_in_compartment(priv->context, coverage_script, debugger_compartment, &error)) g_error("Failed to eval coverage script: %s\n", error->message); jsval coverage_statistics_prototype_value; if (!JS_GetProperty(context, debugger_compartment, "CoverageStatistics", &coverage_statistics_prototype_value) || !JSVAL_IS_OBJECT(coverage_statistics_prototype_value)) { gjs_throw(context, "Failed to get CoverageStatistics prototype"); return FALSE; } JSObject *coverage_statistics_constructor = JSVAL_TO_OBJECT(coverage_statistics_prototype_value); /* Now create the array to pass the desired script names over */ JSObject *filenames_js_array = gjs_build_string_array(context, -1, priv->covered_paths); jsval coverage_statistics_constructor_arguments[] = { OBJECT_TO_JSVAL(filenames_js_array) }; JSObject *coverage_statistics = JS_New(context, coverage_statistics_constructor, 1, coverage_statistics_constructor_arguments); if (!coverage_statistics) { gjs_throw(context, "Failed to create coverage_statitiscs object"); return FALSE; } priv->coverage_statistics = coverage_statistics; } return TRUE; } static void gjs_coverage_constructed(GObject *object) { G_OBJECT_CLASS(gjs_coverage_parent_class)->constructed(object); GjsCoverage *coverage = GJS_DEBUG_COVERAGE(object); GjsCoveragePrivate *priv = (GjsCoveragePrivate *) gjs_coverage_get_instance_private(coverage); JSContext *context = (JSContext *) gjs_context_get_native_context(priv->context); /* Before bootstrapping, turn off the JIT on the context */ guint32 options_flags = JS_GetOptions(context) & ~(JSOPTION_ION | JSOPTION_BASELINE | JSOPTION_ASMJS); JS_SetOptions(context, options_flags); if (!bootstrap_coverage(coverage)) { JSContext *context = (JSContext *) gjs_context_get_native_context(priv->context); JSAutoCompartment compartment(context, JS_GetGlobalObject(context)); gjs_log_exception(context); } } static void gjs_coverage_set_property(GObject *object, unsigned int prop_id, const GValue *value, GParamSpec *pspec) { GjsCoverage *coverage = GJS_DEBUG_COVERAGE(object); GjsCoveragePrivate *priv = (GjsCoveragePrivate *) gjs_coverage_get_instance_private(coverage); switch (prop_id) { case PROP_COVERAGE_PATHS: g_assert(priv->covered_paths == NULL); priv->covered_paths = (char **) g_value_dup_boxed (value); break; case PROP_CONTEXT: priv->context = GJS_CONTEXT(g_value_dup_object(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } static void gjs_coverage_dispose(GObject *object) { GjsCoverage *coverage = GJS_DEBUG_COVERAGE (object); GjsCoveragePrivate *priv = (GjsCoveragePrivate *) gjs_coverage_get_instance_private(coverage); g_clear_object(&priv->context); G_OBJECT_CLASS(gjs_coverage_parent_class)->dispose(object); } static void gjs_coverage_finalize (GObject *object) { GjsCoverage *coverage = GJS_DEBUG_COVERAGE(object); GjsCoveragePrivate *priv = (GjsCoveragePrivate *) gjs_coverage_get_instance_private(coverage); g_strfreev(priv->covered_paths); G_OBJECT_CLASS(gjs_coverage_parent_class)->finalize(object); } static void gjs_coverage_class_init (GjsCoverageClass *klass) { GObjectClass *object_class = (GObjectClass *) klass; object_class->constructed = gjs_coverage_constructed; object_class->dispose = gjs_coverage_dispose; object_class->finalize = gjs_coverage_finalize; object_class->set_property = gjs_coverage_set_property; properties[PROP_COVERAGE_PATHS] = g_param_spec_boxed("coverage-paths", "Coverage Paths", "Paths (and included subdirectories) of which to perform coverage analysis", G_TYPE_STRV, (GParamFlags) (G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE)); properties[PROP_CONTEXT] = g_param_spec_object("context", "Context", "A context to gather coverage stats for", GJS_TYPE_CONTEXT, (GParamFlags) (G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE)); g_object_class_install_properties(object_class, PROP_N, properties); } /** * gjs_coverage_new: * @coverage_paths: (transfer none): A null-terminated strv of directories to generate * coverage_data for * * Returns: A #GjsDebugCoverage */ GjsCoverage * gjs_coverage_new (const char **coverage_paths, GjsContext *context) { GjsCoverage *coverage = GJS_DEBUG_COVERAGE(g_object_new(GJS_TYPE_DEBUG_COVERAGE, "coverage-paths", coverage_paths, "context", context, NULL)); return coverage; } cjs-2.8.0/cjs/importer.cpp0000664000175000017500000011100612610172032014341 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008-2010 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include #include #include #include #define MODULE_INIT_FILENAME "__init__.js" static char **gjs_search_path = NULL; typedef struct { gboolean is_root; } Importer; typedef struct { GPtrArray *elements; unsigned int index; } ImporterIterator; extern struct JSClass gjs_importer_class; GJS_DEFINE_PRIV_FROM_JS(Importer, gjs_importer_class) static JSBool define_meta_properties(JSContext *context, JSObject *module_obj, const char *full_path, const char *module_name, JSObject *parent) { gboolean parent_is_module; /* We define both __moduleName__ and __parentModule__ to null * on the root importer */ parent_is_module = parent && JS_InstanceOf(context, parent, &gjs_importer_class, NULL); gjs_debug(GJS_DEBUG_IMPORTER, "Defining parent %p of %p '%s' is mod %d", parent, module_obj, module_name ? module_name : "", parent_is_module); if (full_path != NULL) { if (!JS_DefineProperty(context, module_obj, "__file__", STRING_TO_JSVAL(JS_NewStringCopyZ(context, full_path)), NULL, NULL, /* don't set ENUMERATE since we wouldn't want to copy * this symbol to any other object for example. */ JSPROP_READONLY | JSPROP_PERMANENT)) return JS_FALSE; } if (!JS_DefineProperty(context, module_obj, "__moduleName__", parent_is_module ? STRING_TO_JSVAL(JS_NewStringCopyZ(context, module_name)) : JSVAL_NULL, NULL, NULL, /* don't set ENUMERATE since we wouldn't want to copy * this symbol to any other object for example. */ JSPROP_READONLY | JSPROP_PERMANENT)) return JS_FALSE; if (!JS_DefineProperty(context, module_obj, "__parentModule__", parent_is_module ? OBJECT_TO_JSVAL(parent) : JSVAL_NULL, NULL, NULL, /* don't set ENUMERATE since we wouldn't want to copy * this symbol to any other object for example. */ JSPROP_READONLY | JSPROP_PERMANENT)) return JS_FALSE; return JS_TRUE; } static JSBool import_directory(JSContext *context, JSObject *obj, const char *name, const char **full_paths) { JSObject *importer; gjs_debug(GJS_DEBUG_IMPORTER, "Importing directory '%s'", name); /* We define a sub-importer that has only the given directories on * its search path. gjs_define_importer() exits if it fails, so * this always succeeds. */ importer = gjs_define_importer(context, obj, name, full_paths, FALSE); if (importer == NULL) return JS_FALSE; return JS_TRUE; } static JSBool define_import(JSContext *context, JSObject *obj, JSObject *module_obj, const char *name) { if (!JS_DefineProperty(context, obj, name, OBJECT_TO_JSVAL(module_obj), NULL, NULL, GJS_MODULE_PROP_FLAGS & ~JSPROP_PERMANENT)) { gjs_debug(GJS_DEBUG_IMPORTER, "Failed to define '%s' in importer", name); return JS_FALSE; } return JS_TRUE; } /* Make the property we set in define_import permament; * we do this after the import succesfully completes. */ static JSBool seal_import(JSContext *context, JSObject *obj, const char *name) { JSBool found; unsigned attrs; if (!JS_GetPropertyAttributes(context, obj, name, &attrs, &found) || !found) { gjs_debug(GJS_DEBUG_IMPORTER, "Failed to get attributes to seal '%s' in importer", name); return JS_FALSE; } attrs |= JSPROP_PERMANENT; if (!JS_SetPropertyAttributes(context, obj, name, attrs, &found) || !found) { gjs_debug(GJS_DEBUG_IMPORTER, "Failed to set attributes to seal '%s' in importer", name); return JS_FALSE; } return JS_TRUE; } /* An import failed. Delete the property pointing to the import * from the parent namespace. In complicated situations this might * not be sufficient to get us fully back to a sane state. If: * * - We import module A * - module A imports module B * - module B imports module A, storing a reference to the current * module A module object * - module A subsequently throws an exception * * Then module B is left imported, but the imported module B has * a reference to the failed module A module object. To handle this * we could could try to track the entire "import operation" and * roll back *all* modifications made to the namespace objects. * It's not clear that the complexity would be worth the small gain * in robustness. (You can still come up with ways of defeating * the attempt to clean up.) */ static void cancel_import(JSContext *context, JSObject *obj, const char *name) { gjs_debug(GJS_DEBUG_IMPORTER, "Cleaning up from failed import of '%s'", name); if (!JS_DeleteProperty(context, obj, name)) { gjs_debug(GJS_DEBUG_IMPORTER, "Failed to delete '%s' in importer", name); } } static JSBool import_native_file(JSContext *context, JSObject *obj, const char *name) { JSObject *module_obj; JSBool retval = JS_FALSE; gjs_debug(GJS_DEBUG_IMPORTER, "Importing '%s'", name); if (!gjs_import_native_module(context, name, &module_obj)) goto out; if (!define_meta_properties(context, module_obj, NULL, name, obj)) goto out; if (JS_IsExceptionPending(context)) { /* I am not sure whether this can happen, but if it does we want to trap it. */ gjs_debug(GJS_DEBUG_IMPORTER, "Module '%s' reported an exception but gjs_import_native_module() returned TRUE", name); goto out; } if (!JS_DefineProperty(context, obj, name, OBJECT_TO_JSVAL(module_obj), NULL, NULL, GJS_MODULE_PROP_FLAGS)) goto out; retval = JS_TRUE; out: return retval; } static JSObject * create_module_object(JSContext *context) { return JS_NewObject(context, NULL, NULL, NULL); } static JSBool import_file(JSContext *context, const char *name, GFile *file, JSObject *module_obj) { JSBool ret = JS_FALSE; char *script = NULL; char *full_path = NULL; gsize script_len = 0; jsval script_retval; GError *error = NULL; JS::CompileOptions options(context); if (!(g_file_load_contents(file, NULL, &script, &script_len, NULL, &error))) { if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY) && !g_error_matches(error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY) && !g_error_matches(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) gjs_throw_g_error(context, error); else g_error_free(error); goto out; } g_assert(script != NULL); full_path = g_file_get_parse_name (file); if (!gjs_eval_with_scope(context, module_obj, script, script_len, full_path, NULL)) goto out; ret = JS_TRUE; out: g_free(script); g_free(full_path); return ret; } static gboolean is_extension_module (const gchar *path) { if (g_strstr_len (path, -1, "applets") || g_strstr_len (path, -1, "desklets") || g_strstr_len (path, -1, "extensions")) return TRUE; return FALSE; } static JSObject * load_module_init(JSContext *context, JSObject *in_object, const char *full_path) { JSObject *module_obj; JSBool found; jsid module_init_name; GFile *file; /* First we check if js module has already been loaded */ module_init_name = gjs_context_get_const_string(context, GJS_STRING_MODULE_INIT); if (!is_extension_module (full_path) && JS_HasPropertyById(context, in_object, module_init_name, &found) && found) { jsval module_obj_val; if (JS_GetPropertyById(context, in_object, module_init_name, &module_obj_val)) { return JSVAL_TO_OBJECT(module_obj_val); } } module_obj = create_module_object (context); file = g_file_new_for_commandline_arg(full_path); if (!import_file (context, "__init__", file, module_obj)) goto out; if (!JS_DefinePropertyById(context, in_object, module_init_name, OBJECT_TO_JSVAL(module_obj), NULL, NULL, GJS_MODULE_PROP_FLAGS & ~JSPROP_PERMANENT)) goto out; out: g_object_unref (file); return module_obj; } static void load_module_elements(JSContext *context, JSObject *in_object, ImporterIterator *iter, const char *init_path) { JSObject *module_obj; JSObject *jsiter; module_obj = load_module_init(context, in_object, init_path); if (module_obj != NULL) { jsid idp; jsiter = JS_NewPropertyIterator(context, module_obj); if (jsiter == NULL) { return; } if (!JS_NextProperty(context, jsiter, &idp)) { return; } while (!JSID_IS_VOID(idp)) { char *name; if (!gjs_get_string_id(context, idp, &name)) { continue; } /* Pass ownership of name */ g_ptr_array_add(iter->elements, name); if (!JS_NextProperty(context, jsiter, &idp)) { break; } } } } static JSBool import_file_on_module(JSContext *context, JSObject *obj, const char *name, GFile *file) { JSObject *module_obj; JSBool retval = JS_FALSE; char *full_path = NULL; module_obj = create_module_object (context); if (!define_import(context, obj, module_obj, name)) goto out; if (!import_file(context, name, file, module_obj)) goto out; full_path = g_file_get_parse_name (file); if (!define_meta_properties(context, module_obj, full_path, name, obj)) goto out; if (!seal_import(context, obj, name)) goto out; retval = JS_TRUE; out: if (!retval) cancel_import(context, obj, name); g_free (full_path); return retval; } static JSBool do_import(JSContext *context, JSObject *obj, Importer *priv, const char *name) { char *filename; char *full_path; char *dirname = NULL; jsval search_path_val; JSObject *search_path; JSObject *module_obj = NULL; guint32 search_path_len; guint32 i; JSBool result; GPtrArray *directories; jsid search_path_name; GFile *gfile; gboolean exists; search_path_name = gjs_context_get_const_string(context, GJS_STRING_SEARCH_PATH); if (!gjs_object_require_property(context, obj, "importer", search_path_name, &search_path_val)) { return JS_FALSE; } if (!JSVAL_IS_OBJECT(search_path_val)) { gjs_throw(context, "searchPath property on importer is not an object"); return JS_FALSE; } search_path = JSVAL_TO_OBJECT(search_path_val); if (!JS_IsArrayObject(context, search_path)) { gjs_throw(context, "searchPath property on importer is not an array"); return JS_FALSE; } if (!JS_GetArrayLength(context, search_path, &search_path_len)) { gjs_throw(context, "searchPath array has no length"); return JS_FALSE; } result = JS_FALSE; filename = g_strdup_printf("%s.js", name); full_path = NULL; directories = NULL; /* First try importing an internal module like byteArray */ if (priv->is_root && gjs_is_registered_native_module(context, obj, name) && import_native_file(context, obj, name)) { gjs_debug(GJS_DEBUG_IMPORTER, "successfully imported module '%s'", name); result = JS_TRUE; goto out; } for (i = 0; i < search_path_len; ++i) { jsval elem; elem = JSVAL_VOID; if (!JS_GetElement(context, search_path, i, &elem)) { /* this means there was an exception, while elem == JSVAL_VOID * means no element found */ goto out; } if (JSVAL_IS_VOID(elem)) continue; if (!JSVAL_IS_STRING(elem)) { gjs_throw(context, "importer searchPath contains non-string"); goto out; } g_free(dirname); dirname = NULL; if (!gjs_string_to_utf8(context, elem, &dirname)) goto out; /* Error message already set */ /* Ignore empty path elements */ if (dirname[0] == '\0') continue; /* Try importing __init__.js and loading the symbol from it */ if (full_path) g_free(full_path); full_path = g_build_filename(dirname, MODULE_INIT_FILENAME, NULL); module_obj = load_module_init(context, obj, full_path); if (module_obj != NULL) { jsval obj_val; if (JS_GetProperty(context, module_obj, name, &obj_val)) { if (!JSVAL_IS_VOID(obj_val) && JS_DefineProperty(context, obj, name, obj_val, NULL, NULL, GJS_MODULE_PROP_FLAGS & ~JSPROP_PERMANENT)) { result = JS_TRUE; goto out; } } } /* Second try importing a directory (a sub-importer) */ if (full_path) g_free(full_path); full_path = g_build_filename(dirname, name, NULL); gfile = g_file_new_for_commandline_arg(full_path); if (g_file_query_file_type(gfile, (GFileQueryInfoFlags) 0, NULL) == G_FILE_TYPE_DIRECTORY) { gjs_debug(GJS_DEBUG_IMPORTER, "Adding directory '%s' to child importer '%s'", full_path, name); if (directories == NULL) { directories = g_ptr_array_new(); } g_ptr_array_add(directories, full_path); /* don't free it twice - pass ownership to ptr array */ full_path = NULL; } g_object_unref(gfile); /* If we just added to directories, we know we don't need to * check for a file. If we added to directories on an earlier * iteration, we want to ignore any files later in the * path. So, always skip the rest of the loop block if we have * directories. */ if (directories != NULL) { continue; } /* Third, if it's not a directory, try importing a file */ g_free(full_path); full_path = g_build_filename(dirname, filename, NULL); gfile = g_file_new_for_commandline_arg(full_path); exists = g_file_query_exists(gfile, NULL); if (!exists) { gjs_debug(GJS_DEBUG_IMPORTER, "JS import '%s' not found in %s", name, dirname); g_object_unref(gfile); continue; } if (import_file_on_module (context, obj, name, gfile)) { gjs_debug(GJS_DEBUG_IMPORTER, "successfully imported module '%s'", name); result = JS_TRUE; } g_object_unref(gfile); /* Don't keep searching path if we fail to load the file for * reasons other than it doesn't exist... i.e. broken files * block searching for nonbroken ones */ goto out; } if (directories != NULL) { /* NULL-terminate the char** */ g_ptr_array_add(directories, NULL); if (import_directory(context, obj, name, (const char**) directories->pdata)) { gjs_debug(GJS_DEBUG_IMPORTER, "successfully imported directory '%s'", name); result = JS_TRUE; } } out: if (directories != NULL) { char **str_array; /* NULL-terminate the char** * (maybe for a second time, but doesn't matter) */ g_ptr_array_add(directories, NULL); str_array = (char**) directories->pdata; g_ptr_array_free(directories, FALSE); g_strfreev(str_array); } g_free(full_path); g_free(filename); g_free(dirname); if (!result && !JS_IsExceptionPending(context)) { /* If no exception occurred, the problem is just that we got to the * end of the path. Be sure an exception is set. */ gjs_throw(context, "No JS module '%s' found in search path", name); } return result; } static ImporterIterator * importer_iterator_new(void) { ImporterIterator *iter; iter = g_slice_new0(ImporterIterator); iter->elements = g_ptr_array_new(); iter->index = 0; return iter; } static void importer_iterator_free(ImporterIterator *iter) { g_ptr_array_foreach(iter->elements, (GFunc)g_free, NULL); g_ptr_array_free(iter->elements, TRUE); g_slice_free(ImporterIterator, iter); } /* * Like JSEnumerateOp, but enum provides contextual information as follows: * * JSENUMERATE_INIT: allocate private enum struct in state_p, return number * of elements in *id_p * JSENUMERATE_NEXT: return next property id in *id_p, and if no new property * free state_p and set to JSVAL_NULL * JSENUMERATE_DESTROY : destroy state_p * * Note that in a for ... in loop, this will be called first on the object, * then on its prototype. * */ static JSBool importer_new_enumerate(JSContext *context, JSObject **object, JSIterateOp enum_op, jsval *state_p, jsid *id_p) { ImporterIterator *iter; switch (enum_op) { case JSENUMERATE_INIT_ALL: case JSENUMERATE_INIT: { Importer *priv; JSObject *search_path; jsval search_path_val; guint32 search_path_len; guint32 i; jsid search_path_name; if (state_p) *state_p = JSVAL_NULL; if (id_p) *id_p = INT_TO_JSID(0); priv = priv_from_js(context, *object); if (!priv) /* we are enumerating the prototype properties */ return JS_TRUE; search_path_name = gjs_context_get_const_string(context, GJS_STRING_SEARCH_PATH); if (!gjs_object_require_property(context, *object, "importer", search_path_name, &search_path_val)) return JS_FALSE; if (!JSVAL_IS_OBJECT(search_path_val)) { gjs_throw(context, "searchPath property on importer is not an object"); return JS_FALSE; } search_path = JSVAL_TO_OBJECT(search_path_val); if (!JS_IsArrayObject(context, search_path)) { gjs_throw(context, "searchPath property on importer is not an array"); return JS_FALSE; } if (!JS_GetArrayLength(context, search_path, &search_path_len)) { gjs_throw(context, "searchPath array has no length"); return JS_FALSE; } iter = importer_iterator_new(); for (i = 0; i < search_path_len; ++i) { char *dirname = NULL; char *init_path; const char *filename; jsval elem; GDir *dir = NULL; elem = JSVAL_VOID; if (!JS_GetElement(context, search_path, i, &elem)) { /* this means there was an exception, while elem == JSVAL_VOID * means no element found */ importer_iterator_free(iter); return JS_FALSE; } if (JSVAL_IS_VOID(elem)) continue; if (!JSVAL_IS_STRING(elem)) { gjs_throw(context, "importer searchPath contains non-string"); importer_iterator_free(iter); return JS_FALSE; } if (!gjs_string_to_utf8(context, elem, &dirname)) { importer_iterator_free(iter); return JS_FALSE; /* Error message already set */ } init_path = g_build_filename(dirname, MODULE_INIT_FILENAME, NULL); load_module_elements(context, *object, iter, init_path); g_free(init_path); dir = g_dir_open(dirname, 0, NULL); if (!dir) { g_free(dirname); continue; } while ((filename = g_dir_read_name(dir))) { char *full_path; /* skip hidden files and directories (.svn, .git, ...) */ if (filename[0] == '.') continue; /* skip module init file */ if (strcmp(filename, MODULE_INIT_FILENAME) == 0) continue; full_path = g_build_filename(dirname, filename, NULL); if (g_file_test(full_path, G_FILE_TEST_IS_DIR)) { g_ptr_array_add(iter->elements, g_strdup(filename)); } else { if (g_str_has_suffix(filename, "." G_MODULE_SUFFIX) || g_str_has_suffix(filename, ".js")) { g_ptr_array_add(iter->elements, g_strndup(filename, strlen(filename) - 3)); } } g_free(full_path); } g_dir_close(dir); g_free(dirname); } if (state_p) *state_p = PRIVATE_TO_JSVAL(iter); if (id_p) *id_p = INT_TO_JSID(iter->elements->len); break; } case JSENUMERATE_NEXT: { jsval element_val; if (!state_p) { gjs_throw(context, "Enumerate with no iterator set?"); return JS_FALSE; } if (JSVAL_IS_NULL(*state_p)) /* Iterating prototype */ return JS_TRUE; iter = (ImporterIterator*) JSVAL_TO_PRIVATE(*state_p); if (iter->index < iter->elements->len) { if (!gjs_string_from_utf8(context, (const char*) g_ptr_array_index(iter->elements, iter->index++), -1, &element_val)) return JS_FALSE; if (!JS_ValueToId(context, element_val, id_p)) return JS_FALSE; break; } /* else fall through to destroying the iterator */ } case JSENUMERATE_DESTROY: { if (state_p && !JSVAL_IS_NULL(*state_p)) { iter = (ImporterIterator*) JSVAL_TO_PRIVATE(*state_p); importer_iterator_free(iter); *state_p = JSVAL_NULL; } } } return JS_TRUE; } /* * Like JSResolveOp, but flags provide contextual information as follows: * * JSRESOLVE_QUALIFIED a qualified property id: obj.id or obj[id], not id * JSRESOLVE_ASSIGNING obj[id] is on the left-hand side of an assignment * JSRESOLVE_DETECTING 'if (o.p)...' or similar detection opcode sequence * JSRESOLVE_DECLARING var, const, or function prolog declaration opcode * JSRESOLVE_CLASSNAME class name used when constructing * * The *objp out parameter, on success, should be null to indicate that id * was not resolved; and non-null, referring to obj or one of its prototypes, * if id was resolved. */ static JSBool importer_new_resolve(JSContext *context, JSObject **obj, jsid *id, unsigned flags, JSObject **objp) { Importer *priv; char *name; JSBool ret = JS_TRUE; jsid module_init_name; *objp = NULL; module_init_name = gjs_context_get_const_string(context, GJS_STRING_MODULE_INIT); if (*id == module_init_name) return JS_TRUE; if (!gjs_get_string_id(context, *id, &name)) return JS_FALSE; /* let Object.prototype resolve these */ if (strcmp(name, "valueOf") == 0 || strcmp(name, "toString") == 0 || strcmp(name, "__iterator__") == 0) goto out; priv = priv_from_js(context, *obj); gjs_debug_jsprop(GJS_DEBUG_IMPORTER, "Resolve prop '%s' hook obj %p priv %p", name, *obj, priv); if (priv == NULL) /* we are the prototype, or have the wrong class */ goto out; JS_BeginRequest(context); if (do_import(context, *obj, priv, name)) { *objp = *obj; } else { ret = JS_FALSE; } JS_EndRequest(context); out: g_free(name); return ret; } GJS_NATIVE_CONSTRUCTOR_DEFINE_ABSTRACT(importer) static JSBool gjs_importer_add_subimporter(JSContext *context, unsigned argc, jsval *vp) { if (argc != 2) { gjs_throw(context, "Must pass two arguments to addSubImporter()"); return JS_FALSE; } JSObject *importer; jsval *argv = JS_ARGV(context, vp); char *name; char *path; char *search_path[2] = { 0, 0 }; JS_BeginRequest(context); importer = JS_THIS_OBJECT(context, vp); gjs_string_to_utf8(context, argv[0], &name); gjs_string_to_utf8(context, argv[1], &path); search_path[0] = path; gjs_define_importer(context, importer, name, (const char **)search_path, FALSE); JS_EndRequest(context); JS_SET_RVAL(context, vp, JSVAL_VOID); return JS_TRUE; } static void importer_finalize(JSFreeOp *fop, JSObject *obj) { Importer *priv; priv = (Importer*) JS_GetPrivate(obj); gjs_debug_lifecycle(GJS_DEBUG_IMPORTER, "finalize, obj %p priv %p", obj, priv); if (priv == NULL) return; /* we are the prototype, not a real instance */ GJS_DEC_COUNTER(importer); g_slice_free(Importer, priv); } /* The bizarre thing about this vtable is that it applies to both * instances of the object, and to the prototype that instances of the * class have. */ struct JSClass gjs_importer_class = { "GjsFileImporter", JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE, JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, (JSEnumerateOp) importer_new_enumerate, /* needs cast since it's the new enumerate signature */ (JSResolveOp) importer_new_resolve, /* needs cast since it's the new resolve signature */ JS_ConvertStub, importer_finalize, JSCLASS_NO_OPTIONAL_MEMBERS }; JSPropertySpec gjs_importer_proto_props[] = { { NULL } }; JSFunctionSpec gjs_importer_proto_funcs[] = { { NULL } }; JSFunctionSpec gjs_global_importer_funcs[] = { { "addSubImporter", JSOP_WRAPPER(gjs_importer_add_subimporter), 0, 0 }, { NULL } }; static JSObject* importer_new(JSContext *context, gboolean is_root) { JSObject *importer; Importer *priv; JSObject *global; JSBool found; global = gjs_get_import_global(context); if (!JS_HasProperty(context, global, gjs_importer_class.name, &found)) g_error("HasProperty call failed creating importer class"); if (!found) { JSObject *prototype; prototype = JS_InitClass(context, global, /* parent prototype JSObject* for * prototype; NULL for * Object.prototype */ NULL, &gjs_importer_class, /* constructor for instances (NULL for * none - just name the prototype like * Math - rarely correct) */ gjs_importer_constructor, /* number of constructor args */ 0, /* props of prototype */ &gjs_importer_proto_props[0], /* funcs of prototype */ &gjs_importer_proto_funcs[0], /* props of constructor, MyConstructor.myprop */ NULL, /* funcs of constructor, MyConstructor.myfunc() */ NULL); if (prototype == NULL) g_error("Can't init class %s", gjs_importer_class.name); gjs_debug(GJS_DEBUG_IMPORTER, "Initialized class %s prototype %p", gjs_importer_class.name, prototype); } importer = JS_NewObject(context, &gjs_importer_class, NULL, global); if (importer == NULL) g_error("No memory to create importer importer"); priv = g_slice_new0(Importer); priv->is_root = is_root; GJS_INC_COUNTER(importer); g_assert(priv_from_js(context, importer) == NULL); JS_SetPrivate(importer, priv); gjs_debug_lifecycle(GJS_DEBUG_IMPORTER, "importer constructor, obj %p priv %p", importer, priv); return importer; } static G_CONST_RETURN char * G_CONST_RETURN * gjs_get_search_path(void) { char **search_path; /* not thread safe */ if (!gjs_search_path) { G_CONST_RETURN gchar* G_CONST_RETURN * system_data_dirs; const char *envstr; GPtrArray *path; gsize i; path = g_ptr_array_new(); /* in order of priority */ /* $GJS_PATH */ envstr = g_getenv("GJS_PATH"); if (envstr) { char **dirs, **d; dirs = g_strsplit(envstr, G_SEARCHPATH_SEPARATOR_S, 0); for (d = dirs; *d != NULL; d++) g_ptr_array_add(path, *d); /* we assume the array and strings are allocated separately */ g_free(dirs); } /* ${datadir}/share/gjs-1.0 */ g_ptr_array_add(path, g_strdup("resource:///org/gnome/gjs/modules/")); g_ptr_array_add(path, NULL); search_path = (char**)g_ptr_array_free(path, FALSE); gjs_search_path = search_path; } else { search_path = gjs_search_path; } return (G_CONST_RETURN char * G_CONST_RETURN *)search_path; } static JSObject* gjs_create_importer(JSContext *context, const char *importer_name, const char **initial_search_path, gboolean add_standard_search_path, gboolean is_root, JSObject *in_object) { JSObject *importer; char **paths[2] = {0}; char **search_path; paths[0] = (char**)initial_search_path; if (add_standard_search_path) { /* Stick the "standard" shared search path after the provided one. */ paths[1] = (char**)gjs_get_search_path(); } search_path = gjs_g_strv_concat(paths, 2); importer = importer_new(context, is_root); /* API users can replace this property from JS, is the idea */ if (!gjs_define_string_array(context, importer, "searchPath", -1, (const char **)search_path, /* settable (no READONLY) but not deleteable (PERMANENT) */ JSPROP_PERMANENT | JSPROP_ENUMERATE)) g_error("no memory to define importer search path prop"); g_strfreev(search_path); if (!define_meta_properties(context, importer, NULL, importer_name, in_object)) g_error("failed to define meta properties on importer"); return importer; } JSObject* gjs_define_importer(JSContext *context, JSObject *in_object, const char *importer_name, const char **initial_search_path, gboolean add_standard_search_path) { JSObject *importer; importer = gjs_create_importer(context, importer_name, initial_search_path, add_standard_search_path, FALSE, in_object); if (!JS_DefineProperty(context, in_object, importer_name, OBJECT_TO_JSVAL(importer), NULL, NULL, GJS_MODULE_PROP_FLAGS)) g_error("no memory to define importer property"); gjs_debug(GJS_DEBUG_IMPORTER, "Defined importer '%s' %p in %p", importer_name, importer, in_object); return importer; } /* If this were called twice for the same runtime with different args it * would basically be a bug, but checking for that is a lot of code so * we just ignore all calls after the first and hope the args are the same. */ JSBool gjs_create_root_importer(JSContext *context, const char **initial_search_path, gboolean add_standard_search_path) { JSObject *importer; jsval v; JS_BeginRequest(context); v = gjs_get_global_slot(context, GJS_GLOBAL_SLOT_IMPORTS); if (G_UNLIKELY (!JSVAL_IS_VOID(v))) { gjs_debug(GJS_DEBUG_IMPORTER, "Someone else already created root importer, ignoring second request"); JS_EndRequest(context); return JS_TRUE; } importer = gjs_create_importer(context, "imports", initial_search_path, add_standard_search_path, TRUE, NULL); JS_DefineFunctions(context, importer, &gjs_global_importer_funcs[0]); gjs_set_global_slot(context, GJS_GLOBAL_SLOT_IMPORTS, OBJECT_TO_JSVAL(importer)); JS_EndRequest(context); return JS_TRUE; } JSBool gjs_define_root_importer(JSContext *context, JSObject *in_object) { jsval importer; JSBool success; jsid imports_name; success = JS_FALSE; JS_BeginRequest(context); importer = gjs_get_global_slot(context, GJS_GLOBAL_SLOT_IMPORTS); imports_name = gjs_context_get_const_string(context, GJS_STRING_IMPORTS); if (!JS_DefinePropertyById(context, in_object, imports_name, importer, NULL, NULL, GJS_MODULE_PROP_FLAGS)) { gjs_debug(GJS_DEBUG_IMPORTER, "DefineProperty imports on %p failed", in_object); goto fail; } success = JS_TRUE; fail: JS_EndRequest(context); return success; } cjs-2.8.0/cjs/jsapi-util-string.cpp0000664000175000017500000001742512610172032016077 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include "jsapi-util.h" #include "compat.h" gboolean gjs_string_to_utf8 (JSContext *context, const jsval value, char **utf8_string_p) { JSString *str; gsize len; char *bytes; JS_BeginRequest(context); if (!JSVAL_IS_STRING(value)) { gjs_throw(context, "Value is not a string, cannot convert to UTF-8"); JS_EndRequest(context); return JS_FALSE; } str = JSVAL_TO_STRING(value); len = JS_GetStringEncodingLength(context, str); if (len == (gsize)(-1)) { JS_EndRequest(context); return JS_FALSE; } if (utf8_string_p) { bytes = JS_EncodeStringToUTF8(context, str); *utf8_string_p = bytes; } JS_EndRequest(context); return JS_TRUE; } JSBool gjs_string_from_utf8(JSContext *context, const char *utf8_string, gssize n_bytes, jsval *value_p) { jschar *u16_string; glong u16_string_length; JSString *str; GError *error; /* intentionally using n_bytes even though glib api suggests n_chars; with * n_chars (from g_utf8_strlen()) the result appears truncated */ error = NULL; u16_string = g_utf8_to_utf16(utf8_string, n_bytes, NULL, &u16_string_length, &error); if (!u16_string) { gjs_throw(context, "Failed to convert UTF-8 string to " "JS string: %s", error->message); g_error_free(error); return JS_FALSE; } JS_BeginRequest(context); if (g_mem_is_system_malloc()) { /* Avoid a copy - assumes that g_malloc == js_malloc == malloc */ str = JS_NewUCString(context, u16_string, u16_string_length); } else { str = JS_NewUCStringCopyN(context, (jschar*)u16_string, u16_string_length); g_free(u16_string); } if (str && value_p) *value_p = STRING_TO_JSVAL(str); JS_EndRequest(context); return str != NULL; } gboolean gjs_string_to_filename(JSContext *context, const jsval filename_val, char **filename_string_p) { GError *error; gchar *tmp, *filename_string; /* gjs_string_to_filename verifies that filename_val is a string */ if (!gjs_string_to_utf8(context, filename_val, &tmp)) { /* exception already set */ return JS_FALSE; } error = NULL; 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); return TRUE; } JSBool gjs_string_from_filename(JSContext *context, const char *filename_string, gssize n_bytes, jsval *value_p) { gsize written; GError *error; gchar *utf8_string; error = NULL; utf8_string = g_filename_to_utf8(filename_string, n_bytes, NULL, &written, &error); if (error) { gjs_throw(context, "Could not convert UTF-8 string '%s' to a filename: '%s'", filename_string, error->message); g_error_free(error); g_free(utf8_string); return JS_FALSE; } if (!gjs_string_from_utf8(context, utf8_string, written, value_p)) return JS_FALSE; g_free(utf8_string); return JS_TRUE; } /** * gjs_string_get_uint16_data: * @context: js context * @value: a jsval * @data_p: address to return allocated data buffer * @len_p: address to return length of data (number of 16-bit integers) * * Get the binary data (as a sequence of 16-bit integers) in the JSString * contained in @value. * Throws a JS exception if value is not a string. * * Returns: JS_FALSE if exception thrown **/ JSBool gjs_string_get_uint16_data(JSContext *context, jsval value, guint16 **data_p, gsize *len_p) { const jschar *js_data; JSBool retval = JS_FALSE; JS_BeginRequest(context); if (!JSVAL_IS_STRING(value)) { gjs_throw(context, "Value is not a string, can't return binary data from it"); goto out; } js_data = JS_GetStringCharsAndLength(context, JSVAL_TO_STRING(value), len_p); if (js_data == NULL) goto out; *data_p = (guint16*) g_memdup(js_data, sizeof(*js_data)*(*len_p)); retval = JS_TRUE; out: JS_EndRequest(context); return retval; } /** * gjs_get_string_id: * @context: a #JSContext * @id: a jsid that is an object hash key (could be an int or string) * @name_p place to store ASCII string version of key * * If the id is not a string ID, return false and set *name_p to %NULL. * Otherwise, return true and fill in *name_p with ASCII name of id. * * Returns: true if *name_p is non-%NULL **/ JSBool gjs_get_string_id (JSContext *context, jsid id, char **name_p) { jsval id_val; if (!JS_IdToValue(context, id, &id_val)) return JS_FALSE; if (JSVAL_IS_STRING(id_val)) { return gjs_string_to_utf8(context, id_val, name_p); } else { *name_p = NULL; return JS_FALSE; } } /** * gjs_unichar_from_string: * @string: A string * @result: (out): A unicode character * * If successful, @result is assigned the Unicode codepoint * corresponding to the first full character in @string. This * function handles characters outside the BMP. * * If @string is empty, @result will be 0. An exception will * be thrown if @string can not be represented as UTF-8. */ gboolean gjs_unichar_from_string (JSContext *context, jsval value, gunichar *result) { char *utf8_str; if (gjs_string_to_utf8(context, value, &utf8_str)) { *result = g_utf8_get_char(utf8_str); g_free(utf8_str); return TRUE; } return FALSE; } jsid gjs_intern_string_to_id (JSContext *context, const char *string) { JSString *str; jsid id; JS_BeginRequest(context); str = JS_InternString(context, string); id = INTERNED_STRING_TO_JSID(context, str); JS_EndRequest(context); return id; } cjs-2.8.0/cjs/jsapi-util-array.cpp0000664000175000017500000001726412610172032015710 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "jsapi-util.h" #include "compat.h" /* Maximum number of elements allowed in a GArray of rooted jsvals. * We pre-alloc that amount and then never allow the array to grow, * or we'd have invalid memory rooted if the internals of GArray decide * to move the contents to a new memory area */ #define ARRAY_MAX_LEN 32 /** * gjs_rooted_array_new: * * Creates an opaque data type that holds jsvals and keeps * their location (NOT their value) GC-rooted. * * Returns: an opaque object prepared to hold GC root locations. **/ GjsRootedArray* gjs_rooted_array_new() { GArray *array; /* we prealloc ARRAY_MAX_LEN to avoid realloc */ array = g_array_sized_new(FALSE, /* zero-terminated */ FALSE, /* clear */ sizeof(jsval), /* element size */ ARRAY_MAX_LEN); /* reserved size */ return (GjsRootedArray*) array; } /* typesafe wrapper */ static void add_root_jsval(JSContext *context, jsval *value_p) { JS_BeginRequest(context); JS_AddValueRoot(context, value_p); JS_EndRequest(context); } /* typesafe wrapper */ static void remove_root_jsval(JSContext *context, jsval *value_p) { JS_BeginRequest(context); JS_RemoveValueRoot(context, value_p); JS_EndRequest(context); } /** * gjs_rooted_array_append: * @context: a #JSContext * @array: a #GjsRootedArray created by gjs_rooted_array_new() * @value: a jsval * * Appends @jsval to @array, calling JS_AddValueRoot on the location where it's stored. * **/ void gjs_rooted_array_append(JSContext *context, GjsRootedArray *array, jsval value) { GArray *garray; g_return_if_fail(context != NULL); g_return_if_fail(array != NULL); garray = (GArray*) array; if (garray->len >= ARRAY_MAX_LEN) { gjs_throw(context, "Maximum number of values (%d)", ARRAY_MAX_LEN); return; } g_array_append_val(garray, value); add_root_jsval(context, & g_array_index(garray, jsval, garray->len - 1)); } /** * gjs_rooted_array_get: * @context: a #JSContext * @array: an array * @i: element to return * Returns: value of an element */ jsval gjs_rooted_array_get(JSContext *context, GjsRootedArray *array, int i) { GArray *garray; g_return_val_if_fail(context != NULL, JSVAL_VOID); g_return_val_if_fail(array != NULL, JSVAL_VOID); garray = (GArray*) array; if (i < 0 || i >= (int) garray->len) { gjs_throw(context, "Index %d is out of range", i); return JSVAL_VOID; } return g_array_index(garray, jsval, i); } /** * gjs_rooted_array_get_data: * * @context: a #JSContext * @array: an array * Returns: the rooted jsval locations in the array */ jsval* gjs_rooted_array_get_data(JSContext *context, GjsRootedArray *array) { GArray *garray; g_return_val_if_fail(context != NULL, NULL); g_return_val_if_fail(array != NULL, NULL); garray = (GArray*) array; return (jsval*) garray->data; } /** * gjs_rooted_array_get_length: * * @context: a #JSContext * @array: an array * Returns: number of jsval in the rooted array */ int gjs_rooted_array_get_length (JSContext *context, GjsRootedArray *array) { GArray *garray; g_return_val_if_fail(context != NULL, 0); g_return_val_if_fail(array != NULL, 0); garray = (GArray*) array; return garray->len; } /** * gjs_root_value_locations: * @context: a #JSContext * @locations: contiguous locations in memory that store jsvals (must be initialized) * @n_locations: the number of locations to root * * Calls JS_AddValueRoot() on each address in @locations. * **/ void gjs_root_value_locations(JSContext *context, jsval *locations, int n_locations) { int i; g_return_if_fail(context != NULL); g_return_if_fail(locations != NULL); g_return_if_fail(n_locations >= 0); JS_BeginRequest(context); for (i = 0; i < n_locations; i++) { add_root_jsval(context, ((jsval*)locations) + i); } JS_EndRequest(context); } /** * gjs_unroot_value_locations: * @context: a #JSContext * @locations: contiguous locations in memory that store jsvals and have been added as GC roots * @n_locations: the number of locations to unroot * * Calls JS_RemoveValueRoot() on each address in @locations. * **/ void gjs_unroot_value_locations(JSContext *context, jsval *locations, int n_locations) { int i; g_return_if_fail(context != NULL); g_return_if_fail(locations != NULL); g_return_if_fail(n_locations >= 0); JS_BeginRequest(context); for (i = 0; i < n_locations; i++) { remove_root_jsval(context, ((jsval*)locations) + i); } JS_EndRequest(context); } /** * gjs_set_values: * @context: a #JSContext * @locations: array of jsval * @n_locations: the number of elements to set * @initializer: what to set each element to * * Assigns initializer to each member of the given array. * **/ void gjs_set_values(JSContext *context, jsval *locations, int n_locations, jsval initializer) { int i; g_return_if_fail(context != NULL); g_return_if_fail(locations != NULL); g_return_if_fail(n_locations >= 0); for (i = 0; i < n_locations; i++) { locations[i] = initializer; } } /** * gjs_rooted_array_free: * @context: a #JSContext * @array: a #GjsRootedArray created with gjs_rooted_array_new() * @free_segment: whether or not to free and unroot the internal jsval array * * Frees the memory allocated for the #GjsRootedArray. If @free_segment is * %TRUE the internal memory block allocated for the jsval array will * be freed and unrooted also. * * Returns: the jsval array if it was not freed **/ jsval* gjs_rooted_array_free(JSContext *context, GjsRootedArray *array, gboolean free_segment) { GArray *garray; g_return_val_if_fail(context != NULL, NULL); g_return_val_if_fail(array != NULL, NULL); garray = (GArray*) array; if (free_segment) gjs_unroot_value_locations(context, (jsval*) garray->data, garray->len); return (jsval*) g_array_free(garray, free_segment); } cjs-2.8.0/cjs/native.cpp0000664000175000017500000000632512610172032013775 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008-2010 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include "native.h" #include "compat.h" #include "jsapi-util.h" static GHashTable *modules = NULL; void gjs_register_native_module (const char *module_id, GjsDefineModuleFunc func) { if (modules == NULL) modules = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); if (g_hash_table_lookup(modules, module_id) != NULL) { g_warning("A second native module tried to register the same id '%s'", module_id); return; } g_hash_table_replace(modules, g_strdup(module_id), (void*) func); gjs_debug(GJS_DEBUG_NATIVE, "Registered native JS module '%s'", module_id); } /** * gjs_is_registered_native_module: * @context: * @parent: the parent object defining the namespace * @name: name of the module * * Checks if a native module corresponding to @name has already * been registered. This is used to check to see if a name is a * builtin module without starting to try and load it. */ gboolean gjs_is_registered_native_module(JSContext *context, JSObject *parent, const char *name) { if (modules == NULL) return FALSE; return g_hash_table_lookup(modules, name) != NULL; } /** * gjs_import_native_module: * @context: * @module_obj: * * Return a native module that's been preloaded. */ JSBool gjs_import_native_module(JSContext *context, const char *name, JSObject **module_out) { GjsDefineModuleFunc func; gjs_debug(GJS_DEBUG_NATIVE, "Defining native module '%s'", name); if (modules != NULL) func = (GjsDefineModuleFunc) g_hash_table_lookup(modules, name); else func = NULL; if (!func) { gjs_throw(context, "No native module '%s' has registered itself", name); return JS_FALSE; } return func (context, module_out); } cjs-2.8.0/cjs/mem.h0000664000175000017500000000512212610172032012724 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_MEM_H__ #define __GJS_MEM_H__ #if !defined (__GJS_GJS_MODULE_H__) && !defined (GJS_COMPILATION) #error "Only can be included directly." #endif #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS typedef struct { unsigned int value; const char *name; } GjsMemCounter; #define GJS_DECLARE_COUNTER(name) \ extern GjsMemCounter gjs_counter_ ## name ; GJS_DECLARE_COUNTER(everything) GJS_DECLARE_COUNTER(boxed) GJS_DECLARE_COUNTER(gerror) GJS_DECLARE_COUNTER(closure) GJS_DECLARE_COUNTER(database) GJS_DECLARE_COUNTER(function) GJS_DECLARE_COUNTER(fundamental) GJS_DECLARE_COUNTER(importer) GJS_DECLARE_COUNTER(ns) GJS_DECLARE_COUNTER(object) GJS_DECLARE_COUNTER(param) GJS_DECLARE_COUNTER(repo) GJS_DECLARE_COUNTER(resultset) GJS_DECLARE_COUNTER(weakhash) GJS_DECLARE_COUNTER(interface) #define GJS_INC_COUNTER(name) \ do { \ gjs_counter_everything.value += 1; \ gjs_counter_ ## name .value += 1; \ } while (0) #define GJS_DEC_COUNTER(name) \ do { \ gjs_counter_everything.value -= 1; \ gjs_counter_ ## name .value -= 1; \ } while (0) #define GJS_GET_COUNTER(name) \ (gjs_counter_ ## name .value) void gjs_memory_report(const char *where, gboolean die_if_leaks); G_END_DECLS #endif /* __GJS_MEM_H__ */ cjs-2.8.0/cjs/jsapi-util-error.cpp0000664000175000017500000001254412610172032015717 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "jsapi-util.h" #include "compat.h" #include "gi/gerror.h" #include #include /* * See: * https://bugzilla.mozilla.org/show_bug.cgi?id=166436 * https://bugzilla.mozilla.org/show_bug.cgi?id=215173 * * Very surprisingly, jsapi.h lacks any way to "throw new Error()" * * So here is an awful hack inspired by * http://egachine.berlios.de/embedding-sm-best-practice/embedding-sm-best-practice.html#error-handling */ static void gjs_throw_valist(JSContext *context, const char *error_class, const char *format, va_list args) { char *s; JSBool result; jsval v_constructor, v_message; JSObject *err_obj; s = g_strdup_vprintf(format, args); JSAutoCompartment compartment(context, JS_GetGlobalObject(context)); JS_BeginRequest(context); if (JS_IsExceptionPending(context)) { /* Often it's unclear whether a given jsapi.h function * will throw an exception, so we will throw ourselves * "just in case"; in those cases, we don't want to * overwrite an exception that already exists. * (Do log in case our second exception adds more info, * but don't log as topic ERROR because if the exception is * caught we don't want an ERROR in the logs.) */ gjs_debug(GJS_DEBUG_CONTEXT, "Ignoring second exception: '%s'", s); g_free(s); JS_EndRequest(context); return; } result = JS_FALSE; if (!gjs_string_from_utf8(context, s, -1, &v_message)) { JS_ReportError(context, "Failed to copy exception string"); goto out; } if (!JS_GetProperty(context, JS_GetGlobalObject(context), error_class, &v_constructor) || !JSVAL_IS_OBJECT(v_constructor)) { JS_ReportError(context, "??? Missing Error constructor in global object?"); goto out; } /* throw new Error(message) */ err_obj = JS_New(context, JSVAL_TO_OBJECT(v_constructor), 1, &v_message); JS_SetPendingException(context, OBJECT_TO_JSVAL(err_obj)); result = JS_TRUE; out: if (!result) { /* try just reporting it to error handler? should not * happen though pretty much */ JS_ReportError(context, "Failed to throw exception '%s'", s); } g_free(s); JS_EndRequest(context); } /* Throws an exception, like "throw new Error(message)" * * If an exception is already set in the context, this will * NOT overwrite it. That's an important semantic since * we want the "root cause" exception. To overwrite, * use JS_ClearPendingException() first. */ void gjs_throw(JSContext *context, const char *format, ...) { va_list args; va_start(args, format); gjs_throw_valist(context, "Error", format, args); va_end(args); } /* * Like gjs_throw, but allows to customize the error * class. Mainly used for throwing TypeError instead of * error. */ void gjs_throw_custom(JSContext *context, const char *error_class, const char *format, ...) { va_list args; va_start(args, format); gjs_throw_valist(context, error_class, format, args); va_end(args); } /** * gjs_throw_literal: * * Similar to gjs_throw(), but does not treat its argument as * a format string. */ void gjs_throw_literal(JSContext *context, const char *string) { gjs_throw(context, "%s", string); } /** * gjs_throw_g_error: * * Convert a GError into a JavaScript Exception, and * frees the GError. Differently from gjs_throw(), it * will overwrite an existing exception, as it is used * to report errors from C functions. */ void gjs_throw_g_error (JSContext *context, GError *error) { JSObject *err_obj; if (error == NULL) return; JS_BeginRequest(context); err_obj = gjs_error_from_gerror(context, error, TRUE); g_error_free (error); if (err_obj) JS_SetPendingException(context, OBJECT_TO_JSVAL(err_obj)); JS_EndRequest(context); } cjs-2.8.0/cjs/gi.h0000664000175000017500000000313312610172032012545 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_GI_H__ #define __GJS_GI_H__ #include #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS JSBool gjs_define_gi_stuff (JSContext *context, JSObject **module_out); JSBool gjs_define_private_gi_stuff (JSContext *context, JSObject **module_out); G_END_DECLS #endif /* __GJS_GI_H__ */ cjs-2.8.0/cjs/jsapi-util.cpp0000664000175000017500000011713612610172032014573 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * Copyright (c) 2009 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include #include "jsapi-util.h" #include "compat.h" #include "context-private.h" #include "jsapi-private.h" #include #include #include static GMutex gc_lock; GQuark gjs_util_error_quark (void) { return g_quark_from_static_string ("gjs-util-error-quark"); } /** * gjs_get_import_global: * @context: a #JSContext * * Gets the "import global" for the context's runtime. The import * global object is the global object for the context. It is used * as the root object for the scope of modules loaded by GJS in this * runtime, and should also be used as the globals 'obj' argument passed * to JS_InitClass() and the parent argument passed to JS_ConstructObject() * when creating a native classes that are shared between all contexts using * the runtime. (The standard JS classes are not shared, but we share * classes such as GObject proxy classes since objects of these classes can * easily migrate between contexts and having different classes depending * on the context where they were first accessed would be confusing.) * * Return value: the "import global" for the context's * runtime. Will never return %NULL while GJS has an active context * for the runtime. */ JSObject* gjs_get_import_global(JSContext *context) { return JS_GetGlobalObject(context); } static JSClass global_class = { "GjsGlobal", JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(GJS_GLOBAL_SLOT_LAST), JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL, NULL /* checkAccess */, NULL /* call */, NULL /* hasInstance */, NULL /* construct */, NULL, { NULL } }; /** * gjs_init_context_standard: * @context: a #JSContext * * This function creates a default global object for @context, * and calls JS_InitStandardClasses using it. * * Returns: %TRUE on success, %FALSE otherwise */ gboolean gjs_init_context_standard (JSContext *context) { JSObject *global; JS::CompartmentOptions options; guint32 options_flags; /* JSOPTION_DONT_REPORT_UNCAUGHT: Don't send exceptions to our * error report handler; instead leave them set. This allows us * to get at the exception object. * * JSOPTION_STRICT: Report warnings to error reporter function. */ options_flags = JSOPTION_DONT_REPORT_UNCAUGHT | JSOPTION_EXTRA_WARNINGS; if (!g_getenv("GJS_DISABLE_JIT")) { gjs_debug(GJS_DEBUG_CONTEXT, "Enabling JIT"); options_flags |= JSOPTION_TYPE_INFERENCE | JSOPTION_ION | JSOPTION_BASELINE | JSOPTION_ASMJS; } JS_SetOptions(context, JS_GetOptions(context) | options_flags); JS_SetErrorReporter(context, gjs_error_reporter); options.setVersion(JSVERSION_LATEST); global = JS_NewGlobalObject(context, &global_class, NULL, options); if (global == NULL) return FALSE; /* Set the context's global */ JSAutoCompartment ac(context, global); JS_SetGlobalObject(context, global); if (!JS_InitStandardClasses(context, global)) return FALSE; if (!JS_InitReflect(context, global)) return FALSE; if (!JS_DefineDebuggerObject(context, global)) return FALSE; return TRUE; } void gjs_set_global_slot (JSContext *context, GjsGlobalSlot slot, jsval value) { JSObject *global; global = JS_GetGlobalObject(context); JS_SetReservedSlot(global, JSCLASS_GLOBAL_SLOT_COUNT + slot, value); } jsval gjs_get_global_slot (JSContext *context, GjsGlobalSlot slot) { JSObject *global; global = JS_GetGlobalObject(context); return JS_GetReservedSlot(global, JSCLASS_GLOBAL_SLOT_COUNT + slot); } /* Returns whether the object had the property; if the object did * not have the property, always sets an exception. Treats * "the property's value is JSVAL_VOID" the same as "no such property,". * Guarantees that *value_p is set to something, if only JSVAL_VOID, * even if an exception is set and false is returned. * * Requires request. */ gboolean gjs_object_require_property(JSContext *context, JSObject *obj, const char *obj_description, jsid property_name, jsval *value_p) { jsval value; char *name; value = JSVAL_VOID; if (value_p) *value_p = value; if (G_UNLIKELY (!JS_GetPropertyById(context, obj, property_name, &value))) return JS_FALSE; if (G_LIKELY (!JSVAL_IS_VOID(value))) { if (value_p) *value_p = value; return JS_TRUE; } /* remember gjs_throw() is a no-op if JS_GetProperty() * already set an exception */ gjs_get_string_id(context, property_name, &name); if (obj_description) gjs_throw(context, "No property '%s' in %s (or its value was undefined)", name, obj_description); else gjs_throw(context, "No property '%s' in object %p (or its value was undefined)", name, obj); g_free(name); return JS_FALSE; } void gjs_throw_constructor_error(JSContext *context) { gjs_throw(context, "Constructor called as normal method. Use 'new SomeObject()' not 'SomeObject()'"); } void gjs_throw_abstract_constructor_error(JSContext *context, jsval *vp) { jsval callee; jsval prototype; JSClass *proto_class; const char *name = "anonymous"; callee = JS_CALLEE(context, vp); if (JSVAL_IS_OBJECT(callee)) { if (gjs_object_get_property_const(context, JSVAL_TO_OBJECT(callee), GJS_STRING_PROTOTYPE, &prototype)) { proto_class = JS_GetClass(JSVAL_TO_OBJECT(prototype)); name = proto_class->name; } } gjs_throw(context, "You cannot construct new instances of '%s'", name); } JSObject * gjs_build_string_array(JSContext *context, gssize array_length, char **array_values) { GArray *elems; JSObject *array; int i; if (array_length == -1) array_length = g_strv_length(array_values); elems = g_array_sized_new(FALSE, FALSE, sizeof(jsval), array_length); for (i = 0; i < array_length; ++i) { jsval element; element = STRING_TO_JSVAL(JS_NewStringCopyZ(context, array_values[i])); g_array_append_val(elems, element); } array = JS_NewArrayObject(context, elems->len, (jsval*) elems->data); g_array_free(elems, TRUE); return array; } JSObject* gjs_define_string_array(JSContext *context, JSObject *in_object, const char *array_name, gssize array_length, const char **array_values, unsigned attrs) { JSObject *array; JSAutoRequest ar(context); array = gjs_build_string_array(context, array_length, (char **) array_values); if (array != NULL) { if (!JS_DefineProperty(context, in_object, array_name, OBJECT_TO_JSVAL(array), NULL, NULL, attrs)) array = NULL; } return array; } /** * gjs_string_readable: * * Return a string that can be read back by gjs-console; for * JS strings that contain valid Unicode, we return a UTF-8 formatted * string. Otherwise, we return one where non-ASCII-printable bytes * are \x escaped. * */ static char * gjs_string_readable (JSContext *context, JSString *string) { GString *buf = g_string_new(""); char *chars; JS_BeginRequest(context); g_string_append_c(buf, '"'); if (!gjs_string_to_utf8(context, STRING_TO_JSVAL(string), &chars)) { size_t i, len; const jschar *uchars; uchars = JS_GetStringCharsAndLength(context, string, &len); for (i = 0; i < len; i++) { jschar c = uchars[i]; if (c >> 8 == 0 && g_ascii_isprint(c & 0xFF)) g_string_append_c(buf, c & 0xFF); else g_string_append_printf(buf, "\\u%04X", c); } } else { g_string_append(buf, chars); g_free(chars); } g_string_append_c(buf, '"'); JS_EndRequest(context); return g_string_free(buf, FALSE); } /** * gjs_value_debug_string: * @context: * @value: Any JavaScript value * * Returns: A UTF-8 encoded string describing @value */ char* gjs_value_debug_string(JSContext *context, jsval value) { JSString *str; char *bytes; char *debugstr; /* Special case debug strings for strings */ if (JSVAL_IS_STRING(value)) { return gjs_string_readable(context, JSVAL_TO_STRING(value)); } JS_BeginRequest(context); str = JS_ValueToString(context, value); if (str == NULL) { if (JSVAL_IS_OBJECT(value)) { /* Specifically the Call object (see jsfun.c in spidermonkey) * does not have a toString; there may be others also. */ JSClass *klass; klass = JS_GetClass(JSVAL_TO_OBJECT(value)); if (klass != NULL) { str = JS_NewStringCopyZ(context, klass->name); JS_ClearPendingException(context); if (str == NULL) { JS_EndRequest(context); return g_strdup("[out of memory copying class name]"); } } else { gjs_log_exception(context); JS_EndRequest(context); return g_strdup("[unknown object]"); } } else { JS_EndRequest(context); return g_strdup("[unknown non-object]"); } } g_assert(str != NULL); size_t len = JS_GetStringEncodingLength(context, str); if (len != (size_t)(-1)) { bytes = (char*) g_malloc((len + 1) * sizeof(char)); JS_EncodeStringToBuffer(context, str, bytes, len); bytes[len] = '\0'; } else { bytes = g_strdup("[invalid string]"); } JS_EndRequest(context); debugstr = _gjs_g_utf8_make_valid(bytes); g_free(bytes); return debugstr; } void gjs_log_object_props(JSContext *context, JSObject *obj, GjsDebugTopic topic, const char *prefix) { JSObject *props_iter; jsid prop_id; JS_BeginRequest(context); props_iter = JS_NewPropertyIterator(context, obj); if (props_iter == NULL) { gjs_log_exception(context); goto done; } prop_id = JSID_VOID; if (!JS_NextProperty(context, props_iter, &prop_id)) goto done; while (!JSID_IS_VOID(prop_id)) { jsval propval; char *debugstr; char *name = NULL; if (!JS_GetPropertyById(context, obj, prop_id, &propval)) goto next; if (!gjs_get_string_id(context, prop_id, &name)) goto next; debugstr = gjs_value_debug_string(context, propval); gjs_debug(topic, "%s%s = '%s'", prefix, name, debugstr); g_free(debugstr); next: g_free(name); prop_id = JSID_VOID; if (!JS_NextProperty(context, props_iter, &prop_id)) break; } done: JS_EndRequest(context); } void gjs_explain_scope(JSContext *context, const char *title) { JSObject *global; JSObject *parent; GString *chain; char *debugstr; gjs_debug(GJS_DEBUG_SCOPE, "=== %s ===", title); JS_BeginRequest(context); gjs_debug(GJS_DEBUG_SCOPE, " Context: %p %s", context, ""); global = JS_GetGlobalObject(context); debugstr = gjs_value_debug_string(context, OBJECT_TO_JSVAL(global)); gjs_debug(GJS_DEBUG_SCOPE, " Global: %p %s", global, debugstr); g_free(debugstr); parent = JS_GetGlobalForScopeChain(context); chain = g_string_new(NULL); while (parent != NULL) { char *debug; debug = gjs_value_debug_string(context, OBJECT_TO_JSVAL(parent)); if (chain->len > 0) g_string_append(chain, ", "); g_string_append_printf(chain, "%p %s", parent, debug); g_free(debug); parent = JS_GetParent(parent); } gjs_debug(GJS_DEBUG_SCOPE, " Chain: %s", chain->str); g_string_free(chain, TRUE); JS_EndRequest(context); } JSBool gjs_log_exception_full(JSContext *context, jsval exc, JSString *message) { jsval stack; JSString *exc_str; char *utf8_exception, *utf8_message; gboolean is_syntax; JS_BeginRequest(context); is_syntax = FALSE; if (JSVAL_IS_OBJECT(exc) && gjs_typecheck_boxed(context, JSVAL_TO_OBJECT(exc), NULL, G_TYPE_ERROR, FALSE)) { GError *gerror; gerror = (GError*) gjs_c_struct_from_boxed(context, JSVAL_TO_OBJECT(exc)); utf8_exception = g_strdup_printf("GLib.Error %s: %s", g_quark_to_string(gerror->domain), gerror->message); } else { if (JSVAL_IS_OBJECT(exc)) { jsval js_name; char *utf8_name; if (gjs_object_get_property_const(context, JSVAL_TO_OBJECT(exc), GJS_STRING_NAME, &js_name) && JSVAL_IS_STRING(js_name) && gjs_string_to_utf8(context, js_name, &utf8_name)) { is_syntax = strcmp("SyntaxError", utf8_name) == 0; } } exc_str = JS_ValueToString(context, exc); if (exc_str != NULL) gjs_string_to_utf8(context, STRING_TO_JSVAL(exc_str), &utf8_exception); else utf8_exception = NULL; } if (message != NULL) gjs_string_to_utf8(context, STRING_TO_JSVAL(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 line number from the exception. */ if (is_syntax) { jsval js_lineNumber, js_fileName; unsigned lineNumber; char *utf8_fileName; gjs_object_get_property_const(context, JSVAL_TO_OBJECT(exc), GJS_STRING_LINE_NUMBER, &js_lineNumber); gjs_object_get_property_const(context, JSVAL_TO_OBJECT(exc), GJS_STRING_FILENAME, &js_fileName); if (JSVAL_IS_STRING(js_fileName)) gjs_string_to_utf8(context, js_fileName, &utf8_fileName); else utf8_fileName = g_strdup("unknown"); lineNumber = JSVAL_TO_INT(js_lineNumber); if (utf8_message) { g_critical("JS ERROR: %s: %s @ %s:%u", utf8_message, utf8_exception, utf8_fileName, lineNumber); } else { g_critical("JS ERROR: %s @ %s:%u", utf8_exception, utf8_fileName, lineNumber); } g_free(utf8_fileName); } else { char *utf8_stack; if (JSVAL_IS_OBJECT(exc) && gjs_object_get_property_const(context, JSVAL_TO_OBJECT(exc), GJS_STRING_STACK, &stack) && JSVAL_IS_STRING(stack)) gjs_string_to_utf8(context, stack, &utf8_stack); else utf8_stack = NULL; if (utf8_message) { if (utf8_stack) g_warning("JS ERROR: %s: %s\n%s", utf8_message, utf8_exception, utf8_stack); else g_warning("JS ERROR: %s: %s", utf8_message, utf8_exception); } else { if (utf8_stack) g_warning("JS ERROR: %s\n%s", utf8_exception, utf8_stack); else g_warning("JS ERROR: %s", utf8_exception); } g_free(utf8_stack); } g_free(utf8_exception); g_free(utf8_message); JS_EndRequest(context); return JS_TRUE; } static JSBool log_and_maybe_keep_exception(JSContext *context, gboolean keep) { jsval exc = JSVAL_VOID; JSBool retval = JS_FALSE; JS_BeginRequest(context); JS_AddValueRoot(context, &exc); if (!JS_GetPendingException(context, &exc)) goto out; JS_ClearPendingException(context); gjs_log_exception_full(context, exc, NULL); /* We clear above and then set it back so any exceptions * from the logging process don't overwrite the original */ if (keep) JS_SetPendingException(context, exc); retval = JS_TRUE; out: JS_RemoveValueRoot(context, &exc); JS_EndRequest(context); return retval; } JSBool gjs_log_exception(JSContext *context) { return log_and_maybe_keep_exception(context, FALSE); } JSBool gjs_log_and_keep_exception(JSContext *context) { return log_and_maybe_keep_exception(context, TRUE); } static void try_to_chain_stack_trace(JSContext *src_context, JSContext *dst_context, jsval src_exc) { /* append current stack of dst_context to stack trace for src_exc. * we bail if anything goes wrong, just using the src_exc unmodified * in that case. */ jsval chained, src_stack, dst_stack, new_stack; JSString *new_stack_str; JS_BeginRequest(src_context); JS_BeginRequest(dst_context); if (!JSVAL_IS_OBJECT(src_exc)) goto out; // src_exc doesn't have a stack trace /* create a new exception in dst_context to get a stack trace */ gjs_throw_literal(dst_context, "Chained exception"); if (!(JS_GetPendingException(dst_context, &chained) && JSVAL_IS_OBJECT(chained))) goto out; // gjs_throw_literal didn't work?! JS_ClearPendingException(dst_context); /* get stack trace for src_exc and chained */ if (!(JS_GetProperty(dst_context, JSVAL_TO_OBJECT(chained), "stack", &dst_stack) && JSVAL_IS_STRING(dst_stack))) goto out; // couldn't get chained stack if (!(JS_GetProperty(src_context, JSVAL_TO_OBJECT(src_exc), "stack", &src_stack) && JSVAL_IS_STRING(src_stack))) goto out; // couldn't get source stack /* add chained exception's stack trace to src_exc */ new_stack_str = JS_ConcatStrings (dst_context, JSVAL_TO_STRING(src_stack), JSVAL_TO_STRING(dst_stack)); if (new_stack_str==NULL) goto out; // couldn't concatenate src and dst stacks?! new_stack = STRING_TO_JSVAL(new_stack_str); JS_SetProperty(dst_context, JSVAL_TO_OBJECT(src_exc), "stack", &new_stack); out: JS_EndRequest(dst_context); JS_EndRequest(src_context); } JSBool gjs_move_exception(JSContext *src_context, JSContext *dest_context) { JSBool success; JS_BeginRequest(src_context); JS_BeginRequest(dest_context); /* NOTE: src and dest could be the same. */ jsval exc; if (JS_GetPendingException(src_context, &exc)) { if (src_context != dest_context) { /* try to add the current stack of dest_context to the * stack trace of exc */ try_to_chain_stack_trace(src_context, dest_context, exc); /* move the exception to dest_context */ JS_SetPendingException(dest_context, exc); JS_ClearPendingException(src_context); } success = JS_TRUE; } else { success = JS_FALSE; } JS_EndRequest(dest_context); JS_EndRequest(src_context); return success; } JSBool gjs_call_function_value(JSContext *context, JSObject *obj, jsval fval, unsigned argc, jsval *argv, jsval *rval) { JSBool result; JS_BeginRequest(context); result = JS_CallFunctionValue(context, obj, fval, argc, argv, rval); if (result) gjs_schedule_gc_if_needed(context); JS_EndRequest(context); return result; } static JSBool log_prop(JSContext *context, JSObject *obj, jsval id, jsval *value_p, const char *what) { if (JSVAL_IS_STRING(id)) { char *name; gjs_string_to_utf8(context, id, &name); gjs_debug(GJS_DEBUG_PROPS, "prop %s: %s", name, what); g_free(name); } else if (JSVAL_IS_INT(id)) { gjs_debug(GJS_DEBUG_PROPS, "prop %d: %s", JSVAL_TO_INT(id), what); } else { gjs_debug(GJS_DEBUG_PROPS, "prop not-sure-what: %s", what); } return JS_TRUE; } JSBool gjs_get_prop_verbose_stub(JSContext *context, JSObject *obj, jsval id, jsval *value_p) { return log_prop(context, obj, id, value_p, "get"); } JSBool gjs_set_prop_verbose_stub(JSContext *context, JSObject *obj, jsval id, jsval *value_p) { return log_prop(context, obj, id, value_p, "set"); } JSBool gjs_add_prop_verbose_stub(JSContext *context, JSObject *obj, jsval id, jsval *value_p) { return log_prop(context, obj, id, value_p, "add"); } JSBool gjs_delete_prop_verbose_stub(JSContext *context, JSObject *obj, jsval id, jsval *value_p) { return log_prop(context, obj, id, value_p, "delete"); } /* get a debug string for type tag in jsval */ const char* gjs_get_type_name(jsval value) { if (JSVAL_IS_NULL(value)) { return "null"; } else if (JSVAL_IS_VOID(value)) { return "undefined"; } else if (JSVAL_IS_INT(value)) { return "integer"; } else if (JSVAL_IS_DOUBLE(value)) { return "double"; } else if (JSVAL_IS_BOOLEAN(value)) { return "boolean"; } else if (JSVAL_IS_STRING(value)) { return "string"; } else if (JSVAL_IS_OBJECT(value)) { return "object"; } else { return ""; } } /** * gjs_value_to_int64: * @context: the Javascript context object * @val: Javascript value to convert * @gint64: location to store the return value * * Converts a Javascript value into the nearest 64 bit signed value. * * This function behaves indentically for rounding to JSValToInt32(), which * means that it rounds (0.5 toward positive infinity) rather than doing * a C-style truncation to 0. If we change to using JSValToEcmaInt32() then * this should be changed to match. * * Return value: If the javascript value converted to a number (see * JS_ValueToNumber()) is NaN, or outside the range of 64-bit signed * numbers, fails and sets an exception. Otherwise returns the value * rounded to the nearest 64-bit integer. Like JS_ValueToInt32(), * undefined throws, but null => 0, false => 0, true => 1. */ JSBool gjs_value_to_int64 (JSContext *context, const jsval val, gint64 *result) { if (JSVAL_IS_INT (val)) { *result = JSVAL_TO_INT (val); return JS_TRUE; } else { double value_double; if (!JS_ValueToNumber(context, val, &value_double)) return JS_FALSE; if (isnan(value_double) || value_double < G_MININT64 || value_double > G_MAXINT64) { gjs_throw(context, "Value is not a valid 64-bit integer"); return JS_FALSE; } *result = (gint64)(value_double + 0.5); return JS_TRUE; } } static JSBool gjs_parse_args_valist (JSContext *context, const char *function_name, const char *format, unsigned argc, jsval *argv, va_list args) { guint i; const char *fmt_iter; guint n_unwind = 0; #define MAX_UNWIND_STRINGS 16 gpointer unwind_strings[MAX_UNWIND_STRINGS]; gboolean ignore_trailing_args = FALSE; guint n_required = 0; guint n_total = 0; guint consumed_args; JS_BeginRequest(context); if (*format == '!') { ignore_trailing_args = TRUE; format++; } for (fmt_iter = format; *fmt_iter; fmt_iter++) { switch (*fmt_iter) { case '|': n_required = n_total; continue; case '?': continue; default: break; } n_total++; } if (n_required == 0) n_required = n_total; if (argc < n_required || (argc > n_total && !ignore_trailing_args)) { if (n_required == n_total) { gjs_throw(context, "Error invoking %s: Expected %d arguments, got %d", function_name, n_required, argc); } else { gjs_throw(context, "Error invoking %s: Expected minimum %d arguments (and %d optional), got %d", function_name, n_required, n_total - n_required, argc); } goto error_unwind; } /* We have 3 iteration variables here. * @i: The current integer position in fmt_args * @fmt_iter: A pointer to the character in fmt_args * @consumed_args: How many arguments we've taken from argv * * consumed_args can currently be different from 'i' because of the '|' character. */ for (i = 0, consumed_args = 0, fmt_iter = format; *fmt_iter; fmt_iter++, i++) { const char *argname; gpointer arg_location; jsval js_value; const char *arg_error_message = NULL; if (*fmt_iter == '|') continue; if (consumed_args == argc) { break; } argname = va_arg (args, char *); arg_location = va_arg (args, gpointer); g_return_val_if_fail (argname != NULL, JS_FALSE); g_return_val_if_fail (arg_location != NULL, JS_FALSE); js_value = argv[consumed_args]; if (*fmt_iter == '?') { fmt_iter++; if (JSVAL_IS_NULL (js_value)) { gpointer *arg = (gpointer*) arg_location; *arg = NULL; goto got_value; } } switch (*fmt_iter) { case 'b': { if (!JSVAL_IS_BOOLEAN(js_value)) { arg_error_message = "Not a boolean"; } else { gboolean *arg = (gboolean*) arg_location; *arg = JSVAL_TO_BOOLEAN(js_value); } } break; case 'o': { if (!JSVAL_IS_OBJECT(js_value)) { arg_error_message = "Not an object"; } else { JSObject **arg = (JSObject**) arg_location; *arg = JSVAL_TO_OBJECT(js_value); } } break; case 's': { char **arg = (char**) arg_location; if (gjs_string_to_utf8 (context, js_value, arg)) { unwind_strings[n_unwind++] = *arg; g_assert(n_unwind < MAX_UNWIND_STRINGS); } else { /* Our error message is going to be more useful */ JS_ClearPendingException(context); arg_error_message = "Couldn't convert to string"; } } break; case 'F': { char **arg = (char**) arg_location; if (gjs_string_to_filename (context, js_value, arg)) { unwind_strings[n_unwind++] = *arg; g_assert(n_unwind < MAX_UNWIND_STRINGS); } else { /* Our error message is going to be more useful */ JS_ClearPendingException(context); arg_error_message = "Couldn't convert to filename"; } } break; case 'i': { if (!JS_ValueToInt32(context, js_value, (gint32*) arg_location)) { /* Our error message is going to be more useful */ JS_ClearPendingException(context); arg_error_message = "Couldn't convert to integer"; } } break; case 'u': { gdouble num; if (!JSVAL_IS_NUMBER(js_value) || !JS_ValueToNumber(context, js_value, &num)) { /* Our error message is going to be more useful */ JS_ClearPendingException(context); arg_error_message = "Couldn't convert to unsigned integer"; } else if (num > G_MAXUINT32 || num < 0) { arg_error_message = "Value is out of range"; } else { *((guint32*) arg_location) = num; } } break; case 't': { if (!gjs_value_to_int64(context, js_value, (gint64*) arg_location)) { /* Our error message is going to be more useful */ JS_ClearPendingException(context); arg_error_message = "Couldn't convert to 64-bit integer"; } } break; case 'f': { double num; if (!JS_ValueToNumber(context, js_value, &num)) { /* Our error message is going to be more useful */ JS_ClearPendingException(context); arg_error_message = "Couldn't convert to double"; } else { *((double*) arg_location) = num; } } break; default: g_assert_not_reached (); } got_value: if (arg_error_message != NULL) { gjs_throw(context, "Error invoking %s, at argument %d (%s): %s", function_name, consumed_args+1, argname, arg_error_message); goto error_unwind; } consumed_args++; } JS_EndRequest(context); return JS_TRUE; error_unwind: /* We still own the strings in the error case, free any we converted */ for (i = 0; i < n_unwind; i++) { g_free (unwind_strings[i]); } JS_EndRequest(context); return JS_FALSE; } /** * gjs_parse_args: * @context: * @function_name: The name of the function being called * @format: Printf-like format specifier containing the expected arguments * @argc: Number of JavaScript arguments * @argv: JavaScript argument array * @Varargs: for each character in @format, a pair of a char * which is the name * of the argument, and a pointer to a location to store the value. The type of * value stored depends on the format character, as described below. * * This function is inspired by Python's PyArg_ParseTuple for those * familiar with it. It takes a format specifier which gives the * types of the expected arguments, and a list of argument names and * value location pairs. The currently accepted format specifiers are: * * b: A boolean * s: A string, converted into UTF-8 * F: A string, converted into "filename encoding" (i.e. active locale) * i: A number, will be converted to a C "gint32" * u: A number, converted into a C "guint32" * t: A 64-bit number, converted into a C "gint64" by way of gjs_value_to_int64() * o: A JavaScript object, as a "JSObject *" * * If the first character in the format string is a '!', then JS is allowed * to pass extra arguments that are ignored, to the function. * * The '|' character introduces optional arguments. All format specifiers * after a '|' when not specified, do not cause any changes in the C * value location. * * A prefix character '?' means that the next value may be null, in * which case the C value %NULL is returned. */ JSBool gjs_parse_args (JSContext *context, const char *function_name, const char *format, unsigned argc, jsval *argv, ...) { va_list args; JSBool ret; va_start (args, argv); ret = gjs_parse_args_valist (context, function_name, format, argc, argv, args); va_end (args); return ret; } JSBool gjs_parse_call_args (JSContext *context, const char *function_name, const char *format, JS::CallArgs &call_args, ...) { va_list args; JSBool ret; va_start (args, call_args); ret = gjs_parse_args_valist (context, function_name, format, call_args.length(), call_args.array(), args); va_end (args); return ret; } #ifdef __linux__ static void _linux_get_self_process_size (gulong *vm_size, gulong *rss_size) { char *contents; char *iter; gsize len; int i; *vm_size = *rss_size = 0; if (!g_file_get_contents ("/proc/self/stat", &contents, &len, NULL)) return; iter = contents; /* See "man proc" for where this 22 comes from */ for (i = 0; i < 22; i++) { iter = strchr (iter, ' '); if (!iter) goto out; iter++; } sscanf (iter, " %lu", vm_size); iter = strchr (iter, ' '); if (iter) sscanf (iter, " %lu", rss_size); out: g_free (contents); } static gulong linux_rss_trigger; static gint64 last_gc_time; #endif /** * gjs_gc_if_needed: * * Split of the low level version of gjs_context_maybe_gc(). */ void gjs_gc_if_needed (JSContext *context) { #ifdef __linux__ { /* We initiate a GC if VM or RSS has grown by this much */ gulong vmsize; gulong rss_size; gint64 now; /* We rate limit GCs to at most one per 5 frames. One frame is 16666 microseconds (1000000/60)*/ now = g_get_monotonic_time(); if (now - last_gc_time < 5 * 16666) return; _linux_get_self_process_size (&vmsize, &rss_size); /* linux_rss_trigger is initialized to 0, so currently * we always do a full GC early. * * Here we see if the RSS has grown by 25% since * our last look; if so, initiate a full GC. In * theory using RSS is bad if we get swapped out, * since we may be overzealous in GC, but on the * other hand, if swapping is going on, better * to GC. */ if (rss_size > linux_rss_trigger) { linux_rss_trigger = (gulong) MIN(G_MAXULONG, rss_size * 1.25); JS_GC(JS_GetRuntime(context)); last_gc_time = now; } else if (rss_size < (0.75 * linux_rss_trigger)) { /* If we've shrunk by 75%, lower the trigger */ linux_rss_trigger = (rss_size * 1.25); } } #endif } /** * gjs_maybe_gc: * * Low level version of gjs_context_maybe_gc(). */ void gjs_maybe_gc (JSContext *context) { JS_MaybeGC(context); gjs_gc_if_needed(context); } void gjs_schedule_gc_if_needed (JSContext *context) { GjsContext *gjs_context; /* We call JS_MaybeGC immediately, but defer a check for a full * GC cycle to an idle handler. */ JS_MaybeGC(context); gjs_context = (GjsContext *) JS_GetContextPrivate(context); if (gjs_context) _gjs_context_schedule_gc_if_needed(gjs_context); } /** * gjs_strip_unix_shebang: * * @script: (in): A pointer to a JS script * @script_len: (inout): A pointer to the script length. The * pointer will be modified if a shebang is stripped. * @new_start_line_number: (out) (allow-none): A pointer to * write the start-line number to account for the offset * as a result of stripping the shebang. * * Returns a pointer to the beginning of a script with unix * shebangs removed. The outparams are useful to know the * new length of the script and on what line of the * original script we're executing from, so that any relevant * offsets can be applied to the results of an execution pass. */ const char * gjs_strip_unix_shebang(const char *script, gssize *script_len, int *start_line_number_out) { g_assert(script_len); /* handle scripts with UNIX shebangs */ if (strncmp(script, "#!", 2) == 0) { /* If we found a newline, advance the script by one line */ const char *s = (const char *) strstr (script, "\n"); if (s != NULL) { if (*script_len > 0) *script_len -= (s + 1 - script); script = s + 1; if (start_line_number_out) *start_line_number_out = 2; return script; } else { /* Just a shebang */ if (start_line_number_out) *start_line_number_out = -1; *script_len = 0; return NULL; } } /* No shebang, return the original script */ if (start_line_number_out) *start_line_number_out = 1; return script; } JSBool gjs_eval_with_scope(JSContext *context, JSObject *object, const char *script, gssize script_len, const char *filename, jsval *retval_p) { int start_line_number = 1; jsval retval = JSVAL_VOID; JSAutoRequest ar(context); if (script_len < 0) script_len = strlen(script); script = gjs_strip_unix_shebang(script, &script_len, &start_line_number); /* log and clear exception if it's set (should not be, normally...) */ if (JS_IsExceptionPending(context)) { g_warning("gjs_eval_in_scope called with a pending exception"); return JS_FALSE; } if (!object) object = JS_NewObject(context, NULL, NULL, NULL); JS::CompileOptions options(context); options.setUTF8(true) .setFileAndLine(filename, start_line_number) .setSourcePolicy(JS::CompileOptions::LAZY_SOURCE); js::RootedObject rootedObj(context, object); if (!JS::Evaluate(context, rootedObj, options, script, script_len, &retval)) return JS_FALSE; gjs_schedule_gc_if_needed(context); if (JS_IsExceptionPending(context)) { g_warning("EvaluateScript returned JS_TRUE but exception was pending; " "did somebody call gjs_throw() without returning JS_FALSE?"); return JS_FALSE; } gjs_debug(GJS_DEBUG_CONTEXT, "Script evaluation succeeded"); if (retval_p) *retval_p = retval; return JS_TRUE; } cjs-2.8.0/cjs/jsapi-private.cpp0000664000175000017500000000543612610172032015267 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2010 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include "jsapi-util.h" #include "jsapi-private.h" #include "compat.h" #include #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstrict-prototypes" #pragma GCC diagnostic ignored "-Winvalid-offsetof" #include #pragma GCC diagnostic pop void gjs_error_reporter(JSContext *context, const char *message, JSErrorReport *report) { const char *warning; GLogLevelFlags level; if (gjs_environment_variable_is_set("GJS_ABORT_ON_OOM") && report->flags == JSREPORT_ERROR && report->errorNumber == JSMSG_OUT_OF_MEMORY) { g_error("GJS ran out of memory at %s: %i.", report->filename, report->lineno); } if ((report->flags & JSREPORT_WARNING) != 0) { warning = "WARNING"; level = G_LOG_LEVEL_MESSAGE; /* suppress bogus warnings. See mozilla/js/src/js.msg */ switch (report->errorNumber) { /* 162, JSMSG_UNDEFINED_PROP: warns every time a lazy property * is resolved, since the property starts out * undefined. When this is a real bug it should usually * fail somewhere else anyhow. */ case 162: return; } } else { warning = "REPORTED"; level = G_LOG_LEVEL_WARNING; } g_log(G_LOG_DOMAIN, level, "JS %s: [%s %d]: %s", warning, report->filename, report->lineno, message); } JSObject * gjs_get_global_object(JSContext *cx){ return js::GetDefaultGlobalForContext(cx); } cjs-2.8.0/cjs/console.cpp0000664000175000017500000001243412610172032014147 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include #include static char **include_path = NULL; static char **coverage_paths = NULL; static char *coverage_output_path = NULL; static char *command = NULL; static GOptionEntry entries[] = { { "command", 'c', 0, G_OPTION_ARG_STRING, &command, "Program passed in as a string", "COMMAND" }, { "coverage-path", 'C', 0, G_OPTION_ARG_STRING_ARRAY, &coverage_paths, "Add the filename FILE to the list of files to generate coverage info for", "FILE" }, { "coverage-output", 0, 0, G_OPTION_ARG_STRING, &coverage_output_path, "Write coverage output to a directory DIR. This option is mandatory when using --coverage-path", "DIR", }, { "include-path", 'I', 0, G_OPTION_ARG_STRING_ARRAY, &include_path, "Add the directory DIR to the list of directories to search for js files.", "DIR" }, { NULL } }; G_GNUC_NORETURN static void print_help (GOptionContext *context, gboolean main_help) { gchar *help; help = g_option_context_get_help (context, main_help, NULL); g_print ("%s", help); g_free (help); exit (0); } int main(int argc, char **argv) { GOptionContext *context; GError *error = NULL; GjsContext *js_context; GjsCoverage *coverage = NULL; char *script; const char *filename; const char *program_name; gsize len; int code; context = g_option_context_new(NULL); /* pass unknown through to the JS script */ g_option_context_set_ignore_unknown_options(context, TRUE); g_option_context_set_help_enabled(context, FALSE); g_option_context_add_main_entries(context, entries, NULL); if (!g_option_context_parse(context, &argc, &argv, &error)) g_error("option parsing failed: %s", error->message); if (argc >= 2) { if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) print_help(context, TRUE); else if (strcmp(argv[1], "--help-all") == 0) print_help(context, FALSE); } g_option_context_free (context); setlocale(LC_ALL, ""); if (command != NULL) { script = command; len = strlen(script); filename = ""; program_name = argv[0]; } else if (argc <= 1) { script = g_strdup("const Console = imports.console; Console.interact();"); len = strlen(script); filename = ""; program_name = argv[0]; } else /*if (argc >= 2)*/ { error = NULL; if (!g_file_get_contents(argv[1], &script, &len, &error)) { g_printerr("%s\n", error->message); exit(1); } filename = argv[1]; program_name = argv[1]; argc--; argv++; } js_context = (GjsContext*) g_object_new(GJS_TYPE_CONTEXT, "search-path", include_path, "program-name", program_name, NULL); if (coverage_paths) { if (!coverage_output_path) g_error("--coverage-output is required when taking coverage statistics"); coverage = gjs_coverage_new((const gchar **) coverage_paths, js_context); } /* prepare command line arguments */ if (!gjs_context_define_string_array(js_context, "ARGV", argc - 1, (const char**)argv + 1, &error)) { code = 1; g_printerr("Failed to defined ARGV: %s", error->message); g_clear_error(&error); goto out; } /* evaluate the script */ if (!gjs_context_eval(js_context, script, len, filename, &code, &error)) { code = 1; g_printerr("%s\n", error->message); g_clear_error(&error); goto out; } out: /* Probably doesn't make sense to write statistics on failure */ if (coverage && code == 0) gjs_coverage_write_statistics(coverage, coverage_output_path); g_object_unref(js_context); g_free(script); exit(code); } cjs-2.8.0/cjs/context-private.h0000664000175000017500000000277412610172032015314 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2014 Colin Walters * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_CONTEXT_PRIVATE_H__ #define __GJS_CONTEXT_PRIVATE_H__ #include "context.h" #include "compat.h" G_BEGIN_DECLS gboolean _gjs_context_destroying (GjsContext *js_context); void _gjs_context_schedule_gc_if_needed (GjsContext *js_context); G_END_DECLS #endif /* __GJS_CONTEXT_PRIVATE_H__ */ cjs-2.8.0/cjs/runtime.h0000664000175000017500000000261212610172032013632 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2013 Giovanni Campagna * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_RUNTIME_H__ #define __GJS_RUNTIME_H__ JSRuntime * gjs_runtime_for_current_thread (void); JSBool gjs_runtime_is_sweeping (JSRuntime *runtime); #endif /* __GJS_RUNTIME_H__ */ cjs-2.8.0/cjs/stack.cpp0000664000175000017500000001031612610172032013607 0ustar fabiofabio/* This file contains code derived from xpcdebug.cpp in Mozilla. The license * for that file follows: */ /* * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * John Bandhauer (original author) * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include #include #include #include "context.h" #include "compat.h" #include "jsapi-util.h" JSBool gjs_context_get_frame_info (JSContext *context, jsval *stack, jsval *fileName, jsval *lineNumber) { jsval v_constructor; JSObject *err_obj; JSObject *global; JSBool ret = JS_FALSE; JS_BeginRequest(context); global = JS_GetGlobalObject(context); JSAutoCompartment ac(context, global); if (!JS_GetProperty(context, JS_GetGlobalObject(context), "Error", &v_constructor) || !JSVAL_IS_OBJECT(v_constructor)) { g_error("??? Missing Error constructor in global object?"); goto out; } err_obj = JS_New(context, JSVAL_TO_OBJECT(v_constructor), 0, NULL); if (stack != NULL) { if (!gjs_object_get_property_const(context, err_obj, GJS_STRING_STACK, stack)) goto out; } if (fileName != NULL) { if (!gjs_object_get_property_const(context, err_obj, GJS_STRING_FILENAME, fileName)) goto out; } if (lineNumber != NULL) { if (!gjs_object_get_property_const(context, err_obj, GJS_STRING_LINE_NUMBER, lineNumber)) goto out; } ret = JS_TRUE; out: JS_EndRequest(context); return ret; } void gjs_context_print_stack_stderr(GjsContext *context) { JSContext *cx = (JSContext*) gjs_context_get_native_context(context); jsval v_stack; char *stack; g_printerr("== Stack trace for context %p ==\n", context); /* Stderr is locale encoding, so we use string_to_filename here */ if (!gjs_context_get_frame_info(cx, &v_stack, NULL, NULL) || !gjs_string_to_filename(cx, v_stack, &stack)) { g_printerr("No stack trace available\n"); return; } g_printerr("%s\n", stack); g_free(stack); } void gjs_dumpstack(void) { GList *contexts = gjs_context_get_all(); GList *iter; for (iter = contexts; iter; iter = iter->next) { GjsContext *context = (GjsContext*)iter->data; gjs_context_print_stack_stderr(context); g_object_unref(context); } g_list_free(contexts); } cjs-2.8.0/cjs/gjs.stp.in0000664000175000017500000000123312610172032013714 0ustar fabiofabio probe gjs.object_proxy_new = process("@EXPANDED_LIBDIR@/libgjs-gi.so.0.0.0").mark("object__proxy__new") { proxy_address = $arg1; gobject_address = $arg2; gi_namespace = user_string($arg3); gi_name = user_string($arg4); probestr = sprintf("gjs.object_proxy_new(%p, %s, %s)", proxy_address, gi_namespace, gi_name); } probe gjs.object_proxy_finalize = process("@EXPANDED_LIBDIR@/libgjs-gi.so.0.0.0").mark("object__proxy__finalize") { proxy_address = $arg1; gobject_address = $arg2; gi_namespace = user_string($arg3); gi_name = user_string($arg4); probestr = sprintf("gjs.object_proxy_finalize(%p, %s, %s)", proxy_address, gi_namespace, gi_name); } cjs-2.8.0/cjs/gi.cpp0000664000175000017500000000270012610172032013077 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "gi.h" #include #include #include "cjs/native.h" #include "cjs/compat.h" #include "gi/repo.h" JSBool gjs_define_gi_stuff(JSContext *context, JSObject **module_out) { return gjs_define_repo(context, module_out, "gi"); } cjs-2.8.0/cjs/coverage.h0000664000175000017500000000552012610172032013743 0ustar fabiofabio/* * Copyright © 2014 Endless Mobile, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Authored By: Sam Spilsbury */ #ifndef _GJS_DEBUG_COVERAGE_H #define _GJS_DEBUG_COVERAGE_H #include G_BEGIN_DECLS #define GJS_TYPE_DEBUG_COVERAGE gjs_coverage_get_type() #define GJS_DEBUG_COVERAGE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), \ GJS_TYPE_DEBUG_COVERAGE, GjsCoverage)) #define GJS_DEBUG_COVERAGE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), \ GJS_TYPE_DEBUG_COVERAGE, GjsCoverageClass)) #define GJS_IS_DEBUG_COVERAGE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), \ GJS_TYPE_DEBUG_COVERAGE)) #define GJS_IS_DEBUG_COVERAGE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ GJS_TYPE_DEBUG_COVERAGE)) #define GJS_DEBUG_COVERAGE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ GJS_TYPE_DEBUG_COVERAGE, GjsCoverageClass)) typedef struct _GFile GFile; typedef struct _GjsDebugHooks GjsDebugHooks; typedef struct _GjsContext GjsContext; typedef struct _GjsCoverage GjsCoverage; typedef struct _GjsCoverageClass GjsCoverageClass; typedef struct _GjsCoveragePrivate GjsCoveragePrivate; struct _GjsCoverage { GObject parent; }; struct _GjsCoverageClass { GObjectClass parent_class; }; GType gjs_debug_coverage_get_type(void); /** * gjs_debug_coverage_write_statistics: * @coverage: A #GjsDebugCoverage * @output_file (allow-none): A #GFile to write statistics to. If NULL is provided then coverage data * will be written to files in the form of (filename).info in the same directory as the input file * * This function takes all available statistics and writes them out to either the file provided * or to files of the pattern (filename).info in the same directory as the scanned files. It will * provide coverage data for all files ending with ".js" in the coverage directories, even if they * were never actually executed. */ void gjs_coverage_write_statistics(GjsCoverage *coverage, const char *output_directory); GjsCoverage * gjs_coverage_new(const char **covered_directories, GjsContext *coverage_context); G_END_DECLS #endif cjs-2.8.0/cjs/jsapi-util.h0000664000175000017500000005407212610172032014237 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_JSAPI_UTIL_H__ #define __GJS_JSAPI_UTIL_H__ #if !defined (__GJS_GJS_MODULE_H__) && !defined (GJS_COMPILATION) #error "Only can be included directly." #endif #include #include #include #include G_BEGIN_DECLS #define GJS_UTIL_ERROR gjs_util_error_quark () GQuark gjs_util_error_quark (void); enum { GJS_UTIL_ERROR_NONE, GJS_UTIL_ERROR_ARGUMENT_INVALID, GJS_UTIL_ERROR_ARGUMENT_UNDERFLOW, GJS_UTIL_ERROR_ARGUMENT_OVERFLOW, GJS_UTIL_ERROR_ARGUMENT_TYPE_MISMATCH }; typedef enum { GJS_GLOBAL_SLOT_IMPORTS, GJS_GLOBAL_SLOT_KEEP_ALIVE, GJS_GLOBAL_SLOT_BYTE_ARRAY_PROTOTYPE, GJS_GLOBAL_SLOT_LAST, } GjsGlobalSlot; typedef struct GjsRootedArray GjsRootedArray; /* Flags that should be set on properties exported from native code modules. * Basically set these on API, but do NOT set them on data. * * READONLY: forbid setting prop to another value * PERMANENT: forbid deleting the prop * ENUMERATE: allows copyProperties to work among other reasons to have it */ #define GJS_MODULE_PROP_FLAGS (JSPROP_PERMANENT | JSPROP_ENUMERATE) /* * Helper methods to access private data: * * do_base_typecheck: checks that object has the right JSClass, and possibly * throw a TypeError exception if the check fails * priv_from_js: accesses the object private field; as a debug measure, * it also checks that the object is of a compatible * JSClass, but it doesn't raise an exception (it * wouldn't be of much use, if subsequent code crashes on * NULL) * priv_from_js_with_typecheck: a convenience function to call * do_base_typecheck and priv_from_js */ #define GJS_DEFINE_PRIV_FROM_JS(type, klass) \ __attribute__((unused)) static inline JSBool \ do_base_typecheck(JSContext *context, \ JSObject *object, \ JSBool throw_error) \ { \ return gjs_typecheck_instance(context, object, &klass, throw_error); \ } \ static inline type * \ priv_from_js(JSContext *context, \ JSObject *object) \ { \ type *priv; \ JS_BeginRequest(context); \ priv = (type*) JS_GetInstancePrivate(context, object, &klass, NULL); \ JS_EndRequest(context); \ return priv; \ } \ __attribute__((unused)) static JSBool \ priv_from_js_with_typecheck(JSContext *context, \ JSObject *object, \ type **out) \ { \ if (!do_base_typecheck(context, object, JS_FALSE)) \ return JS_FALSE; \ *out = priv_from_js(context, object); \ return JS_TRUE; \ } /** * GJS_DEFINE_PROTO: * @tn: The name of the prototype, as a string * @cn: The name of the prototype, separated by _ * * A convenience macro for prototype implementations. */ #define GJS_DEFINE_PROTO(tn, cn) \ GJS_NATIVE_CONSTRUCTOR_DECLARE(cn); \ _GJS_DEFINE_PROTO_FULL(tn, cn, gjs_##cn##_constructor, G_TYPE_NONE) /** * GJS_DEFINE_PROTO_ABSTRACT: * @tn: The name of the prototype, as a string * @cn: The name of the prototype, separated by _ * * A convenience macro for prototype implementations. * Similar to GJS_DEFINE_PROTO but marks the prototype as abstract, * you won't be able to instantiate it using the new keyword */ #define GJS_DEFINE_PROTO_ABSTRACT(tn, cn) \ _GJS_DEFINE_PROTO_FULL(tn, cn, NULL, G_TYPE_NONE) #define GJS_DEFINE_PROTO_WITH_GTYPE(tn, cn, gtype) \ GJS_NATIVE_CONSTRUCTOR_DECLARE(cn); \ _GJS_DEFINE_PROTO_FULL(tn, cn, gjs_##cn##_constructor, gtype) #define GJS_DEFINE_PROTO_ABSTRACT_WITH_GTYPE(tn, cn, gtype) \ _GJS_DEFINE_PROTO_FULL(tn, cn, NULL, gtype) #define _GJS_DEFINE_PROTO_FULL(type_name, cname, ctor, gtype) \ extern JSPropertySpec gjs_##cname##_proto_props[]; \ extern JSFunctionSpec gjs_##cname##_proto_funcs[]; \ static void gjs_##cname##_finalize(JSFreeOp *fop, JSObject *obj); \ static JSBool gjs_##cname##_new_resolve(JSContext *context, \ JSObject *obj, \ jsval id, \ unsigned flags, \ JSObject **objp) \ { \ return JS_TRUE; \ } \ static struct JSClass gjs_##cname##_class = { \ type_name, \ JSCLASS_HAS_PRIVATE | \ JSCLASS_NEW_RESOLVE, \ JS_PropertyStub, \ JS_DeletePropertyStub, \ JS_PropertyStub, \ JS_StrictPropertyStub, \ JS_EnumerateStub,\ (JSResolveOp) gjs_##cname##_new_resolve, \ JS_ConvertStub, \ gjs_##cname##_finalize, \ NULL, \ NULL, \ NULL, NULL, NULL \ }; \ jsval gjs_##cname##_create_proto(JSContext *context, JSObject *module, const char *proto_name, JSObject *parent) \ { \ jsval rval; \ JSObject *global = gjs_get_import_global(context); \ jsid class_name = gjs_intern_string_to_id(context, gjs_##cname##_class.name); \ if (!JS_GetPropertyById(context, global, class_name, &rval)) \ return JSVAL_NULL; \ if (JSVAL_IS_VOID(rval)) { \ jsval value; \ JSObject *prototype = JS_InitClass(context, global, \ parent, \ &gjs_##cname##_class, \ ctor, \ 0, \ &gjs_##cname##_proto_props[0], \ &gjs_##cname##_proto_funcs[0], \ NULL, \ NULL); \ if (prototype == NULL) { \ return JSVAL_NULL; \ } \ if (!gjs_object_require_property( \ context, global, NULL, \ class_name, &rval)) { \ return JSVAL_NULL; \ } \ if (!JS_DefineProperty(context, module, proto_name, \ rval, NULL, NULL, GJS_MODULE_PROP_FLAGS)) \ return JSVAL_NULL; \ if (gtype != G_TYPE_NONE) { \ value = OBJECT_TO_JSVAL(gjs_gtype_create_gtype_wrapper(context, gtype)); \ JS_DefineProperty(context, JSVAL_TO_OBJECT(rval), "$gtype", value, \ NULL, NULL, JSPROP_PERMANENT); \ } \ } \ return rval; \ } gboolean gjs_init_context_standard (JSContext *context); JSObject* gjs_get_import_global (JSContext *context); jsval gjs_get_global_slot (JSContext *context, GjsGlobalSlot slot); void gjs_set_global_slot (JSContext *context, GjsGlobalSlot slot, jsval value); gboolean gjs_object_require_property (JSContext *context, JSObject *obj, const char *obj_description, jsid property_name, jsval *value_p); JSObject *gjs_new_object_for_constructor (JSContext *context, JSClass *clasp, jsval *vp); JSBool gjs_init_class_dynamic (JSContext *context, JSObject *in_object, JSObject *parent_proto, const char *ns_name, const char *class_name, JSClass *clasp, JSNative constructor, unsigned nargs, JSPropertySpec *ps, JSFunctionSpec *fs, JSPropertySpec *static_ps, JSFunctionSpec *static_fs, JSObject **constructor_p, JSObject **prototype_p); void gjs_throw_constructor_error (JSContext *context); void gjs_throw_abstract_constructor_error (JSContext *context, jsval *vp); JSBool gjs_typecheck_instance (JSContext *context, JSObject *obj, JSClass *static_clasp, JSBool _throw); JSObject* gjs_construct_object_dynamic (JSContext *context, JSObject *proto, unsigned argc, jsval *argv); JSObject* gjs_build_string_array (JSContext *context, gssize array_length, char **array_values); JSObject* gjs_define_string_array (JSContext *context, JSObject *obj, const char *array_name, gssize array_length, const char **array_values, unsigned attrs); void gjs_throw (JSContext *context, const char *format, ...) G_GNUC_PRINTF (2, 3); void gjs_throw_custom (JSContext *context, const char *error_class, const char *format, ...) G_GNUC_PRINTF (3, 4); void gjs_throw_literal (JSContext *context, const char *string); void gjs_throw_g_error (JSContext *context, GError *error); JSBool gjs_log_exception (JSContext *context); JSBool gjs_log_and_keep_exception (JSContext *context); JSBool gjs_move_exception (JSContext *src_context, JSContext *dest_context); JSBool gjs_log_exception_full (JSContext *context, jsval exc, JSString *message); #ifdef __GJS_UTIL_LOG_H__ void gjs_log_object_props (JSContext *context, JSObject *obj, GjsDebugTopic topic, const char *prefix); #endif char* gjs_value_debug_string (JSContext *context, jsval value); void gjs_explain_scope (JSContext *context, const char *title); JSBool gjs_call_function_value (JSContext *context, JSObject *obj, jsval fval, unsigned argc, jsval *argv, jsval *rval); void gjs_error_reporter (JSContext *context, const char *message, JSErrorReport *report); JSObject* gjs_get_global_object (JSContext *cx); JSBool gjs_get_prop_verbose_stub (JSContext *context, JSObject *obj, jsval id, jsval *value_p); JSBool gjs_set_prop_verbose_stub (JSContext *context, JSObject *obj, jsval id, jsval *value_p); JSBool gjs_add_prop_verbose_stub (JSContext *context, JSObject *obj, jsval id, jsval *value_p); JSBool gjs_delete_prop_verbose_stub (JSContext *context, JSObject *obj, jsval id, jsval *value_p); JSBool gjs_string_to_utf8 (JSContext *context, const jsval string_val, char **utf8_string_p); JSBool gjs_string_from_utf8 (JSContext *context, const char *utf8_string, gssize n_bytes, jsval *value_p); JSBool gjs_string_to_filename (JSContext *context, const jsval string_val, char **filename_string_p); JSBool gjs_string_from_filename (JSContext *context, const char *filename_string, gssize n_bytes, jsval *value_p); JSBool gjs_string_get_uint16_data (JSContext *context, jsval value, guint16 **data_p, gsize *len_p); JSBool gjs_get_string_id (JSContext *context, jsid id, char **name_p); jsid gjs_intern_string_to_id (JSContext *context, const char *string); gboolean gjs_unichar_from_string (JSContext *context, jsval string, gunichar *result); const char* gjs_get_type_name (jsval value); JSBool gjs_value_to_int64 (JSContext *context, const jsval val, gint64 *result); JSBool gjs_parse_args (JSContext *context, const char *function_name, const char *format, unsigned argc, jsval *argv, ...); JSBool gjs_parse_call_args (JSContext *context, const char *function_name, const char *format, JS::CallArgs &args, ...); GjsRootedArray* gjs_rooted_array_new (void); void gjs_rooted_array_append (JSContext *context, GjsRootedArray *array, jsval value); jsval gjs_rooted_array_get (JSContext *context, GjsRootedArray *array, int i); jsval* gjs_rooted_array_get_data (JSContext *context, GjsRootedArray *array); int gjs_rooted_array_get_length (JSContext *context, GjsRootedArray *array); jsval* gjs_rooted_array_free (JSContext *context, GjsRootedArray *array, gboolean free_segment); void gjs_set_values (JSContext *context, jsval *locations, int n_locations, jsval initializer); void gjs_root_value_locations (JSContext *context, jsval *locations, int n_locations); void gjs_unroot_value_locations (JSContext *context, jsval *locations, int n_locations); /* Functions intended for more "internal" use */ void gjs_maybe_gc (JSContext *context); JSBool gjs_context_get_frame_info (JSContext *context, jsval *stack, jsval *fileName, jsval *lineNumber); JSBool gjs_eval_with_scope (JSContext *context, JSObject *object, const char *script, gssize script_len, const char *filename, jsval *retval_p); typedef enum { GJS_STRING_CONSTRUCTOR, GJS_STRING_PROTOTYPE, GJS_STRING_LENGTH, GJS_STRING_IMPORTS, GJS_STRING_PARENT_MODULE, GJS_STRING_MODULE_INIT, GJS_STRING_SEARCH_PATH, GJS_STRING_KEEP_ALIVE_MARKER, GJS_STRING_PRIVATE_NS_MARKER, GJS_STRING_GI_MODULE, GJS_STRING_GI_VERSIONS, GJS_STRING_GI_OVERRIDES, GJS_STRING_GOBJECT_INIT, GJS_STRING_NEW_INTERNAL, GJS_STRING_NEW, GJS_STRING_MESSAGE, GJS_STRING_CODE, GJS_STRING_STACK, GJS_STRING_FILENAME, GJS_STRING_LINE_NUMBER, GJS_STRING_NAME, GJS_STRING_LAST } GjsConstString; jsid gjs_context_get_const_string (JSContext *context, GjsConstString string); gboolean gjs_object_get_property_const (JSContext *context, JSObject *obj, GjsConstString property_name, jsval *value_p); const char * gjs_strip_unix_shebang(const char *script, gssize *script_len, int *new_start_line_number); G_END_DECLS #endif /* __GJS_JSAPI_UTIL_H__ */ cjs-2.8.0/cjs/jsapi-private.h0000664000175000017500000000325212610172032014726 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2010 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ /* This file wraps C++ stuff from the spidermonkey private API so we * can use it from our other C files. This file should be included by * jsapi-util.c only. "Public" API from this jsapi-private.c should be * declared in jsapi-util.h */ #ifndef __GJS_JSAPI_PRIVATE_H__ #define __GJS_JSAPI_PRIVATE_H__ #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS void gjs_schedule_gc_if_needed (JSContext *context); void gjs_gc_if_needed (JSContext *context); G_END_DECLS #endif /* __GJS_JSAPI_PRIVATE_H__ */ cjs-2.8.0/cjs/importer.h0000664000175000017500000000412212610172032014006 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_IMPORTER_H__ #define __GJS_IMPORTER_H__ #if !defined (__GJS_GJS_MODULE_H__) && !defined (GJS_COMPILATION) #error "Only can be included directly." #endif #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS JSBool gjs_create_root_importer (JSContext *context, const char **initial_search_path, gboolean add_standard_search_path); JSBool gjs_define_root_importer (JSContext *context, JSObject *in_object); JSObject* gjs_define_importer (JSContext *context, JSObject *in_object, const char *importer_name, const char **initial_search_path, gboolean add_standard_search_path); G_END_DECLS #endif /* __GJS_IMPORTER_H__ */ cjs-2.8.0/cjs/mem.cpp0000664000175000017500000000643312610172032013265 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "mem.h" #include "compat.h" #include #define GJS_DEFINE_COUNTER(name) \ GjsMemCounter gjs_counter_ ## name = { \ 0, #name \ }; GJS_DEFINE_COUNTER(everything) GJS_DEFINE_COUNTER(boxed) GJS_DEFINE_COUNTER(gerror) GJS_DEFINE_COUNTER(closure) GJS_DEFINE_COUNTER(database) GJS_DEFINE_COUNTER(function) GJS_DEFINE_COUNTER(fundamental) GJS_DEFINE_COUNTER(importer) GJS_DEFINE_COUNTER(ns) GJS_DEFINE_COUNTER(object) GJS_DEFINE_COUNTER(param) GJS_DEFINE_COUNTER(repo) GJS_DEFINE_COUNTER(resultset) GJS_DEFINE_COUNTER(weakhash) GJS_DEFINE_COUNTER(interface) #define GJS_LIST_COUNTER(name) \ & gjs_counter_ ## name static GjsMemCounter* counters[] = { GJS_LIST_COUNTER(boxed), GJS_LIST_COUNTER(gerror), GJS_LIST_COUNTER(closure), GJS_LIST_COUNTER(database), GJS_LIST_COUNTER(function), GJS_LIST_COUNTER(fundamental), GJS_LIST_COUNTER(importer), GJS_LIST_COUNTER(ns), GJS_LIST_COUNTER(object), GJS_LIST_COUNTER(param), GJS_LIST_COUNTER(repo), GJS_LIST_COUNTER(resultset), GJS_LIST_COUNTER(weakhash), GJS_LIST_COUNTER(interface) }; void gjs_memory_report(const char *where, gboolean die_if_leaks) { int i; int n_counters; guint total_objects; gjs_debug(GJS_DEBUG_MEMORY, "Memory report: %s", where); n_counters = G_N_ELEMENTS(counters); total_objects = 0; for (i = 0; i < n_counters; ++i) { total_objects += counters[i]->value; } if (total_objects != GJS_GET_COUNTER(everything)) { gjs_debug(GJS_DEBUG_MEMORY, "Object counts don't add up!"); } gjs_debug(GJS_DEBUG_MEMORY, " %d objects currently alive", GJS_GET_COUNTER(everything)); for (i = 0; i < n_counters; ++i) { gjs_debug(GJS_DEBUG_MEMORY, " %12s = %d", counters[i]->name, counters[i]->value); } if (die_if_leaks && GJS_GET_COUNTER(everything) > 0) { g_error("%s: JavaScript objects were leaked.", where); } } cjs-2.8.0/cjs/compat.h0000664000175000017500000001106412610172032013433 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2009 litl, LLC * Copyright (c) 2010 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #if !defined (__GJS_GJS_MODULE_H__) && !defined (GJS_COMPILATION) #error "Only can be included directly." #endif #ifndef __GJS_COMPAT_H__ #define __GJS_COMPAT_H__ #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wstrict-prototypes\"") _Pragma("GCC diagnostic ignored \"-Winvalid-offsetof\"") #endif #include #include // Needed by some bits #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) _Pragma("GCC diagnostic pop") #endif #include #include G_BEGIN_DECLS /* This file inspects jsapi.h and attempts to provide a compatibility shim. * See https://bugzilla.gnome.org/show_bug.cgi?id=622896 for some initial discussion. */ #define JSVAL_IS_OBJECT(obj) (JSVAL_IS_NULL(obj) || !JSVAL_IS_PRIMITIVE(obj)) #define JS_GetGlobalObject(cx) gjs_get_global_object(cx) static JSBool G_GNUC_UNUSED JS_NewNumberValue(JSContext *cx, double d, jsval *rval) { *rval = JS_NumberValue(d); if (JSVAL_IS_NUMBER(*rval)) return JS_TRUE; return JS_FALSE; } /** * GJS_NATIVE_CONSTRUCTOR_DECLARE: * Prototype a constructor. */ #define GJS_NATIVE_CONSTRUCTOR_DECLARE(name) \ static JSBool \ gjs_##name##_constructor(JSContext *context, \ unsigned argc, \ jsval *vp) /** * GJS_NATIVE_CONSTRUCTOR_VARIABLES: * Declare variables necessary for the constructor; should * be at the very top. */ #define GJS_NATIVE_CONSTRUCTOR_VARIABLES(name) \ JSObject *object = NULL; \ jsval *argv G_GNUC_UNUSED = JS_ARGV(context, vp); /** * GJS_NATIVE_CONSTRUCTOR_PRELUDE: * Call after the initial variable declaration. */ #define GJS_NATIVE_CONSTRUCTOR_PRELUDE(name) \ { \ if (!JS_IsConstructing(context, vp)) { \ gjs_throw_constructor_error(context); \ return JS_FALSE; \ } \ object = gjs_new_object_for_constructor(context, &gjs_##name##_class, vp); \ if (object == NULL) \ return JS_FALSE; \ } /** * GJS_NATIVE_CONSTRUCTOR_FINISH: * Call this at the end of a constructor when it's completed * successfully. */ #define GJS_NATIVE_CONSTRUCTOR_FINISH(name) \ JS_SET_RVAL(context, vp, OBJECT_TO_JSVAL(object)); /** * GJS_NATIVE_CONSTRUCTOR_DEFINE_ABSTRACT: * Defines a constructor whose only purpose is to throw an error * and fail. To be used with classes that require a constructor (because they have * instances), but whose constructor cannot be used from JS code. */ #define GJS_NATIVE_CONSTRUCTOR_DEFINE_ABSTRACT(name) \ GJS_NATIVE_CONSTRUCTOR_DECLARE(name) \ { \ gjs_throw_abstract_constructor_error(context, vp); \ return JS_FALSE; \ } G_END_DECLS #endif /* __GJS_COMPAT_H__ */ cjs-2.8.0/cjs/byteArray.h0000664000175000017500000000500512610172032014110 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2010 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_BYTE_ARRAY_H__ #define __GJS_BYTE_ARRAY_H__ #if !defined (__GJS_GJS_H__) && !defined (GJS_COMPILATION) #error "Only can be included directly." #endif #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS JSBool gjs_typecheck_bytearray (JSContext *context, JSObject *obj, JSBool throw_error); JSBool gjs_define_byte_array_stuff (JSContext *context, JSObject **module_out); JSObject * gjs_byte_array_from_byte_array (JSContext *context, GByteArray *array); JSObject * gjs_byte_array_from_bytes (JSContext *context, GBytes *bytes); GByteArray * gjs_byte_array_get_byte_array (JSContext *context, JSObject *object); GBytes * gjs_byte_array_get_bytes (JSContext *context, JSObject *object); void gjs_byte_array_peek_data (JSContext *context, JSObject *object, guint8 **out_data, gsize *out_len); G_END_DECLS #endif /* __GJS_BYTE_ARRAY_H__ */ cjs-2.8.0/cjs/type-module.h0000664000175000017500000000413312610172032014413 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2012 Giovanni Campagna * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef GJS_TYPE_MODULE_H #define GJS_TYPE_MODULE_H #include typedef struct _GjsTypeModule GjsTypeModule; typedef struct _GjsTypeModuleClass GjsTypeModuleClass; #define GJS_TYPE_TYPE_MODULE (gjs_type_module_get_type ()) #define GJS_TYPE_MODULE(module) (G_TYPE_CHECK_INSTANCE_CAST ((module), GJS_TYPE_TYPE_MODULE, GjsTypeModule)) #define GJS_TYPE_MODULE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GJS_TYPE_TYPE_MODULE, GjsTypeModuleClass)) #define GJS_IS_TYPE_MODULE(module) (G_TYPE_CHECK_INSTANCE_TYPE ((module), GJS_TYPE_TYPE_MODULE)) #define GJS_IS_TYPE_MODULE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GJS_TYPE_TYPE_MODULE)) #define GJS_TYPE_MODULE_GET_CLASS(module) (G_TYPE_INSTANCE_GET_CLASS ((module), GJS_TYPE_TYPE_MODULE, GjsTypeModuleClass)) GType gjs_type_module_get_type (void) G_GNUC_CONST; GjsTypeModule *gjs_type_module_get (void); #endif cjs-2.8.0/cjs/native.h0000664000175000017500000000430412610172032013435 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_NATIVE_H__ #define __GJS_NATIVE_H__ #if !defined (__GJS_GJS_MODULE_H__) && !defined (GJS_COMPILATION) #error "Only can be included directly." #endif #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS typedef JSBool (* GjsDefineModuleFunc) (JSContext *context, JSObject **module_out); /* called on context init */ void gjs_register_native_module (const char *module_id, GjsDefineModuleFunc func); /* called by importer.c to to check for already loaded modules */ gboolean gjs_is_registered_native_module(JSContext *context, JSObject *parent, const char *name); /* called by importer.c to load a statically linked native module */ JSBool gjs_import_native_module (JSContext *context, const char *name, JSObject **module_out); G_END_DECLS #endif /* __GJS_NATIVE_H__ */ cjs-2.8.0/libgjs-private/0000775000175000017500000000000012610172032014140 5ustar fabiofabiocjs-2.8.0/libgjs-private/gjs-gdbus-wrapper.h0000664000175000017500000000550212610172032017656 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2011 Giovanni Campagna * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_UTIL_DBUS_H__ #define __GJS_UTIL_DBUS_H__ #include #include #include G_BEGIN_DECLS typedef struct _GjsDBusImplementation GjsDBusImplementation; typedef struct _GjsDBusImplementationClass GjsDBusImplementationClass; typedef struct _GjsDBusImplementationPrivate GjsDBusImplementationPrivate; #define GJS_TYPE_DBUS_IMPLEMENTATION (gjs_dbus_implementation_get_type ()) #define GJS_DBUS_IMPLEMENTATION(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GJS_TYPE_DBUS_IMPLEMENTATION, GjsDBusImplementation)) #define GJS_DBUS_IMPLEMENTATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GJS_TYPE_DBUS_IMPLEMENTATION, GjsDBusImplementationClass)) #define GJS_IS_DBUS_IMPLEMENTATION(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GJS_TYPE_DBUS_IMPLEMENTATION)) #define GJS_IS_DBUS_IMPLEMENTATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GJS_TYPE_DBUS_IMPLEMENTATION)) #define GJS_DBUS_IMPLEMENTATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GJS_TYPE_DBUS_IMPLEMENTATION, GjsDBusImplementationClass)) struct _GjsDBusImplementation { GDBusInterfaceSkeleton parent; GjsDBusImplementationPrivate *priv; }; struct _GjsDBusImplementationClass { GDBusInterfaceSkeletonClass parent_class; }; GType gjs_dbus_implementation_get_type (void); void gjs_dbus_implementation_emit_property_changed (GjsDBusImplementation *self, gchar *property, GVariant *newvalue); void gjs_dbus_implementation_emit_signal (GjsDBusImplementation *self, gchar *signal_name, GVariant *parameters); G_END_DECLS #endif /* __GJS_UTIL_DBUS_H__ */ cjs-2.8.0/libgjs-private/gjs-util.cpp0000664000175000017500000000325212610172032016404 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2012 Giovanni Campagna * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include "gjs-util.h" char * gjs_format_int_alternative_output(int n) { return g_strdup_printf("%Id", n); } void gjs_textdomain(const char *domain) { textdomain(domain); } void gjs_bindtextdomain(const char *domain, const char *location) { bindtextdomain(domain, location); /* Always use UTF-8; we assume it internally here */ bind_textdomain_codeset(domain, "UTF-8"); } cjs-2.8.0/libgjs-private/gjs-gtk-util.c0000664000175000017500000000527112610172032016632 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2014 Endless Mobile, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include "gjs-gtk-util.h" void gjs_gtk_container_child_set_property (GtkContainer *container, GtkWidget *child, const gchar *property, const GValue *value) { GParamSpec *pspec; pspec = gtk_container_class_find_child_property (G_OBJECT_GET_CLASS (container), property); if (pspec == NULL) { g_warning ("%s does not have a property called %s", g_type_name (G_OBJECT_TYPE (container)), property); return; } if ((G_VALUE_TYPE (value) == G_TYPE_POINTER) && (g_value_get_pointer (value) == NULL) && !g_value_type_transformable (G_VALUE_TYPE (value), pspec->value_type)) { /* Set an empty value. This will happen when we set a NULL value from JS. * Since GJS doesn't know the GParamSpec for this property, it * will just put NULL into a G_TYPE_POINTER GValue, which will later * fail when trying to transform it to the GParamSpec's GType. */ GValue null_value = G_VALUE_INIT; g_value_init (&null_value, pspec->value_type); gtk_container_child_set_property (container, child, property, &null_value); g_value_unset (&null_value); } else { gtk_container_child_set_property (container, child, property, value); } } cjs-2.8.0/libgjs-private/gjs-gdbus-wrapper.cpp0000664000175000017500000003216512610172032020216 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2011 Giovanni Campagna. All Rights Reserved. */ #include #include #include "gjs-gdbus-wrapper.h" enum { PROP_0, PROP_G_INTERFACE_INFO, PROP_LAST }; enum { SIGNAL_HANDLE_METHOD, SIGNAL_HANDLE_PROPERTY_GET, SIGNAL_HANDLE_PROPERTY_SET, SIGNAL_LAST, }; static guint signals[SIGNAL_LAST] = { 0 }; struct _GjsDBusImplementationPrivate { GDBusInterfaceVTable vtable; GDBusInterfaceInfo *ifaceinfo; // from gchar* to GVariant* GHashTable *outstanding_properties; guint idle_id; }; G_DEFINE_TYPE(GjsDBusImplementation, gjs_dbus_implementation, G_TYPE_DBUS_INTERFACE_SKELETON) static void gjs_dbus_implementation_method_call(GDBusConnection *connection, const char *sender, const char *object_path, const char *interface_name, const char *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) { GjsDBusImplementation *self = GJS_DBUS_IMPLEMENTATION (user_data); g_signal_emit(self, signals[SIGNAL_HANDLE_METHOD], 0, method_name, parameters, invocation); } static GVariant * gjs_dbus_implementation_property_get(GDBusConnection *connection, const char *sender, const char *object_path, const char *interface_name, const char *property_name, GError **error, gpointer user_data) { GjsDBusImplementation *self = GJS_DBUS_IMPLEMENTATION (user_data); GVariant *value; g_signal_emit(self, signals[SIGNAL_HANDLE_PROPERTY_GET], 0, property_name, &value); /* Marshaling GErrors is not supported, so this is the best we can do (GIO will assert if value is NULL and error is not set) */ if (!value) g_set_error(error, g_quark_from_static_string("gjs-error-domain"), 0, "Property retrieval failed"); return value; } static gboolean gjs_dbus_implementation_property_set(GDBusConnection *connection, const char *sender, const char *object_path, const char *interface_name, const char *property_name, GVariant *value, GError **error, gpointer user_data) { GjsDBusImplementation *self = GJS_DBUS_IMPLEMENTATION (user_data); g_signal_emit(self, signals[SIGNAL_HANDLE_PROPERTY_SET], 0, property_name, value); return TRUE; } static void gjs_dbus_implementation_init(GjsDBusImplementation *self) { GjsDBusImplementationPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GJS_TYPE_DBUS_IMPLEMENTATION, GjsDBusImplementationPrivate); self->priv = priv; priv->vtable.method_call = gjs_dbus_implementation_method_call; priv->vtable.get_property = gjs_dbus_implementation_property_get; priv->vtable.set_property = gjs_dbus_implementation_property_set; priv->outstanding_properties = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_variant_unref); } static void gjs_dbus_implementation_finalize(GObject *object) { GjsDBusImplementation *self = GJS_DBUS_IMPLEMENTATION (object); g_dbus_interface_info_unref (self->priv->ifaceinfo); g_hash_table_unref (self->priv->outstanding_properties); G_OBJECT_CLASS(gjs_dbus_implementation_parent_class)->finalize(object); } static void gjs_dbus_implementation_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GjsDBusImplementation *self = GJS_DBUS_IMPLEMENTATION (object); switch (property_id) { case PROP_G_INTERFACE_INFO: self->priv->ifaceinfo = (GDBusInterfaceInfo*) g_value_dup_boxed (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static GDBusInterfaceInfo * gjs_dbus_implementation_get_info (GDBusInterfaceSkeleton *skeleton) { GjsDBusImplementation *self = GJS_DBUS_IMPLEMENTATION (skeleton); return self->priv->ifaceinfo; } static GDBusInterfaceVTable * gjs_dbus_implementation_get_vtable (GDBusInterfaceSkeleton *skeleton) { GjsDBusImplementation *self = GJS_DBUS_IMPLEMENTATION (skeleton); return &(self->priv->vtable); } static GVariant * gjs_dbus_implementation_get_properties (GDBusInterfaceSkeleton *skeleton) { GjsDBusImplementation *self = GJS_DBUS_IMPLEMENTATION (skeleton); GDBusInterfaceInfo *info = self->priv->ifaceinfo; GDBusPropertyInfo **props; GVariantBuilder builder; g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); for (props = info->properties; *props; ++props) { GDBusPropertyInfo *prop = *props; GVariant *value; /* If we have a cached value, we use that instead of querying again */ if ((value = (GVariant*) g_hash_table_lookup(self->priv->outstanding_properties, prop->name))) { g_variant_builder_add(&builder, "{sv}", prop->name, value); continue; } g_signal_emit(self, signals[SIGNAL_HANDLE_PROPERTY_GET], 0, prop->name, &value); g_variant_builder_add(&builder, "{sv}", prop->name, value); } return g_variant_builder_end(&builder); } static void gjs_dbus_implementation_flush (GDBusInterfaceSkeleton *skeleton) { GjsDBusImplementation *self = GJS_DBUS_IMPLEMENTATION (skeleton); GVariantBuilder changed_props; GVariantBuilder invalidated_props; GHashTableIter iter; GVariant *val; gchar *prop_name; g_variant_builder_init(&changed_props, G_VARIANT_TYPE_VARDICT); g_variant_builder_init(&invalidated_props, G_VARIANT_TYPE_STRING_ARRAY); g_hash_table_iter_init(&iter, self->priv->outstanding_properties); while (g_hash_table_iter_next(&iter, (void**) &prop_name, (void**) &val)) { if (val) g_variant_builder_add(&changed_props, "{sv}", prop_name, val); else g_variant_builder_add(&invalidated_props, "s", prop_name); } g_dbus_connection_emit_signal(g_dbus_interface_skeleton_get_connection(skeleton), NULL, /* bus name */ g_dbus_interface_skeleton_get_object_path(skeleton), "org.freedesktop.DBus.Properties", "PropertiesChanged", g_variant_new("(s@a{sv}@as)", self->priv->ifaceinfo->name, g_variant_builder_end(&changed_props), g_variant_builder_end(&invalidated_props)), NULL /* error */); g_hash_table_remove_all(self->priv->outstanding_properties); if (self->priv->idle_id) { g_source_remove(self->priv->idle_id); self->priv->idle_id = 0; } } void gjs_dbus_implementation_class_init(GjsDBusImplementationClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS(klass); GDBusInterfaceSkeletonClass *skeleton_class = G_DBUS_INTERFACE_SKELETON_CLASS(klass); g_type_class_add_private(klass, sizeof(GjsDBusImplementationPrivate)); gobject_class->finalize = gjs_dbus_implementation_finalize; gobject_class->set_property = gjs_dbus_implementation_set_property; skeleton_class->get_info = gjs_dbus_implementation_get_info; skeleton_class->get_vtable = gjs_dbus_implementation_get_vtable; skeleton_class->get_properties = gjs_dbus_implementation_get_properties; skeleton_class->flush = gjs_dbus_implementation_flush; g_object_class_install_property(gobject_class, PROP_G_INTERFACE_INFO, g_param_spec_boxed("g-interface-info", "Interface Info", "A DBusInterfaceInfo representing the exported object", G_TYPE_DBUS_INTERFACE_INFO, (GParamFlags) (G_PARAM_STATIC_STRINGS | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY))); signals[SIGNAL_HANDLE_METHOD] = g_signal_new("handle-method-call", G_TYPE_FROM_CLASS(klass), (GSignalFlags) 0, /* flags */ 0, /* closure */ NULL, /* accumulator */ NULL, /* accumulator data */ NULL, /* C marshal */ G_TYPE_NONE, 3, G_TYPE_STRING, /* method name */ G_TYPE_VARIANT, /* parameters */ G_TYPE_DBUS_METHOD_INVOCATION); signals[SIGNAL_HANDLE_PROPERTY_GET] = g_signal_new("handle-property-get", G_TYPE_FROM_CLASS(klass), (GSignalFlags) 0, /* flags */ 0, /* closure */ g_signal_accumulator_first_wins, NULL, /* accumulator data */ NULL, /* C marshal */ G_TYPE_VARIANT, 1, G_TYPE_STRING /* property name */); signals[SIGNAL_HANDLE_PROPERTY_SET] = g_signal_new("handle-property-set", G_TYPE_FROM_CLASS(klass), (GSignalFlags) 0, /* flags */ 0, /* closure */ NULL, /* accumulator */ NULL, /* accumulator data */ NULL, /* C marshal */ G_TYPE_NONE, 2, G_TYPE_STRING, /* property name */ G_TYPE_VARIANT /* parameters */); } static gboolean idle_cb (gpointer data) { GDBusInterfaceSkeleton *skeleton = G_DBUS_INTERFACE_SKELETON (data); g_dbus_interface_skeleton_flush(skeleton); return FALSE; } /** * gjs_dbus_implementation_emit_property_changed: * @self: a #GjsDBusImplementation * @property: the name of the property that changed * @newvalue: (allow-none): the new value, or %NULL to just invalidate it * * Queue a PropertyChanged signal for emission, or update the one queued * adding @property */ void gjs_dbus_implementation_emit_property_changed (GjsDBusImplementation *self, gchar *property, GVariant *newvalue) { g_hash_table_replace (self->priv->outstanding_properties, g_strdup (property), g_variant_ref (newvalue)); if (!self->priv->idle_id) self->priv->idle_id = g_idle_add(idle_cb, self); } /** * gjs_dbus_implementation_emit_signal: * @self: a #GjsDBusImplementation * @signal_name: the name of the signal * @parameters: (allow-none): signal parameters, or %NULL for none * * Emits a signal named @signal_name from the object and interface represented * by @self. This signal has no destination. */ void gjs_dbus_implementation_emit_signal (GjsDBusImplementation *self, gchar *signal_name, GVariant *parameters) { GDBusInterfaceSkeleton *skeleton = G_DBUS_INTERFACE_SKELETON (self); g_dbus_connection_emit_signal(g_dbus_interface_skeleton_get_connection(skeleton), NULL, g_dbus_interface_skeleton_get_object_path(skeleton), self->priv->ifaceinfo->name, signal_name, parameters, NULL); } cjs-2.8.0/libgjs-private/gjs-gtk-util.h0000664000175000017500000000317212610172032016635 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2014 Endless Mobile, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_PRIVATE_GTK_UTIL_H__ #define __GJS_PRIVATE_GTK_UTIL_H__ #include "config.h" #ifdef ENABLE_GTK #include G_BEGIN_DECLS void gjs_gtk_container_child_set_property (GtkContainer *container, GtkWidget *child, const gchar *property, const GValue *value); G_END_DECLS #endif #endif /* __GJS_PRIVATE_GTK_UTIL_H__ */ cjs-2.8.0/libgjs-private/gjs-util.h0000664000175000017500000000304012610172032016044 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2012 Giovanni Campagna * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_PRIVATE_UTIL_H__ #define __GJS_PRIVATE_UTIL_H__ #include G_BEGIN_DECLS /* For imports.format */ char * gjs_format_int_alternative_output (int n); /* For imports.gettext */ void gjs_textdomain (const char *domain); void gjs_bindtextdomain (const char *domain, const char *location); G_END_DECLS #endif cjs-2.8.0/ChangeLog0000664000175000017500000000000012610172032012756 0ustar fabiofabiocjs-2.8.0/verbump.py0000664000175000017500000000270712610172032013256 0ustar fabiofabio#!/usr/bin/env python2 # Automakes a release preparation for a post-release project # * Create a git tag # * Bump version in configure.ac and commit it import re import os import sys import subprocess micro_version_re = re.compile('m4_define.*pkg_micro_version, ([0-9]+)') micro_version_replace = 'm4_define(pkg_micro_version, %d)\n' def _extract_config_log_variable(name): f = open('config.log') keystart = name + '=\'' for line in f: if line.startswith(keystart): return line[len(keystart):-2] f.close() fatal("Failed to find '%s' in config.status" % (name, )) if not os.path.isfile('config.log'): fatal("Couldn't find config.log; did you run configure?") package = _extract_config_log_variable('PACKAGE_TARNAME') version = _extract_config_log_variable('VERSION') configure_path=os.path.join(os.environ['top_srcdir'], 'configure.ac') f = open(configure_path) newf = open(configure_path + '.tmp', 'w') for line in f: m = micro_version_re.match(line) if not m: newf.write(line) continue v = int(m.group(1)) newv = v+1 print "Will update micro version from %s to %s" % (v, newv) newf.write(micro_version_replace % (newv, )) newf.close() os.rename(configure_path + '.tmp', configure_path) print "Successfully wrote new 'configure.ac' with post-release version bump" args=['git', 'commit', '-m', "configure: Post-release version bump", configure_path] print "Running: %r" % (args, ) subprocess.check_call(args) cjs-2.8.0/Makefile-test.am0000664000175000017500000000674112610172032014237 0ustar fabiofabioEXTRA_DIST += \ test/run-with-dbus \ test/test-bus.conf RUN_WITH_DBUS = ${top_srcdir}/test/run-with-dbus --session --system GTESTER = ${TESTS_ENVIRONMENT} ${RUN_WITH_DBUS} gtester CLEANFILES += uninstalled-system-test-bus.conf uninstalled-test-bus.conf clean-local: test-user-data-clean test-user-data-clean: -rm -fr $(builddir)/test_user_data ######################################################################## TEST_PROGS += gjs-tests gjs_tests_CPPFLAGS = \ $(AM_CPPFLAGS) \ -DGJS_COMPILATION \ $(GJSTESTS_CFLAGS) \ $(gjs_directory_defines) \ -I$(top_srcdir)/test mock_js_resources_files = $(shell glib-compile-resources --sourcedir=$(srcdir) --generate-dependencies $(srcdir)/test/mock-js-resources.gresource.xml) mock-js-resources.h: $(srcdir)/test/mock-js-resources.gresource.xml $(modules_resource_files) $(AM_V_GEN) glib-compile-resources --target=$@ --sourcedir=$(srcdir) --sourcedir=$(builddir) --generate --c-name mock_js_resources $< mock-js-resources.c: $(srcdir)/test/mock-js-resources.gresource.xml $(modules_resource_files) $(AM_V_GEN) glib-compile-resources --target=$@ --sourcedir=$(srcdir) --sourcedir=$(builddir) --generate --c-name mock_js_resources $< EXTRA_DIST += $(mock_js_resources_files) $(srcdir)/test/mock-js-resources.gresource.xml \ $(srcdir)/test/gjs-test-coverage/loadedJSFromResource.js ## -rdynamic makes backtraces work gjs_tests_LDFLAGS = -rdynamic gjs_tests_LDADD = \ libcjs.la \ $(GJSTESTS_LIBS) gjs_tests_SOURCES = \ test/gjs-tests.cpp \ test/gjs-tests-add-funcs.h \ test/gjs-test-coverage.cpp \ mock-js-resources.c check-local: gjs-tests @test -z "${TEST_PROGS}" || ${GTESTER} -k --verbose ${TEST_PROGS} ${TEST_PROGS_OPTIONS} # GJS_PATH is empty here since we want to force the use of our own # resources TESTS_ENVIRONMENT = \ TOP_SRCDIR=$(abs_top_srcdir) \ DBUS_SESSION_BUS_ADDRESS='' \ XDG_DATA_HOME=test_user_data \ GJS_DEBUG_OUTPUT=test_user_data/logs/cjs.log \ BUILDDIR=. \ GJS_USE_UNINSTALLED_FILES=1 \ GJS_TEST_TIMEOUT=420 \ GJS_PATH= \ GI_TYPELIB_PATH=$(builddir):$(GI_TYPELIB_PATH) \ LD_LIBRARY_PATH="$(LD_LIBRARY_PATH):$(FIREFOX_JS_LIBDIR)" \ G_FILENAME_ENCODING=latin1 # ensure filenames are not utf8 if ENABLE_COVERAGE # These paths are resource paths but they have resource:// # stripped out, for ease of parsing in the test runner coverage_paths = \ /org/gnome/gjs/modules/cairo.js \ /org/gnome/gjs/modules/coverage.js \ /org/gnome/gjs/modules/format.js \ /org/gnome/gjs/modules/gettext.js \ /org/gnome/gjs/modules/jsUnit.js \ /org/gnome/gjs/modules/lang.js \ /org/gnome/gjs/modules/mainloop.js \ /org/gnome/gjs/modules/signals.js empty := space := $(empty) $(empty) colon := : coverage_env := $(subst $(space),$(colon),$(coverage_paths)) TESTS_ENVIRONMENT += \ GJS_UNIT_COVERAGE_OUTPUT=lcov \ GJS_UNIT_COVERAGE_PATHS=$(coverage_env) endif ######################################################################## if ENABLE_COVERAGE lcov: test -d lcov || mkdir lcov $(LCOV) --compat-libtool --directory . --capture -o lcov/lcov_tmp.info $(LCOV) --extract lcov/lcov_tmp.info "$(PWD)/*" -o lcov/lcov.info rm -f lcov/lcov_tmp.info $(GENHTML) --legend -o lcov lcov/lcov.info lcov/coverage.lcov lcov-clean: find . -name '*.gcda' -delete rm -rf lcov lcov-realclean: lcov-clean find . -name '*.gcno' -delete clean-local: lcov-realclean .PHONY: lcov lcov-clean lcov-realclean else lcov: @echo >&1 "*** ERROR: 'configure --enable-coverage' required" @exit 1 .PHONY: lcov endif cjs-2.8.0/autogen.sh0000775000175000017500000000213112610172032013214 0ustar fabiofabio#!/bin/sh # Run this to generate all the initial makefiles, etc. srcdir=`dirname $0` test -z "$srcdir" && srcdir=. PKG_NAME="gjs" REQUIRED_AUTOCONF_VERSION=2.53 REQUIRED_AUTOMAKE_VERSION=1.7.2 (test -f $srcdir/configure.ac \ && test -f $srcdir/autogen.sh) || { echo -n "**Error**: Directory "\`$srcdir\'" does not look like the" echo " top-level $PKG_NAME directory" exit 1 } DIE=0 # This is a bit complicated here since we can't use gnome-config yet. # It'll be easier after switching to pkg-config since we can then # use pkg-config to find the gnome-autogen.sh script. gnome_autogen= gnome_datadir= ifs_save="$IFS"; IFS=":" for dir in $PATH ; do test -z "$dir" && dir=. if test -f $dir/gnome-autogen.sh ; then gnome_autogen="$dir/gnome-autogen.sh" gnome_datadir=`echo $dir | sed -e 's,/bin$,/share,'` break fi done IFS="$ifs_save" if test -z "$gnome_autogen" ; then echo "You need to install the gnome-common module and make" echo "sure the gnome-autogen.sh script is in your \$PATH." exit 1 fi GNOME_DATADIR="$gnome_datadir" USE_GNOME2_MACROS=1 . $gnome_autogen cjs-2.8.0/AUTHORS0000664000175000017500000000000012610172032012254 0ustar fabiofabiocjs-2.8.0/README0000664000175000017500000000113212610172032012073 0ustar fabiofabioThis module contains JavaScript bindings based on gobject-introspection. Because JavaScript is pretty free-form, consistent coding style and unit tests are critical to give it some structure and keep it readable. We propose that all GNOME usage of JavaScript conform to the style guide in doc/Style_Guide.txt to help keep things sane. = Testing = This module is prototyping https://live.gnome.org/GnomeGoals/InstalledTests To run the tests, simply build it as normal. Now: cd installed-tests ./autogen.sh && ./configure --prefix=... && make && make install $prefix/libexec/gjs/gjs-installed-tests cjs-2.8.0/Makefile.am0000664000175000017500000001420412610172032013253 0ustar fabiofabio# http://people.gnome.org/~walters/docs/build-api.txt .buildapi-allow-builddir: -include $(INTROSPECTION_MAKEFILE) bin_PROGRAMS = lib_LTLIBRARIES = noinst_HEADERS = noinst_LTLIBRARIES = dist_gjsjs_DATA = BUILT_SOURCES = CLEANFILES = EXTRA_DIST = TEST_PROGS = check_PROGRAMS = $(TEST_PROGS) INTROSPECTION_GIRS = ACLOCAL_AMFLAGS = ${ACLOCAL_FLAGS} AM_CFLAGS = $(WARN_CFLAGS) -DG_LOG_DOMAIN=\"Cjs\" AM_CPPFLAGS = $(WARN_CXXFLAGS) -DG_LOG_DOMAIN=\"Cjs\" gjs_public_includedir = $(includedir)/cjs-1.0 gjs_module_includedir = $(includedir)/cjs-1.0 ######################################################################## nobase_gjs_public_include_HEADERS = \ cjs/context.h \ cjs/gjs.h nobase_gjs_module_include_HEADERS = \ cjs/gjs-module.h \ cjs/compat.h \ cjs/coverage.h \ cjs/byteArray.h \ cjs/importer.h \ cjs/jsapi-util.h \ cjs/runtime.h \ cjs/type-module.h \ cjs/mem.h \ cjs/native.h \ gi/ns.h \ gi/object.h \ gi/foreign.h \ gi/fundamental.h \ gi/param.h \ gi/repo.h \ gi/union.h \ gi/value.h \ gi/arg.h \ gi/boxed.h \ gi/closure.h \ gi/enumeration.h \ gi/function.h \ gi/keep-alive.h \ gi/interface.h \ gi/gtype.h \ gi/gerror.h noinst_HEADERS += \ cjs/jsapi-private.h \ cjs/context-private.h \ gi/proxyutils.h \ util/crash.h \ util/hash-x32.h \ util/error.h \ util/glib.h \ util/log.h \ util/misc.h ######################################################################## pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = cjs-1.0.pc cjs-internals-1.0.pc EXTRA_DIST += \ cjs-1.0.pc.in \ cjs-internals-1.0.pc.in ######################################################################## gjs_directory_defines = \ -DGJS_TOP_SRCDIR=\"$(top_srcdir)\" \ -DPKGLIBDIR=\"$(pkglibdir)\" ######################################################################## lib_LTLIBRARIES += libcjs.la libcjs_la_CPPFLAGS = \ $(AM_CPPFLAGS) \ $(GJS_CFLAGS) \ $(gjs_directory_defines)\ -I$(top_srcdir)/gi \ -DGJS_COMPILATION libcjs_la_CFLAGS = \ $(AM_CFLAGS) libcjs_la_LDFLAGS = \ $(EXTRA_LINK_FLAGS) \ -export-symbols-regex "^[^_]" -version-info 0:0:0 \ -no-undefined \ -rdynamic libcjs_la_LIBADD = \ $(GJS_LIBS) if ENABLE_GTK libcjs_la_CPPFLAGS += $(GJS_GTK_CFLAGS) libcjs_la_LIBADD += $(GJS_GTK_LIBS) endif libcjs_la_SOURCES = \ cjs/byteArray.cpp \ cjs/context.cpp \ cjs/importer.cpp \ cjs/gi.h \ cjs/gi.cpp \ cjs/coverage.cpp \ cjs/jsapi-private.cpp \ cjs/jsapi-util.cpp \ cjs/jsapi-dynamic-class.cpp \ cjs/jsapi-util-array.cpp \ cjs/jsapi-util-error.cpp \ cjs/jsapi-util-string.cpp \ cjs/mem.cpp \ cjs/native.cpp \ cjs/runtime.cpp \ cjs/stack.cpp \ cjs/type-module.cpp \ modules/modules.cpp \ modules/modules.h \ util/error.cpp \ util/hash-x32.cpp \ util/glib.cpp \ util/crash.cpp \ util/log.cpp \ util/misc.cpp # For historical reasons, some files live in gi/ libcjs_la_SOURCES += \ gi/gjs_gi_trace.h \ gi/arg.cpp \ gi/boxed.cpp \ gi/closure.cpp \ gi/enumeration.cpp \ gi/function.cpp \ gi/keep-alive.cpp \ gi/ns.cpp \ gi/object.cpp \ gi/foreign.cpp \ gi/fundamental.cpp \ gi/param.cpp \ gi/proxyutils.cpp \ gi/repo.cpp \ gi/union.cpp \ gi/value.cpp \ gi/interface.cpp \ gi/gtype.cpp \ gi/gerror.cpp # Also, these files used to be a separate library libcjs_private_source_files = \ libgjs-private/gjs-gdbus-wrapper.cpp \ libgjs-private/gjs-gdbus-wrapper.h \ libgjs-private/gjs-util.cpp \ libgjs-private/gjs-util.h \ libgjs-private/gjs-gtk-util.h if ENABLE_GTK libcjs_private_source_files += \ libgjs-private/gjs-gtk-util.c \ $(NULL) endif libcjs_la_SOURCES += $(libcjs_private_source_files) CjsPrivate-1.0.gir: libcjs.la CjsPrivate_1_0_gir_LIBS = libcjs.la CjsPrivate_1_0_gir_INCLUDES = GObject-2.0 Gio-2.0 CjsPrivate_1_0_gir_FILES = $(libcjs_private_source_files) CjsPrivate_1_0_gir_SCANNERFLAGS = --identifier-prefix=Gjs --symbol-prefix=gjs_ --warn-all if ENABLE_GTK CjsPrivate_1_0_gir_INCLUDES += Gtk-3.0 endif INTROSPECTION_GIRS += CjsPrivate-1.0.gir if ENABLE_DTRACE gjs_gi_probes.h: gi/gjs_gi_probes.d $(DTRACE) -C -h -s $< -o $@ gjs_gi_probes.o: gi/gjs_gi_probes.d $(DTRACE) -G -s $< -o $@ BUILT_SOURCES += gjs_gi_probes.h gjs_gi_probes.o libcjs_la_LIBADD += gjs_gi_probes.o endif EXTRA_DIST += gi/gjs_gi_probes.d tapset_in_files = cjs/cjs.stp.in EXTRA_DIST += $(tapset_in_files) if ENABLE_SYSTEMTAP cjs/cjs.stp: cjs/cjs.stp.in Makefile sed -e s,@EXPANDED_LIBDIR@,$(libdir), < $< > $@.tmp && mv $@.tmp $@ tapsetdir = $(DESTDIR)$(datadir)/systemtap/tapset tapset_DATA = $(tapset_in_files:.stp.in=.stp) endif include Makefile-modules.am include Makefile-examples.am typelibdir = $(pkglibdir)/girepository-1.0 typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib) CLEANFILES += $(INTROSPECTION_GIRS) $(typelib_DATA) ######################################################################## bin_PROGRAMS += cjs-console cjs_console_CPPFLAGS = \ $(AM_CPPFLAGS) \ $(GOBJECT_CFLAGS) cjs_console_LDADD = \ $(JS_LIBS) \ $(GOBJECT_LIBS) \ libcjs.la cjs_console_LDFLAGS = -rdynamic cjs_console_SOURCES = cjs/console.cpp install-exec-hook: (cd $(DESTDIR)$(bindir) && ln -sf cjs-console$(EXEEXT) cjs$(EXEEXT)) include Makefile-test.am include Makefile-insttest.am EXTRA_DIST += autogen.sh COPYING.LGPL # Colin's handy Makefile bits for: # 1) stuffing tarballs with pre-generated scripts from your workstation # 2) bumping configure.ac version post-release # 3) tagging correctly in git # 4) uploading to gnome.org # To use: # $ make check # $ make dist # $ make prepare-minor-release # Customize to taste TAG_PREFIX=CJS_ COMPRESSION=.bz2 PACKAGE=@PACKAGE@ VERSION=@VERSION@ DISTNAME=$(PACKAGE)-$(VERSION).tar$(COMPRESSION) TAG_VERSION=$(shell echo $(VERSION) |sed s/\\\./_/g) prepare-release-tag: Makefile git tag -m "Tag $(TAG_VERSION)" -a $(TAG_PREFIX)$(TAG_VERSION) prepare-minor-release: $(DISTNAME) prepare-release-tag Makefile env top_srcdir=$(top_srcdir) python $(top_srcdir)/verbump.py upload-release: $(DISTNAME) Makefile git log origin/master..master @echo -n "Ok to push? [y/N] "; read ans; test x$$ans == xy || exit 1 git push --tags origin master:master scp $(DISTNAME) master.gnome.org: ssh master.gnome.org install-module $(DISTNAME) cjs-2.8.0/examples/0000775000175000017500000000000012610172032013034 5ustar fabiofabiocjs-2.8.0/examples/gio-cat.js0000664000175000017500000000114012610172032014711 0ustar fabiofabio const GLib = imports.gi.GLib; const Gio = imports.gi.Gio; let loop = GLib.MainLoop.new(null, false); function cat(filename) { let f = Gio.file_new_for_path(filename); f.load_contents_async(null, function(f, res) { let contents; try { contents = f.load_contents_finish(res)[1]; } catch (e) { log("*** ERROR: " + e.message); loop.quit(); return; } print(contents); loop.quit(); }); loop.run(); } if (ARGV.length != 1) { printerr("Usage: gio-cat.js filename"); } else { cat(ARGV[0]); } cjs-2.8.0/examples/clutter.js0000664000175000017500000000100112610172032015044 0ustar fabiofabioconst Clutter = imports.gi.Clutter; Clutter.init(null); let stage = new Clutter.Stage(); let texture = new Clutter.Texture({ filename: 'test.jpg', reactive: true }); texture.connect('button-press-event', function(o, event) { log('Clicked!'); return true; }); let color = new Clutter.Color(); color.from_string('Black'); stage.color = color; stage.add_actor(texture); stage.show(); Clutter.main(); cjs-2.8.0/examples/webkit.js0000664000175000017500000000046712610172032014666 0ustar fabiofabioconst Gtk = imports.gi.Gtk; const WebKit = imports.gi.WebKit; Gtk.init(null); let win = new Gtk.Window(); let sw = new Gtk.ScrolledWindow({}); win.add(sw); let view = new WebKit.WebView(); view.load_uri("http://www.google.com/"); sw.add(view); win.set_size_request(640, 480); win.show_all(); Gtk.main(); cjs-2.8.0/examples/test.jpg0000664000175000017500000003442012610172032014520 0ustar fabiofabioJFIFHHC     C    B !1AQa"q2 #B3Rb$CSr46!1A"Qa2qB#3CRS ?;4jOkJiMZqh=T%x hB2>ZdbHb?MO]K_~}~/VyUշR,GjyYKGۼH=7vN3n.)4$gvR1 E!:4̤ls5b}>H*"eٲOdԃ3RQKt@UwD5Ov* RJOR AJH!iBic)R\cJagZkJ!=L92\u<,%Oo[o8 {V~͉hB"*<ϽV[\.(*hѩ8M +je,SXN{SߒHiǵA?(*$sxQrpa=@ "&ExLQ^QPjgLwoNy?XҬ׿|;!ꙣoq_~ <лh&ڪkjfJwcc8895` Mg}_i {ԄD mz(g?E,ʱ ƒj*L3$#I-(!R "TqS  B}9ܛb[+ORڌJ1KjXnu^GVI7ž]IkmR^XrN5ȔV{54}Rs.N(JjGێ,p*%%^$n|=ϕnvύrzaZOz:r5-;*PRxHQV w A#MU9[yzlٯIJuN9% uN)Grs%ɘ ݣÙM\z:Fی-`m4:,io ёsr+t7RjS:1NŭgnohO'KȚ9Pk # WI/DoB)Xkp bEcPi,ET,At$sH(<ԐщME$ )Sx*]ّMi ~ TiH<△U() )*Q)P   *\)zSZtbAHrDOj`#R3P' tCv47/8S `V$mۜe#*W?ʫJZ^|"6fP`C1Og`.Ǡ+|S֖Oq1v7<\-X @=ƗI>C@^dۚGʩжmnU?|6zVGC&7X,۴RGi)~IYܵ-dg'!t`U7{ll ͣi[9I>沤pzihJ_LCX9,l- ,I%\*67]!FH)jQXW-ncctTx^$RJAM6<$8SkNPF%dԻIRF)%HXE=!I::bqUJ?(āQ%JtOM)#iJӣSQ)ѩR;nV@{n,hKWۜK!Jn>6)`'g򾋦[Q#(:MC0Gʕm #8 z&Y7Ȗ&Z puwda#YݳeU~Kk#plMûℽOZFa,|T{!Ê 6AH!*#NHCN[ u2;ޕTN|7}} D94tґ-/B_r1kesb*3 Y@BpxQDp\DeOmd))Hޞ ?Z^t 415_bՈ!bA:HJGMGlFBauy Ax (ʰJ"P\1I1_1NGDI|SJ!=r:j* htNÿ"R14#8I?X}PA۸R@==𛨵B'j ҵ| ih) oy`@nl.:S|ًW}幹+RI VOl`r{ js(4FUgnrq-rmJm,>O$ym ۇgAfIY)8O^j$k5pϨv@⣷]14#n|W 9\@c6Ԗ f@VEs9+5Y 7hC-W "C6HtC3=M$x7{\CmJF28~lUonP㑢>qAZކ|qNJf|d󸸤^rPكr16=).# u=Ч>$OG] Ҳ@|rp79Ț9qCXI+W R+;wQVH8ӴF@=14m<29roc Z ֬ʸsǭ0#-{}? uFF1'{Ի~"M:#N]$"3/Q$g+60.|?K:6w[Z|j)DyA]R7r$m ~|Q&k$icJ!y)!d'p8ϒ{/e<8X(ǯv.4 G(ʎ2qϿ4BMYrolU!*_*{<ȸ8!ۡ3e;!{iNA+I'$KoNưQ긛e̒vC<e%nG ʕ1Uuԭ>dqZ+*#tm)uFIJ\uA{P$r0yCf3o*BTN!{>[J5`OgE|>N g[%60kԽ(l234HQGm߿iahy#>UAQV_PFGZ„/,Kh:<CI'aXo6j΁5s Caeѐ2#5.A BAPsI5yr}vCu,OmjjOM5ݴѮQ -l/,X^kmh .|GK,qj)”>^@vt_Gb`cqTE' @=ϕ]kKY;MfRPQD Q Y{{ä3Ze+< wI'?"# )Gtkjt-^udg$>yX ?Vݹ7=P.HBZCHSM$.1IP$܀qt:26ϑ??}gֽJބ7[r4X!I*.$R{`C5+dzif:Kz|72z;IR-o-mބ(AT8զL׀*Y2@m~^BqR쇶ʘda)@ 8˜ zRe 8RC9- EbH{fx ji̩. qp^@ۼU;Ge$G N0GjcaT9u-6ƂBɞBJtWm6AJV*4~tVŭ:[K|`)4~Fiov&b` RW;;\( MGa͒c&Fɩ𣞨 m@RQ!?%1C[GiɒaqA)B{j$z'ܷoW";V=(b318U`^i}!Lm%@浣 c>BeH*i%JT"tצZYz$^Vv'U<=DXCyZ:)Nt7D\ _D?e R#?Pl](5ڙcNq"3H8ӳIi=G^U^V£FH9'}g>_4Q(qH (dDyjN8“ZmbqVLp a0ac܃SJvvF-1_\2Yi88d^7_M=;MXNa\;BgVЗΨ JV8A#jE'|Ni<^~W1.]F Z JqC~VR6%9+>X4$g~b%w4ܝÂqֳ 4.CytFXS% -e#*x2EV{=nP7jh3VlχDzjz] vjͻtDSuЛ[ڢ>omPu di`ehM*غ݆ʔ`zt>0dT2jO^SW)p!b=CWB )u$ȅ tQXD]ic#C0NDc Ə:{QXAÉ:{ :- m ;9 )SڕR#*BZu R"1W5}BߵWZrnf,Fm606{j?xf:q7+˫[<$y+6}NMͭulf+x N\:Ǭշ,iK<䓵+nx#$qkB _ߚ$p YK2^5n/[600MD3Jr}PŧC6SMy2F>6>~sC NGiyԱW75qݐt^Oz~c,eS$:i8s؟j\Ҽ|:^ 6-޻qBfL' emI U۱Kz-3bèL]cC 1n.&`g )o 품 8m 'j>p9^*R$ V,cWűgcC J| (c!N녊>Uq-\}a6v*8'3} BѺե.+Ks(PRǨUPSZ/\Oj$lyK[&4*gEhڬE"ިUƝ$ét=WhkBJvP(Q⻤t3c(rԥ+$X%caRjPMH l%\62Kc5]PKٟG=46}Gik'P;GEw%͠ CMĠQ$3n65hަvʹ1TAs9Z? ɊVů]eΏq.&Pg˻%Jڡ?js#9P{r)d:Rv{Z eR:ׂ w~Jԭ sP-V@:I) m\B@7K4}6!lZy'''Fjg*p:#b\wPk JNA< nmlG /!(xIeҟH8ڑ1ul.!(v*d\yT`?_jf2[Z1٣ DzjSuHe0$ҐVR=|+?W3ġ:h}ډ|B_5OصkG%拉"״ 䁟L͘u[K s+Z>4)t}[ܢ\ܖ%_7*Q)$sUked6NЁѽx4sDg)G׊sSjk5-)wN\a Tq@8)CP.ݒ);<$ ٺ^47XEYJ %+8N{cmlDW9:nʏD\"%ثT9*d>j@װ8ΖqoUxr(Jvť]K" /N,)06js@A|v,v*?tGr0k1)]e8Rz^_^=nVMg͉v`$}생#i>$~}S{Zŕ2)Pw$[B #$v=5A@JD) .czr}k~Ju'P8(i$xc wֲmW8ulv)PۉI'r'w}1چ3jN=c!}z)<0m?. oq[$ VlgJՍ}qH|IzGHO&5ZM7V\Y8fH\Ҡ0Bv{[RM =G/7G+V /Kܗ}e+[N iAن8h Imv AmA +"N\PZJx%~X]Kp6ą O|PSaL5}$RۈV33pTe{T{xI Y/T!67>9ZZR^Jÿ8AZsֶKv<)G<"*{7=lÁH ϪEYhr+i)2AEE$#Y3\rGjy.9IA,5uʡSU '? [{cr7m#je-unb )*Vx5kWhǼ9PR*bAl$@ǸVuNmpЙ'i',+mU?y=5BkkK:Ng"v݇wBf (r&AQQŽ삐pG>Hƛ)-/-›n/\PwbmNS+jtofVzjY\.q "dYqw.QTD87mVRqGi22'<myNI7L5CTZVw6?0F@ϯߎ1& -ڂFcQxz-突xѱ^#pq~mbFǾ|)b}Jy 20W A5gژݢ)b+˯evyG)JBN}<|}"öxxm7BHT p{J_EVs_lOTV~`3ϵFW S v[3Bb,q%Rv6G9oh[uҰ,Xw+~/)+ZBKkHWIfGM[zu 靲ElyѮPI7; +#}-VmUR$֔qbbZ㎁o 2ʂR YNp7u#^ua)HPOG3y秩BkL8BQVwSwpe:A9؟H(PQ'WէH1JB}0פCYwU]IUS߅.(um=&LͮC#psM*'P|:id\ ^Rɬ!ّ)[]DyX HvaJ0SO6? oifLiR l3ZHPQsAR FЌbo Z $Xx={ 'f8Ef]Pe H Pޭ22{zI iiuYٰ9S B`s-XPm['b r;wjZgu*ctyh+L T3#I \ݧszEc]Ӡ+uil9|\FP[ ##`>=ɫ-mI"Ypa)}+:PmU.Su6 IZharzEҔd#=&B0ֆǒC"X$)drqVYPBtC4y4Ue2Hyr2|{x)&:gB^&У @Unn>]J9_`%n-|U'ԉZ6P\NsڇGNR2ATr)¤Rid%vcIѽPNS+ 4e>E*RBɅ91y/R~>94XmBm2}ojM9r:]niCL7|j0PK.1Q'=YFf[[C%C  _¦k\w;fVŃ;({YҴhhE#wkuࣨVRnm+`x3?ڍqsG~88tTewӒy˴NaM/P fmlǮAjPmEJY94--&78bZۓ>kkQ(BO Y>S3Nvi/ •}[noCۡ '>Ϡ9 P:ok-VRXma$Rk#5 KPBRY=kIwTaT!ݤRH+9|tţ\v.U>Hl]RJ ChkI@?ohKm$0 U>w0B;qԴ9@$-!Yl]mkhhpRsœȸ{6=>ә ϟA6)^Ou?ZHjz~ډjaDF=+GU{lp֊ Dt '>{+9S AJ8wNn^t'Z;Pex|Ǝg%rk|tWP\VR}yU33>*bى7f6~e2A(FFȷR<"rhmmԜbZ *z{ilrܖRQ@s|3$s FwmZNJS!9'<?,P\|@gYϸ+ܥꁺY3|!VM# r~nHXSENR@R @귐ZEtz} hsaLNLer%2*27Df랟.w |0 B) *FrG% hI̓d&m8ФPIlv{VƛPE8Ҷ^X\ڨs*+%);e k8M\VXSe#9²yҳD.iaǘF_H77޴ s&3tCI8䁐2{WMهv'漇O=@]/ee@H`V \~BAܯAɩo 梾ʆUqSlr;rR{ .br@:)+@Hcmg~~>e"ܭ}އ*"ٙ ^TCuzu/>H"as9톽Җw;#џzb`"kyޒ<+q\ܥ%Gi4PDhXHA4" NQ<+)WC% JJ8!(T('9+uH>G&q'Ί†iCeA!pbP55 ]ec:ZqM: )ROP"k#![=?NV؇}rtdc3I_p<40 QV۪x_:mR$ACLj;CYO6/dN)?ai~ pi7ƟMd꾄Աټ0҈{ ړug3-HYgb]ZO9V?CE'k9KfLGXqK4J4S3w I ;Vꛕ SWZqPg"{C|f31hme)@?47̳tj<K7̶C*a:+H2{e}%I$'hMĩ{ZyQXꉠ;EJ8:ڝZ>8hzm'F%T}>8= k?&4e/ha0#sԽNGJjWQ/fK#O;#lK٥)MD?uzD腫S!SF*PsU; $c8D9KڟP;Z5֣[vq$~Iio P: #"S.>p[*?\ۊJAڌ;~QRXOΆHV#P2sP.Fhە/> r0oTJTA㏥1!L9KBrG3Mip%)^)Lz\ڕ (`9!F_cjs-2.8.0/examples/gtk.js0000664000175000017500000000437312610172032014166 0ustar fabiofabioconst Gtk = imports.gi.Gtk; // This is a callback function. The data arguments are ignored // in this example. More on callbacks below. function hello(widget) { log("Hello World"); } function onDeleteEvent(widget, event) { // If you return FALSE in the "delete_event" signal handler, // GTK will emit the "destroy" signal. Returning TRUE means // you don't want the window to be destroyed. // This is useful for popping up 'are you sure you want to quit?' // type dialogs. log("delete event occurred"); // Change FALSE to TRUE and the main window will not be destroyed // with a "delete_event". return false; } function onDestroy(widget) { log("destroy signal occurred"); Gtk.main_quit(); } Gtk.init(null); // create a new window let win = new Gtk.Window({ type: Gtk.WindowType.TOPLEVEL }); // When the window is given the "delete_event" signal (this is given // by the window manager, usually by the "close" option, or on the // titlebar), we ask it to call the onDeleteEvent () function // as defined above. win.connect("delete-event", onDeleteEvent); // Here we connect the "destroy" event to a signal handler. // This event occurs when we call gtk_widget_destroy() on the window, // or if we return FALSE in the "onDeleteEvent" callback. win.connect("destroy", onDestroy); // Sets the border width of the window. win.set_border_width(10); // Creates a new button with the label "Hello World". let button = new Gtk.Button({ label: "Hello World" }); // When the button receives the "clicked" signal, it will call the // function hello(). The hello() function is defined above. button.connect("clicked", hello); // This will cause the window to be destroyed by calling // gtk_widget_destroy(window) when "clicked". Again, the destroy // signal could come from here, or the window manager. button.connect("clicked", function() { win.destroy(); }); // This packs the button into the window (a GTK container). win.add(button); // The final step is to display this newly created widget. button.show(); // and the window win.show(); // All gtk applications must have a Gtk.main(). Control ends here // and waits for an event to occur (like a key press or mouse event). Gtk.main(); cjs-2.8.0/examples/http-server.js0000664000175000017500000000171312610172032015657 0ustar fabiofabio// This is a simple example of a HTTP server in Gjs using libsoup const Soup = imports.gi.Soup; function main() { let handler = function(server, msg, path, query, client) { msg.status_code = 200; msg.response_headers.set_content_type('text/html', {}); msg.response_body.append('Greetings, visitor from ' + client.get_host() + '
What is your name?
\n'); }; let helloHandler = function(server, msg, path, query, client) { if (!query) { msg.set_redirect(302, '/'); return; } msg.status_code = 200; msg.response_headers.set_content_type('text/html', { charset: 'UTF-8' }); msg.response_body.append('Hello, ' + query.myname + '! \u263A
Go back'); }; let server = new Soup.Server({ port: 1080 }); server.add_handler('/', handler); server.add_handler('/hello', helloHandler); server.run(); } main(); cjs-2.8.0/examples/README0000664000175000017500000000011512610172032013711 0ustar fabiofabioIn order to run those example scripts, do: gjs-console script-filename.js cjs-2.8.0/examples/gettext.js0000664000175000017500000000051112610172032015053 0ustar fabiofabio const Gettext = imports.gettext; const Gtk = imports.gi.Gtk; Gettext.bindtextdomain("gnome-panel-3.0", "/usr/share/locale"); Gettext.textdomain("gnome-panel-3.0"); Gtk.init(null); let w = new Gtk.Window({ type: Gtk.WindowType.TOPLEVEL }); w.add(new Gtk.Label({ label: Gettext.gettext("Panel") })); w.show_all(); Gtk.main(); cjs-2.8.0/util/0000775000175000017500000000000012610172032012173 5ustar fabiofabiocjs-2.8.0/util/misc.h0000664000175000017500000000256712610172032013311 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_UTIL_MISC_H__ #define __GJS_UTIL_MISC_H__ #include G_BEGIN_DECLS gboolean gjs_environment_variable_is_set (const char *env_variable_name); G_END_DECLS #endif /* __GJS_UTIL_MISC_H__ */ cjs-2.8.0/util/glib.cpp0000664000175000017500000001040312610172032013612 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "glib.h" #include typedef struct { void *key; void *value; } StoreOneData; static gboolean get_first_one_predicate(void *key, void *value, void *data) { StoreOneData *sod = (StoreOneData *) data; sod->key = key; sod->value = value; /* found it! */ return TRUE; } static gboolean remove_or_steal_one(GHashTable *hash, void **key_p, void **value_p, gboolean steal) { StoreOneData sod; sod.key = NULL; sod.value = NULL; g_hash_table_find(hash, get_first_one_predicate, &sod); if (sod.key == NULL) return FALSE; if (key_p) *key_p = sod.key; if (value_p) *value_p = sod.value; if (steal) g_hash_table_steal(hash, sod.key); else g_hash_table_remove(hash, sod.key); return sod.value != NULL; } gboolean gjs_g_hash_table_remove_one(GHashTable *hash, void **key_p, void **value_p) { return remove_or_steal_one(hash, key_p, value_p, FALSE); } gboolean gjs_g_hash_table_steal_one(GHashTable *hash, void **key_p, void **value_p) { return remove_or_steal_one(hash, key_p, value_p, TRUE); } /** gjs_g_strv_concat: * * Concate an array of string arrays to one string array. The strings in each * array is copied to the resulting array. * * @strv_array: array of NULL-terminated arrays of strings. NULL elements are * allowed. * @len: number of arrays in @strv_array * * @return: a newly allocated NULL-terminated array of strings. Use * g_strfreev() to free it */ char** gjs_g_strv_concat(char ***strv_array, int len) { GPtrArray *array; int i; array = g_ptr_array_sized_new(16); for (i = 0; i < len; i++) { char **strv; int j; strv = strv_array[i]; if (strv == NULL) continue; for (j = 0; strv[j] != NULL; ++j) g_ptr_array_add(array, g_strdup(strv[j])); } g_ptr_array_add(array, NULL); return (char**)g_ptr_array_free(array, FALSE); } gchar * _gjs_g_utf8_make_valid (const gchar *name) { GString *string; const gchar *remainder, *invalid; gint remaining_bytes, valid_bytes; g_return_val_if_fail (name != NULL, NULL); string = NULL; remainder = name; remaining_bytes = strlen (name); while (remaining_bytes != 0) { if (g_utf8_validate (remainder, remaining_bytes, &invalid)) break; valid_bytes = invalid - remainder; if (string == NULL) string = g_string_sized_new (remaining_bytes); g_string_append_len (string, remainder, valid_bytes); /* append U+FFFD REPLACEMENT CHARACTER */ g_string_append (string, "\357\277\275"); remaining_bytes -= valid_bytes + 1; remainder = invalid + 1; } if (string == NULL) return g_strdup (name); g_string_append (string, remainder); g_assert (g_utf8_validate (string->str, -1, NULL)); return g_string_free (string, FALSE); } cjs-2.8.0/util/hash-x32.cpp0000664000175000017500000000452112610172032014236 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2013 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "hash-x32.h" /* Note: Not actually tested on x32 */ #define HASH_GSIZE_FITS_POINTER (sizeof(gsize) == sizeof(gpointer)) GHashTable * gjs_hash_table_new_for_gsize (GDestroyNotify value_destroy) { if (HASH_GSIZE_FITS_POINTER) { return g_hash_table_new_full (NULL, NULL, NULL, value_destroy); } else { return g_hash_table_new_full (g_int64_hash, g_int64_equal, g_free, value_destroy); } } void gjs_hash_table_for_gsize_insert (GHashTable *table, gsize key, gpointer value) { if (HASH_GSIZE_FITS_POINTER) { g_hash_table_insert (table, (gpointer)key, value); } else { guint64 *keycopy = g_new (guint64, 1); *keycopy = (guint64) key; g_hash_table_insert (table, keycopy, value); } } void gjs_hash_table_for_gsize_remove (GHashTable *table, gsize key) { if (HASH_GSIZE_FITS_POINTER) g_hash_table_remove (table, (gpointer)key); else g_hash_table_remove (table, &key); } gpointer gjs_hash_table_for_gsize_lookup (GHashTable *table, gsize key) { if (HASH_GSIZE_FITS_POINTER) return g_hash_table_lookup (table, (gpointer)key); else return g_hash_table_lookup (table, &key); } cjs-2.8.0/util/misc.cpp0000664000175000017500000000265712610172032013644 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "misc.h" gboolean gjs_environment_variable_is_set(const char *env_variable_name) { const char *s; s = g_getenv(env_variable_name); if (s == NULL) return FALSE; if (*s == '\0') return FALSE; return TRUE; } cjs-2.8.0/util/error.cpp0000664000175000017500000000244212610172032014032 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "error.h" GQuark gjs_error_quark (void) { return g_quark_from_static_string ("gjs-error-quark"); } cjs-2.8.0/util/crash.cpp0000664000175000017500000001155512610172032014006 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include "crash.h" #ifdef HAVE_BACKTRACE #include #endif #include #include #include #include #include #include #include #include #include #ifdef HAVE_BACKTRACE static void unbuffered_write_stderr(const char *s) { size_t len; len = strlen(s); write(STDERR_FILENO, s, len); } static void gjs_print_maps(void) { int fd; fd = open("/proc/self/maps", O_RDONLY); if (fd != -1) { char buf[128]; size_t n; while ((n = read(fd, buf, sizeof(buf))) > 0) { write(STDERR_FILENO, buf, n); } (void)close(fd); unbuffered_write_stderr("\n"); } } #endif /* this only works if we build with -rdynamic */ void gjs_print_backtrace(void) { #ifdef HAVE_BACKTRACE void *bt[500]; int bt_size; char buf[128]; bt_size = backtrace(bt, 500); /* Avoid dynamic allocations since we may in SIGSEGV signal handler, so use * backtrace_symbols_fd */ unbuffered_write_stderr("\n"); backtrace_symbols_fd(bt, bt_size, STDERR_FILENO); unbuffered_write_stderr("\n"); sprintf(buf, "backtrace pid %lu\n\n", (gulong) getpid()); unbuffered_write_stderr(buf); /* best effort attempt to extract shared library relocations so that * mapping backtrace addresses to symbols is possible after the fact */ gjs_print_maps(); #endif } /* 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); } cjs-2.8.0/util/glib.h0000664000175000017500000000346512610172032013271 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_UTIL_GLIB_H__ #define __GJS_UTIL_GLIB_H__ #include G_BEGIN_DECLS gchar * _gjs_g_utf8_make_valid (const gchar *name); gboolean gjs_g_hash_table_remove_one (GHashTable *hash, void **key_p, void **value_p); gboolean gjs_g_hash_table_steal_one (GHashTable *hash, void **key_p, void **value_p); char** gjs_g_strv_concat (char ***strv_array, int len); G_END_DECLS #endif /* __GJS_UTIL_GLIB_H__ */ cjs-2.8.0/util/error.h0000664000175000017500000000263612610172032013504 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_UTIL_ERROR_H__ #define __GJS_UTIL_ERROR_H__ #include G_BEGIN_DECLS GQuark gjs_error_quark(void); #define GJS_ERROR gjs_error_quark() typedef enum { GJS_ERROR_FAILED } GjsError; G_END_DECLS #endif /* __GJS_UTIL_ERROR_H__ */ cjs-2.8.0/util/crash.h0000664000175000017500000000257312610172032013453 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_UTIL_CRASH_H__ #define __GJS_UTIL_CRASH_H__ #include G_BEGIN_DECLS void gjs_print_backtrace (void); void gjs_crash_after_timeout (int seconds); G_END_DECLS #endif /* __GJS_UTIL_CRASH_H__ */ cjs-2.8.0/util/log.h0000664000175000017500000001151312610172032013126 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_UTIL_LOG_H__ #define __GJS_UTIL_LOG_H__ #include G_BEGIN_DECLS /* The idea of this is to be able to have one big log file for the entire * environment, and grep out what you care about. So each module or app * should have its own entry in the enum. Be sure to add new enum entries * to the switch in log.c */ typedef enum { GJS_DEBUG_STRACE_TIMESTAMP, GJS_DEBUG_GI_USAGE, GJS_DEBUG_MEMORY, GJS_DEBUG_CONTEXT, GJS_DEBUG_IMPORTER, GJS_DEBUG_NATIVE, GJS_DEBUG_KEEP_ALIVE, GJS_DEBUG_GREPO, GJS_DEBUG_GNAMESPACE, GJS_DEBUG_GOBJECT, GJS_DEBUG_GFUNCTION, GJS_DEBUG_GCLOSURE, GJS_DEBUG_GBOXED, GJS_DEBUG_GENUM, GJS_DEBUG_GPARAM, GJS_DEBUG_DATABASE, GJS_DEBUG_RESULTSET, GJS_DEBUG_WEAK_HASH, GJS_DEBUG_MAINLOOP, GJS_DEBUG_PROPS, GJS_DEBUG_SCOPE, GJS_DEBUG_HTTP, GJS_DEBUG_BYTE_ARRAY, GJS_DEBUG_GERROR, GJS_DEBUG_GFUNDAMENTAL, } GjsDebugTopic; /* These defines are because we have some pretty expensive and * extremely verbose debug output in certain areas, that's useful * sometimes, but just too much to compile in by default. The areas * tend to be broader and less focused than the ones represented by * GjsDebugTopic. * * Don't use these special "disabled by default" log macros to print * anything that's an abnormal or error situation. * * Don't use them for one-time events, either. They are for routine * stuff that happens over and over and would deluge the logs, so * should be off by default. */ /* Whether to be verbose about JavaScript property access and resolution */ #ifndef GJS_VERBOSE_ENABLE_PROPS #define GJS_VERBOSE_ENABLE_PROPS 0 #endif /* Whether to be verbose about JavaScript function arg and closure marshaling */ #ifndef GJS_VERBOSE_ENABLE_MARSHAL #define GJS_VERBOSE_ENABLE_MARSHAL 0 #endif /* Whether to be verbose about constructing, destroying, and gc-rooting * various kinds of JavaScript thingy */ #ifndef GJS_VERBOSE_ENABLE_LIFECYCLE #define GJS_VERBOSE_ENABLE_LIFECYCLE 0 #endif /* Whether to log all gobject-introspection types and methods we use */ #ifndef GJS_VERBOSE_ENABLE_GI_USAGE #define GJS_VERBOSE_ENABLE_GI_USAGE 0 #endif /* Whether to log all callback GClosure debugging (finalizing, invalidating etc) */ #ifndef GJS_VERBOSE_ENABLE_GCLOSURE #define GJS_VERBOSE_ENABLE_GCLOSURE 0 #endif /* Whether to log all GObject signal debugging */ #ifndef GJS_VERBOSE_ENABLE_GSIGNAL #define GJS_VERBOSE_ENABLE_GSIGNAL 0 #endif #if GJS_VERBOSE_ENABLE_PROPS #define gjs_debug_jsprop(topic, format...) \ do { gjs_debug(topic, format); } while(0) #else #define gjs_debug_jsprop(topic, format...) #endif #if GJS_VERBOSE_ENABLE_MARSHAL #define gjs_debug_marshal(topic, format...) \ do { gjs_debug(topic, format); } while(0) #else #define gjs_debug_marshal(topic, format...) #endif #if GJS_VERBOSE_ENABLE_LIFECYCLE #define gjs_debug_lifecycle(topic, format...) \ do { gjs_debug(topic, format); } while(0) #else #define gjs_debug_lifecycle(topic, format...) #endif #if GJS_VERBOSE_ENABLE_GI_USAGE #define gjs_debug_gi_usage(format...) \ do { gjs_debug(GJS_DEBUG_GI_USAGE, format); } while(0) #else #define gjs_debug_gi_usage(format...) #endif #if GJS_VERBOSE_ENABLE_GCLOSURE #define gjs_debug_closure(format...) \ do { gjs_debug(GJS_DEBUG_GCLOSURE, format); } while(0) #else #define gjs_debug_closure(format, ...) #endif #if GJS_VERBOSE_ENABLE_GSIGNAL #define gjs_debug_gsignal(format...) \ do { gjs_debug(GJS_DEBUG_GOBJECT, format); } while(0) #else #define gjs_debug_gsignal(format...) #endif void gjs_debug(GjsDebugTopic topic, const char *format, ...) G_GNUC_PRINTF (2, 3); G_END_DECLS #endif /* __GJS_UTIL_LOG_H__ */ cjs-2.8.0/util/log.cpp0000664000175000017500000002011312610172032013455 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "config.h" #include "log.h" #include "misc.h" #include #include #include #include #include #include /* prefix is allowed if it's in the ;-delimited environment variable * GJS_DEBUG_TOPICS or if that variable is not set. */ static gboolean is_allowed_prefix (const char *prefix) { static const char *topics = NULL; static char **prefixes = NULL; gboolean found = FALSE; int i; if (topics == NULL) { topics = g_getenv("GJS_DEBUG_TOPICS"); if (!topics) return TRUE; /* We never really free this, should be gone when the process exits */ prefixes = g_strsplit(topics, ";", -1); } if (!prefixes) return TRUE; for (i = 0; prefixes[i] != NULL; i++) { if (!strcmp(prefixes[i], prefix)) { found = TRUE; break; } } return found; } #define PREFIX_LENGTH 12 static void write_to_stream(FILE *logfp, const char *prefix, const char *s) { /* seek to end to avoid truncating in case we're using shared logfile */ (void)fseek(logfp, 0, SEEK_END); fprintf(logfp, "%*s: %s", PREFIX_LENGTH, prefix, s); if (!g_str_has_suffix(s, "\n")) fputs("\n", logfp); fflush(logfp); } void gjs_debug(GjsDebugTopic topic, const char *format, ...) { static FILE *logfp = NULL; static gboolean debug_log_enabled = FALSE; static gboolean strace_timestamps = FALSE; static gboolean checked_for_timestamp = FALSE; static gboolean print_timestamp = FALSE; static GTimer *timer = NULL; const char *prefix; va_list args; char *s; if (!checked_for_timestamp) { print_timestamp = gjs_environment_variable_is_set("GJS_DEBUG_TIMESTAMP"); checked_for_timestamp = TRUE; } if (print_timestamp && !timer) { timer = g_timer_new(); } if (logfp == NULL) { const char *debug_output = g_getenv("GJS_DEBUG_OUTPUT"); if (debug_output != NULL && strcmp(debug_output, "stderr") == 0) { debug_log_enabled = TRUE; } else if (debug_output != NULL) { const char *log_file; char *free_me; char *c; /* Allow debug-%u.log for per-pid logfiles as otherwise log * messages from multiple processes can overwrite each other. * * (printf below should be safe as we check '%u' is the only format * string) */ c = strchr((char *) debug_output, '%'); if (c && c[1] == 'u' && !strchr(c+1, '%')) { free_me = g_strdup_printf(debug_output, (guint)getpid()); log_file = free_me; } else { log_file = debug_output; free_me = NULL; } /* avoid truncating in case we're using shared logfile */ logfp = fopen(log_file, "a"); if (!logfp) fprintf(stderr, "Failed to open log file `%s': %s\n", log_file, g_strerror(errno)); g_free(free_me); debug_log_enabled = TRUE; } if (logfp == NULL) logfp = stderr; strace_timestamps = gjs_environment_variable_is_set("GJS_STRACE_TIMESTAMPS"); } /* only strace timestamps if debug * log wasn't specifically switched on */ if (!debug_log_enabled && topic != GJS_DEBUG_STRACE_TIMESTAMP) return; switch (topic) { case GJS_DEBUG_STRACE_TIMESTAMP: /* return early if strace timestamps are disabled, avoiding * printf format overhead and so forth. */ if (!strace_timestamps) return; /* this is a special magic topic for use with * git clone http://www.gnome.org/~federico/git/performance-scripts.git * http://www.gnome.org/~federico/news-2006-03.html#timeline-tools */ prefix = "MARK"; break; case GJS_DEBUG_GI_USAGE: prefix = "JS GI USE"; break; case GJS_DEBUG_MEMORY: prefix = "JS MEMORY"; break; case GJS_DEBUG_CONTEXT: prefix = "JS CTX"; break; case GJS_DEBUG_IMPORTER: prefix = "JS IMPORT"; break; case GJS_DEBUG_NATIVE: prefix = "JS NATIVE"; break; case GJS_DEBUG_KEEP_ALIVE: prefix = "JS KP ALV"; break; case GJS_DEBUG_GREPO: prefix = "JS G REPO"; break; case GJS_DEBUG_GNAMESPACE: prefix = "JS G NS"; break; case GJS_DEBUG_GOBJECT: prefix = "JS G OBJ"; break; case GJS_DEBUG_GFUNCTION: prefix = "JS G FUNC"; break; case GJS_DEBUG_GFUNDAMENTAL: prefix = "JS G FNDMTL"; break; case GJS_DEBUG_GCLOSURE: prefix = "JS G CLSR"; break; case GJS_DEBUG_GBOXED: prefix = "JS G BXD"; break; case GJS_DEBUG_GENUM: prefix = "JS G ENUM"; break; case GJS_DEBUG_GPARAM: prefix = "JS G PRM"; break; case GJS_DEBUG_DATABASE: prefix = "JS DB"; break; case GJS_DEBUG_RESULTSET: prefix = "JS RS"; break; case GJS_DEBUG_WEAK_HASH: prefix = "JS WEAK"; break; case GJS_DEBUG_MAINLOOP: prefix = "JS MAINLOOP"; break; case GJS_DEBUG_PROPS: prefix = "JS PROPS"; break; case GJS_DEBUG_SCOPE: prefix = "JS SCOPE"; break; case GJS_DEBUG_HTTP: prefix = "JS HTTP"; break; case GJS_DEBUG_BYTE_ARRAY: prefix = "JS BYTE ARRAY"; break; case GJS_DEBUG_GERROR: prefix = "JS G ERR"; break; default: prefix = "???"; break; } if (!is_allowed_prefix(prefix)) return; va_start (args, format); s = g_strdup_vprintf (format, args); va_end (args); if (topic == GJS_DEBUG_STRACE_TIMESTAMP) { /* Put a magic string in strace output */ char *s2; s2 = g_strdup_printf("%s: gjs: %s", prefix, s); access(s2, F_OK); g_free(s2); } else { if (print_timestamp) { static gdouble previous = 0.0; gdouble total = g_timer_elapsed(timer, NULL) * 1000.0; gdouble since = total - previous; const char *ts_suffix; char *s2; if (since > 50.0) { ts_suffix = "!! "; } else if (since > 100.0) { ts_suffix = "!!! "; } else if (since > 200.0) { ts_suffix = "!!!!"; } else { ts_suffix = " "; } s2 = g_strdup_printf("%g %s%s", total, ts_suffix, s); g_free(s); s = s2; previous = total; } write_to_stream(logfp, prefix, s); } g_free(s); } cjs-2.8.0/util/hash-x32.h0000664000175000017500000000343212610172032013703 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2013 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_UTIL_HASH_X32_H__ #define __GJS_UTIL_HASH_X32_H__ #include G_BEGIN_DECLS /* Hash table that operates on gsize; on every architecture except x32, * sizeof(gsize) == sizeof(gpointer), and so we can just use it as a * hash key directly. But on x32, we have to fall back to malloc(). */ GHashTable *gjs_hash_table_new_for_gsize (GDestroyNotify value_destroy); void gjs_hash_table_for_gsize_insert (GHashTable *table, gsize key, gpointer value); void gjs_hash_table_for_gsize_remove (GHashTable *table, gsize key); gpointer gjs_hash_table_for_gsize_lookup (GHashTable *table, gsize key); G_END_DECLS #endif cjs-2.8.0/doc/0000775000175000017500000000000012610172032011763 5ustar fabiofabiocjs-2.8.0/doc/Style_Guide.txt0000664000175000017500000002434512610172032014751 0ustar fabiofabio= Coding style = Our goal is to have all JavaScript code in GNOME follow a consistent style. In a dynamic language like JavaScript, it is essential to be rigorous about style (and unit tests), or you rapidly end up with a spaghetti-code mess. == Semicolons == JavaScript allows omitting semicolons at the end of lines, but don't. Always end statements with a semicolon. == js2-mode == If using Emacs, try js2-mode. It functions as a "lint" by highlighting missing semicolons and the like. == Imports == Use CamelCase when importing modules to distinguish them from ordinary variables, e.g.
const Big = imports.big;
const GLib = imports.gi.GLib;
== Variable declaration == Always use one of "const", "var", or "let" when defining a variable. Always use "let" when block scope is intended; in particular, inside for() and while() loops, let is almost always correct.
// Iterating over an array
for (let i = 0; i < 10; ++i) {
  let foo = bar(i);
}
// Iterating over an object's properties
for (let prop in someobj) {
  ...
}
If you don't use "let" then the variable is added to function scope, not the for loop block scope. See [http://developer.mozilla.org/en/docs/index.php?title=New_in_JavaScript_1.7&printable=yes#Block_scope_with_let What's new in JavaScript 1.7] A common case where this matters is when you have a closure inside a loop:
for (let i = 0; i < 10; ++i) {
  mainloop.idle_add(function() { log("number is: " + i); });
}
If you used "var" instead of "let" it would print "10" a bunch of times. Inside functions, "let" is always correct instead of "var" as far as we know. "var" is useful when you want to add something to the with() object, though... in particular we think you need "var" to define module variables, since our module system loads modules with the equivalent of "with (moduleObject)" == "this" in closures == "this" will not be captured in a closure; "this" is relative to how the closure is invoked, not to the value of this where the closure is created, because "this" is a keyword with a value passed in at function invocation time, it is not a variable that can be captured in closures. To solve this, use Lang.bind, eg:
const Lang = imports.lang;

let closure = Lang.bind(this, function() { this._fnorbate() });
A more realistic example would be connecting to a signal on a method of a prototype:
const Lang = imports.lang;

MyPrototype = {
    _init : function() {
       fnorb.connect('frobate', Lang.bind(this, this._onFnorbFrobate));
    },

    _onFnorbFrobate : function(fnorb) {
       this._updateFnorb();
    },
};
== Object literal syntax == JavaScript allows equivalently:
foo = { 'bar' : 42 };
foo = { bar: 42 };
and
var b = foo['bar'];
var b = foo.bar;
If your usage of an object is like an object, then you're defining "member variables." For member variables, use the no-quotes no-brackets syntax, that is, "{ bar: 42 }" and "foo.bar". If your usage of an object is like a hash table (and thus conceptually the keys can have special chars in them), don't use quotes, but use brackets, "{ bar: 42 }", "foo['bar']". == Variable naming == # We use javaStyle variable names, with CamelCase for type names and lowerCamelCase for variable and method names. However, when calling a C method with underscore-based names via introspection, we just keep them looking as they do in C for simplicity. # Private variables, whether object member variables or module-scoped variables, should begin with "_". # True global variables (in the global or 'window' object) should be avoided whenever possible. If you do create them, the variable name should have a namespace in it, like "BigFoo" # When you assign a module to an alias to avoid typing "imports.foo.bar" all the time, the alias should be "const TitleCase" so "const Bar = imports.foo.bar;" # If you need to name a variable something weird to avoid a namespace collision, add a trailing "_" (not leading, leading "_" means private). # For GObject constructors, always use the lowerCamelCase style for property names instead of dashes or underscores. == Whitespace == * 4-space indentation (the Java style) * No trailing whitespace. * No tabs. * If you chmod +x .git/hooks/pre-commit it will not let you commit with messed-up whitespace (well, it doesn't catch tabs. turn off tabs in your text editor.) == JavaScript "classes" == Keep in mind that JavaScript does not "really" have classes in the sense of C++ or Java; you can't create new types beyond the built-in ones (Object, Array, RegExp, String). However, you can create object instances that share common properties, including methods, using the prototype mechanism. Each JavaScript object has a property __proto__; if you write obj.foo and "foo" is not in "obj", JavaScript will look for "foo" in __proto__. If several objects have the same __proto__, then they can share methods or other state. You can create objects with a constructor, which is a special function. Say you have:
function Foo() {}
let f = new Foo();
For "new Foo()" JavaScript will create a new, empty object; and execute Foo() with the new, empty object as "this". So the function Foo() sets up the new object. "new Foo()" will also set __proto__ on the new object to Foo.prototype. The property "prototype" on a constructor is used to initialize __proto__ for objects the constructor creates. To get the right __proto__ on objects, we need the right prototype property on the constructor. You could think of "f = new Foo()" as:
let f = {}; // create new object
f.__proto__ = Foo.prototype; // doing this by hand isn't actually allowed
Foo.call(f); // invoke Foo() with new object as "this"
Our pattern for writing classes is:
function Foo(arg1, arg2) {
  this._init(arg1, arg2);
}

Foo.prototype = {
  _init : function(arg1, arg2) {
    this._myPrivateInstanceVariable = arg1;
  },
  myMethod : function() {

  },
  myClassVariable : 42,
  myOtherClassVariable : "Hello"
}
This pattern means that when you do "let f = new Foo()", f will be a new object, f.__proto__ will point to Foo.prototype, and Foo.prototype._init will be called to set up the object. {{Note|Again, on the JavaScript language level, Foo is not a class in the sense of Java or C++; it's just a constructor function, which means it's intended for use with the "new Foo()" syntax to create an object. Once the object is created, from a JavaScript language perspective its type is the built-in type "Object" - though we're using it and thinking of it as if it had type "Foo", JavaScript doesn't have a clue about that and will never do any type-checking based on which constructor was used to create something. All typing is "duck typing." The built-in types, such as Object, String, Error, RegExp, and Array, ''are'' real types, however, and do get type-checked.}} {{Note|If a constructor function has a return value, it is used as the value of "new Foo()" - replacing the automatically-created "this" object passed in to the constructor. If a constructor function returns nothing (undefined), then the passed-in "this" is used. In general, avoid this feature - constructors should have no return value. But this feature may be necessary if you need the new instance to have a built-in type other than Object. If you return a value from the constructor, "this" is simply discarded, so referring to "this" while in the constructor won't make sense. }} == JavaScript "inheritance" == There are lots of ways to simulate "inheritance" in JavaScript. In general, it's a good idea to avoid class hierarchies in JavaScript. But sometimes it's the best solution. Our preferred approach is to use a Spidermonkey-specific extension and directly set the __proto__ member of the subclass's prototype to point to the prototype of the base class. Looking up a property in the subclass starts with the properties of the instance. If the property isn't there, then the prototype chain is followed first to the subclass's prototype and then to the base class's prototype.
const Lang = imports.lang;

function Base(foo) {
  this._init(foo);
}

Base.prototype = {
  _init : function(foo) {
    this._foo = foo;
  },
  frobate : function() {
  }
};

function Sub(foo, bar) {
  this._init(foo, bar);
}

Sub.prototype = {
  __proto__ : Base.prototype,

  _init : function(foo, bar) {
    // here is an example of how to "chain up"
    Base.prototype._init.call(this, foo);
    this._bar = bar;
  }

  // add a newMethod property in Sub.prototype
  newMethod : function() {
  }
}

{{Note|You cannot use this mechanism to inherit from a built-in type, such as String or Error, because the methods on those objects expect them to have primitive type String or Error, while your constructor will create an Object instead.}} {{Note|You cannot use this mechanism to inherit from a GObject. For that to work, we would need to create a new GType in C that was a subclass of the base class GType, and we'd need to override the class methods in the new GType so they called into JavaScript. Tons of work. For now, GObject subclasses must be implemented in C.}} In portable JavaScript code you'll often see a different technique used to get this prototype chain:
function Base(foo) ...

Base.prototype = ...

function Sub(foo, bar) ...

// Do NOT do this - do not use an instance of Base as the Sub prototype
Sub.prototype = new Base();
The problem with this pattern is that you might want to have side effects in the Base() constructor. Say Base() in its constructor creates a window on the screen, because Base() is a dialog class or something. If you use the pattern that some instances of Base() are just prototypes for subclasses, you'll get extra windows on the screen. The other problem with this pattern is that it's just confusing and weird. == JavaScript attributes == Don't use the getter/setter syntax when getting and setting has side effects, that is, the code:
foo.bar = 10;
should not do anything other than save "10" as a property of foo. It's obfuscated otherwise; if the setting has side effects, it's better if it looks like a method. In practice this means the only use of attributes is to create read-only properties:
get bar() {
    return this._bar;
}
If the property requires a setter, or if getting it has side effects, methods are probably clearer. cjs-2.8.0/doc/SpiderMonkey_Memory.txt0000664000175000017500000001625112610172032016472 0ustar fabiofabio= Memory management in SpiderMonkey = When writing JavaScript extensions in C, we have to understand and be careful about memory management. This document only applies to C code using the jsapi.h API. If you simply write a GObject-style library and describe it via gobject-introspection typelib, there is no need to understand garbage collection details. == Mark-and-sweep collector == As background, SpiderMonkey uses mark-and-sweep garbage collection. (see [http://www.brpreiss.com/books/opus5/html/page424.html this page] for one explanation, if not familiar with this.) This is a good approach for "embeddable" interpreters, because unlike say the Boehm GC, it doesn't rely on any weird hacks like scanning the entire memory or stack of the process. The collector only has to know about stuff that the language runtime created itself. Also, mark-and-sweep is simple to understand when working with the embedding API. == Representation of objects == An object has two forms. * "jsval" is a type-tagged version, think of GValue (though it is much more efficient) * inside a jsval can be one of: a 31-bit signed integer, a boolean, a double*, a JSString*, a JSObject*, JSVAL_NULL, or JSVAL_VOID. jsval is 32 bits. The three least significant bits are a type tag. When JavaScript allocates a double*, JSString*, or JSObject* it aligns the allocations so the least significant 3 bits are always zero in the pointer. This leaves those three bits for the type tag. The least significant bit in the type tag indicates "integer or not." If the value is an integer, then the remaining 31 bits are all used for the integer value. What would have been MAXINT is used for JSVAL_VOID. So JSVAL_VOID has the integer flag set, but is not an integer. If the type tag is not an integer, the remaining two bits distinguish objects, doubles, and strings. You check the type tag with macros JSVAL_IS_OBJECT(), JSVAL_IS_INT(), JSVAL_IS_DOUBLE(), JSVAL_IS_STRING(), JSVAL_IS_BOOLEAN(). You can just compare "val == JSVAL_NULL" and "val == JSVAL_VOID" or you can use JSVAL_IS_NULL(), JSVAL_IS_VOID(). NULL counts as an object, so JSVAL_IS_OBJECT() returns true for null. VOID does not count as an integer. JSVAL_IS_INT() looks like:
#define JSVAL_IS_INT(v)         (((v) & JSVAL_INT) && (v) != JSVAL_VOID)
where JSVAL_INT is 0x1, the flag for integer. JSVAL_VOID shows up in the language as "undefined". The macros JSVAL_TO_OBJECT(), JSVAL_TO_INT(), etc. are just stripping out the type tag and returning the underlying JSObject* or integer or whatever. The jsapi.h header is pretty readable, if you want to learn more. Types you see in there not mentioned above, such as JSFunction*, would show up as an object - JSVAL_IS_OBJECT() would return true. From a jsval perspective, everything is one of object, string, double, int, boolean, null, or void. null is a special object, and again JSVAL_IS_OBJECT() returns true for it. void is stored like an integer but not considered an integer, so JSVAL_IS_INT() returns false. == Value types vs. allocated types; "gcthing" == For integers, booleans, JSVAL_NULL, and JSVAL_VOID there is no pointer. The value is just stuffed into the jsval. So there is no way to "free" these, and no way for them to be finalized or become dangling. The importance is: these types just get ignored by the garbage collector. However, doubles, strings, and objects are all allocated pointers that get finalized eventually. These are what garbage collection applies to. The API refers to these allocated types as "GC things." The macro JSVAL_TO_GCTHING() masks out the type tag to return a pointer (remember the pointer has the low three bits all zero, since it was allocated aligned). JSVAL_IS_GCTHING() returns true for string, double, object, null; and false for void, boolean, integer. == Tracing == The general rule is that SpiderMonkey has a set of GC roots. To do the garbage collection, it finds all objects accessible from those roots, and finalizes all objects that are not. So if you store a jsval or JSObject*/JSString*/JSFunction* somewhere that SpiderMonkey does not know about - say, in the private data of an object - that will not be found. SpiderMonkey may try to finalize this object even though you have a reference to it. If you reference JavaScript objects from your custom object, you have to set the JSCLASS_MARK_IS_TRACE flag in your JSClass, and define a trace function in the class struct. A trace function just invokes JS_CallTracer() to tell SpiderMonkey about any objects you reference. See [http://developer.mozilla.org/en/docs/JSTraceOp JSTraceOp docs]. == Global roots == The GC roots would generally include any stack variables SpiderMonkey knows about and the global object set on each JSContext*. You can also manually add roots with [http://developer.mozilla.org/en/docs/JS_AddRoot JS_AddRoot()]. Anything reachable from any of these root objects will not be collected. JS_AddRoot() pins an object in memory forever until you call JS_RemoveRoot(), so be careful of leaks. Basically JS_AddRoot() changes memory management of an object to manual mode. Note that the argument to JS_AddRoot() is the location of your value, not the value itself. That is, a "JSObject**" or "jsval*". Some implications are: * the location can't go away (don't use a stack address that will vanish before you JS_RemoveRoot(), for example) * the root is keeping "whatever is at the location" from being collected, not "whatever was originally at the location" == Local roots== Here is the trickier part. If you create an object, say: JSObject *obj = JS_ConstructObject(NULL, NULL, whatever, ...); "obj" is NOT now referenced by any other object. If the GC ran right away, "obj" would be collected. As a partial solution, SpiderMonkey keeps the last-created object of each type as a GC root. This "newborn roots" feature avoids the problem in the simple case where you create an object then immediately reference it somehow. For example, if you create an object then use it as the value in JS_DefineProperty(), now the object is referenced by the object you defined it in, and should not vanish. If you do not immediately add the object to some other object so it's referenced, you have to arrange to hold onto it in some way. There are three basic ways. # call JS_AddRoot() on its location (and JS_RemoveRoot() when done) # when defining a [http://developer.mozilla.org/en/docs/JSFunctionSpec JSFunctionSpec], ask for argv to have "extra local roots" === JSFunctionSpec and extra local roots === When SpiderMonkey is calling a native function, it will pass in an argv of jsval. It already has to add all the argv values as GC roots. The "extra local roots" feature tells SpiderMonkey to stick some extra slots on the end of argv that are also GC roots. You can then assign to argv[MAX(min_args, actual_argc)] and whatever you put in there won't get garbage collected. This is kind of a confusing and unreadable hack IMO, though it is probably efficient and thus justified in certain cases. I don't know really. Note that newborn roots are tracked as *values* while JS_AddRoot() tracks *locations*. == More tips == For another attempt to explain all this, see [http://developer.mozilla.org/en/docs/SpiderMonkey_Garbage_Collection_Tips GC tips from Mozilla.org]. cjs-2.8.0/doc/gjs-byte-array.txt0000664000175000017500000001514512610172032015372 0ustar fabiofabioThe ByteArray type in the imports.byteArray module is based on an ECMAScript 4 proposal that was never adopted. This can be found at: http://wiki.ecmascript.org/doku.php?id=proposals:bytearray and the wikitext of that page is appended to this file. The main difference from the ECMA proposal is that gjs's ByteArray is inside a module, and toString()/fromString() default to UTF-8 and take optional encoding arguments. There are a number of more elaborate byte array proposals in the Common JS project at http://wiki.commonjs.org/wiki/Binary We went with the ECMA proposal because it seems simplest, and the main goal for most gjs users will be to shovel bytes between various C APIs, for example reading from an IO stream and then pushing the bytes into a parser. Actually manipulating bytes in JS is likely to be pretty rare, and slow ... an advantage of the gjs/gobject-introspection setup is that stuff best done in C, like messing with bytes, can be done in C. ======================== ECMAScript proposal follows; remember it's almost but not quite like gjs ByteArray, in particular we use UTF-8 instead of busted Latin-1 as default encoding. ======================== ====== ByteArray ====== (Also see the [[discussion:bytearray|discussion page]] for this proposal) ===== Overview ====== In previous versions of ECMAScript, there wasn't a good way to efficiently represent a packed array of arbitrary bytes. The predefined core object Array is inefficient for this purpose; some developers have (mis)used character strings for this purpose, which may be slightly more efficient for some implementations, but still a misuse of the string type and either a less efficient use of memory (if one byte per character was stored) or cycles (if two bytes per char). Edition 4 will add a new predefined core object, ByteArray. A ByteArray can be thought of as similar to an Array of uint ([uint]) with each element truncated to the integer range of 0..255. ===== Creating a ByteArray ===== To create a ByteArray object: byteArrayObject = new ByteArray(byteArrayLength:uint) byteArrayLength is the initial length of the ByteArray, in bytes. If omitted, the initial length is zero. All elements in a ByteArray are initialized to the value of zero. Unlike Array, there is no special form that allows you to list the initial values for the ByteArray's elements. However, the ByteArray class has an ''intrinsic::to'' static method that can convert an Array to a ByteArray, and implementations are free to optimize away the Array instance if it is used exclusively to initialize a ByteArray: var values:ByteArray = [1, 2, 3, 4]; // legal by virtue of ByteArray.intrinsic::to ===== Populating a ByteArray ===== You can populate a ByteArray by assigning values to its elements. Each element can hold only an unsigned integer in the range 0..255 (inclusive). Values will be converted to unsigned integer (if necessary), then truncated to the least-significant 8 bits. For example, var e = new ByteArray(3); e[0] = 0x12; // stores 18 e[1] = Math.PI; // stores 3 e[2] = "foo"; // stores 0 (generates compile error in strict mode) e[2] = "42"; // stores 42 (generates compile error in strict mode) e[2] = null; // stores 0 e[2] = undefined; // stores 0 ===== Referring to ByteArray Elements ===== You refer to a ByteArray's elements by using the element's ordinal number; you refer to the first element of the array as myArray[0] and the second element of the array as myArray[1], etc. The index of the elements begins with zero (0), but the length of array (for example, myArray.length) reflects the number of elements in the array. ===== ByteArray Methods ===== The ByteArray object has the follow methods: 1. static function fromString(s:String):ByteArray Convert a String into newly constructed ByteArray; this creates a new ByteArray of the same length as the String, then assigns each ByteArray entry the corresponding charCodeAt() value of the String. As with other ByteArray assignments, only the lower 8 bits of the charCode value will be retained. class ByteArray { ... static function fromString(s:String):ByteArray { var a:ByteArray = new Bytearray(s.length); for (var i:int = 0; i < s.length; ++i) a[i] = s.charCodeAt(i); return a; } ... } 2. static function fromArray(s:Array):ByteArray Converts an Array into a newly constructed ByteArray; this creates a new ByteArray of the same length as the input Array, then assigns each ByteArray entry the corresponding entry value of the Array. As with other ByteArray assignments, only the lower 8 bits of the charCode value will be retained. class ByteArray { ... static function fromArray(s:Array):ByteArray { var a:ByteArray = new Bytearray(s.length); for (var i:int = 0; i < s.length; ++i) a[i] = s[i]; return a; ... } 3. function toString():String Converts the ByteArray into a literal string, with each character entry set to the value of the corresponding ByteArray entry. The resulting string is guaranteed to round-trip back into an identical ByteArray by passing the result to ByteArray.fromString(), i.e., b === ByteArray.fromString(b.toString()). (Note that the reverse is not guaranteed to be true: ByteArray.fromString(s).toString != s unless all character codes in s are <= 255) class ByteArray { ... function toString():String { // XXX return String.fromCharCode.apply(String, this); var s:String = ""; for (var i:int = 0; i < this.length; ++i) s += String.fromCharCode(this[i]); return s; } ... } ===== ByteArray Properties ===== The ByteArray object has the following properties: 1. length:uint This property contains the number of bytes in the ByteArray. Setting the length property to a smaller value decreases the size of the ByteArray, discarding the unused bytes. Setting the length property to a larger value increases the size of the ByteArray, initializing all newly-allocated elements to zero. 2. prototype:Object This property contains the methods of ByteArray. ===== Prior Art ===== Adobe's ActionScript 3.0 provides [[http://livedocs.macromedia.com/flex/2/langref/flash/utils/ByteArray.html|flash.utils.ByteArray]], which was the primary inspiration for this proposal. Note that the ActionScript version of ByteArray includes additional functionality which has been omitted here for the sake of allowing small implementations; it is anticipated that the extra functionality can be written in ES4 itself and provided as a standard library. [Synopsis of ActionScript's implementation too detailed and moved to [[discussion:bytearray|discussion]] page] cjs-2.8.0/doc/cairo.txt0000664000175000017500000000426712610172032013632 0ustar fabiofabioThe cairo bindings follows the C API pretty closely. Naming ====== The module name is called 'cairo' and usually imported into the namespace as 'Cairo'. gjs> const Cairo = imports.cairo; Methods are studlyCaps, similar to other JavaScript apis, eg cairo_move_to is wrapped to Cairo.Context.moveTo() cairo_surface_write_to_png to Cairo.Context.writeToPNG(). Abbrevations such as RGB, RGBA, PNG, PDF, SVG are always upper-case. Enums are set in the cairo namespace, the enum names are capitalized: CAIRO_FORMAT_ARGB32 is mapped to Cairo.Format.ARGB32 etc. Surfaces (cairo_surface_t) ========================== Prototype hierarchya * Surface (abstract) * ImageSurface * PDFSurface * SVGSurface * PostScriptSurface The native surfaces (win32, quartz, xlib) are not supported at this point. Methods manipulating a surface are present in the surface class. Creating an ImageSurface from a PNG is done by calling a static method: gjs> let surface = Cairo.ImageSurface.createFromPNG("filename.png"); Context (cairo_t) ================= cairo_t is mapped as Cairo.Context. You will either get a context from a third-party library such as Clutter/Gtk/Poppler or by calling the cairo.Context constructor. gjs> let cr = new Cairo.Context(surface); gjs> let cr = Gdk.cairo_create(...); All introspection methods taking or returning a cairo_t will automatically create a Cairo.Context. Patterns (cairo_pattern_t) ========================== Prototype hierarchy * Pattern * Gradient * LinearGradient * RadialGradient * SurfacePattern * SolidPattern You can create a linear gradient by calling the constructor: Constructors: gjs> let pattern = new Cairo.LinearGradient(0, 0, 100, 100); gjs> let pattern = new Cairo.RadialGradient(0, 0, 10, 100, 100, 10); gjs> let pattern = new Cairo.SurfacePattern(surface); gjs> let pattern = new Cairo.SolidPattern.createRGB(0, 0, 0); gjs> let pattern = new Cairo.SolidPattern.createRGBA(0, 0, 0, 0); TODO: * context: wrap the remaning methods * surface methods * image surface methods * matrix * version * iterating over cairo_path_t Fonts & Glyphs are not wrapped, use PangoCairo instead. * glyphs * text cluster * font face * scaled font * font options cjs-2.8.0/COPYING.LGPL0000664000175000017500000006131412610172032013013 0ustar fabiofabio GNU LIBRARY GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the library GPL. It is numbered 2 because it goes with version 2 of the ordinary GPL.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Library General Public License, applies to some specially designated Free Software Foundation software, and to any other libraries whose authors decide to use it. You can use it for your libraries, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library, or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link a program with the library, you must provide complete object files to the recipients so that they can relink them with the library, after making changes to the library and recompiling it. And you must show them these terms so they know their rights. Our method of protecting your rights has two steps: (1) copyright the library, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the library. Also, for each distributor's protection, we want to make certain that everyone understands that there is no warranty for this free library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that companies distributing free software will individually obtain patent licenses, thus in effect transforming the program into proprietary software. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License, which was designed for utility programs. This license, the GNU Library General Public License, applies to certain designated libraries. This license is quite different from the ordinary one; be sure to read it in full, and don't assume that anything in it is the same as in the ordinary license. The reason we have a separate public license for some libraries is that they blur the distinction we usually make between modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in some sense simply using the library, and is analogous to running a utility program or application program. However, in a textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary General Public License treats it as such. Because of this blurred distinction, using the ordinary General Public License for libraries did not effectively promote software sharing, because most developers did not use the libraries. We concluded that weaker conditions might promote sharing better. However, unrestricted linking of non-free programs would deprive the users of those programs of all benefit from the free status of the libraries themselves. This Library General Public License is intended to permit developers of non-free programs to use free libraries, while preserving your freedom as a user of such programs to change the free libraries that are incorporated in them. (We have not seen how to achieve this as regards changes in header files, but we have achieved it as regards changes in the actual functions of the Library.) The hope is that this will lead to faster development of free libraries. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, while the latter only works together with the library. Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one. GNU LIBRARY GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also compile or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. c) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. d) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Library General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! cjs-2.8.0/NEWS0000664000175000017500000001525312610172032011723 0ustar fabiofabioVersion 1.40.0 -------------- - No changes Version 1.39.90 --------------- - Implemented fundamental object support [Lionel Landwerlin, #621716, #725061] - Fixed GIRepositoryGType prototype [#724925] - Moved GObject.prototype.disconnect() to a JS implementation [#698283] - Added support for enumeration methods [#725143] - Added pseudo-classes for fundamental types [#722554] - Build fixes [Ting-Wei Lan, #724853] Version 0.3 (03-Jul-2009) ------------------------- Changes: - DBus support At a high level there are three pieces. First, the "gjs-dbus" library is a C support library for DBus. Second, the modules/dbus*.[ch] implement parts of the JavaScript API in C. Third, the modules/dbus.js file fills out the rest of the JavaScript API and implementation. - Support simple fields for boxed types - Support "copy construction" of boxed types - Support simple structures not registered as boxed - Allow access to nested structures - Allow direct assignment to nested structure fields - Allow enum and flag structure fields - Allow creating boxed wrapper without copy - Support for non-default constructor (i.e. constructors like GdkPixbuf.Pixbuf.new_from_file(file)) - Add a Lang.bind function which binds the meaning of 'this' - Add an interactive console gjs-console - Allow code in directory modules (i.e. the code should reside in __init__.js files) - Fix handling of enum/flags return values - Handle non-gobject-registered flags - Add Tweener.registerSpecialProperty to tweener module - Add profiler for javascript code - Add gjs_context_get_all and gjs_dumpstack - useful to invoke from a debugger such as gdb - Support GHashTable - GHashTable return values/out parameters - Support GHashTable 'in' parameters - Convert JSON-style object to a GHashTable - Add support for UNIX shebang (i.e. #!/usr/bin/gjs-console) - Support new introspection short/ushort type tags - Support GI_TYPE_TAG_FILENAME - Improve support for machine-dependent integer types and arrays of integers - Fix several memory leaks Contributors: Colin Walters, C. Scott Ananian, Dan Winship, David Zeuthen, Havoc Pennington, Johan Bilien, Johan Dahlin, Lucas Rocha, Marco Pesenti Gritti, Marina Zhurakhinskaya, Owen Taylor, Tommi Komulainen Bugs fixed: Bug 560506 - linking problem Bug 560670 - Turn on compilation warnings Bug 560808 - Simple field supported for boxed types Bug 561514 - memory leak when skipping deprecated methods Bug 561516 - skipping deprecated methods shouldn't signal error case Bug 561849 - Alternate way of connecting signals. Bug 562892 - valgrind errors on get_obj_key Bug 564424 - Add an interactive console Bug 564664 - Expose JS_SetDebugErrorHook Bug 566185 - Allow code in directory modules Bug 567675 - Handle non-gobject-registered flags Bug 569178 - Add readline support to the console module Bug 570775 - array of parameters leaked on each new GObject Bug 570964 - Race when shutting down a context, r=hp Bug 580948 - Add DBus support Bug 584560 - Add support for UNIX shebang Bug 584850 - Clean up some __BIG definitions. Bug 584858 - Fix errors (and leftover debugging) from dbus merge. Bug 584858 - Move gjsdbus to gjs-dbus to match installed location. Bug 585386 - Add a flush() method to DBus bus object. Bug 585460 - Fix segfault when a non-existing node is introspected. Bug 586665 - Fix seg fault when attempting to free callback type. Bug 586760 - Support converting JavaScript doubles to DBus int64/uint64. Bug 561203 - Fix priv_from_js_with_typecheck() for dynamic types Bug 561573 - Add non-default constructors and upcoming static methods to "class" Bug 561585 - allow using self-built spidermonkey Bug 561664 - Closure support is broken Bug 561686 - Don't free prov->gboxed for "has_parent" Boxed Bug 561812 - Support time_t Bug 562575 - Set correct GC parameters and max memory usage Bug 565029 - Style guide - change recommendation for inheritance Bug 567078 - Don't keep an extra reference to closures Bug 569374 - Logging exceptions from tweener callbacks could be better Bug 572113 - add profiler Bug 572121 - Invalid read of size 1 Bug 572130 - memory leaks Bug 572258 - profiler hooks should be installed only when needed Bug 580865 - Call JS_SetLocaleCallbacks() Bug 580947 - Validate UTF-8 strings in gjs_string_to_utf8() Bug 580957 - Change the way we trigger a warning in testDebugger Bug 581277 - Call JS_SetScriptStackQuota on our contexts Bug 581384 - Propagate exceptions from load context Bug 581385 - Return false when gjs_g_arg_release_internal fails Bug 581389 - Fix arg.c to use 'interface' instead of 'symbol' Bug 582686 - Don't g_error() when failing to release an argument Bug 582704 - Don't depend on hash table order in test cases Bug 582707 - Fix problems with memory management for in parameters Bug 584849 - Kill warnings (uninitialized variables, strict aliasing) Bug 560808 - Structure support Version 0.2 (12-Nov-2008) ------------------------- Changes: - Compatible with gobject-introspection 0.6.0 - New modules: mainloop, signals, tweener - Support passing string arrays to gobject-introspection methods - Added jsUnit based unit testing framework - Improved error handling and error reporting Contributors: Colin Walters, Havoc Pennington, Johan Bilien, Johan Dahlin, Lucas Rocha, Owen Taylor, Tommi Komulainen Bugs fixed: Bug 557398 – Allow requiring namespace version Bug 557448 – Enum and Flags members should be uppercase Bug 557451 – Add search paths from environment variables Bug 557451 – Add search paths from environment variables Bug 557466 – Module name mangling considered harmful Bug 557579 – Remove use of GJS_USE_UNINSTALLED_FILES in favor of GI_TYPELIB_PATH Bug 557772 - gjs_invoke_c_function should work with union and boxed as well Bug 558114 – assertRaises should print return value Bug 558115 – Add test for basic types Bug 558148 – 'const char*' in arguments are leaked Bug 558227 – Memory leak if invoked function returns an error Bug 558741 – Mutual imports cause great confusion Bug 558882 – Bad error if you omit 'new' Bug 559075 – enumerating importer should skip hidden files Bug 559079 – generate code coverage report Bug 559194 - Fix wrong length buffer in get_obj_key() Bug 559612 - Ignore deprecated methods definitions. Bug 560244 - support for strv parameters Version 0.1 ----------- Initial version! Ha! cjs-2.8.0/.gitignore0000664000175000017500000000121512610172032013205 0ustar fabiofabio*.la *.lo *.o *.gir *.typelib *~ .deps/ .libs/ INSTALL Makefile Makefile.in aclocal.m4 autom4te.cache compile config.guess config.h config.h.in config.log config.status config.sub configure depcomp gjs-1.0.pc gjs-console gjs-internals-1.0.pc gjs-gi-1.0.pc gjs-tests gjs-unit gjs/gjs.stp gjs_gi_probes.h install-sh libtool ltmain.sh missing stamp-h1 test_user_data uninstalled-test-bus.conf uninstalled-system-test-bus.conf valgrind.gjs-tests.log valgrind.gjs-unit.log installed-tests/gjs-installed-tests installed-tests/gjs-installed-tests.testmeta jsunit jsunit.test testSystemExit.test modules-resources.[ch] mock-js-resources.[ch] *.gcda *.gcno lcov