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 JSBool gjs_locale_compare (JSContext *context, JSString *src_1, JSString *src_2, jsval *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 = INT_TO_JSVAL(result); success = JS_TRUE; out: g_free(utf8_1); g_free(utf8_2); return success; }
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 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; }
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; }
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_print_parse_args(JSContext *context, uintN argc, jsval *argv, char **buffer) { GString *str; gchar *s; guint n; JS_BeginRequest(context); str = g_string_new(""); (void)JS_EnterLocalRootScope(context); 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_LeaveLocalRootScope(context); 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_LeaveLocalRootScope(context); JS_EndRequest(context); *buffer = g_string_free(str, TRUE); if (!*buffer) *buffer = g_strdup("<invalid string>"); return JS_TRUE; } } JS_LeaveLocalRootScope(context); *buffer = g_string_free(str, FALSE); JS_EndRequest(context); return JS_TRUE; }
/** * 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. */ bool gjs_unichar_from_string (JSContext *context, JS::Value value, gunichar *result) { GjsAutoJSChar utf8_str; if (gjs_string_to_utf8(context, value, &utf8_str)) { *result = g_utf8_get_char(utf8_str); return true; } return 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; }
static JSBool gjs_log_error(JSContext *context, uintN argc, jsval *vp) { jsval *argv = JS_ARGV(context, vp); char *s; JSExceptionState *exc_state; JSString *jstr; jsval exc; if (argc != 2) { gjs_throw(context, "Must pass an exception and message string to logError()"); return JS_FALSE; } JS_BeginRequest(context); exc = argv[0]; /* 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); if (jstr == NULL) { gjs_debug(GJS_DEBUG_ERROR, "<cannot convert value to string>"); gjs_log_exception_props(context, exc); JS_EndRequest(context); return JS_TRUE; } if (!gjs_string_to_utf8(context, STRING_TO_JSVAL(jstr), &s)) { JS_EndRequest(context); return JS_FALSE; } gjs_debug(GJS_DEBUG_ERROR, "%s", s); gjs_log_exception_props(context, exc); g_free(s); JS_EndRequest(context); JS_SET_RVAL(context, vp, JSVAL_VOID); return JS_TRUE; }
static char * get_module_name(JSContext *context, JSObject *module_obj, jsval *tmp) { char *name; if (gjs_object_get_property(context, module_obj, "__moduleName__", tmp) && JSVAL_IS_STRING(*tmp)) if (gjs_string_to_utf8(context, *tmp, &name)) return name; else return NULL; else return NULL; }
/** * 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_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 **/ bool gjs_get_string_id (JSContext *context, jsid id, char **name_p) { JS::RootedValue id_val(context); if (!JS_IdToValue(context, id, &id_val)) return false; if (id_val.isString()) { return gjs_string_to_utf8(context, id_val, name_p); } else { *name_p = NULL; return false; } }
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); }
bool gjs_string_to_filename(JSContext *context, const JS::Value filename_val, GjsAutoChar *filename_string) { GError *error; GjsAutoJSChar tmp; /* gjs_string_to_filename verifies that filename_val is a string */ if (!gjs_string_to_utf8(context, filename_val, &tmp)) { /* exception already set */ return false; } error = NULL; *filename_string = g_filename_from_utf8(tmp, -1, NULL, NULL, &error); if (!*filename_string) { gjs_throw_g_error(context, error); return false; } return 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; }
static JSBool do_import(JSContext *context, JSObject *obj, Importer *priv, std::string &name) { std::string filename; std::string full_path; std::string dirname; jsval search_path_val; JSObject *search_path; JSObject *module_obj = NULL; uint32_t search_path_len; uint32_t i; JSBool result; std::vector<std::string> directories; jsid search_path_name; bool 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 (!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; } result = JS_FALSE; filename = std::string(name) + ".js"; /* 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; } 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 */ full_path = pathCombine(dirname, MODULE_INIT_FILENAME); module_obj = load_module_init(context, obj, full_path); if (module_obj != NULL) { jsval obj_val; if (JS_GetProperty(context, module_obj, name.c_str(), &obj_val)) { if (!JSVAL_IS_VOID(obj_val) && JS_DefineProperty(context, obj, name.c_str(), obj_val, NULL, NULL, GJS_MODULE_PROP_FLAGS & ~JSPROP_PERMANENT)) { result = JS_TRUE; goto out; } } } /* Second try importing a directory (a sub-importer) */ full_path = pathCombine(dirname, name); if (is_directory(full_path)) { std::cout << "Adding directory '" << full_path << "' to child importer '" << name << "'\n", directories.push_back(full_path); } /* 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.size() > 0) { continue; } /* Third, if it's not a directory, try importing a file */ full_path = pathCombine(dirname, filename); std::cout << "full path: " << full_path << "\n"; exists = is_regular(full_path); if (!exists) { std::cout << "JS import '" << name << "' not found in " << dirname << "\n"; continue; } if (import_file_on_module (context, obj, name, full_path)) { std::cout << "successfully imported module '" << name << "'\n"; result = JS_TRUE; } /* 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.size() > 0) { /* NULL-terminate the char** */ if (import_directory(context, obj, name, directories)) { std::cout << "successfully imported directory '" << name << "'\n"; result = JS_TRUE; } } out: 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.c_str()); } return result; }
static JSBool log_and_maybe_keep_exception(JSContext *context, char **message_p, gboolean keep) { jsval exc = JSVAL_VOID; JSString *s; char *message; JSBool retval = JS_FALSE; JS_BeginRequest(context); if (message_p) *message_p = NULL; JS_AddRoot(context, &exc); if (!JS_GetPendingException(context, &exc)) goto out; JS_ClearPendingException(context); s = JS_ValueToString(context, exc); if (s == NULL) { gjs_debug(GJS_DEBUG_ERROR, "Failed to convert exception to string"); goto out; /* Exception should be thrown already */ } if (!gjs_string_to_utf8(context, STRING_TO_JSVAL(s), &message)) { gjs_debug(GJS_DEBUG_ERROR, "Failed to convert exception string to UTF-8"); goto out; /* Error already set */ } gjs_debug(GJS_DEBUG_ERROR, "Exception was: %s", message); if (message_p) { *message_p = message; } else { g_free(message); } gjs_log_exception_props(context, exc); /* 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_RemoveRoot(context, &exc); JS_EndRequest(context); return retval; }
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) { if (JSVAL_IS_NULL(value)) { /* do nothing */ } else if (gjs_object_has_property(context, JSVAL_TO_OBJECT(value), "length")) { jsval length_value; guint32 length; if (!gjs_object_require_property(context, JSVAL_TO_OBJECT(value), NULL, "length", &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 = 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 { /* 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 (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 = 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; /* See arg.c:_gjs_enum_to_int() */ v = g_enum_get_value(G_ENUM_CLASS(g_type_class_peek(gtype)), (int)value_int64); 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, 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; }
/* 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; } }
/* fromString() function implementation */ static JSBool from_string_func(JSContext *context, unsigned argc, jsval *vp) { JS::CallArgs argv = JS::CallArgsFromVp (argc, 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); } argv.rval().set(OBJECT_TO_JSVAL(obj)); retval = JS_TRUE; out: JS_RemoveObjectRoot(context, &obj); 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; search_path_name = gjs_runtime_get_const_string(JS_GetRuntime(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); if (g_file_test(full_path, G_FILE_TEST_IS_DIR)) { 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; } /* 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); if (g_file_test(full_path, G_FILE_TEST_EXISTS)) { if (import_file(context, obj, name, full_path)) { gjs_debug(GJS_DEBUG_IMPORTER, "successfully imported module '%s'", name); result = JS_TRUE; } /* 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; } gjs_debug(GJS_DEBUG_IMPORTER, "JS import '%s' not found in %s", name, dirname); } 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; }
/* * 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; }
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); }