static JSBool error_to_string(JSContext *context, uintN 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 gjs_ngettext(JSContext *context, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(context, vp); char *msgid1; char *msgid2; guint32 n; const char *translated; JSBool result; jsval retval; if (!gjs_parse_args (context, "ngettext", "ssu", argc, argv, "msgid1", &msgid1, "msgid2", &msgid2, "n", &n)) return JS_FALSE; translated = ngettext(msgid1, msgid2, n); result = gjs_string_from_utf8(context, translated, -1, &retval); if (result) JS_SET_RVAL(context, vp, retval); g_free (msgid1); g_free (msgid2); return result; }
static JSBool gjs_pgettext(JSContext *context, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(context, vp); char *src_context; char *msgid; const char *translated; JSBool result; jsval retval; if (!gjs_parse_args (context, "pgettext", "ss", argc, argv, "context", &src_context, "msgid", &msgid)) return JS_FALSE; translated = g_dpgettext2(NULL, src_context, msgid); g_free (src_context); result = gjs_string_from_utf8(context, translated, -1, &retval); if (result) JS_SET_RVAL(context, vp, retval); g_free (msgid); return result; }
static JSBool gjs_locale_to_lower_case (JSContext *context, JSString *src, jsval *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)) goto out; success = JS_TRUE; out: g_free(utf8); g_free(lower_case_utf8); return success; }
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 JSBool gjs_dgettext(JSContext *context, unsigned argc, jsval *vp) { jsval *argv = JS_ARGV(context, vp); char *domain; char *msgid; const char *translated; JSBool result; jsval retval; if (!gjs_parse_args (context, "dgettext", "zs", argc, argv, "domain", &domain, "msgid", &msgid)) return JS_FALSE; translated = dgettext(domain, msgid); g_free (domain); result = gjs_string_from_utf8(context, translated, -1, &retval); if (result) JS_SET_RVAL(context, vp, retval); g_free (msgid); return result; }
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; }
bool gjs_string_from_filename(JSContext *context, const char *filename_string, ssize_t n_bytes, JS::MutableHandleValue 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 false; } if (!gjs_string_from_utf8(context, utf8_string, written, value_p)) return false; g_free(utf8_string); return true; }
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; }
/* define properties that JS Error() expose, such as fileName, lineNumber and stack */ static void define_error_properties(JSContext *context, JSObject *obj) { JSStackFrame *frame; JSScript *script; jsbytecode *pc; jsval v; GString *stack; const char *filename; GjsContext *gjs_context; /* find the JS frame that triggered the error */ frame = NULL; while (JS_FrameIterator(context, &frame)) { if (JS_IsScriptFrame(context, frame)) break; } /* someone called gjs_throw at top of the stack? well, no stack in that case */ if (!frame) return; script = JS_GetFrameScript(context, frame); pc = JS_GetFramePC(context, frame); stack = g_string_new(NULL); gjs_context = JS_GetContextPrivate(context); gjs_context_print_stack_to_buffer(gjs_context, frame, stack); if (gjs_string_from_utf8(context, stack->str, stack->len, &v)) JS_DefineProperty(context, obj, "stack", v, NULL, NULL, JSPROP_ENUMERATE); filename = JS_GetScriptFilename(context, script); if (gjs_string_from_filename(context, filename, -1, &v)) JS_DefineProperty(context, obj, "fileName", v, NULL, NULL, JSPROP_ENUMERATE); v = INT_TO_JSVAL(JS_PCToLineNumber(context, script, pc)); JS_DefineProperty(context, obj, "lineNumber", v, NULL, NULL, JSPROP_ENUMERATE); g_string_free(stack, TRUE); }
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)); ret = gjs_string_from_utf8(context, g_type_name(gtype), -1, &retval); if (ret) JS_SET_RVAL(context, vp, retval); return ret; }
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 to_string_func(JSContext *context, uintN 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)); 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 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); if (ret) JS_SET_RVAL(context, vp, retval); return ret; }
static JSBool get_name (JSContext *context, JS::HandleObject obj, JS::HandleId id, jsval *vp) { Ns *priv; jsval retval; JSBool ret = JS_FALSE; priv = priv_from_js(context, obj); if (priv == NULL) goto out; if (gjs_string_from_utf8(context, priv->gi_namespace, -1, &retval)) { *vp = retval; ret = JS_TRUE; } out: return ret; }
static JSBool gjs_locale_to_unicode (JSContext *context, const char *src, jsval *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); g_free (utf8); return success; }
static JSBool gjs_format_int_alternative_output(JSContext *context, uintN argc, jsval *vp) { jsval *argv = JS_ARGV(cx, vp); char *str; jsval rval; int intval; JSBool ret; if (!gjs_parse_args(context, "format_int_alternative_output", "i", argc, argv, "intval", &intval)) return JS_FALSE; str = g_strdup_printf("%Id", intval); ret = gjs_string_from_utf8(context, str, -1, &rval); if (ret) JS_SET_RVAL(context, vp, rval); g_free (str); return ret; }
/* * 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_runtime_get_const_string(JS_GetRuntime(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 = JSVAL_TO_PRIVATE(*state_p); if (iter->index < iter->elements->len) { if (!gjs_string_from_utf8(context, 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 = JSVAL_TO_PRIVATE(*state_p); importer_iterator_free(iter); *state_p = JSVAL_NULL; } } } return JS_TRUE; }
JSBool gjs_js_define_system_stuff(JSContext *context, JSObject *module) { GjsContext *gjs_context; char *program_name; jsval value; JSBool retval; if (!JS_DefineFunction(context, module, "addressOf", (JSNative) gjs_address_of, 1, GJS_MODULE_PROP_FLAGS)) return JS_FALSE; if (!JS_DefineFunction(context, module, "refcount", (JSNative) gjs_refcount, 1, GJS_MODULE_PROP_FLAGS)) return JS_FALSE; if (!JS_DefineFunction(context, module, "breakpoint", (JSNative) gjs_breakpoint, 0, GJS_MODULE_PROP_FLAGS)) return JS_FALSE; if (!JS_DefineFunction(context, module, "gc", (JSNative) gjs_gc, 0, GJS_MODULE_PROP_FLAGS)) return JS_FALSE; if (!JS_DefineFunction(context, module, "exit", (JSNative) gjs_exit, 1, GJS_MODULE_PROP_FLAGS)) return JS_FALSE; retval = JS_FALSE; gjs_context = 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); return retval; }
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) { uint 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 = 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, 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, 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 |= 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 { gjs_throw(context, "Don't know how to convert GType %s to JavaScript object", g_type_name(gtype)); return JS_FALSE; } return JS_TRUE; }
/* * 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, JS::HandleObject object, JSIterateOp enum_op, JS::MutableHandleValue statep, JS::MutableHandleId idp) { ImporterIterator *iter; switch (enum_op) { case JSENUMERATE_INIT_ALL: case JSENUMERATE_INIT: { Importer *priv; JSObject *search_path; jsval search_path_val; uint32_t search_path_len; uint32_t i; jsid search_path_name; statep.set(JSVAL_NULL); idp.set(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 (!search_path_val.isObject()) { 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) { std::string dirname; std::string init_path; const char *filename; jsval elem; std::vector<std::string> dir; 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 = pathCombine(dirname, MODULE_INIT_FILENAME); load_module_elements(context, object, iter, init_path); dir = enumerateFilesInDirectory(dirname); if (dir.size() == 0) { continue; } for(auto filename : dir) { std::string full_path; /* skip hidden files and directories (.svn, .git, ...) */ if (filename[0] == '.') continue; /* skip module init file */ if (filename == MODULE_INIT_FILENAME) continue; full_path = pathCombine(dirname, filename); if (is_directory(full_path)) { iter->elements.push_back(filename); } else { if (filename.rfind(MODULE_SUFFIX) != std::string::npos || filename.rfind(JS_SUFFIX) != std::string::npos) { iter->elements.push_back(filename.substr(0, filename.size()-3)); } } } } statep.set(PRIVATE_TO_JSVAL(iter)); idp.set(INT_TO_JSID(iter->elements.size())); break; } case JSENUMERATE_NEXT: { jsval element_val; if (JSVAL_IS_NULL(statep)) /* Iterating prototype */ return JS_TRUE; iter = (ImporterIterator*) JSVAL_TO_PRIVATE(statep); if (iter->index < iter->elements.size()) { if (!gjs_string_from_utf8(context, iter->elements.at(iter->index++), &element_val)) return JS_FALSE; jsid id; if (!JS_ValueToId(context, element_val, &id)) return JS_FALSE; idp.set(id); break; } /* else fall through to destroying the iterator */ } case JSENUMERATE_DESTROY: { if (!JSVAL_IS_NULL(statep)) { iter = (ImporterIterator*) JSVAL_TO_PRIVATE(statep); importer_iterator_free(iter); statep.set(JSVAL_NULL); } } } return JS_TRUE; }
/* implement toString() with an optional encoding arg */ static JSBool to_string_func(JSContext *context, unsigned argc, jsval *vp) { JS::CallArgs argv = JS::CallArgsFromVp (argc, vp); JSObject *object = JSVAL_TO_OBJECT(argv.thisv()); 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) argv.rval().set(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; argv.rval().set(STRING_TO_JSVAL(s)); } g_free(u16_str); return ok; } }
/* * 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); 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; (void)JS_EnterLocalRootScope(context); if (!gjs_string_from_utf8(context, s, -1, &v_message)) { JS_ReportError(context, "Failed to copy exception string"); goto out; } if (!gjs_object_get_property(context, JS_GetGlobalObject(context), error_class, &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: JS_LeaveLocalRootScope(context); 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); }
/* * 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 *format, va_list args) { char *s; jsval retval; jsval argv[1]; JSFunction *func; const char *body; JSBool result; const char *names[] = { "message" }; guint options; s = g_strdup_vprintf(format, args); 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); return; } result = JS_FALSE; JS_EnterLocalRootScope(context); if (!gjs_string_from_utf8(context, s, -1, &argv[0])) { JS_ReportError(context, "Failed to copy exception string"); goto out; } body = "throw new Error(message);"; func = JS_CompileFunction(context, JS_GetGlobalObject(context), /* parent object (scope chain) */ NULL, /* name of function if we wanted to define it in parent */ 1, /* nargs */ &names[0], /* array of arg names if we had args */ body, strlen(body), "gjs_throw", /* file */ 0); /* line */ if (func == NULL) { JS_ReportError(context, "Failed to compile function"); goto out; } /* we need JS_CallFunctionValue() to leave the exception set */ options = JS_GetOptions(context); if (!(options & JSOPTION_DONT_REPORT_UNCAUGHT)) { JS_SetOptions(context, options | JSOPTION_DONT_REPORT_UNCAUGHT); } retval = JSVAL_VOID; /* note the return value is whether function succeeded, which it shouldn't, since it * throws... */ JS_CallFunctionValue(context, JS_GetGlobalObject(context), OBJECT_TO_JSVAL(JS_GetFunctionObject(func)), 1, &argv[0], &retval); if (!(options & JSOPTION_DONT_REPORT_UNCAUGHT)) { JS_SetOptions(context, options); } if (!JS_IsExceptionPending(context)) { JS_ReportError(context, "Failed to set exception by calling our exception-setting function"); goto out; } result = JS_TRUE; out: JS_LeaveLocalRootScope(context); 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); }