bool jsval_to_FBInfo(JSContext *cx, jsval v, StringMap* ret) { JSObject* tmp = JSVAL_TO_OBJECT(v); if (!tmp) { LOGD("jsval_to_TProductInfo: the jsval is not an object."); return false; } JSObject* it = JS_NewPropertyIterator(cx, tmp); while (true) { jsid idp; jsval key; if (! JS_NextProperty(cx, it, &idp) || ! JS_IdToValue(cx, idp, &key)) return false; // error if (key == JSVAL_VOID) break; // end of iteration if (! JSVAL_IS_STRING(key)) continue; // ignore integer properties JS::RootedValue value(cx); JS_GetPropertyById(cx, tmp, idp, &value); // if (! JSVAL_IS_STRING(value)) // continue; // ignore integer properties if(JSVAL_IS_STRING(value)) { JSStringWrapper strWrapper(JSVAL_TO_STRING(key), cx); JSStringWrapper strWrapper2(JSVAL_TO_STRING(value), cx); ret->insert(std::map<std::string, std::string>::value_type(strWrapper.get(), strWrapper2.get())); } else if(JSVAL_IS_NUMBER(value)) { double number = 0.0; JS::ToNumber(cx, value, &number); std::stringstream ss; ss << number; JSStringWrapper strWrapper(JSVAL_TO_STRING(key), cx); //JSStringWrapper strWrapper2(JSVAL_TO_STRING(value), cx); ret->insert(std::map<std::string, std::string>::value_type(strWrapper.get(), ss.str())); } else if(JSVAL_IS_BOOLEAN(value)) { bool boolVal = JS::ToBoolean(value); JSStringWrapper strWrapper(JSVAL_TO_STRING(key), cx); //JSStringWrapper strWrapper2(JSVAL_TO_STRING(value), cx); std::string boolstring = boolVal ? "true" : "false"; ret->insert(std::map<std::string, std::string>::value_type(strWrapper.get(), boolstring)); } } return true; }
void consumeEvent (JSContext* jsCtx, AmityEvent* event) { jsval fval; if (!JS_GetPropertyById(jsCtx, event->obj, event->handler, &fval)) { return; // Missing handler } jsval rval; JS_CallFunctionValue(jsCtx, event->obj, fval, 1, &event->param, &rval); }
bool ScriptInterface::GetPropertyInt_(jsval obj, int name, jsval& out) { if (! JSVAL_IS_OBJECT(obj)) return false; JSObject* object = JSVAL_TO_OBJECT(obj); if (!JS_GetPropertyById(m->m_cx, object, INT_TO_JSID(name), &out)) return false; return true; }
// Generate 'usage' and 'help' properties for the given object. // JS_DefineFunctionsWithHelp will define individual function objects with both // of those properties (eg getpid.usage = "getpid()" and getpid.help = "return // the process id"). This function will generate strings for an "interface // object", eg os.file, which contains some number of functions. // // .usage will be set to "<name> - interface object". // // .help will be set to a newline-separated list of functions that have either // 'help' or 'usage' properties. Functions are described with their usage // strings, if they have them, else with just their names. // bool GenerateInterfaceHelp(JSContext* cx, HandleObject obj, const char* name) { AutoIdVector idv(cx); if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN, &idv)) return false; StringBuffer buf(cx); int numEntries = 0; for (size_t i = 0; i < idv.length(); i++) { RootedId id(cx, idv[i]); RootedValue v(cx); if (!JS_GetPropertyById(cx, obj, id, &v)) return false; if (!v.isObject()) continue; RootedObject prop(cx, &v.toObject()); RootedValue usage(cx); RootedValue help(cx); if (!JS_GetProperty(cx, prop, "usage", &usage)) return false; if (!JS_GetProperty(cx, prop, "help", &help)) return false; if (!usage.isString() && !help.isString()) continue; if (numEntries && !buf.append("\n")) return false; numEntries++; if (!buf.append(" ", 2)) return false; if (!buf.append(usage.isString() ? usage.toString() : JSID_TO_FLAT_STRING(id))) return false; } RootedString s(cx, buf.finishString()); if (!s || !JS_DefineProperty(cx, obj, "help", s, 0)) return false; buf.clear(); if (!buf.append(name, strlen(name)) || !buf.append(" - interface object with ", 25)) return false; char cbuf[100]; SprintfLiteral(cbuf, "%d %s", numEntries, numEntries == 1 ? "entry" : "entries"); if (!buf.append(cbuf, strlen(cbuf))) return false; s = buf.finishString(); if (!s || !JS_DefineProperty(cx, obj, "usage", s, 0)) return false; return true; }
static JSBool XPC_COW_GetOrSetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp, JSBool isSet) { obj = GetWrapper(obj); if (!obj) { return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); } XPCCallContext ccx(JS_CALLER, cx); if (!ccx.IsValid()) { return ThrowException(NS_ERROR_FAILURE, cx); } AUTO_MARK_JSVAL(ccx, vp); JSObject *wrappedObj = GetWrappedObject(cx, obj); if (!wrappedObj) { return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); } jsid interned_id; if (!JS_ValueToId(cx, id, &interned_id)) { return JS_FALSE; } if (interned_id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_PROTO) || interned_id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_PARENT) || interned_id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_EXPOSEDPROPS)) { // No getting or setting __proto__ or __parent__ on my object. return ThrowException(NS_ERROR_INVALID_ARG, cx); // XXX better error message } JSBool canTouch; if (!CanTouchProperty(cx, obj, interned_id, isSet, &canTouch)) { return JS_FALSE; } if (!canTouch) { return ThrowException(NS_ERROR_XPC_SECURITY_MANAGER_VETO, cx); } if (!XPC_COW_RewrapForChrome(cx, obj, vp)) { return JS_FALSE; } JSBool ok = isSet ? JS_SetPropertyById(cx, wrappedObj, interned_id, vp) : JS_GetPropertyById(cx, wrappedObj, interned_id, vp); if (!ok) { return JS_FALSE; } return XPC_COW_RewrapForContent(cx, obj, vp); }
int to_erl_object(ErlNifEnv* env, JSContext* cx, JSObject* obj, ERL_NIF_TERM* term) { ERL_NIF_TERM* array = NULL; ERL_NIF_TERM list; ERL_NIF_TERM keyterm; ERL_NIF_TERM valterm; JSObject* iter; jsid idp; jsval val; int length; int index; int ret = ERROR; iter = JS_NewPropertyIterator(cx, obj); if(iter == NULL) goto done; length = 0; while(JS_NextProperty(cx, iter, &idp)) { if(idp == JSID_VOID) break; length += 1; } array = enif_alloc(length * sizeof(ERL_NIF_TERM)); if(array == NULL) goto done; iter = JS_NewPropertyIterator(cx, obj); if(iter == NULL) goto done; index = 0; while(JS_NextProperty(cx, iter, &idp)) { if(idp == JSID_VOID) { list = enif_make_list_from_array(env, array, length); *term = enif_make_tuple1(env, list); ret = OK; goto done; } if(!JS_IdToValue(cx, idp, &val)) goto done; if(!to_erl_string(env, cx, val, &keyterm)) goto done; if(!JS_GetPropertyById(cx, obj, idp, &val)) goto done; if(!to_erl_intern(env, cx, val, &valterm)) goto done; array[index] = enif_make_tuple2(env, keyterm, valterm); index += 1; } done: if(array != NULL) enif_free(array); return ret; }
bool ScriptInterface::GetPropertyInt_(JS::HandleValue obj, int name, JS::MutableHandleValue out) { JSAutoRequest rq(m->m_cx); JS::RootedId nameId(m->m_cx, INT_TO_JSID(name)); if (!obj.isObject()) return false; JS::RootedObject object(m->m_cx, &obj.toObject()); if (!JS_GetPropertyById(m->m_cx, object, nameId, out)) return false; return true; }
bool jsval_to_std_map_string_string(JSContext *cx, jsval v, std::map<std::string, std::string>* ret) { if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) { return true; } JSObject* tmp = JSVAL_TO_OBJECT(v); if (!tmp) { CCLOG("%s", "jsval_to_ccvaluemap: the jsval is not an object."); return false; } JSObject* it = JS_NewPropertyIterator(cx, tmp); while (true) { jsid idp; jsval key; if (! JS_NextProperty(cx, it, &idp) || ! JS_IdToValue(cx, idp, &key)) { return false; // error } if (key == JSVAL_VOID) { break; // end of iteration } if (!JSVAL_IS_STRING(key)) { continue; // ignore integer properties } JSStringWrapper keyWrapper(JSVAL_TO_STRING(key), cx); JS::RootedValue value(cx); JS_GetPropertyById(cx, tmp, idp, &value); if (value.isString()) { JSStringWrapper valueWapper(JSVAL_TO_STRING(value), cx); ret->insert(std::make_pair(keyWrapper.get(), valueWapper.get())); } else { CCASSERT(false, "not a string"); } } return true; }
bool CallbackInterface::GetCallableProperty(JSContext* cx, JS::Handle<jsid> aPropId, JS::MutableHandle<JS::Value> aCallable) { if (!JS_GetPropertyById(cx, CallbackKnownNotGray(), aPropId, aCallable)) { return false; } if (!aCallable.isObject() || !JS::IsCallable(&aCallable.toObject())) { char* propName = JS_EncodeString(cx, JS_FORGET_STRING_FLATNESS(JSID_TO_FLAT_STRING(aPropId))); nsPrintfCString description("Property '%s'", propName); JS_free(cx, propName); ThrowErrorMessage(cx, MSG_NOT_CALLABLE, description.get()); return false; } return true; }
static JSObject * load_module_init(JSContext *context, JSObject *in_object, const char *full_path) { JSObject *module_obj; JSBool found; jsid module_init_name; GFile *file; /* First we check if js module has already been loaded */ module_init_name = gjs_context_get_const_string(context, GJS_STRING_MODULE_INIT); if (!is_extension_module (full_path) && JS_HasPropertyById(context, in_object, module_init_name, &found) && found) { jsval module_obj_val; if (JS_GetPropertyById(context, in_object, module_init_name, &module_obj_val)) { return JSVAL_TO_OBJECT(module_obj_val); } } module_obj = create_module_object (context); file = g_file_new_for_commandline_arg(full_path); if (!import_file (context, "__init__", file, module_obj)) goto out; if (!JS_DefinePropertyById(context, in_object, module_init_name, OBJECT_TO_JSVAL(module_obj), NULL, NULL, GJS_MODULE_PROP_FLAGS & ~JSPROP_PERMANENT)) goto out; out: g_object_unref (file); return module_obj; }
void XPCWrappedNativeScope::SetGlobal(XPCCallContext& ccx, JSObject* aGlobal) { // We allow for calling this more than once. This feature is used by // nsXPConnect::InitClassesWithNewWrappedGlobal. mGlobalJSObject = aGlobal; mScriptObjectPrincipal = nsnull; // Now init our script object principal, if the new global has one const JSClass* jsClass = aGlobal->getJSClass(); if(!(~jsClass->flags & (JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS))) { // Our global has an nsISupports native pointer. Let's // see whether it's what we want. nsISupports* priv = (nsISupports*)xpc_GetJSPrivate(aGlobal); nsCOMPtr<nsIXPConnectWrappedNative> native = do_QueryInterface(priv); nsCOMPtr<nsIScriptObjectPrincipal> sop; if(native) { sop = do_QueryWrappedNative(native); } if(!sop) { sop = do_QueryInterface(priv); } mScriptObjectPrincipal = sop; } // Lookup 'globalObject.Object.prototype' for our wrapper's proto { AutoJSErrorAndExceptionEater eater(ccx); // scoped error eater jsval val; jsid idObj = mRuntime->GetStringID(XPCJSRuntime::IDX_OBJECT); jsid idFun = mRuntime->GetStringID(XPCJSRuntime::IDX_FUNCTION); jsid idProto = mRuntime->GetStringID(XPCJSRuntime::IDX_PROTOTYPE); if(JS_GetPropertyById(ccx, aGlobal, idObj, &val) && !JSVAL_IS_PRIMITIVE(val) && JS_GetPropertyById(ccx, JSVAL_TO_OBJECT(val), idProto, &val) && !JSVAL_IS_PRIMITIVE(val)) { mPrototypeJSObject = JSVAL_TO_OBJECT(val); } else { NS_ERROR("Can't get globalObject.Object.prototype"); } if(JS_GetPropertyById(ccx, aGlobal, idFun, &val) && !JSVAL_IS_PRIMITIVE(val) && JS_GetPropertyById(ccx, JSVAL_TO_OBJECT(val), idProto, &val) && !JSVAL_IS_PRIMITIVE(val)) { mPrototypeJSFunction = JSVAL_TO_OBJECT(val); } else { NS_ERROR("Can't get globalObject.Function.prototype"); } } // Clear the no helper wrapper prototype object so that a new one // gets created if needed. mPrototypeNoHelper = nsnull; }
ENTERM vm_call(JSContext* cx, JSObject* gl, job_ptr job) { ENTERM resp; ENTERM head; ENTERM tail; jsval func; jsval args[256]; jsval rval; jsid idp; int argc; // Get the function object. func = to_js(job->env, cx, job->name); if(func == JSVAL_VOID) { resp = vm_mk_error(job->env, util_mk_atom(job->env, "invalid_name")); goto send; } if(!JS_ValueToId(cx, func, &idp)) { resp = vm_mk_error(job->env, util_mk_atom(job->env, "internal_error")); goto send; } if(!JS_GetPropertyById(cx, gl, idp, &func)) { resp = vm_mk_error(job->env, util_mk_atom(job->env, "bad_property")); goto send; } if(JS_TypeOfValue(cx, func) != JSTYPE_FUNCTION) { resp = vm_mk_error(job->env, util_mk_atom(job->env, "not_a_function")); goto send; } // Creating function arguments. if(enif_is_empty_list(job->env, job->args)) { argc = 0; } else { if(!enif_get_list_cell(job->env, job->args, &head, &tail)) { resp = vm_mk_error(job->env, util_mk_atom(job->env, "invalid_argv")); goto send; } argc = 0; do { args[argc++] = to_js(job->env, cx, head); } while(enif_get_list_cell(job->env, tail, &head, &tail) && argc < 256); } // Call function if(!JS_CallFunctionValue(cx, gl, func, argc, args, &rval)) { if(job->error != 0) { resp = vm_mk_error(job->env, job->error); } else { resp = vm_mk_error(job->env, util_mk_atom(job->env, "unknown")); } } else { resp = vm_mk_ok(job->env, to_erl(job->env, cx, rval)); } send: return enif_make_tuple2(job->env, job->ref, resp); }
// Clone a new value (and root it and add it to the mapping) jsval Clone(jsval val) { if (JSVAL_IS_DOUBLE(val)) { jsval rval; CLONE_REQUIRE(JS_NewNumberValue(cxTo, JSVAL_TO_DOUBLE(val), &rval), L"JS_NewNumberValue"); m_RooterTo.Push(rval); return rval; } if (JSVAL_IS_STRING(val)) { size_t len; const jschar* chars = JS_GetStringCharsAndLength(cxFrom, JSVAL_TO_STRING(val), &len); CLONE_REQUIRE(chars, L"JS_GetStringCharsAndLength"); JSString* str = JS_NewUCStringCopyN(cxTo, chars, len); CLONE_REQUIRE(str, L"JS_NewUCStringCopyN"); jsval rval = STRING_TO_JSVAL(str); m_Mapping[JSVAL_TO_GCTHING(val)] = rval; m_RooterTo.Push(rval); return rval; } ENSURE(JSVAL_IS_OBJECT(val)); JSObject* newObj; if (JS_IsArrayObject(cxFrom, JSVAL_TO_OBJECT(val))) { jsuint length; CLONE_REQUIRE(JS_GetArrayLength(cxFrom, JSVAL_TO_OBJECT(val), &length), L"JS_GetArrayLength"); newObj = JS_NewArrayObject(cxTo, length, NULL); CLONE_REQUIRE(newObj, L"JS_NewArrayObject"); } else { newObj = JS_NewObject(cxTo, NULL, NULL, NULL); CLONE_REQUIRE(newObj, L"JS_NewObject"); } m_Mapping[JSVAL_TO_GCTHING(val)] = OBJECT_TO_JSVAL(newObj); m_RooterTo.Push(newObj); AutoJSIdArray ida (cxFrom, JS_Enumerate(cxFrom, JSVAL_TO_OBJECT(val))); CLONE_REQUIRE(ida.get(), L"JS_Enumerate"); AutoGCRooter idaRooter(scriptInterfaceFrom); idaRooter.Push(ida.get()); for (size_t i = 0; i < ida.length(); ++i) { jsid id = ida[i]; jsval idval, propval; CLONE_REQUIRE(JS_IdToValue(cxFrom, id, &idval), L"JS_IdToValue"); CLONE_REQUIRE(JS_GetPropertyById(cxFrom, JSVAL_TO_OBJECT(val), id, &propval), L"JS_GetPropertyById"); jsval newPropval = GetOrClone(propval); if (JSVAL_IS_INT(idval)) { // int jsids are portable across runtimes CLONE_REQUIRE(JS_SetPropertyById(cxTo, newObj, id, &newPropval), L"JS_SetPropertyById"); } else if (JSVAL_IS_STRING(idval)) { // string jsids are runtime-specific, so we need to copy the string content JSString* idstr = JS_ValueToString(cxFrom, idval); CLONE_REQUIRE(idstr, L"JS_ValueToString (id)"); size_t len; const jschar* chars = JS_GetStringCharsAndLength(cxFrom, idstr, &len); CLONE_REQUIRE(idstr, L"JS_GetStringCharsAndLength (id)"); CLONE_REQUIRE(JS_SetUCProperty(cxTo, newObj, chars, len, &newPropval), L"JS_SetUCProperty"); } else { // this apparently could be an XML object; ignore it } } return OBJECT_TO_JSVAL(newObj); }
static JSObject * load_module_init(JSContext *context, JSObject *in_object, const char *full_path) { char *script; gsize script_len; jsval script_retval; JSObject *module_obj; GError *error; JSBool found; jsid module_init_name; /* First we check if js module has already been loaded */ module_init_name = gjs_runtime_get_const_string(JS_GetRuntime(context), GJS_STRING_MODULE_INIT); if (JS_HasPropertyById(context, in_object, module_init_name, &found) && found) { jsval module_obj_val; if (JS_GetPropertyById(context, in_object, module_init_name, &module_obj_val)) { return JSVAL_TO_OBJECT(module_obj_val); } } module_obj = JS_NewObject(context, NULL, NULL, NULL); if (module_obj == NULL) { return JS_FALSE; } /* https://bugzilla.mozilla.org/show_bug.cgi?id=599651 means we * can't just pass in the global as the parent */ JS_SetParent(context, module_obj, gjs_get_import_global (context)); /* Define module in importer for future use and to avoid module_obj * object to be garbage collected during the evaluation of the script */ JS_DefinePropertyById(context, in_object, module_init_name, OBJECT_TO_JSVAL(module_obj), NULL, NULL, GJS_MODULE_PROP_FLAGS & ~JSPROP_PERMANENT); script_len = 0; error = NULL; if (!g_file_get_contents(full_path, &script, &script_len, &error)) { if (!g_error_matches(error, G_FILE_ERROR, G_FILE_ERROR_ISDIR) && !g_error_matches(error, G_FILE_ERROR, G_FILE_ERROR_NOTDIR) && !g_error_matches(error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) gjs_throw_g_error(context, error); else g_error_free(error); return NULL; } g_assert(script != NULL); gjs_debug(GJS_DEBUG_IMPORTER, "Importing %s", full_path); if (!JS_EvaluateScript(context, module_obj, script, script_len, full_path, 1, /* line number */ &script_retval)) { g_free(script); /* If JSOPTION_DONT_REPORT_UNCAUGHT is set then the exception * would be left set after the evaluate and not go to the error * reporter function. */ if (JS_IsExceptionPending(context)) { gjs_debug(GJS_DEBUG_IMPORTER, "Module " MODULE_INIT_FILENAME " left an exception set"); gjs_log_and_keep_exception(context); } else { gjs_throw(context, "JS_EvaluateScript() returned FALSE but did not set exception"); } return NULL; } g_free(script); return module_obj; }
static JSBool XPC_XOW_GetOrSetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp, JSBool isSet) { if (id == GetRTStringByIndex(cx, XPCJSRuntime::IDX_TO_STRING)) { return JS_TRUE; } // Don't do anything if we already resolved to a wrapped function in // NewResolve. In practice, this means that this is a wrapped eval // function. jsval v = *vp; if (!JSVAL_IS_PRIMITIVE(v) && JS_ObjectIsFunction(cx, JSVAL_TO_OBJECT(v)) && JS_GetFunctionNative(cx, JS_ValueToFunction(cx, v)) == XPC_XOW_FunctionWrapper) { return JS_TRUE; } JSObject *origObj = obj; obj = GetWrapper(obj); if (!obj) { return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); } XPCCallContext ccx(JS_CALLER, cx); if (!ccx.IsValid()) { return ThrowException(NS_ERROR_FAILURE, cx); } AUTO_MARK_JSVAL(ccx, vp); JSObject *wrappedObj = GetWrappedObject(cx, obj); if (!wrappedObj) { return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); } JSBool privilegeEnabled; nsresult rv = CanAccessWrapper(cx, wrappedObj, &privilegeEnabled); if (NS_FAILED(rv)) { if (rv != NS_ERROR_DOM_PROP_ACCESS_DENIED) { return JS_FALSE; } // This is a request to get a property across origins. We need to // determine if this property is allAccess. If it is, then we need to // actually get the property. If not, we simply need to throw an // exception. XPCWrappedNative *wn = XPCWrappedNative::GetWrappedNativeOfJSObject(cx, wrappedObj); NS_ASSERTION(wn, "How did we wrap a non-WrappedNative?"); if (!IsValFrame(wrappedObj, id, wn)) { nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); if (!ssm) { return ThrowException(NS_ERROR_NOT_INITIALIZED, cx); } rv = ssm->CheckPropertyAccess(cx, wrappedObj, STOBJ_GET_CLASS(wrappedObj)->name, id, isSet ? XPCWrapper::sSecMgrSetProp : XPCWrapper::sSecMgrGetProp); if (NS_FAILED(rv)) { // The security manager threw an exception for us. return JS_FALSE; } } return XPCWrapper::GetOrSetNativeProperty(cx, obj, wn, id, vp, isSet, JS_FALSE); } JSObject *proto = nsnull; // Initialize this to quiet GCC. JSBool checkProto = (isSet && id == GetRTStringByIndex(cx, XPCJSRuntime::IDX_PROTO)); if (checkProto) { proto = STOBJ_GET_PROTO(wrappedObj); } // Same origin, pass this request along as though nothing interesting // happened. jsid asId; if (!JS_ValueToId(cx, id, &asId)) { return JS_FALSE; } JSBool ok = isSet ? JS_SetPropertyById(cx, wrappedObj, asId, vp) : JS_GetPropertyById(cx, wrappedObj, asId, vp); if (!ok) { return JS_FALSE; } if (checkProto) { JSObject *newProto = STOBJ_GET_PROTO(wrappedObj); // If code is trying to set obj.__proto__ and we're on obj's // prototype chain, then the JS_GetPropertyById above will do the // wrong thing if wrappedObj still delegates to Object.prototype. // However, it's hard to figure out if wrappedObj still does // delegate to Object.prototype so check to see if proto changed as a // result of setting __proto__. if (origObj != obj) { // Undo the damage. if (!JS_SetPrototype(cx, wrappedObj, proto) || !JS_SetPrototype(cx, origObj, newProto)) { return JS_FALSE; } } else if (newProto) { // __proto__ setting is a bad hack, people shouldn't do it. In // this case we're setting the direct prototype of a XOW object, // in the interests of sanity only allow it to be set to null in // this case. JS_SetPrototype(cx, wrappedObj, proto); JS_ReportError(cx, "invalid __proto__ value (can only be set to null)"); return JS_FALSE; } } return WrapSameOriginProp(cx, obj, vp); }
static JSBool JO(JSContext *cx, jsval *vp, StringifyContext *scx) { JSObject *obj = JSVAL_TO_OBJECT(*vp); if (!scx->cb.append('{')) return JS_FALSE; jsval vec[3] = {JSVAL_NULL, JSVAL_NULL, JSVAL_NULL}; JSAutoTempValueRooter tvr(cx, 3, vec); jsval& key = vec[0]; jsval& outputValue = vec[1]; JSObject *iterObj = NULL; jsval *keySource = vp; bool usingWhitelist = false; // if the replacer is an array, we use the keys from it if (scx->replacer && JS_IsArrayObject(cx, scx->replacer)) { usingWhitelist = true; vec[2] = OBJECT_TO_JSVAL(scx->replacer); keySource = &vec[2]; } if (!js_ValueToIterator(cx, JSITER_ENUMERATE, keySource)) return JS_FALSE; iterObj = JSVAL_TO_OBJECT(*keySource); JSBool memberWritten = JS_FALSE; bool ok = false; while (true) { outputValue = JSVAL_VOID; if (!js_CallIteratorNext(cx, iterObj, &key)) goto error_break; if (key == JSVAL_HOLE) break; jsuint index = 0; if (usingWhitelist) { // skip non-index properties if (!js_IdIsIndex(key, &index)) continue; jsval newKey; if (!scx->replacer->getProperty(cx, key, &newKey)) goto error_break; key = newKey; } JSString *ks; if (JSVAL_IS_STRING(key)) { ks = JSVAL_TO_STRING(key); } else { ks = js_ValueToString(cx, key); if (!ks) goto error_break; } JSAutoTempValueRooter keyStringRoot(cx, ks); // Don't include prototype properties, since this operation is // supposed to be implemented as if by ES3.1 Object.keys() jsid id; jsval v = JS_FALSE; if (!js_ValueToStringId(cx, STRING_TO_JSVAL(ks), &id) || !js_HasOwnProperty(cx, obj->map->ops->lookupProperty, obj, id, &v)) { goto error_break; } if (v != JSVAL_TRUE) continue; if (!JS_GetPropertyById(cx, obj, id, &outputValue)) goto error_break; if (JSVAL_IS_OBJECT(outputValue) && !js_TryJSON(cx, &outputValue)) goto error_break; // call this here, so we don't write out keys if the replacer function // wants to elide the value. if (!CallReplacerFunction(cx, id, obj, scx, &outputValue)) goto error_break; JSType type = JS_TypeOfValue(cx, outputValue); // elide undefined values and functions and XML if (outputValue == JSVAL_VOID || type == JSTYPE_FUNCTION || type == JSTYPE_XML) continue; // output a comma unless this is the first member to write if (memberWritten && !scx->cb.append(',')) goto error_break; memberWritten = JS_TRUE; if (!WriteIndent(cx, scx, scx->depth)) goto error_break; // Be careful below, this string is weakly rooted JSString *s = js_ValueToString(cx, key); if (!s) goto error_break; const jschar *chars; size_t length; s->getCharsAndLength(chars, length); if (!write_string(cx, scx->cb, chars, length) || !scx->cb.append(':') || !Str(cx, id, obj, scx, &outputValue, false)) { goto error_break; } } ok = true; error_break: if (iterObj) { // Always close the iterator, but make sure not to stomp on OK JS_ASSERT(OBJECT_TO_JSVAL(iterObj) == *keySource); ok &= js_CloseIterator(cx, *keySource); } if (!ok) return JS_FALSE; if (memberWritten && !WriteIndent(cx, scx, scx->depth - 1)) return JS_FALSE; return scx->cb.append('}'); }
void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val) { JSContext* cx = m_ScriptInterface.GetContext(); JSAutoRequest rq(cx); switch (JS_TypeOfValue(cx, val)) { case JSTYPE_VOID: { m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_VOID); break; } case JSTYPE_NULL: // This type is never actually returned (it's a JS2 feature) { m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_NULL); break; } case JSTYPE_OBJECT: { if (val.isNull()) { m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_NULL); break; } JS::RootedObject obj(cx, &val.toObject()); // If we've already serialized this object, just output a reference to it u32 tag = GetScriptBackrefTag(obj); if (tag) { m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_BACKREF); m_Serializer.NumberU32_Unbounded("tag", tag); break; } // Arrays are special cases of Object if (JS_IsArrayObject(cx, obj)) { m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_ARRAY); // TODO: probably should have a more efficient storage format // Arrays like [1, 2, ] have an 'undefined' at the end which is part of the // length but seemingly isn't enumerated, so store the length explicitly uint length = 0; if (!JS_GetArrayLength(cx, obj, &length)) throw PSERROR_Serialize_ScriptError("JS_GetArrayLength failed"); m_Serializer.NumberU32_Unbounded("array length", length); } else if (JS_IsTypedArrayObject(obj)) { m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_TYPED_ARRAY); m_Serializer.NumberU8_Unbounded("array type", GetArrayType(JS_GetArrayBufferViewType(obj))); m_Serializer.NumberU32_Unbounded("byte offset", JS_GetTypedArrayByteOffset(obj)); m_Serializer.NumberU32_Unbounded("length", JS_GetTypedArrayLength(obj)); // Now handle its array buffer // this may be a backref, since ArrayBuffers can be shared by multiple views JS::RootedValue bufferVal(cx, JS::ObjectValue(*JS_GetArrayBufferViewBuffer(cx, obj))); HandleScriptVal(bufferVal); break; } else if (JS_IsArrayBufferObject(obj)) { m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_ARRAY_BUFFER); #if BYTE_ORDER != LITTLE_ENDIAN #error TODO: need to convert JS ArrayBuffer data to little-endian #endif u32 length = JS_GetArrayBufferByteLength(obj); m_Serializer.NumberU32_Unbounded("buffer length", length); JS::AutoCheckCannotGC nogc; m_Serializer.RawBytes("buffer data", (const u8*)JS_GetArrayBufferData(obj, nogc), length); break; } else { // Find type of object const JSClass* jsclass = JS_GetClass(obj); if (!jsclass) throw PSERROR_Serialize_ScriptError("JS_GetClass failed"); // TODO: Remove this workaround for upstream API breakage when updating SpiderMonkey // See https://bugzilla.mozilla.org/show_bug.cgi?id=1236373 #define JSCLASS_CACHED_PROTO_WIDTH js::JSCLASS_CACHED_PROTO_WIDTH JSProtoKey protokey = JSCLASS_CACHED_PROTO_KEY(jsclass); #undef JSCLASS_CACHED_PROTO_WIDTH if (protokey == JSProto_Object) { // Object class - check for user-defined prototype JS::RootedObject proto(cx); JS_GetPrototype(cx, obj, &proto); if (!proto) throw PSERROR_Serialize_ScriptError("JS_GetPrototype failed"); if (m_SerializablePrototypes->empty() || !IsSerializablePrototype(proto)) { // Standard Object prototype m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_OBJECT); // TODO: maybe we should throw an error for unrecognized non-Object prototypes? // (requires fixing AI serialization first and excluding component scripts) } else { // User-defined custom prototype m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_OBJECT_PROTOTYPE); const std::wstring prototypeName = GetPrototypeName(proto); m_Serializer.String("proto name", prototypeName, 0, 256); // Does it have custom Serialize function? // if so, we serialize the data it returns, rather than the object's properties directly bool hasCustomSerialize; if (!JS_HasProperty(cx, obj, "Serialize", &hasCustomSerialize)) throw PSERROR_Serialize_ScriptError("JS_HasProperty failed"); if (hasCustomSerialize) { JS::RootedValue serialize(cx); if (!JS_GetProperty(cx, obj, "Serialize", &serialize)) throw PSERROR_Serialize_ScriptError("JS_GetProperty failed"); // If serialize is null, so don't serialize anything more if (!serialize.isNull()) { JS::RootedValue data(cx); if (!m_ScriptInterface.CallFunction(val, "Serialize", &data)) throw PSERROR_Serialize_ScriptError("Prototype Serialize function failed"); HandleScriptVal(data); } break; } } } else if (protokey == JSProto_Number) { // Standard Number object m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_OBJECT_NUMBER); // Get primitive value double d; if (!JS::ToNumber(cx, val, &d)) throw PSERROR_Serialize_ScriptError("JS::ToNumber failed"); m_Serializer.NumberDouble_Unbounded("value", d); break; } else if (protokey == JSProto_String) { // Standard String object m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_OBJECT_STRING); // Get primitive value JS::RootedString str(cx, JS::ToString(cx, val)); if (!str) throw PSERROR_Serialize_ScriptError("JS_ValueToString failed"); ScriptString("value", str); break; } else if (protokey == JSProto_Boolean) { // Standard Boolean object m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_OBJECT_BOOLEAN); // Get primitive value bool b = JS::ToBoolean(val); m_Serializer.Bool("value", b); break; } // TODO: Follow upstream progresses about a JS::IsMapObject // https://bugzilla.mozilla.org/show_bug.cgi?id=1285909 else if (protokey == JSProto_Map) { m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_OBJECT_MAP); m_Serializer.NumberU32_Unbounded("map size", JS::MapSize(cx, obj)); JS::RootedValue keyValueIterator(cx); if (!JS::MapEntries(cx, obj, &keyValueIterator)) throw PSERROR_Serialize_ScriptError("JS::MapEntries failed"); JS::ForOfIterator it(cx); if (!it.init(keyValueIterator)) throw PSERROR_Serialize_ScriptError("JS::ForOfIterator::init failed"); JS::RootedValue keyValuePair(cx); bool done; while (true) { if (!it.next(&keyValuePair, &done)) throw PSERROR_Serialize_ScriptError("JS::ForOfIterator::next failed"); if (done) break; JS::RootedObject keyValuePairObj(cx, &keyValuePair.toObject()); JS::RootedValue key(cx); JS::RootedValue value(cx); ENSURE(JS_GetElement(cx, keyValuePairObj, 0, &key)); ENSURE(JS_GetElement(cx, keyValuePairObj, 1, &value)); HandleScriptVal(key); HandleScriptVal(value); } break; } // TODO: Follow upstream progresses about a JS::IsSetObject // https://bugzilla.mozilla.org/show_bug.cgi?id=1285909 else if (protokey == JSProto_Set) { // TODO: When updating SpiderMonkey to a release after 38 use the C++ API for Sets. // https://bugzilla.mozilla.org/show_bug.cgi?id=1159469 u32 setSize; m_ScriptInterface.GetProperty(val, "size", setSize); m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_OBJECT_SET); m_Serializer.NumberU32_Unbounded("set size", setSize); JS::RootedValue valueIterator(cx); m_ScriptInterface.CallFunction(val, "values", &valueIterator); for (u32 i=0; i<setSize; ++i) { JS::RootedValue currentIterator(cx); JS::RootedValue value(cx); ENSURE(m_ScriptInterface.CallFunction(valueIterator, "next", ¤tIterator)); m_ScriptInterface.GetProperty(currentIterator, "value", &value); HandleScriptVal(value); } break; } else { // Unrecognized class LOGERROR("Cannot serialise JS objects with unrecognized class '%s'", jsclass->name); throw PSERROR_Serialize_InvalidScriptValue(); } } // Find all properties (ordered by insertion time) JS::AutoIdArray ida (cx, JS_Enumerate(cx, obj)); if (!ida) throw PSERROR_Serialize_ScriptError("JS_Enumerate failed"); m_Serializer.NumberU32_Unbounded("num props", (u32)ida.length()); for (size_t i = 0; i < ida.length(); ++i) { JS::RootedId id(cx, ida[i]); JS::RootedValue idval(cx); JS::RootedValue propval(cx); // Forbid getters, which might delete values and mess things up. JS::Rooted<JSPropertyDescriptor> desc(cx); if (!JS_GetPropertyDescriptorById(cx, obj, id, &desc)) throw PSERROR_Serialize_ScriptError("JS_GetPropertyDescriptorById failed"); if (desc.hasGetterObject()) throw PSERROR_Serialize_ScriptError("Cannot serialize property getters"); // Get the property name as a string if (!JS_IdToValue(cx, id, &idval)) throw PSERROR_Serialize_ScriptError("JS_IdToValue failed"); JS::RootedString idstr(cx, JS::ToString(cx, idval)); if (!idstr) throw PSERROR_Serialize_ScriptError("JS_ValueToString failed"); ScriptString("prop name", idstr); if (!JS_GetPropertyById(cx, obj, id, &propval)) throw PSERROR_Serialize_ScriptError("JS_GetPropertyById failed"); HandleScriptVal(propval); } break; } case JSTYPE_FUNCTION: { // We can't serialise functions, but we can at least name the offender (hopefully) std::wstring funcname(L"(unnamed)"); JS::RootedFunction func(cx, JS_ValueToFunction(cx, val)); if (func) { JS::RootedString string(cx, JS_GetFunctionId(func)); if (string) { if (JS_StringHasLatin1Chars(string)) { size_t length; JS::AutoCheckCannotGC nogc; const JS::Latin1Char* ch = JS_GetLatin1StringCharsAndLength(cx, nogc, string, &length); if (ch && length > 0) funcname.assign(ch, ch + length); } else { size_t length; JS::AutoCheckCannotGC nogc; const char16_t* ch = JS_GetTwoByteStringCharsAndLength(cx, nogc, string, &length); if (ch && length > 0) funcname.assign(ch, ch + length); } } } LOGERROR("Cannot serialise JS objects of type 'function': %s", utf8_from_wstring(funcname)); throw PSERROR_Serialize_InvalidScriptValue(); } case JSTYPE_STRING: { m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_STRING); JS::RootedString stringVal(cx, val.toString()); ScriptString("string", stringVal); break; } case JSTYPE_NUMBER: { // To reduce the size of the serialized data, we handle integers and doubles separately. // We can't check for val.isInt32 and val.isDouble directly, because integer numbers are not guaranteed // to be represented as integers. A number like 33 could be stored as integer on the computer of one player // and as double on the other player's computer. That would cause out of sync errors in multiplayer games because // their binary representation and thus the hash would be different. double d; d = val.toNumber(); i32 integer; if (JS_DoubleIsInt32(d, &integer)) { m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_INT); m_Serializer.NumberI32_Unbounded("value", integer); } else { m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_DOUBLE); m_Serializer.NumberDouble_Unbounded("value", d); } break; } case JSTYPE_BOOLEAN: { m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_BOOLEAN); bool b = val.toBoolean(); m_Serializer.NumberU8_Unbounded("value", b ? 1 : 0); break; } default: { debug_warn(L"Invalid TypeOfValue"); throw PSERROR_Serialize_InvalidScriptValue(); } } }
// Convert from a jsval to an AtNode static AtSmartPtr<AtNode> ConvertNode(JSContext* cx, jsval node) { AtSmartPtr<AtNode> obj (new AtNode()); // Non-objects get converted into strings if (!JSVAL_IS_OBJECT(node)) { JSString* str = JS_ValueToString(cx, node); if (!str) return obj; // error size_t valueLen; const jschar* valueChars = JS_GetStringCharsAndLength(cx, str, &valueLen); if (!valueChars) return obj; // error wxString valueWx(reinterpret_cast<const char*>(valueChars), wxMBConvUTF16(), valueLen*2); obj->value = valueWx.c_str(); // Annotate numbers/booleans specially, to allow round-tripping if (JSVAL_IS_NUMBER(node)) { obj->children.insert(AtNode::child_pairtype( "@number", AtSmartPtr<AtNode>(new AtNode()) )); } else if (JSVAL_IS_BOOLEAN(node)) { obj->children.insert(AtNode::child_pairtype( "@boolean", AtSmartPtr<AtNode>(new AtNode()) )); } return obj; } JSObject* it = JS_NewPropertyIterator(cx, JSVAL_TO_OBJECT(node)); if (!it) return obj; // error while (true) { jsid idp; jsval val; if (! JS_NextProperty(cx, it, &idp) || ! JS_IdToValue(cx, idp, &val)) return obj; // error if (val == JSVAL_VOID) break; // end of iteration if (! JSVAL_IS_STRING(val)) continue; // ignore integer properties JSString* name = JSVAL_TO_STRING(val); size_t len; const jschar* chars = JS_GetStringCharsAndLength(cx, name, &len); wxString nameWx(reinterpret_cast<const char*>(chars), wxMBConvUTF16(), len*2); std::string nameStr(nameWx.ToUTF8().data()); jsval vp; if (!JS_GetPropertyById(cx, JSVAL_TO_OBJECT(node), idp, &vp)) return obj; // error // Unwrap arrays into a special format like <$name><item>$i0</item><item>... // (This assumes arrays aren't nested) if (JSVAL_IS_OBJECT(vp) && JS_IsArrayObject(cx, JSVAL_TO_OBJECT(vp))) { AtSmartPtr<AtNode> child(new AtNode()); child->children.insert(AtNode::child_pairtype( "@array", AtSmartPtr<AtNode>(new AtNode()) )); jsuint arrayLength; if (!JS_GetArrayLength(cx, JSVAL_TO_OBJECT(vp), &arrayLength)) return obj; // error for (jsuint i = 0; i < arrayLength; ++i) { jsval val; if (!JS_GetElement(cx, JSVAL_TO_OBJECT(vp), i, &val)) return obj; // error child->children.insert(AtNode::child_pairtype( "item", ConvertNode(cx, val) )); } obj->children.insert(AtNode::child_pairtype( nameStr, child )); } else { obj->children.insert(AtNode::child_pairtype( nameStr, ConvertNode(cx, vp) )); } } return obj; }
NS_IMETHODIMP IDBDatabase::CreateObjectStore(const nsAString& aName, const jsval& aOptions, JSContext* aCx, nsIIDBObjectStore** _retval) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); IDBTransaction* transaction = AsyncConnectionHelper::GetCurrentTransaction(); if (!transaction || transaction->Mode() != nsIIDBTransaction::VERSION_CHANGE) { return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR; } DatabaseInfo* databaseInfo = transaction->DBInfo(); nsString keyPath; keyPath.SetIsVoid(true); nsTArray<nsString> keyPathArray; bool autoIncrement = false; if (!JSVAL_IS_VOID(aOptions) && !JSVAL_IS_NULL(aOptions)) { if (JSVAL_IS_PRIMITIVE(aOptions)) { // XXX This isn't the right error return NS_ERROR_DOM_TYPE_ERR; } NS_ASSERTION(JSVAL_IS_OBJECT(aOptions), "Huh?!"); JSObject* options = JSVAL_TO_OBJECT(aOptions); // Get keyPath jsval val; if (!JS_GetPropertyById(aCx, options, nsDOMClassInfo::sKeyPath_id, &val)) { NS_WARNING("JS_GetPropertyById failed!"); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } if (!JSVAL_IS_VOID(val) && !JSVAL_IS_NULL(val)) { if (!JSVAL_IS_PRIMITIVE(val) && JS_IsArrayObject(aCx, JSVAL_TO_OBJECT(val))) { JSObject* obj = JSVAL_TO_OBJECT(val); jsuint length; if (!JS_GetArrayLength(aCx, obj, &length)) { return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } if (!length) { return NS_ERROR_DOM_SYNTAX_ERR; } keyPathArray.SetCapacity(length); for (jsuint index = 0; index < length; index++) { jsval val; JSString* jsstr; nsDependentJSString str; if (!JS_GetElement(aCx, obj, index, &val) || !(jsstr = JS_ValueToString(aCx, val)) || !str.init(aCx, jsstr)) { return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } if (!IDBObjectStore::IsValidKeyPath(aCx, str)) { return NS_ERROR_DOM_SYNTAX_ERR; } keyPathArray.AppendElement(str); } NS_ASSERTION(!keyPathArray.IsEmpty(), "This shouldn't have happened!"); } else { JSString* jsstr; nsDependentJSString str; if (!(jsstr = JS_ValueToString(aCx, val)) || !str.init(aCx, jsstr)) { return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } if (!IDBObjectStore::IsValidKeyPath(aCx, str)) { return NS_ERROR_DOM_SYNTAX_ERR; } keyPath = str; } } if (!JS_GetPropertyById(aCx, options, nsDOMClassInfo::sAutoIncrement_id, &val)) { NS_WARNING("JS_GetPropertyById failed!"); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } JSBool boolVal; if (!JS_ValueToBoolean(aCx, val, &boolVal)) { NS_WARNING("JS_ValueToBoolean failed!"); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } autoIncrement = !!boolVal; } if (databaseInfo->ContainsStoreName(aName)) { return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR; } if (autoIncrement && ((!keyPath.IsVoid() && keyPath.IsEmpty()) || !keyPathArray.IsEmpty())) { return NS_ERROR_DOM_INVALID_ACCESS_ERR; } nsRefPtr<ObjectStoreInfo> newInfo(new ObjectStoreInfo()); newInfo->name = aName; newInfo->id = databaseInfo->nextObjectStoreId++; newInfo->keyPath = keyPath; newInfo->keyPathArray = keyPathArray; newInfo->nextAutoIncrementId = autoIncrement ? 1 : 0; newInfo->comittedAutoIncrementId = newInfo->nextAutoIncrementId; if (!databaseInfo->PutObjectStore(newInfo)) { NS_WARNING("Put failed!"); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } // Don't leave this in the hash if we fail below! AutoRemoveObjectStore autoRemove(databaseInfo, aName); nsRefPtr<IDBObjectStore> objectStore = transaction->GetOrCreateObjectStore(aName, newInfo); NS_ENSURE_TRUE(objectStore, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); nsRefPtr<CreateObjectStoreHelper> helper = new CreateObjectStoreHelper(transaction, objectStore); nsresult rv = helper->DispatchToTransactionPool(); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); autoRemove.forget(); objectStore.forget(_retval); return NS_OK; }