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; }
JSBool gjs_define_root_importer(JSContext *context, JSObject *in_object, const char *importer_name) { JSObject *global; jsval value; JSBool success; success = JS_FALSE; global = gjs_get_import_global(context); JS_BeginRequest(context); if (!gjs_object_require_property(context, global, "global object", "imports", &value) || !JSVAL_IS_OBJECT(value)) { gjs_debug(GJS_DEBUG_IMPORTER, "Root importer did not exist, couldn't get from load context; must create it"); goto fail; } if (!JS_DefineProperty(context, in_object, importer_name, value, NULL, NULL, GJS_MODULE_PROP_FLAGS)) { gjs_debug(GJS_DEBUG_IMPORTER, "DefineProperty %s on %p failed", importer_name, in_object); goto fail; } success = JS_TRUE; fail: JS_EndRequest(context); return success; }
static JSBool boxed_invoke_constructor(JSContext *context, JSObject *obj, const gchar *constructor_name, unsigned argc, jsval *argv, jsval *rval) { jsval js_constructor, js_constructor_func; if (!gjs_object_require_property (context, obj, NULL, "constructor", &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 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; }
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_runtime_get_const_string(JS_GetRuntime(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; }
static JSBool error_constructor_value_of(JSContext *context, uintN argc, jsval *vp) { jsval v_self, v_prototype; Error *priv; jsval v_out; 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; } if (!gjs_object_require_property(context, JSVAL_TO_OBJECT(v_self), "constructor", "prototype", &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; }
/* * 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 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, 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; }
JSObject* gjs_init_class_dynamic(JSContext *context, JSObject *in_object, JSObject *parent_proto, const char *ns_name, const char *class_name, JSClass *clasp, JSNative constructor, uintN nargs, JSPropertySpec *ps, JSFunctionSpec *fs, JSPropertySpec *static_ps, JSFunctionSpec *static_fs) { jsval value; char *private_name; JSObject *prototype; JSContext *load_context; if (clasp->name != NULL) { g_warning("Dynamic class should not have a name in the JSClass struct"); return NULL; } JS_BeginRequest(context); /* We replace the passed-in context and global object with our * runtime-global permanent load context. Otherwise, in a * process with multiple contexts, we'd arbitrarily define * the class in whatever global object initialized the * class first, which is not desirable. */ load_context = gjs_runtime_get_load_context(JS_GetRuntime(context)); JS_BeginRequest(load_context); /* JS_InitClass() wants to define the constructor in the global object, so * we give it a private and namespaced name... passing in the namespace * object instead of global object seems to break JS_ConstructObject() * which then can't find the constructor for the class. I am probably * missing something. */ private_name = g_strdup_printf("_private_%s_%s", ns_name, class_name); prototype = NULL; if (gjs_object_get_property(load_context, JS_GetGlobalObject(load_context), private_name, &value) && JSVAL_IS_OBJECT(value)) { jsval proto_val; g_free(private_name); /* don't need it anymore */ if (!gjs_object_require_property(load_context, JSVAL_TO_OBJECT(value), NULL, "prototype", &proto_val) || !JSVAL_IS_OBJECT(proto_val)) { gjs_throw(load_context, "prototype was not defined or not an object?"); goto error; } prototype = JSVAL_TO_OBJECT(proto_val); } else { DynamicJSClass *class_copy; RuntimeData *rd; rd = get_data_from_context(load_context); class_copy = g_slice_new0(DynamicJSClass); class_copy->base = *clasp; class_copy->base.name = private_name; /* Pass ownership of memory */ class_copy->static_class = clasp; /* record the allocated class to be destroyed with the runtime and so * we can do an IS_DYNAMIC_CLASS check */ g_hash_table_replace(rd->dynamic_classes, class_copy, class_copy); gjs_debug(GJS_DEBUG_GREPO, "Initializing dynamic class %s %p", class_name, class_copy); prototype = JS_InitClass(load_context, JS_GetGlobalObject(load_context), parent_proto, &class_copy->base, constructor, nargs, ps, fs, static_ps, static_fs); /* Retrieve the property again so we can define it in * in_object */ if (!gjs_object_require_property(load_context, JS_GetGlobalObject(load_context), NULL, class_copy->base.name, &value)) goto error; } g_assert(value != JSVAL_VOID); g_assert(prototype != NULL); /* Now manually define our constructor with a sane name, in the * namespace object. */ if (!JS_DefineProperty(load_context, in_object, class_name, value, NULL, NULL, GJS_MODULE_PROP_FLAGS)) goto error; JS_EndRequest(load_context); JS_EndRequest(context); return prototype; error: /* Move the exception to the calling context from load context. */ if (!gjs_move_exception(load_context, context)) { /* set an exception since none was set */ gjs_throw(context, "No exception was set, but class initialize failed somehow"); } JS_EndRequest(load_context); JS_EndRequest(context); return NULL; }
/* 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 = 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", name, &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; }
JSObject* gjs_init_class_dynamic(JSContext *context, JSObject *in_object, JSObject *parent_proto, const char *ns_name, const char *class_name, JSClass *clasp, JSNative constructor, uintN nargs, JSPropertySpec *ps, JSFunctionSpec *fs, JSPropertySpec *static_ps, JSFunctionSpec *static_fs) { jsval value; char *private_name; JSObject *global; JSObject *prototype; if (clasp->name != NULL) { g_warning("Dynamic class should not have a name in the JSClass struct"); return NULL; } JS_BeginRequest(context); /* We use a special "fake" global object to store our constructors * in for future use. Using the actual global object of the context would * result in different contexts having different class definitions for * the same GObject class; since the proxies are shared between all * contexts, this would produce confusing results. */ global = gjs_get_import_global(context); /* JS_InitClass() wants to define the constructor in the global object, so * we give it a private and namespaced name... passing in the namespace * object instead of global object seems to break JS_ConstructObject() * which then can't find the constructor for the class. I am probably * missing something. */ private_name = g_strdup_printf("_private_%s_%s", ns_name, class_name); prototype = NULL; if (gjs_object_get_property(context, global, private_name, &value) && JSVAL_IS_OBJECT(value)) { jsval proto_val; g_free(private_name); /* don't need it anymore */ if (!gjs_object_require_property(context, JSVAL_TO_OBJECT(value), NULL, "prototype", &proto_val) || !JSVAL_IS_OBJECT(proto_val)) { gjs_throw(context, "prototype was not defined or not an object?"); goto error; } prototype = JSVAL_TO_OBJECT(proto_val); } else { DynamicJSClass *class_copy; RuntimeData *rd; rd = get_data_from_context(context); class_copy = g_slice_new0(DynamicJSClass); class_copy->base = *clasp; class_copy->base.name = private_name; /* Pass ownership of memory */ class_copy->static_class = clasp; /* record the allocated class to be destroyed with the runtime and so * we can do an IS_DYNAMIC_CLASS check */ g_hash_table_replace(rd->dynamic_classes, class_copy, class_copy); gjs_debug(GJS_DEBUG_GREPO, "Initializing dynamic class %s %p", class_name, class_copy); prototype = JS_InitClass(context, global, parent_proto, &class_copy->base, constructor, nargs, ps, fs, static_ps, static_fs); if (prototype == NULL) goto error; /* Retrieve the property again so we can define it in * in_object */ if (!gjs_object_require_property(context, global, NULL, class_copy->base.name, &value)) goto error; } g_assert(!JSVAL_IS_VOID(value)); g_assert(prototype != NULL); /* Now manually define our constructor with a sane name, in the * namespace object. */ if (!JS_DefineProperty(context, in_object, class_name, value, NULL, NULL, GJS_MODULE_PROP_FLAGS)) goto error; JS_EndRequest(context); return prototype; error: JS_EndRequest(context); return NULL; }
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; }