JSStringWrapper::JSStringWrapper(JSContext* cx, JSString* str) : _isSet(true) { if (!str) throwCurrentJSException(cx, ErrorCodes::InternalError, "Cannot encode null JSString"); // We have to do this flatstring thing because no public api tells us // how long the utf8 strings we get out are. // // Well, at least js/CharacterEncoding's GetDeflatedUTF8StringLength // and JS_flattenString are all in the public headers... JSFlatString* flat = JS_FlattenString(cx, str); if (!flat) throwCurrentJSException(cx, ErrorCodes::InternalError, "Failed to flatten JSString"); _length = JS::GetDeflatedUTF8StringLength(flat); char* out; if (_length < sizeof(_buf)) { out = _buf; } else { _str.reset(new char[_length + 1]); out = _str.get(); } JS::DeflateStringToUTF8Buffer(flat, mozilla::RangedPtr<char>(out, _length)); out[_length] = '\0'; }
int32_t ValueWriter::toInt32() { int32_t out; if (JS::ToInt32(_context, _value, &out)) return out; throwCurrentJSException(_context, ErrorCodes::BadValue, "Failure to convert value to number"); }
double ValueWriter::toNumber() { double out; if (JS::ToNumber(_context, _value, &out)) return out; throwCurrentJSException(_context, ErrorCodes::BadValue, "Failure to convert value to number"); }
void ObjectWrapper::Key::set(JSContext* cx, JS::HandleObject o, JS::HandleValue value) { switch (_type) { case Type::Field: if (JS_SetProperty(cx, o, _field, value)) return; break; case Type::Index: if (JS_SetElement(cx, o, _idx, value)) return; break; case Type::Id: { JS::RootedId id(cx, _id); if (JS_SetPropertyById(cx, o, id, value)) return; break; } case Type::InternedString: { InternedStringId id(cx, _internedString); if (JS_SetPropertyById(cx, o, id, value)) return; break; } } throwCurrentJSException(cx, ErrorCodes::InternalError, "Failed to set value on a JSObject"); }
void ObjectWrapper::Key::del(JSContext* cx, JS::HandleObject o) { switch (_type) { case Type::Field: if (JS_DeleteProperty(cx, o, _field)) return; break; case Type::Index: if (JS_DeleteElement(cx, o, _idx)) return; break; case Type::Id: { JS::RootedId id(cx, _id); // For some reason JS_DeletePropertyById doesn't link if (JS_DeleteProperty(cx, o, IdWrapper(cx, id).toString().c_str())) return; break; } case Type::InternedString: { InternedStringId id(cx, _internedString); if (JS_DeleteProperty(cx, o, IdWrapper(cx, id).toString().c_str())) break; } } throwCurrentJSException(cx, ErrorCodes::InternalError, "Failed to delete value on a JSObject"); }
bool ObjectWrapper::Key::hasOwn(JSContext* cx, JS::HandleObject o) { bool has; switch (_type) { case Type::Field: if (JS_AlreadyHasOwnProperty(cx, o, _field, &has)) return has; break; case Type::Index: if (JS_AlreadyHasOwnElement(cx, o, _idx, &has)) return has; break; case Type::Id: { JS::RootedId id(cx, _id); if (JS_AlreadyHasOwnPropertyById(cx, o, id, &has)) return has; break; } case Type::InternedString: { InternedStringId id(cx, _internedString); if (JS_AlreadyHasOwnPropertyById(cx, o, id, &has)) return has; break; } } throwCurrentJSException(cx, ErrorCodes::InternalError, "Failed to hasOwn value on a JSObject"); }
void ObjectWrapper::callMethod(JS::HandleValue fun, const JS::HandleValueArray& args, JS::MutableHandleValue out) { if (JS::Call(_context, _object, fun, args, out)) return; throwCurrentJSException(_context, ErrorCodes::InternalError, "Failed to call method"); }
int64_t ValueWriter::toInt64() { int64_t out; if (getScope(_context)->getProto<NumberLongInfo>().instanceOf(_value)) return NumberLongInfo::ToNumberLong(_context, _value); if (JS::ToInt64(_context, _value, &out)) return out; throwCurrentJSException(_context, ErrorCodes::BadValue, "Failure to convert value to number"); }
ObjectWrapper::WriteFieldRecursionFrame::WriteFieldRecursionFrame(JSContext* cx, JSObject* obj, BSONObjBuilder* parent, StringData sd) : thisv(cx, obj), ids(cx, JS::IdVector(cx)) { bool isArray = false; if (parent) { if (!JS_IsArrayObject(cx, thisv, &isArray)) { throwCurrentJSException( cx, ErrorCodes::JSInterpreterFailure, "Failure to check object is an array"); } subbob.emplace(isArray ? parent->subarrayStart(sd) : parent->subobjStart(sd)); } if (isArray) { uint32_t length; if (!JS_GetArrayLength(cx, thisv, &length)) { throwCurrentJSException( cx, ErrorCodes::JSInterpreterFailure, "Failure to get array length"); } if (!ids.reserve(length)) { throwCurrentJSException( cx, ErrorCodes::JSInterpreterFailure, "Failure to reserve array"); } JS::RootedId rid(cx); for (uint32_t i = 0; i < length; i++) { rid.set(INT_TO_JSID(i)); ids.infallibleAppend(rid); } } else { if (!JS_Enumerate(cx, thisv, &ids)) { throwCurrentJSException( cx, ErrorCodes::JSInterpreterFailure, "Failure to enumerate object"); } } if (getScope(cx)->getProto<BSONInfo>().instanceOf(thisv)) { std::tie(originalBSON, altered) = BSONInfo::originalBSON(cx, thisv); } }
StringData IdWrapper::toStringData(JSStringWrapper* jsstr) const { if (JSID_IS_STRING(_value)) { *jsstr = JSStringWrapper(_context, JSID_TO_STRING(_value)); } else if (JSID_IS_INT(_value)) { *jsstr = JSStringWrapper(JSID_TO_INT(_value)); } else { throwCurrentJSException(_context, ErrorCodes::TypeMismatch, "Cannot toString() non-string and non-integer jsid"); } return jsstr->toStringData(); }
void JSThreadInfo::Functions::_threadInject::call(JSContext* cx, JS::CallArgs args) { uassert(ErrorCodes::JSInterpreterFailure, "threadInject takes exactly 1 argument", args.length() == 1); uassert(ErrorCodes::JSInterpreterFailure, "threadInject needs to be passed a prototype", args.get(0).isObject()); JS::RootedObject o(cx, args.get(0).toObjectOrNull()); if (!JS_DefineFunctions(cx, o, JSThreadInfo::threadMethods)) throwCurrentJSException(cx, ErrorCodes::JSInterpreterFailure, "Failed to define functions"); args.rval().setUndefined(); }