JavaClass_newEnumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, jsval *statep, jsid *idp) { JavaMemberDescriptor *member_descriptor; JavaClassDescriptor *class_descriptor; JNIEnv *jEnv; JSJavaThreadState *jsj_env; class_descriptor = JS_GetPrivate(cx, obj); /* Check for prototype JavaClass object */ if (!class_descriptor) { *statep = JSVAL_NULL; if (idp) *idp = INT_TO_JSVAL(0); return JS_TRUE; } switch(enum_op) { case JSENUMERATE_INIT: /* Get the Java per-thread environment pointer for this JSContext */ jsj_env = jsj_EnterJava(cx, &jEnv); if (!jEnv) return JS_FALSE; member_descriptor = jsj_GetClassStaticMembers(cx, jEnv, class_descriptor); *statep = PRIVATE_TO_JSVAL(member_descriptor); if (idp) *idp = INT_TO_JSVAL(class_descriptor->num_instance_members); jsj_ExitJava(jsj_env); return JS_TRUE; case JSENUMERATE_NEXT: member_descriptor = JSVAL_TO_PRIVATE(*statep); if (member_descriptor) { /* Don't enumerate explicit-signature methods, i.e. enumerate toValue, but not toValue(int), toValue(double), etc. */ while (member_descriptor->methods && member_descriptor->methods->is_alias) { member_descriptor = member_descriptor->next; if (!member_descriptor) { *statep = JSVAL_NULL; return JS_TRUE; } } *idp = member_descriptor->id; *statep = PRIVATE_TO_JSVAL(member_descriptor->next); return JS_TRUE; } /* Fall through ... */ case JSENUMERATE_DESTROY: *statep = JSVAL_NULL; return JS_TRUE; default: JS_ASSERT(0); return JS_FALSE; } }
JSBool XPCDispInterface::Member::GetValue(XPCCallContext& ccx, XPCNativeInterface * iface, jsval * retval) const { // This is a method or attribute - we'll be needing a function object // We need to use the safe context for this thread because we don't want // to parent the new (and cached forever!) function object to the current // JSContext's global object. That would be bad! if((mType & RESOLVED) == 0) { JSContext* cx = ccx.GetSafeJSContext(); if(!cx) return JS_FALSE; intN argc; JSNative callback; // Is this a function or a parameterized getter/setter if(IsFunction() || IsParameterizedProperty()) { argc = GetParamCount(); callback = XPC_IDispatch_CallMethod; } else { argc = 0; callback = XPC_IDispatch_GetterSetter; } JSFunction *fun = JS_NewFunctionById(cx, callback, argc, 0, nsnull, mName); if(!fun) return JS_FALSE; JSObject* funobj = JS_GetFunctionObject(fun); if(!funobj) return JS_FALSE; // Store ourselves and our native interface within the JSObject if(!JS_SetReservedSlot(ccx, funobj, 0, PRIVATE_TO_JSVAL((void *) this))) return JS_FALSE; if(!JS_SetReservedSlot(ccx, funobj, 1, PRIVATE_TO_JSVAL(iface))) return JS_FALSE; { // scoped lock XPCAutoLock lock(ccx.GetRuntime()->GetMapLock()); const_cast<Member*>(this)->mVal = OBJECT_TO_JSVAL(funobj); const_cast<Member*>(this)->mType |= RESOLVED; } } *retval = mVal; return JS_TRUE; }
// Returns a weak reference. static nsIPrincipal * FindObjectPrincipals(JSContext *cx, JSObject *safeObj, JSObject *innerObj) { // Check if we have a cached principal first. jsval v; if (!JS_GetReservedSlot(cx, safeObj, XPC_SJOW_SLOT_PRINCIPAL, &v)) { return nsnull; } if (!JSVAL_IS_VOID(v)) { // Found one! No need to do any more refcounting. return static_cast<nsIPrincipal *>(JSVAL_TO_PRIVATE(v)); } nsCOMPtr<nsIPrincipal> objPrincipal; nsresult rv = FindPrincipals(cx, innerObj, getter_AddRefs(objPrincipal), nsnull, nsnull); if (NS_FAILED(rv)) { return nsnull; } if (!JS_SetReservedSlot(cx, safeObj, XPC_SJOW_SLOT_PRINCIPAL, PRIVATE_TO_JSVAL(objPrincipal.get()))) { return nsnull; } // The wrapper owns the principal now. return objPrincipal.forget().get(); }
JSBool jsd_SetExecutionHook(JSDContext* jsdc, JSDScript* jsdscript, uintptr_t pc, JSD_ExecutionHookProc hook, void* callerdata) { JSDExecHook* jsdhook; JSBool rv; JSCompartment* oldCompartment; JSD_LOCK(); if( ! hook ) { jsd_ClearExecutionHook(jsdc, jsdscript, pc); JSD_UNLOCK(); return JS_TRUE; } jsdhook = _findHook(jsdc, jsdscript, pc); if( jsdhook ) { jsdhook->hook = hook; jsdhook->callerdata = callerdata; JSD_UNLOCK(); return JS_TRUE; } /* else... */ jsdhook = (JSDExecHook*)calloc(1, sizeof(JSDExecHook)); if( ! jsdhook ) { JSD_UNLOCK(); return JS_FALSE; } jsdhook->jsdscript = jsdscript; jsdhook->pc = pc; jsdhook->hook = hook; jsdhook->callerdata = callerdata; oldCompartment = JS_EnterCompartmentOfScript(jsdc->dumbContext, jsdscript->script); rv = JS_SetTrap(jsdc->dumbContext, jsdscript->script, (jsbytecode*)pc, jsd_TrapHandler, PRIVATE_TO_JSVAL(jsdhook)); JS_LeaveCompartment(jsdc->dumbContext, oldCompartment); if ( ! rv ) { free(jsdhook); JSD_UNLOCK(); return JS_FALSE; } JS_APPEND_LINK(&jsdhook->links, &jsdscript->hooks); JSD_UNLOCK(); return JS_TRUE; }
JSBool jsd_SetExecutionHook(JSDContext* jsdc, JSDScript* jsdscript, jsuword pc, JSD_ExecutionHookProc hook, void* callerdata) { JSDExecHook* jsdhook; JSD_LOCK(); if( ! hook ) { jsd_ClearExecutionHook(jsdc, jsdscript, pc); JSD_UNLOCK(); return JS_TRUE; } jsdhook = _findHook(jsdc, jsdscript, pc); if( jsdhook ) { jsdhook->hook = hook; jsdhook->callerdata = callerdata; JSD_UNLOCK(); return JS_TRUE; } /* else... */ jsdhook = (JSDExecHook*)calloc(1, sizeof(JSDExecHook)); if( ! jsdhook ) { JSD_UNLOCK(); return JS_FALSE; } jsdhook->jsdscript = jsdscript; jsdhook->pc = pc; jsdhook->hook = hook; jsdhook->callerdata = callerdata; if( ! JS_SetTrap(jsdc->dumbContext, jsdscript->script, (jsbytecode*)pc, jsd_TrapHandler, (void*) PRIVATE_TO_JSVAL(jsdhook)) ) { free(jsdhook); JSD_UNLOCK(); return JS_FALSE; } JS_APPEND_LINK(&jsdhook->links, &jsdscript->hooks); JSD_UNLOCK(); return JS_TRUE; }
void CThreadDebugger::SetNewTrap(CActiveBreakPoint* activeBreakPoint, std::string filename, uint line) { ENSURE(activeBreakPoint->m_Script == NULL); // The trap must not be set already! ENSURE(CheckIfMappingPresent(filename, line)); // You have to check if the mapping exists before calling this function! jsbytecode* pc = m->m_LineToPCMap[filename][line].pBytecode; JSScript* script = m->m_LineToPCMap[filename][line].pScript; activeBreakPoint->m_Script = script; activeBreakPoint->m_Pc = pc; ENSURE(script != NULL && pc != NULL); activeBreakPoint->m_ActualLine = JS_PCToLineNumber(m->m_pScriptInterface->GetContext(), script, pc); JS_SetTrap(m->m_pScriptInterface->GetContext(), script, pc, TrapHandler_, PRIVATE_TO_JSVAL(this)); }
static JSObject * XPC_XOW_Iterator(JSContext *cx, JSObject *obj, JSBool keysonly) { JSObject *wrappedObj = GetWrappedObject(cx, obj); if (!wrappedObj) { ThrowException(NS_ERROR_INVALID_ARG, cx); return nsnull; } XPCCallContext ccx(JS_CALLER, cx); if (!ccx.IsValid()) { ThrowException(NS_ERROR_FAILURE, cx); return nsnull; } nsresult rv = CanAccessWrapper(cx, wrappedObj, nsnull); if (NS_FAILED(rv)) { if (rv == NS_ERROR_DOM_PROP_ACCESS_DENIED) { // Can't create iterators for foreign objects. ThrowException(rv, cx); return nsnull; } ThrowException(NS_ERROR_FAILURE, cx); return nsnull; } JSObject *wrapperIter = JS_NewObject(cx, &sXPC_XOW_JSClass.base, nsnull, JS_GetGlobalForObject(cx, obj)); if (!wrapperIter) { return nsnull; } JSAutoTempValueRooter tvr(cx, OBJECT_TO_JSVAL(wrapperIter)); // Initialize our XOW. jsval v = OBJECT_TO_JSVAL(wrappedObj); if (!JS_SetReservedSlot(cx, wrapperIter, XPCWrapper::sWrappedObjSlot, v) || !JS_SetReservedSlot(cx, wrapperIter, XPCWrapper::sFlagsSlot, JSVAL_ZERO) || !JS_SetReservedSlot(cx, wrapperIter, XPC_XOW_ScopeSlot, PRIVATE_TO_JSVAL(nsnull))) { return nsnull; } return XPCWrapper::CreateIteratorObj(cx, wrapperIter, obj, wrappedObj, keysonly); }
bool ScriptInterface::ReplaceNondeterministicRNG(boost::rand48& rng) { jsval math; if (JS_GetProperty(m->m_cx, m->m_glob, "Math", &math) && JSVAL_IS_OBJECT(math)) { JSFunction* random = JS_DefineFunction(m->m_cx, JSVAL_TO_OBJECT(math), "random", Math_random, 0, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT); if (random) { // Store the RNG in a slot which is sort-of-guaranteed to be unused by the JS engine if (JS_SetReservedSlot(m->m_cx, JS_GetFunctionObject(random), 0, PRIVATE_TO_JSVAL(&rng))) return true; } } LOGERROR(L"ReplaceNondeterministicRNG: failed to replace Math.random"); return false; }
static JSBool perlarray_enumerate( JSContext *cx, JSObject *obj, JSIterateOp enum_op, jsval *statep, jsid *idp ) { dTHX; SV *ref = (SV *)JS_GetPrivate(cx, obj); AV *av = (AV *)SvRV(ref); PJS_ARRAY_CHECK if(enum_op == JSENUMERATE_INIT) { SV *cc = newSViv(0); *statep = PRIVATE_TO_JSVAL(cc); if(idp) { I32 alen = av_len(av); *idp = INT_TO_JSVAL(alen + 1); } return JS_TRUE; } if(enum_op == JSENUMERATE_NEXT) { SV *cc = (SV *)JSVAL_TO_PRIVATE(*statep); I32 alen = av_len(av); I32 curr; if(!SvIOK(cc)) { JS_ReportError(cx, "Wrong Array iterator"); return JS_FALSE; } curr = (I32)SvIVX(cc); if(curr > alen) { // At end *statep = JSVAL_NULL; sv_free(cc); } else { jsval key = INT_TO_JSVAL(curr); SvIV_set(cc, (IV)(curr+1)); return JS_ValueToId(cx, key, idp); } } return JS_TRUE; }
int add_body_stream(bfd_t *body_stream) { jsval smtpClient, bodyStream; JSObject *global; global = JS_GetGlobalForScopeChain(js_context); // Get smtpClient if (!JS_GetProperty(js_context, global, "smtpClient", &smtpClient)) { return -1; } bodyStream = PRIVATE_TO_JSVAL(body_stream); // Add path property if (!JS_SetProperty(js_context, JSVAL_TO_OBJECT(smtpClient), "bodyStream", &bodyStream)) { return -1; } return 0; }
// static XPCWrappedNativeScope* XPCWrappedNativeScope::GetNewOrUsed(XPCCallContext& ccx, JSObject* aGlobal) { XPCWrappedNativeScope* scope = FindInJSObjectScope(ccx, aGlobal, true); if (!scope) scope = new XPCWrappedNativeScope(ccx, aGlobal); else { // We need to call SetGlobal in order to refresh our cached // mPrototypeJSObject and to clear mPrototypeNoHelper (so we get a new // new one if requested in the new scope) in the case where the global // object is being reused (JS_ClearScope has been called). NOTE: We are // only called by nsXPConnect::InitClasses. scope->SetGlobal(ccx, aGlobal); } if (js::GetObjectClass(aGlobal)->flags & JSCLASS_XPCONNECT_GLOBAL) JS_SetReservedSlot(aGlobal, JSCLASS_GLOBAL_SLOT_COUNT, PRIVATE_TO_JSVAL(scope)); return scope; }
JSBool Library::Close(JSContext* cx, uintN argc, jsval* vp) { JSObject* obj = JS_THIS_OBJECT(cx, vp); if (!obj || !IsLibrary(cx, obj)) { JS_ReportError(cx, "not a library"); return JS_FALSE; } if (argc != 0) { JS_ReportError(cx, "close doesn't take any arguments"); return JS_FALSE; } // delete our internal objects Finalize(cx, obj); JS_SetReservedSlot(cx, obj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(NULL)); JS_SET_RVAL(cx, vp, JSVAL_VOID); return JS_TRUE; }
void ScriptInterface_impl::Register(const char* name, JSNative fptr, uintN nargs) { JSFunction* func = JS_DefineFunction(m_cx, m_nativeScope, name, fptr, nargs, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT); if (!func) return; if (g_ScriptProfilingEnabled) { // Store the function name in a slot, so we can pass it to the profiler. // Use a flyweight std::string because we can't assume the caller has // a correctly-aligned string and that its lifetime is long enough typedef boost::flyweight< std::string, boost::flyweights::no_tracking // can't use no_locking; Register might be called in threads > LockedStringFlyweight; LockedStringFlyweight fw(name); JS_SetReservedSlot(m_cx, JS_GetFunctionObject(func), 0, PRIVATE_TO_JSVAL((void*)fw.get().c_str())); } }
JSBool WrapperMoved(JSContext *cx, XPCWrappedNative *innerObj, XPCWrappedNativeScope *newScope) { typedef WrappedNative2WrapperMap::Link Link; XPCJSRuntime *rt = nsXPConnect::GetRuntimeInstance(); WrappedNative2WrapperMap *map = innerObj->GetScope()->GetWrapperMap(); Link *link; { // Scoped lock XPCAutoLock al(rt->GetMapLock()); link = map->FindLink(innerObj->GetFlatJSObject()); } if (!link) { // No link here means that there were no XOWs for this object. return JS_TRUE; } JSObject *xow = link->obj; { // Scoped lock. XPCAutoLock al(rt->GetMapLock()); if (!newScope->GetWrapperMap()->AddLink(innerObj->GetFlatJSObject(), link)) return JS_FALSE; map->Remove(innerObj->GetFlatJSObject()); } if (!xow) { // Nothing else to do. return JS_TRUE; } return JS_SetReservedSlot(cx, xow, XPC_XOW_ScopeSlot, PRIVATE_TO_JSVAL(newScope)) && JS_SetParent(cx, xow, newScope->GetGlobalJSObject()); }
/* * 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; }
/* * 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; }
JSBool js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp) { JSErrNum errorNumber; JSExnType exn; JSBool ok; JSObject *errProto, *errObject; JSString *messageStr, *filenameStr; uintN lineno; JSExnPrivate *privateData; /* Find the exception index associated with this error. */ JS_ASSERT(reportp); if (JSREPORT_IS_WARNING(reportp->flags)) return JS_FALSE; errorNumber = (JSErrNum) reportp->errorNumber; exn = errorToExceptionNum[errorNumber]; JS_ASSERT(exn < JSEXN_LIMIT); #if defined( DEBUG_mccabe ) && defined ( PRINTNAMES ) /* Print the error name and the associated exception name to stderr */ fprintf(stderr, "%s\t%s\n", errortoexnname[errorNumber].name, errortoexnname[errorNumber].exception); #endif /* * Return false (no exception raised) if no exception is associated * with the given error number. */ if (exn == JSEXN_NONE) return JS_FALSE; /* * Prevent runaway recursion, just as the Exception native constructor * must do, via cx->creatingException. If an out-of-memory error occurs, * no exception object will be created, but we don't assume that OOM is * the only kind of error that subroutines of this function called below * might raise. */ if (cx->creatingException) return JS_FALSE; cx->creatingException = JS_TRUE; /* * Try to get an appropriate prototype by looking up the corresponding * exception constructor name in the scope chain of the current context's * top stack frame, or in the global object if no frame is active. * * XXXbe hack around JSCLASS_NEW_RESOLVE code in js_LookupProperty that * checks cx->fp, cx->fp->pc, and js_CodeSpec[*cx->fp->pc] in order * to compute resolve flags such as JSRESOLVE_ASSIGNING. The bug * is that this "internal" js_GetClassPrototype call may trigger a * resolve of exceptions[exn].name if the global object uses a lazy * standard class resolver (see JS_ResolveStandardClass), but the * current frame and bytecode end up affecting the resolve flags. */ { JSStackFrame *fp = cx->fp; jsbytecode *pc = NULL; if (fp) { pc = fp->pc; fp->pc = NULL; } ok = js_GetClassPrototype(cx, exceptions[exn].name, &errProto); if (pc) fp->pc = pc; if (!ok) goto out; } errObject = js_NewObject(cx, &ExceptionClass, errProto, NULL); if (!errObject) { ok = JS_FALSE; goto out; } messageStr = JS_NewStringCopyZ(cx, message); if (!messageStr) { ok = JS_FALSE; goto out; } if (reportp) { filenameStr = JS_NewStringCopyZ(cx, reportp->filename); if (!filenameStr) { ok = JS_FALSE; goto out; } lineno = reportp->lineno; } else { filenameStr = cx->runtime->emptyString; lineno = 0; } ok = InitExceptionObject(cx, errObject, messageStr, filenameStr, lineno); if (!ok) goto out; /* * Construct a new copy of the error report struct, and store it in the * exception object's private data. We can't use the error report struct * that was passed in, because it's stack-allocated, and also because it * may point to transient data in the JSTokenStream. */ privateData = exn_newPrivate(cx, reportp); if (!privateData) { ok = JS_FALSE; goto out; } OBJ_SET_SLOT(cx, errObject, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(privateData)); /* Set the generated Exception object as the current exception. */ JS_SetPendingException(cx, OBJECT_TO_JSVAL(errObject)); /* Flag the error report passed in to indicate an exception was raised. */ reportp->flags |= JSREPORT_EXCEPTION; out: cx->creatingException = JS_FALSE; return ok; }
JSBool XPCNativeMember::Resolve(XPCCallContext& ccx, XPCNativeInterface* iface) { if(IsConstant()) { const nsXPTConstant* constant; if(NS_FAILED(iface->GetInterfaceInfo()->GetConstant(mIndex, &constant))) return JS_FALSE; const nsXPTCMiniVariant& mv = *constant->GetValue(); // XXX Big Hack! nsXPTCVariant v; v.flags = 0; v.type = constant->GetType(); memcpy(&v.val, &mv.val, sizeof(mv.val)); jsval resultVal; if(!XPCConvert::NativeData2JS(ccx, &resultVal, &v.val, v.type, nsnull, nsnull, nsnull)) return JS_FALSE; { // scoped lock XPCAutoLock lock(ccx.GetRuntime()->GetMapLock()); mVal = resultVal; mFlags |= RESOLVED; } return JS_TRUE; } // else... // This is a method or attribute - we'll be needing a function object intN argc; intN flags; JSNative callback; if(IsMethod()) { const nsXPTMethodInfo* info; if(NS_FAILED(iface->GetInterfaceInfo()->GetMethodInfo(mIndex, &info))) return JS_FALSE; // Note: ASSUMES that retval is last arg. argc = (intN) info->GetParamCount(); if(argc && info->GetParam((uint8)(argc-1)).IsRetval()) argc-- ; flags = 0; callback = XPC_WN_CallMethod; } else { if(IsWritableAttribute()) flags = JSFUN_GETTER | JSFUN_SETTER; else flags = JSFUN_GETTER; argc = 0; callback = XPC_WN_GetterSetter; } // We need to use the safe context for this thread because we don't want // to parent the new (and cached forever!) function object to the current // JSContext's global object. That would be bad! JSContext* cx = ccx.GetSafeJSContext(); if(!cx) return JS_FALSE; const char *memberName = iface->GetMemberName(ccx, this); jsrefcount suspendDepth = 0; if(cx != ccx) { // Switching contexts, suspend the old and enter the new request. suspendDepth = JS_SuspendRequest(ccx); JS_BeginRequest(cx); } JSFunction *fun = JS_NewFunction(cx, callback, argc, flags, nsnull, memberName); if(suspendDepth) { JS_EndRequest(cx); JS_ResumeRequest(ccx, suspendDepth); } if(!fun) return JS_FALSE; JSObject* funobj = JS_GetFunctionObject(fun); if(!funobj) return JS_FALSE; AUTO_MARK_JSVAL(ccx, OBJECT_TO_JSVAL(funobj)); STOBJ_CLEAR_PARENT(funobj); STOBJ_CLEAR_PROTO(funobj); if(!JS_SetReservedSlot(ccx, funobj, 0, PRIVATE_TO_JSVAL(iface))|| !JS_SetReservedSlot(ccx, funobj, 1, PRIVATE_TO_JSVAL(this))) return JS_FALSE; { // scoped lock XPCAutoLock lock(ccx.GetRuntime()->GetMapLock()); mVal = OBJECT_TO_JSVAL(funobj); mFlags |= RESOLVED; } return JS_TRUE; }
static JSObject* CreateInterfaceObject(JSContext* cx, JSObject* global, JSObject* receiver, JSClass* constructorClass, JSNative constructorNative, unsigned ctorNargs, JSObject* proto, Prefable<JSFunctionSpec>* staticMethods, Prefable<ConstantSpec>* constants, const char* name) { JSObject* constructor; if (constructorClass) { JSObject* functionProto = JS_GetFunctionPrototype(cx, global); if (!functionProto) { return NULL; } constructor = JS_NewObject(cx, constructorClass, functionProto, global); } else { MOZ_ASSERT(constructorNative); JSFunction* fun = JS_NewFunction(cx, constructorNative, ctorNargs, JSFUN_CONSTRUCTOR, global, name); if (!fun) { return NULL; } constructor = JS_GetFunctionObject(fun); } if (!constructor) { return NULL; } if (staticMethods && !DefinePrefable(cx, constructor, staticMethods)) { return NULL; } if (constructorClass) { JSFunction* toString = js::DefineFunctionWithReserved(cx, constructor, "toString", InterfaceObjectToString, 0, 0); if (!toString) { return NULL; } JSObject* toStringObj = JS_GetFunctionObject(toString); js::SetFunctionNativeReserved(toStringObj, TOSTRING_CLASS_RESERVED_SLOT, PRIVATE_TO_JSVAL(constructorClass)); JSString *str = ::JS_InternString(cx, name); if (!str) { return NULL; } js::SetFunctionNativeReserved(toStringObj, TOSTRING_NAME_RESERVED_SLOT, STRING_TO_JSVAL(str)); } if (constants && !DefinePrefable(cx, constructor, constants)) { return NULL; } if (proto && !JS_LinkConstructorAndPrototype(cx, constructor, proto)) { return NULL; } JSBool alreadyDefined; if (!JS_AlreadyHasOwnProperty(cx, receiver, name, &alreadyDefined)) { return NULL; } // This is Enumerable: False per spec. if (!alreadyDefined && !JS_DefineProperty(cx, receiver, name, OBJECT_TO_JSVAL(constructor), NULL, NULL, 0)) { return NULL; } return constructor; }
// Wrap a JS value in a safe wrapper of a function wrapper if // needed. Note that rval must point to something rooted when calling // this function. static JSBool WrapJSValue(JSContext *cx, JSObject *obj, jsval val, jsval *rval) { JSBool ok = JS_TRUE; if (JSVAL_IS_PRIMITIVE(val)) { *rval = val; } else { // Construct a new safe wrapper. Note that it doesn't matter what // parent we pass in here, the construct hook will ensure we get // the right parent for the wrapper. JSObject *safeObj = ::JS_ConstructObjectWithArguments(cx, &sXPC_SJOW_JSClass.base, nsnull, nsnull, 1, &val); if (!safeObj) { return JS_FALSE; } // Set *rval to safeObj here to ensure it doesn't get collected in // any of the code below. *rval = OBJECT_TO_JSVAL(safeObj); if (JS_GetGlobalForObject(cx, obj) != JS_GetGlobalForObject(cx, safeObj)) { // Check to see if the new object we just wrapped is accessible // from the unsafe object we got the new object through. If not, // force the new wrapper to use the principal of the unsafe // object we got the new object from. nsCOMPtr<nsIPrincipal> srcObjPrincipal; nsCOMPtr<nsIPrincipal> subjPrincipal; nsCOMPtr<nsIPrincipal> valObjPrincipal; nsresult rv = FindPrincipals(cx, obj, getter_AddRefs(srcObjPrincipal), getter_AddRefs(subjPrincipal), nsnull); if (NS_FAILED(rv)) { return ThrowException(rv, cx); } rv = FindPrincipals(cx, JSVAL_TO_OBJECT(val), getter_AddRefs(valObjPrincipal), nsnull, nsnull); if (NS_FAILED(rv)) { return ThrowException(rv, cx); } PRBool subsumes = PR_FALSE; rv = srcObjPrincipal->Subsumes(valObjPrincipal, &subsumes); if (NS_FAILED(rv)) { return ThrowException(rv, cx); } // If the subject can access both the source and object principals, then // don't bother forcing the principal below. if (!subsumes && subjPrincipal) { PRBool subjSubsumes = PR_FALSE; rv = subjPrincipal->Subsumes(srcObjPrincipal, &subjSubsumes); if (NS_SUCCEEDED(rv) && subjSubsumes) { rv = subjPrincipal->Subsumes(valObjPrincipal, &subjSubsumes); if (NS_SUCCEEDED(rv) && subjSubsumes) { subsumes = PR_TRUE; } } } if (!subsumes) { // The unsafe object we got the new object from can not access // the new object, force the wrapper we just created to use // the principal of the unsafe object to prevent users of the // new object wrapper from evaluating code through the new // wrapper with the principal of the new object. if (!::JS_SetReservedSlot(cx, safeObj, XPC_SJOW_SLOT_PRINCIPAL, PRIVATE_TO_JSVAL(srcObjPrincipal.get()))) { return JS_FALSE; } // Pass on ownership of the new object principal to the // wrapper. nsIPrincipal *tmp = nsnull; srcObjPrincipal.swap(tmp); } } } return ok; }
JSBool js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp) { JSErrNum errorNumber; JSExnType exn; JSBool ok; JSObject *errProto, *errObject; JSString *messageStr, *filenameStr; uintN lineno; JSExnPrivate *privateData; const JSErrorFormatString *errorString; /* * Tell our caller to report immediately if cx has no active frames, or if * this report is just a warning. */ JS_ASSERT(reportp); if (!cx->fp || JSREPORT_IS_WARNING(reportp->flags)) return JS_FALSE; /* Find the exception index associated with this error. */ errorNumber = (JSErrNum) reportp->errorNumber; errorString = js_GetLocalizedErrorMessage(cx, NULL, NULL, errorNumber); exn = errorString ? errorString->exnType : JSEXN_NONE; JS_ASSERT(exn < JSEXN_LIMIT); #if defined( DEBUG_mccabe ) && defined ( PRINTNAMES ) /* Print the error name and the associated exception name to stderr */ fprintf(stderr, "%s\t%s\n", errortoexnname[errorNumber].name, errortoexnname[errorNumber].exception); #endif /* * Return false (no exception raised) if no exception is associated * with the given error number. */ if (exn == JSEXN_NONE) return JS_FALSE; /* * Prevent runaway recursion, just as the Exception native constructor * must do, via cx->creatingException. If an out-of-memory error occurs, * no exception object will be created, but we don't assume that OOM is * the only kind of error that subroutines of this function called below * might raise. */ if (cx->creatingException) return JS_FALSE; cx->creatingException = JS_TRUE; /* Protect the newly-created strings below from nesting GCs. */ ok = js_EnterLocalRootScope(cx); if (!ok) goto out; /* * Try to get an appropriate prototype by looking up the corresponding * exception constructor name in the scope chain of the current context's * top stack frame, or in the global object if no frame is active. */ ok = js_GetClassPrototype(cx, exceptions[exn].name, &errProto); if (!ok) goto out; errObject = js_NewObject(cx, &ExceptionClass, errProto, NULL); if (!errObject) { ok = JS_FALSE; goto out; } /* * Set the generated Exception object early, so it won't be GC'd by a last * ditch attempt to collect garbage, or a GC that otherwise nests or races * under any of the following calls. If one of the following calls fails, * it will overwrite this exception object with one of its own (except in * case of OOM errors, of course). */ JS_SetPendingException(cx, OBJECT_TO_JSVAL(errObject)); messageStr = JS_NewStringCopyZ(cx, message); if (!messageStr) { ok = JS_FALSE; goto out; } if (reportp) { filenameStr = JS_NewStringCopyZ(cx, reportp->filename); if (!filenameStr) { ok = JS_FALSE; goto out; } lineno = reportp->lineno; } else { filenameStr = cx->runtime->emptyString; lineno = 0; } ok = InitExceptionObject(cx, errObject, messageStr, filenameStr, lineno); if (!ok) goto out; /* * Construct a new copy of the error report struct, and store it in the * exception object's private data. We can't use the error report struct * that was passed in, because it's stack-allocated, and also because it * may point to transient data in the JSTokenStream. */ privateData = exn_newPrivate(cx, reportp); if (!privateData) { ok = JS_FALSE; goto out; } OBJ_SET_SLOT(cx, errObject, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(privateData)); /* Flag the error report passed in to indicate an exception was raised. */ reportp->flags |= JSREPORT_EXCEPTION; out: js_LeaveLocalRootScope(cx); cx->creatingException = JS_FALSE; return ok; }
static JSBool InitExnPrivate(JSContext *cx, JSObject *exnObject, JSString *message, JSString *filename, uintN lineno, JSErrorReport *report) { JSCheckAccessOp checkAccess; JSErrorReporter older; JSExceptionState *state; jsval callerid, v; JSStackFrame *fp, *fpstop; size_t stackDepth, valueCount, size; JSBool overflow; JSExnPrivate *priv; JSStackTraceElem *elem; jsval *values; JS_ASSERT(OBJ_GET_CLASS(cx, exnObject) == &js_ErrorClass); /* * Prepare stack trace data. * * Set aside any error reporter for cx and save its exception state * so we can suppress any checkAccess failures. Such failures should stop * the backtrace procedure, not result in a failure of this constructor. */ checkAccess = cx->runtime->checkObjectAccess; older = JS_SetErrorReporter(cx, NULL); state = JS_SaveExceptionState(cx); callerid = ATOM_KEY(cx->runtime->atomState.callerAtom); stackDepth = 0; valueCount = 0; for (fp = cx->fp; fp; fp = fp->down) { if (fp->fun && fp->argv) { v = JSVAL_NULL; if (checkAccess && !checkAccess(cx, fp->callee, callerid, JSACC_READ, &v)) { break; } valueCount += fp->argc; } ++stackDepth; } JS_RestoreExceptionState(cx, state); JS_SetErrorReporter(cx, older); fpstop = fp; size = offsetof(JSExnPrivate, stackElems); overflow = (stackDepth > ((size_t)-1 - size) / sizeof(JSStackTraceElem)); size += stackDepth * sizeof(JSStackTraceElem); overflow |= (valueCount > ((size_t)-1 - size) / sizeof(jsval)); size += valueCount * sizeof(jsval); if (overflow) { js_ReportAllocationOverflow(cx); return JS_FALSE; } priv = (JSExnPrivate *)JS_malloc(cx, size); if (!priv) return JS_FALSE; /* * We initialize errorReport with a copy of report after setting the * private slot, to prevent GC accessing a junk value we clear the field * here. */ priv->errorReport = NULL; priv->message = message; priv->filename = filename; priv->lineno = lineno; priv->stackDepth = stackDepth; values = GetStackTraceValueBuffer(priv); elem = priv->stackElems; for (fp = cx->fp; fp != fpstop; fp = fp->down) { if (!fp->fun) { elem->funName = NULL; elem->argc = 0; } else { elem->funName = fp->fun->atom ? ATOM_TO_STRING(fp->fun->atom) : cx->runtime->emptyString; elem->argc = fp->argc; memcpy(values, fp->argv, fp->argc * sizeof(jsval)); values += fp->argc; } elem->ulineno = 0; elem->filename = NULL; if (fp->script) { elem->filename = fp->script->filename; if (fp->regs) elem->ulineno = js_PCToLineNumber(cx, fp->script, fp->regs->pc); } ++elem; } JS_ASSERT(priv->stackElems + stackDepth == elem); JS_ASSERT(GetStackTraceValueBuffer(priv) + valueCount == values); STOBJ_SET_SLOT(exnObject, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(priv)); if (report) { /* * Construct a new copy of the error report struct. We can't use the * error report struct that was passed in, because it's allocated on * the stack, and also because it may point to transient data in the * JSTokenStream. */ priv->errorReport = CopyErrorReport(cx, report); if (!priv->errorReport) { /* The finalizer realeases priv since it is in the private slot. */ return JS_FALSE; } } return JS_TRUE; }
JSBool XPC_XOW_WrapObject(JSContext *cx, JSObject *parent, jsval *vp, XPCWrappedNative* wn) { NS_ASSERTION(XPCPerThreadData::IsMainThread(cx), "Can't do this off the main thread!"); // Our argument should be a wrapped native object, but the caller may have // passed it in as an optimization. JSObject *wrappedObj; if (!JSVAL_IS_OBJECT(*vp) || !(wrappedObj = JSVAL_TO_OBJECT(*vp)) || STOBJ_GET_CLASS(wrappedObj) == &sXPC_XOW_JSClass.base) { return JS_TRUE; } if (!wn && !(wn = XPCWrappedNative::GetAndMorphWrappedNativeOfJSObject(cx, wrappedObj))) { return JS_TRUE; } XPCJSRuntime *rt = nsXPConnect::GetRuntimeInstance(); // The parent must be the inner global object for its scope. parent = JS_GetGlobalForObject(cx, parent); JSClass *clasp = STOBJ_GET_CLASS(parent); if (clasp->flags & JSCLASS_IS_EXTENDED) { JSExtendedClass *xclasp = reinterpret_cast<JSExtendedClass *>(clasp); if (xclasp->innerObject) { parent = xclasp->innerObject(cx, parent); if (!parent) { return JS_FALSE; } } } XPCWrappedNativeScope *parentScope = XPCWrappedNativeScope::FindInJSObjectScope(cx, parent, nsnull, rt); #ifdef DEBUG_mrbkap_off printf("Wrapping object at %p (%s) [%p]\n", (void *)wrappedObj, STOBJ_GET_CLASS(wrappedObj)->name, (void *)parentScope); #endif JSObject *outerObj = nsnull; WrappedNative2WrapperMap *map = parentScope->GetWrapperMap(); outerObj = map->Find(wrappedObj); if (outerObj) { NS_ASSERTION(STOBJ_GET_CLASS(outerObj) == &sXPC_XOW_JSClass.base, "What crazy object are we getting here?"); #ifdef DEBUG_mrbkap_off printf("But found a wrapper in the map %p!\n", (void *)outerObj); #endif *vp = OBJECT_TO_JSVAL(outerObj); return JS_TRUE; } outerObj = JS_NewObjectWithGivenProto(cx, &sXPC_XOW_JSClass.base, nsnull, parent); if (!outerObj) { return JS_FALSE; } if (!JS_SetReservedSlot(cx, outerObj, XPCWrapper::sWrappedObjSlot, *vp) || !JS_SetReservedSlot(cx, outerObj, XPCWrapper::sFlagsSlot, JSVAL_ZERO) || !JS_SetReservedSlot(cx, outerObj, XPC_XOW_ScopeSlot, PRIVATE_TO_JSVAL(parentScope))) { return JS_FALSE; } *vp = OBJECT_TO_JSVAL(outerObj); map->Add(wn->GetScope()->GetWrapperMap(), wrappedObj, outerObj); return JS_TRUE; }
JSObject* Library::Create(JSContext* cx, jsval path, JSCTypesCallbacks* callbacks) { JSObject* libraryObj = JS_NewObject(cx, &sLibraryClass, NULL, NULL); if (!libraryObj) return NULL; js::AutoObjectRooter root(cx, libraryObj); // initialize the library if (!JS_SetReservedSlot(cx, libraryObj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(NULL))) return NULL; // attach API functions if (!JS_DefineFunctions(cx, libraryObj, sLibraryFunctions)) return NULL; if (!JSVAL_IS_STRING(path)) { JS_ReportError(cx, "open takes a string argument"); return NULL; } PRLibSpec libSpec; JSFlatString* pathStr = JS_FlattenString(cx, JSVAL_TO_STRING(path)); if (!pathStr) return NULL; #ifdef XP_WIN // On Windows, converting to native charset may corrupt path string. // So, we have to use Unicode path directly. const PRUnichar* pathChars = JS_GetFlatStringChars(pathStr); if (!pathChars) return NULL; libSpec.value.pathname_u = pathChars; libSpec.type = PR_LibSpec_PathnameU; #else // Convert to platform native charset if the appropriate callback has been // provided. char* pathBytes; if (callbacks && callbacks->unicodeToNative) { pathBytes = callbacks->unicodeToNative(cx, pathStr->chars(), pathStr->length()); if (!pathBytes) return NULL; } else { // Fallback: assume the platform native charset is UTF-8. This is true // for Mac OS X, Android, and probably Linux. size_t nbytes = js_GetDeflatedUTF8StringLength(cx, pathStr->chars(), pathStr->length()); if (nbytes == (size_t) -1) return NULL; pathBytes = static_cast<char*>(JS_malloc(cx, nbytes + 1)); if (!pathBytes) return NULL; ASSERT_OK(js_DeflateStringToUTF8Buffer(cx, pathStr->chars(), pathStr->length(), pathBytes, &nbytes)); pathBytes[nbytes] = 0; } libSpec.value.pathname = pathBytes; libSpec.type = PR_LibSpec_Pathname; #endif PRLibrary* library = PR_LoadLibraryWithFlags(libSpec, 0); #ifndef XP_WIN JS_free(cx, pathBytes); #endif if (!library) { JS_ReportError(cx, "couldn't open library"); return NULL; } // stash the library if (!JS_SetReservedSlot(cx, libraryObj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(library))) return NULL; return libraryObj; }
JSBool WrapObject(JSContext *cx, JSObject *parent, jsval *vp, XPCWrappedNative* wn) { NS_ASSERTION(XPCPerThreadData::IsMainThread(cx), "Can't do this off the main thread!"); // Our argument should be a wrapped native object, but the caller may have // passed it in as an optimization. JSObject *wrappedObj; if (JSVAL_IS_PRIMITIVE(*vp) || !(wrappedObj = JSVAL_TO_OBJECT(*vp)) || wrappedObj->getClass() == &XOWClass) { return JS_TRUE; } if (!wn && !(wn = XPCWrappedNative::GetAndMorphWrappedNativeOfJSObject(cx, wrappedObj))) { return JS_TRUE; } CheckWindow(wn); // The parent must be the inner global object for its scope. parent = JS_GetGlobalForObject(cx, parent); OBJ_TO_INNER_OBJECT(cx, parent); if (!parent) { return JS_FALSE; } XPCWrappedNativeWithXOW *wnxow = nsnull; if (wn->NeedsXOW()) { JSObject *innerWrappedObj = wrappedObj; OBJ_TO_INNER_OBJECT(cx, innerWrappedObj); if (!innerWrappedObj) { return JS_FALSE; } if (innerWrappedObj == parent) { wnxow = static_cast<XPCWrappedNativeWithXOW *>(wn); JSObject *xow = wnxow->GetXOW(); if (xow) { *vp = OBJECT_TO_JSVAL(xow); return JS_TRUE; } } } XPCWrappedNative *parentwn = XPCWrappedNative::GetWrappedNativeOfJSObject(cx, parent); XPCWrappedNativeScope *parentScope; if (NS_LIKELY(parentwn)) { parentScope = parentwn->GetScope(); } else { parentScope = XPCWrappedNativeScope::FindInJSObjectScope(cx, parent); } JSObject *outerObj = nsnull; WrappedNative2WrapperMap *map = parentScope->GetWrapperMap(); outerObj = map->Find(wrappedObj); if (outerObj) { NS_ASSERTION(outerObj->getClass() == &XOWClass, "What crazy object are we getting here?"); *vp = OBJECT_TO_JSVAL(outerObj); if (wnxow) { // NB: wnxow->GetXOW() must have returned false. SetFlags(cx, outerObj, AddFlags(GetFlags(cx, outerObj), FLAG_IS_CACHED)); wnxow->SetXOW(outerObj); } return JS_TRUE; } outerObj = JS_NewObjectWithGivenProto(cx, js::Jsvalify(&XOWClass), nsnull, parent); if (!outerObj) { return JS_FALSE; } jsval flags = INT_TO_JSVAL(wnxow ? FLAG_IS_CACHED : 0); if (!JS_SetReservedSlot(cx, outerObj, sWrappedObjSlot, *vp) || !JS_SetReservedSlot(cx, outerObj, sFlagsSlot, flags) || !JS_SetReservedSlot(cx, outerObj, XPC_XOW_ScopeSlot, PRIVATE_TO_JSVAL(parentScope))) { return JS_FALSE; } *vp = OBJECT_TO_JSVAL(outerObj); map->Add(wn->GetScope()->GetWrapperMap(), wrappedObj, outerObj); if(wnxow) { wnxow->SetXOW(outerObj); } return JS_TRUE; }
JSBool XPCNativeMember::Resolve(XPCCallContext& ccx, XPCNativeInterface* iface, JSObject *parent, jsval *vp) { if (IsConstant()) { const nsXPTConstant* constant; if (NS_FAILED(iface->GetInterfaceInfo()->GetConstant(mIndex, &constant))) return false; const nsXPTCMiniVariant& mv = *constant->GetValue(); // XXX Big Hack! nsXPTCVariant v; v.flags = 0; v.type = constant->GetType(); memcpy(&v.val, &mv.val, sizeof(mv.val)); jsval resultVal; if (!XPCConvert::NativeData2JS(ccx, &resultVal, &v.val, v.type, nsnull, nsnull)) return false; *vp = resultVal; return true; } // else... // This is a method or attribute - we'll be needing a function object int argc; JSNative callback; if (IsMethod()) { const nsXPTMethodInfo* info; if (NS_FAILED(iface->GetInterfaceInfo()->GetMethodInfo(mIndex, &info))) return false; // Note: ASSUMES that retval is last arg. argc = (int) info->GetParamCount(); if (argc && info->GetParam((uint8_t)(argc-1)).IsRetval()) argc-- ; callback = XPC_WN_CallMethod; } else { argc = 0; callback = XPC_WN_GetterSetter; } JSFunction *fun = js::NewFunctionByIdWithReserved(ccx, callback, argc, 0, parent, GetName()); if (!fun) return false; JSObject* funobj = JS_GetFunctionObject(fun); if (!funobj) return false; js::SetFunctionNativeReserved(funobj, 0, PRIVATE_TO_JSVAL(iface)); js::SetFunctionNativeReserved(funobj, 1, PRIVATE_TO_JSVAL(this)); *vp = OBJECT_TO_JSVAL(funobj); return true; }
/* Wrap a JS value to export into perl * Returns a new SV, REFCNT_dec is caller's responsability */ JSBool PJS_ReflectJS2Perl( pTHX_ JSContext *cx, jsval value, SV** sv, int full ) { if(JSVAL_IS_PRIMITIVE(value)) { *sv = PrimJSVALToSV(aTHX_ cx, value); if(*sv) return JS_TRUE; } else if(JSVAL_IS_OBJECT(value)) { PJS_Context *pcx = PJS_GET_CONTEXT(cx); JSObject *object = JSVAL_TO_OBJECT(value); JSClass *clasp = PJS_GET_CLASS(cx, object); const char *classname = clasp->name; JSObject *passport; SV *wrapper; SV *box; char hkey[32]; jsval temp = JSVAL_VOID; snprintf(hkey, 32, "%p", (void *)object); PJS_DEBUG2("Wrapping a %s(%s)\n", classname, hkey); if(PJS_getFlag(pcx, "ConvertRegExp") && strEQ(classname, "RegExp")) { jsval src; char *str; if(JS_CallFunctionName(cx, object, "toSource", 0, NULL, &src) && (str = JS_GetStringBytes(JS_ValueToString(cx, src))) ) { dSP; SV *tmp = newSVpvf("qr%s", str); eval_sv(tmp, G_SCALAR); sv_free(tmp); // Don't leak SPAGAIN; tmp = POPs; PUTBACK; if(!SvTRUE(ERRSV)) { *sv = SvREFCNT_inc_simple_NN(tmp); return JS_TRUE; } } return JS_FALSE; } if(IS_PERL_CLASS(clasp)) { /* IS_PERL_CLASS means actual perl object is there */ SV *priv = (SV *)JS_GetPrivate(cx, object); if(priv && SvOK(priv) && SvROK(priv)) { *sv = SvREFCNT_inc_simple_NN(priv); return JS_TRUE; } croak("A private %s?!\n", classname); return JS_FALSE; } /* Common JSObject case */ /* Check registered perl visitors */ JS_LookupProperty(cx, pcx->pvisitors, hkey, &temp); if(temp != JSVAL_VOID) { /* Already registered, so exits a reference in perl space * _must_ hold a PASSPORT */ assert(JSVAL_TO_OBJECT(temp) == object); box = PJS_GetPassport(aTHX_ cx, object); SvREFCNT_inc_void_NN(box); /* In perl should be one more */ PJS_DEBUG1("Cached!: %s\n", hkey); } else { /* Check if with a PASSPORT */ JS_LookupPropertyWithFlags(cx, object, PJS_PASSPORT_PROP, 0, &temp); if(JSVAL_IS_OBJECT(temp) && (passport = JSVAL_TO_OBJECT(temp)) && PJS_GET_CLASS(cx, passport) == &passport_class && JS_GetReservedSlot(cx, passport, 0, &temp) && object == (JSObject *)JSVAL_TO_PRIVATE(temp) ) { /* Yes, reentering perl */ box = (SV *)JS_GetPrivate(cx, passport); /* Here we don't increment refcount, the ownership in passport is * transferred to perl land. */ PJS_DEBUG1("Reenter: %s\n", hkey); } else { /* No, first time, must wrap the object */ SV *boxref; const char *package; SV *robj = newSV(0); SV *rjsv = newSV(0); if (JS_ObjectIsFunction(cx, object)) package = PJS_FUNCTION_PACKAGE; else if(JS_IsArrayObject(cx, object)) package = PJS_ARRAY_PACKAGE; else if(strEQ(classname, PJS_PACKAGE_CLASS_NAME)) package = PJS_STASH_PACKAGE; #if JS_HAS_XML_SUPPORT else if(strEQ(classname, "XML")) package = PJS_XMLOBJ_PACKAGE; #endif else if(strEQ(classname, "Error")) package = PJS_ERROR_PACKAGE; else { SV **sv = hv_fetch(get_hv(NAMESPACE"ClassMap", 1), classname, strlen(classname), 0); if(sv) package = SvPV_nolen(*sv); else package = PJS_OBJECT_PACKAGE; } sv_setref_pv(robj, PJS_RAW_OBJECT, (void*)object); sv_setref_iv(rjsv, PJS_RAW_JSVAL, (IV)value); boxref = PJS_CallPerlMethod(aTHX_ cx, "__new", sv_2mortal(newSVpv(package, 0)), // package sv_2mortal(robj), // content sv_2mortal(rjsv), // jsval NULL ); if(!boxref) return JS_FALSE; if(!SvOK(boxref) || !sv_derived_from(boxref, PJS_BOXED_PACKAGE)) croak("PJS_Assert: Contructor must return a "NAMESPACE"Boxed"); /* Create a new PASSPORT */ passport = JS_NewObject(cx, &passport_class, NULL, object); if(!passport || !JS_DefineProperty(cx, object, PJS_PASSPORT_PROP, OBJECT_TO_JSVAL(passport), NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT)) return JS_FALSE; box = SvRV(boxref); /* boxref is mortal, so we need to increment its rc, at end of * scope, PASSPORT owns created box */ JS_SetPrivate(cx, passport, (void *)SvREFCNT_inc_simple_NN(box)); JS_SetReservedSlot(cx, passport, 0, PRIVATE_TO_JSVAL(object)); PJS_DEBUG2("New boxed: %s brc: %d\n", hkey, SvREFCNT(box)); } /* Root object adding it to pvisitors list, will be unrooted by * jsc_free_root at Boxed DESTROY time */ JS_DefineProperty(cx, pcx->pvisitors, hkey, value, NULL, NULL, 0); } /* Here the RC of box in PASSPORT reflects wrapper's ownership */ if(full && PJS_getFlag(pcx, "AutoTie") && (strEQ(classname, "Object") || strEQ(classname, "Array")) ) { /* Return tied */ AV *avbox = (AV *)SvRV(box); SV **last; SV *tied; SV *tier; if(strEQ(classname, "Array")) { last = av_fetch(avbox, 6, 1); if(last && SvOK(*last) && SvROK(*last)) { // Cached *sv = newSVsv(*last); sv_free(box); /* Hard copy 'sv' owns the reference */ return JS_TRUE; } tied = (SV *)newAV(); } else { // Object last = av_fetch(avbox, 5, 1); if(last && SvOK(*last) && SvROK(*last)) { // Cached *sv = newSVsv(*last); sv_free(box); /* Hard copy 'sv' owns the reference */ return JS_TRUE; } tied = (SV *)newHV(); } /* hv_magic below own a reference to box, we use an explicit path, * to make clear that to perl land only one reference is given */ tier = newRV_inc(box); hv_magic((HV *)tied, (GV *)tier, PERL_MAGIC_tied); sv_free(tier); wrapper = newRV_noinc(tied); /* Don't leak the hidden tied variable */ /* Save in cache a weaken copy, the cache itself dosn't hold a reference */ sv_setsv(*last, wrapper); sv_rvweaken(*last); PJS_DEBUG1("Return tied for %s\n", SvPV_nolen(tier)); } else { wrapper = newRV_noinc(box); /* Transfer ownership to wrapper */ #if PERL_VERSION < 9 sv_bless(wrapper, SvSTASH(box)); #endif } *sv = wrapper; return JS_TRUE; } return JS_FALSE; }