50602: Add support in XPConnect for sharing of refcounted string BufferHandles,

in both directions.
72552: Remedy overzealous CHECK_REQUEST placement in jsapi.c, to produce a
       minimal-but-complete set of engine entry points that require a Request
       for safe execution.
r=brendan, sr=jband, assist=scc,pinkerton


git-svn-id: svn://10.0.0.236/trunk@90483 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
shaver%mozilla.org 2001-03-27 06:04:44 +00:00
parent 6e92e75066
commit 44326fc42c
23 changed files with 642 additions and 92 deletions

Binary file not shown.

View File

@ -89,28 +89,24 @@
JS_PUBLIC_API(jsval)
JS_GetNaNValue(JSContext *cx)
{
CHECK_REQUEST(cx);
return DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
}
JS_PUBLIC_API(jsval)
JS_GetNegativeInfinityValue(JSContext *cx)
{
CHECK_REQUEST(cx);
return DOUBLE_TO_JSVAL(cx->runtime->jsNegativeInfinity);
}
JS_PUBLIC_API(jsval)
JS_GetPositiveInfinityValue(JSContext *cx)
{
CHECK_REQUEST(cx);
return DOUBLE_TO_JSVAL(cx->runtime->jsPositiveInfinity);
}
JS_PUBLIC_API(jsval)
JS_GetEmptyStringValue(JSContext *cx)
{
CHECK_REQUEST(cx);
return STRING_TO_JSVAL(cx->runtime->emptyString);
}
@ -599,7 +595,6 @@ JS_TypeOfValue(JSContext *cx, jsval v)
JS_PUBLIC_API(const char *)
JS_GetTypeName(JSContext *cx, JSType type)
{
CHECK_REQUEST(cx);
if ((uintN)type >= (uintN)JSTYPE_LIMIT)
return NULL;
return js_type_str[type];
@ -930,7 +925,6 @@ JS_SetVersion(JSContext *cx, JSVersion version)
{
JSVersion oldVersion;
CHECK_REQUEST(cx);
oldVersion = cx->version;
if (version == oldVersion)
return oldVersion;
@ -1423,7 +1417,6 @@ JS_EnumerateStandardClasses(JSContext *cx, JSObject *obj)
JS_PUBLIC_API(JSObject *)
JS_GetScopeChain(JSContext *cx)
{
CHECK_REQUEST(cx);
return cx->fp ? cx->fp->scopeChain : NULL;
}
@ -1495,6 +1488,12 @@ JS_AddRoot(JSContext *cx, void *rp)
return js_AddRoot(cx, rp, NULL);
}
JS_PUBLIC_API(JSBool)
JS_AddNamedRootRT(JSRuntime *rt, void *rp, const char *name)
{
return js_AddRootRT(rt, rp, name);
}
JS_PUBLIC_API(JSBool)
JS_RemoveRoot(JSContext *cx, void *rp)
{
@ -1742,28 +1741,24 @@ JS_IdToValue(JSContext *cx, jsid id, jsval *vp)
JS_PUBLIC_API(JSBool)
JS_PropertyStub(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
CHECK_REQUEST(cx);
return JS_TRUE;
}
JS_PUBLIC_API(JSBool)
JS_EnumerateStub(JSContext *cx, JSObject *obj)
{
CHECK_REQUEST(cx);
return JS_TRUE;
}
JS_PUBLIC_API(JSBool)
JS_ResolveStub(JSContext *cx, JSObject *obj, jsval id)
{
CHECK_REQUEST(cx);
return JS_TRUE;
}
JS_PUBLIC_API(JSBool)
JS_ConvertStub(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
{
CHECK_REQUEST(cx);
#if JS_BUG_EAGER_TOSTRING
if (type == JSTYPE_STRING)
return JS_TRUE;
@ -1856,14 +1851,12 @@ bad:
JS_PUBLIC_API(JSClass *)
JS_GetClass(JSContext *cx, JSObject *obj)
{
CHECK_REQUEST(cx);
return OBJ_GET_CLASS(cx, obj);
}
#else
JS_PUBLIC_API(JSClass *)
JS_GetClass(JSObject *obj)
{
CHECK_REQUEST(cx);
return LOCKED_OBJ_GET_CLASS(obj);
}
#endif
@ -1893,7 +1886,6 @@ JS_GetPrivate(JSContext *cx, JSObject *obj)
{
jsval v;
CHECK_REQUEST(cx);
JS_ASSERT(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_HAS_PRIVATE);
v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
if (!JSVAL_IS_INT(v))
@ -1904,7 +1896,6 @@ JS_GetPrivate(JSContext *cx, JSObject *obj)
JS_PUBLIC_API(JSBool)
JS_SetPrivate(JSContext *cx, JSObject *obj, void *data)
{
CHECK_REQUEST(cx);
JS_ASSERT(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_HAS_PRIVATE);
OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(data));
return JS_TRUE;
@ -1914,7 +1905,6 @@ JS_PUBLIC_API(void *)
JS_GetInstancePrivate(JSContext *cx, JSObject *obj, JSClass *clasp,
jsval *argv)
{
CHECK_REQUEST(cx);
if (!JS_InstanceOf(cx, obj, clasp, argv))
return NULL;
return JS_GetPrivate(cx, obj);
@ -1947,7 +1937,6 @@ JS_GetParent(JSContext *cx, JSObject *obj)
{
JSObject *parent;
CHECK_REQUEST(cx);
parent = JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj, JSSLOT_PARENT));
/* Beware ref to dead object (we may be called from obj's finalizer). */
@ -2495,7 +2484,6 @@ JS_NewArrayObject(JSContext *cx, jsint length, jsval *vector)
JS_PUBLIC_API(JSBool)
JS_IsArrayObject(JSContext *cx, JSObject *obj)
{
CHECK_REQUEST(cx);
return OBJ_GET_CLASS(cx, obj) == &js_ArrayClass;
}
@ -3322,7 +3310,6 @@ JS_SetBranchCallback(JSContext *cx, JSBranchCallback cb)
{
JSBranchCallback oldcb;
CHECK_REQUEST(cx);
oldcb = cx->branchCallback;
cx->branchCallback = cb;
return oldcb;
@ -3337,7 +3324,6 @@ JS_IsRunning(JSContext *cx)
JS_PUBLIC_API(JSBool)
JS_IsConstructing(JSContext *cx)
{
CHECK_REQUEST(cx);
return cx->fp && cx->fp->constructing;
}
@ -3511,7 +3497,6 @@ JS_ReportError(JSContext *cx, const char *format, ...)
{
va_list ap;
CHECK_REQUEST(cx);
va_start(ap, format);
js_ReportErrorVA(cx, JSREPORT_ERROR, format, ap);
va_end(ap);
@ -3523,7 +3508,6 @@ JS_ReportErrorNumber(JSContext *cx, JSErrorCallback errorCallback,
{
va_list ap;
CHECK_REQUEST(cx);
va_start(ap, errorNumber);
js_ReportErrorNumberVA(cx, JSREPORT_ERROR, errorCallback, userRef,
errorNumber, JS_TRUE, ap);
@ -3536,7 +3520,6 @@ JS_ReportErrorNumberUC(JSContext *cx, JSErrorCallback errorCallback,
{
va_list ap;
CHECK_REQUEST(cx);
va_start(ap, errorNumber);
js_ReportErrorNumberVA(cx, JSREPORT_ERROR, errorCallback, userRef,
errorNumber, JS_FALSE, ap);
@ -3563,7 +3546,6 @@ JS_ReportErrorFlagsAndNumber(JSContext *cx, uintN flags,
va_list ap;
JSBool ok;
CHECK_REQUEST(cx);
va_start(ap, errorNumber);
ok = js_ReportErrorNumberVA(cx, flags, errorCallback, userRef,
errorNumber, JS_TRUE, ap);
@ -3579,7 +3561,6 @@ JS_ReportErrorFlagsAndNumberUC(JSContext *cx, uintN flags,
va_list ap;
JSBool ok;
CHECK_REQUEST(cx);
va_start(ap, errorNumber);
ok = js_ReportErrorNumberVA(cx, flags, errorCallback, userRef,
errorNumber, JS_FALSE, ap);
@ -3598,7 +3579,6 @@ JS_SetErrorReporter(JSContext *cx, JSErrorReporter er)
{
JSErrorReporter older;
CHECK_REQUEST(cx);
older = cx->errorReporter;
cx->errorReporter = er;
return older;
@ -3674,7 +3654,6 @@ JS_ClearRegExpRoots(JSContext *cx)
{
JSRegExpStatics *res;
CHECK_REQUEST(cx);
/* No locking required, cx is thread-private and input must be live. */
res = &cx->regExpStatics;
res->input = NULL;
@ -3702,7 +3681,6 @@ JS_GetLocaleCallbacks(JSContext *cx)
JS_PUBLIC_API(JSBool)
JS_IsExceptionPending(JSContext *cx)
{
CHECK_REQUEST(cx);
#if JS_HAS_EXCEPTIONS
return (JSBool) cx->throwing;
#else
@ -3713,8 +3691,8 @@ JS_IsExceptionPending(JSContext *cx)
JS_PUBLIC_API(JSBool)
JS_GetPendingException(JSContext *cx, jsval *vp)
{
CHECK_REQUEST(cx);
#if JS_HAS_EXCEPTIONS
CHECK_REQUEST(cx);
if (!cx->throwing)
return JS_FALSE;
*vp = cx->exception;
@ -3737,7 +3715,6 @@ JS_SetPendingException(JSContext *cx, jsval v)
JS_PUBLIC_API(void)
JS_ClearPendingException(JSContext *cx)
{
CHECK_REQUEST(cx);
#if JS_HAS_EXCEPTIONS
cx->throwing = JS_FALSE;
#endif

View File

@ -512,15 +512,18 @@ JS_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval);
extern JS_PUBLIC_API(JSBool)
JS_AddRoot(JSContext *cx, void *rp);
extern JS_PUBLIC_API(JSBool)
JS_AddNamedRoot(JSContext *cx, void *rp, const char *name);
extern JS_PUBLIC_API(JSBool)
JS_AddNamedRootRT(JSRuntime *rt, void *rp, const char *name);
extern JS_PUBLIC_API(JSBool)
JS_RemoveRoot(JSContext *cx, void *rp);
extern JS_PUBLIC_API(JSBool)
JS_RemoveRootRT(JSRuntime *rt, void *rp);
extern JS_PUBLIC_API(JSBool)
JS_AddNamedRoot(JSContext *cx, void *rp, const char *name);
#ifdef DEBUG
extern JS_PUBLIC_API(void)
JS_DumpNamedRoots(JSRuntime *rt,

View File

@ -373,7 +373,15 @@ js_FinishGC(JSRuntime *rt)
JSBool
js_AddRoot(JSContext *cx, void *rp, const char *name)
{
JSRuntime *rt;
JSBool ok = js_AddRootRT(cx->runtime, rp, name);
if (!ok)
JS_ReportOutOfMemory(cx);
return ok;
}
JSBool
js_AddRootRT(JSRuntime *rt, void *rp, const char *name)
{
JSBool ok;
/*
@ -389,7 +397,6 @@ js_AddRoot(JSContext *cx, void *rp, const char *name)
* of deadlock because the GC doesn't set rt->gcRunning until after it has
* waited for all active requests to end.
*/
rt = cx->runtime;
JS_LOCK_GC(rt);
#ifdef JS_THREADSAFE
JS_ASSERT(!rt->gcRunning || rt->gcLevel > 0);
@ -401,8 +408,6 @@ js_AddRoot(JSContext *cx, void *rp, const char *name)
#endif
ok = (JS_HashTableAdd(rt->gcRootsHash, rp, (void *)name) != NULL);
JS_UNLOCK_GC(rt);
if (!ok)
JS_ReportOutOfMemory(cx);
return ok;
}

View File

@ -82,6 +82,9 @@ js_FinishGC(JSRuntime *rt);
extern JSBool
js_AddRoot(JSContext *cx, void *rp, const char *name);
extern JSBool
js_AddRootRT(JSRuntime *rt, void *rp, const char *name);
extern JSBool
js_RemoveRoot(JSRuntime *rt, void *rp);

View File

@ -172,7 +172,7 @@ interface nsIXPConnectWrappedJS : nsIXPConnectJSObjectHolder
/**
* This is a somewhat special interface. It is available from the global
* nsIXPConnect object when native methods have been called. It is only relevent
* nsIXPConnect object when native methods have been called. It is only relevant
* to the currently called native method on the given JSContext/thread. Holding
* a reference past that time (or while other native methods are being called)
* will not assure access to this data.

View File

@ -60,6 +60,7 @@ CPPSRCS = \
xpcnativecallcontext.cpp \
xpcruntimesvc.cpp \
xpcstack.cpp \
xpcstring.cpp \
xpcthreadcontext.cpp \
xpcthrower.cpp \
xpcwrappedjs.cpp \

View File

@ -62,6 +62,7 @@ OBJS= \
.\$(OBJDIR)\xpcnativecallcontext.obj \
.\$(OBJDIR)\xpcruntimesvc.obj \
.\$(OBJDIR)\xpcstack.obj \
.\$(OBJDIR)\xpcstring.obj \
.\$(OBJDIR)\xpcthreadcontext.obj \
.\$(OBJDIR)\xpcthrower.obj \
.\$(OBJDIR)\xpcwrappedjs.obj \

View File

@ -21,6 +21,7 @@
* Contributor(s):
* John Bandhauer <jband@netscape.com>
* Pierre Phaneuf <pp@ludusdesign.com>
* Mike Shaver <shaver@mozilla.org>
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU Public License (the "GPL"), in which case the
@ -336,11 +337,11 @@ XPCConvert::NativeData2JS(JSContext* cx, jsval* d, const void* s,
case nsXPTType::T_IID:
{
nsID* iid = *((nsID**)s);
if(!iid)
nsID* iid2 = *((nsID**)s);
if(!iid2)
break;
JSObject* obj;
if(!(obj = xpc_NewIDObject(cx, scope, *iid)))
if(!(obj = xpc_NewIDObject(cx, scope, *iid2)))
return JS_FALSE;
*d = OBJECT_TO_JSVAL(obj);
break;
@ -351,28 +352,10 @@ XPCConvert::NativeData2JS(JSContext* cx, jsval* d, const void* s,
const nsAReadableString* p = *((const nsAReadableString**)s);
if(!p)
break;
PRUint32 length = p->Length();
jschar* chars = (jschar *)
JS_malloc(cx, (length + 1) * sizeof(jschar));
if(!chars)
return JS_FALSE;
JSString *str = XPCStringConvert::ReadableToJSString(cx, *p);
if (!str)
return JS_FALSE;
if(length && !CopyUnicodeTo(*p, 0, (PRUnichar*)chars, length))
{
JS_free(cx, chars);
return JS_FALSE;
}
chars[length] = 0;
JSString* str;
if(!(str = JS_NewUCString(cx, chars, length)))
{
JS_free(cx, chars);
return JS_FALSE;
}
*d = STRING_TO_JSVAL(str);
break;
}
@ -440,7 +423,6 @@ XPCConvert::NativeData2JS(JSContext* cx, jsval* d, const void* s,
return JS_TRUE;
}
/***************************************************************************/
// static
JSBool
@ -618,8 +600,9 @@ XPCConvert::JSData2Native(JSContext* cx, void* d, jsval s,
static const NS_NAMED_LITERAL_STRING(sVoidString, "undefined");
const PRUnichar* chars;
PRUint32 length;
JSString* str = nsnull;
JSBool isNewString = JS_FALSE;
PRUint32 length;
if(JSVAL_IS_VOID(s))
{
@ -635,7 +618,7 @@ XPCConvert::JSData2Native(JSContext* cx, void* d, jsval s,
}
else
{
JSString* str = JS_ValueToString(cx, s);
str = JS_ValueToString(cx, s);
if(!str)
return JS_FALSE;
@ -650,6 +633,7 @@ XPCConvert::JSData2Native(JSContext* cx, void* d, jsval s,
}
else
{
str = nsnull;
chars = sEmptyString.get();
}
}
@ -658,20 +642,27 @@ XPCConvert::JSData2Native(JSContext* cx, void* d, jsval s,
if(useAllocator)
{
nsAReadableString* rs;
// If the underlying JSString may have been created in the
// JS_ValueToString call, then we need to make a copied string
// to avoid the possibility of the string data being gc'd before
// we are done.
if(isNewString)
rs = new nsString(chars, length);
if (str)
{
XPCReadableJSStringWrapper *wrapper =
XPCStringConvert::JSStringToReadable(str);
if (!wrapper)
return JS_FALSE;
// Ask for the shared buffer handle, which will root the
// string.
if (isNewString && ! wrapper->GetSharedBufferHandle())
return JS_FALSE;
*((nsAReadableString**)d) = wrapper;
}
else
rs = new nsLiteralString(chars, length);
if(!rs)
return JS_FALSE;
*((nsAReadableString**)d) = rs;
{
nsAReadableString *rs = new nsString(chars, length);
if (!rs)
return JS_FALSE;
*((nsAReadableString**)d) = rs;
}
}
else
{
@ -1248,6 +1239,7 @@ XPCConvert::JSErrorToXPCException(JSContext* cx,
return result;
}
/***************************************************************************/
/*

View File

@ -158,10 +158,10 @@ static char* FormatJSFrame(JSContext* cx, JSStackFrame* fp,
{
for(uint32 k = namedArgCount; k < argCount; k++)
{
char num[8];
JS_snprintf(num, 8, "%d", (int) k);
char number[8];
JS_snprintf(number, 8, "%d", (int) k);
if(JS_GetProperty(cx, argsObj, num, &val))
if(JS_GetProperty(cx, argsObj, number, &val))
{
value = JSVAL2String(cx, val, &isString);
buf = JS_sprintf_append(buf, "%s%s%s%s",

View File

@ -383,13 +383,13 @@ nsJSIID::HasInstance(JSContext *cx, JSObject *obj,
if(!JSVAL_IS_PRIMITIVE(v))
{
// we have a JSObject
JSObject* obj = JSVAL_TO_OBJECT(v);
JSObject* obj2 = JSVAL_TO_OBJECT(v);
NS_ASSERTION(obj, "when is an object not an object?");
NS_ASSERTION(obj2, "when is an object not an object?");
// is this really a native xpcom object with a wrapper?
nsXPCWrappedNative* other_wrapper =
nsXPCWrappedNativeClass::GetWrappedNativeOfJSObject(cx,obj);
nsXPCWrappedNativeClass::GetWrappedNativeOfJSObject(cx,obj2);
if(!other_wrapper)
return NS_OK;

View File

@ -225,6 +225,9 @@ XPCJSRuntime::~XPCJSRuntime()
printf("deleting XPCJSRuntime with %d live nsXPCWrappedNative (found in wrapper check)\n", (int)LiveWrapperCount);
JS_HashTableDestroy(DEBUG_WrappedNativeHashtable);
#endif
// unwire the readable/JSString sharing magic
XPCStringConvert::ShutdownDOMStringFinalizer();
}
XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect,

View File

@ -1,4 +1,4 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
@ -209,8 +209,8 @@ public:
static XPCJSRuntime* newXPCJSRuntime(nsXPConnect* aXPConnect,
nsIJSRuntimeService* aJSRuntimeService);
JSRuntime* GetJSRuntime() const {return mJSRuntime;}
nsXPConnect* GetXPConnect() const {return mXPConnect;}
JSRuntime* GetJSRuntime() const {return mJSRuntime;}
nsXPConnect* GetXPConnect() const {return mXPConnect;}
JSObject2WrappedJSMap* GetWrappedJSMap() const
{return mWrappedJSMap;}
@ -224,7 +224,7 @@ public:
XPCContext* GetXPCContext(JSContext* cx);
XPCContext* SyncXPCContextList(JSContext* cx = nsnull);
// Mapping of often used strings to jsid atoms that live 'forever'.
//
// To add a new string: add to this list and to XPCJSRuntime::mStrings
@ -1145,7 +1145,7 @@ private:
};
/***************************************************************************/
// data convertion
// data conversion
// class here just for static methods
class XPCConvert
@ -1215,6 +1215,82 @@ public:
private:
XPCConvert(); // not implemented
};
// class to export a JSString as an nsAReadableString, including refcounting
class XPCReadableJSStringWrapper : public nsLiteralString
{
public:
XPCReadableJSStringWrapper(JSString *str) :
nsLiteralString(NS_REINTERPRET_CAST(PRUnichar *,
JS_GetStringChars(str)),
JS_GetStringLength(str)),
mStr(str), mBufferHandle(0), mHandleIsShared(JS_FALSE)
{ }
~XPCReadableJSStringWrapper();
// buffer-handle accessors
const nsBufferHandle<PRUnichar>* GetBufferHandle() const
{
return BufferHandle(JS_FALSE);
}
const nsSharedBufferHandle<PRUnichar>* GetSharedBufferHandle() const
{
return BufferHandle(JS_TRUE);
}
protected:
struct WrapperBufferHandle :
public nsSharedBufferHandleWithAllocator<PRUnichar>
{
WrapperBufferHandle(XPCReadableJSStringWrapper *outer, JSString *str) :
nsSharedBufferHandleWithAllocator<PRUnichar>
(NS_CONST_CAST(PRUnichar *, outer->get()),
NS_CONST_CAST(PRUnichar *, outer->get() + outer->Length()),
mAllocator),
mAllocator(str)
{ }
XPCReadableJSStringWrapper *mOuter;
struct Allocator : nsStringAllocator<PRUnichar>
{
Allocator(JSString *str) : mStr(OBJECT_TO_JSVAL(str)) { }
virtual ~Allocator() { }
virtual void Deallocate(PRUnichar *) const;
JSBool RootString();
jsval mStr;
};
Allocator mAllocator;
};
const nsSharedBufferHandle<PRUnichar>* BufferHandle(JSBool shared) const;
JSString *mStr;
WrapperBufferHandle *mBufferHandle;
JSBool mHandleIsShared;
};
// readable string conversions, static methods only
class XPCStringConvert
{
public:
static JSString *ReadableToJSString(JSContext *cx,
const nsAReadableString &readable);
static XPCReadableJSStringWrapper *JSStringToReadable(JSString *str);
static void ShutdownDOMStringFinalizer();
private:
XPCStringConvert(); // not implemented
};
extern JSBool JS_DLL_CALLBACK

View File

@ -54,7 +54,7 @@ nsJSRuntimeServiceImpl::~nsJSRuntimeServiceImpl() {
JS_DestroyRuntime(mRuntime);
JS_ShutDown();
#ifdef DEBUG_shaver
fprintf(stderr, "nJRSI: destroyed runtime %p\n", mRuntime);
fprintf(stderr, "nJRSI: destroyed runtime %p\n", (void *)mRuntime);
#endif
}
}
@ -98,7 +98,7 @@ nsJSRuntimeServiceImpl::GetRuntime(JSRuntime **runtime)
}
*runtime = mRuntime;
#ifdef DEBUG_shaver
fprintf(stderr, "nJRSI: returning %p\n", mRuntime);
fprintf(stderr, "nJRSI: returning %p\n", (void *)mRuntime);
#endif
return NS_OK;
}

View File

@ -0,0 +1,300 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 2001 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Mike Shaver <shaver@mozilla.org>
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU Public License (the "GPL"), in which case the
* provisions of the GPL are applicable instead of those above.
* If you wish to allow use of your version of this file only
* under the terms of the GPL and not to allow others to use your
* version of this file under the NPL, indicate your decision by
* deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete
* the provisions above, a recipient may use your version of this
* file under either the NPL or the GPL.
*/
/*
* Infrastructure for sharing DOMString data with JSStrings.
*
* Importing an nsAReadableString into JS:
* If possible (GetSharedBufferHandle works) use the external string support in
* JS to create a JSString that points to the readable's buffer. We keep a
* reference to the buffer handle until the JSString is finalized.
*
* Exporting a JSString as an nsAReadable:
* Wrap the JSString with a root-holding XPCJSReadableStringWrapper, which roots
* the string and exposes its buffer via the nsAReadableString interface, as
* well as providing refcounting support.
*/
#include "xpcprivate.h"
#include "nscore.h"
/*
* We require that STRING_TO_JSVAL(s) != (jsval)s, for tracking rootedness.
* When we colonize Mars and change JSVAL_STRING, we'll need to update this
* code.
*/
#if JSVAL_STRING == 0
#error "JSVAL_STRING has zero value -- need to fix root management!"
#endif
XPCReadableJSStringWrapper::~XPCReadableJSStringWrapper()
{
if (mBufferHandle)
{
if (mHandleIsShared)
{
mBufferHandle->ReleaseReference();
}
else
{
delete mBufferHandle;
}
}
}
const nsSharedBufferHandle<PRUnichar>*
XPCReadableJSStringWrapper::BufferHandle(JSBool shared) const
{
XPCReadableJSStringWrapper * mutable_this =
NS_CONST_CAST(XPCReadableJSStringWrapper *, this);
if (!mBufferHandle)
{
mutable_this->mBufferHandle =
new WrapperBufferHandle(mutable_this, mStr);
}
if (shared && !mHandleIsShared)
{
mutable_this->mBufferHandle->AcquireReference();
mutable_this->mBufferHandle->mAllocator.RootString();
mutable_this->mHandleIsShared = JS_TRUE;
}
return mBufferHandle;
}
void
XPCReadableJSStringWrapper::WrapperBufferHandle::Allocator::
Deallocate(PRUnichar *) const
{
if (JSVAL_IS_STRING(mStr))
{
// unroot
JSRuntime *rt;
nsCOMPtr<nsIJSRuntimeService> rtsvc =
nsJSRuntimeServiceImpl::GetSingleton();
if (rtsvc && NS_SUCCEEDED(rtsvc->GetRuntime(&rt)))
{
JS_RemoveRootRT(rt,
NS_CONST_CAST(void **,
NS_REINTERPRET_CAST(void * const *,
&mStr)));
Allocator *mutable_this = NS_CONST_CAST(Allocator *, this);
// store untagged to indicate that we're not rooted
mutable_this->mStr = NS_REINTERPRET_CAST(jsval,
JSVAL_TO_STRING(mStr));
}
else
{
NS_ERROR("Unable to unroot mStr! Prepare for leak or crash!");
}
}
}
JSBool
XPCReadableJSStringWrapper::WrapperBufferHandle::Allocator::RootString()
{
JSRuntime *rt;
nsCOMPtr<nsIJSRuntimeService> rtsvc =
nsJSRuntimeServiceImpl::GetSingleton();
JSBool ok = rtsvc &&
NS_SUCCEEDED(rtsvc->GetRuntime(&rt)) &&
JS_AddNamedRootRT(rt,
NS_CONST_CAST(void **,
NS_REINTERPRET_CAST(void * const *,
&mStr)),
"WrapperBufferHandle.mAllocator.mStr");
if (ok)
{
// Indicate that we've rooted the string by storing it as a
// string-tagged jsval
mStr = STRING_TO_JSVAL(NS_REINTERPRET_CAST(jsval, mStr));
}
return ok;
}
// structure for entry in the DOMStringTable
struct SharedStringEntry : public JSDHashEntryHdr {
void *key;
const nsSharedBufferHandle<PRUnichar> *handle;
};
// table to match JSStrings to their handles at finalization time
static JSDHashTable DOMStringTable;
static intN DOMStringFinalizerIndex = -1;
// unref the appropriate handle when the matching string is finalized
static void
FinalizeDOMString(JSContext *cx, JSString *str)
{
NS_ASSERTION(DOMStringFinalizerIndex != -1,
"XPCConvert: DOM string finalizer called uninitialized!");
SharedStringEntry *entry =
NS_STATIC_CAST(SharedStringEntry *,
JS_DHashTableOperate(&DOMStringTable, str,
JS_DHASH_LOOKUP));
// entry might be empty if we ran out of memory adding it to the hash
if (JS_DHASH_ENTRY_IS_BUSY(entry))
{
entry->handle->ReleaseReference();
(void) JS_DHashTableOperate(&DOMStringTable, str, JS_DHASH_REMOVE);
}
}
// initialize the aforementioned hash table and register our external finalizer
static JSBool
InitializeDOMStringFinalizer()
{
if (!JS_DHashTableInit(&DOMStringTable, JS_DHashGetStubOps(), NULL,
sizeof(SharedStringEntry), JS_DHASH_MIN_SIZE))
{
return JS_FALSE;
}
DOMStringFinalizerIndex =
JS_AddExternalStringFinalizer(FinalizeDOMString);
if (DOMStringFinalizerIndex == -1)
{
JS_DHashTableFinish(&DOMStringTable);
return JS_FALSE;
}
return JS_TRUE;
}
// cleanup enumerator for the DOMStringTable
static JSDHashOperator
ReleaseHandleAndRemove(JSDHashTable *table, JSDHashEntryHdr *hdr,
uint32 number, void *arg)
{
SharedStringEntry *entry = NS_STATIC_CAST(SharedStringEntry *, hdr);
entry->handle->ReleaseReference();
return JS_DHASH_REMOVE;
}
// clean up the finalizer infrastructure, releasing all outstanding handles
// static
void
XPCStringConvert::ShutdownDOMStringFinalizer()
{
if (DOMStringFinalizerIndex == -1)
{
return;
}
// enumerate and release
(void)JS_DHashTableEnumerate(&DOMStringTable, ReleaseHandleAndRemove, NULL);
JS_DHashTableFinish(&DOMStringTable);
DOMStringFinalizerIndex =
JS_RemoveExternalStringFinalizer(FinalizeDOMString);
DOMStringFinalizerIndex = -1;
}
// convert a readable to a JSString, sharing if possible and copying otherwise
// static
JSString *
XPCStringConvert::ReadableToJSString(JSContext *cx,
const nsAReadableString &readable)
{
const nsSharedBufferHandle<PRUnichar> *handle;
handle = readable.GetSharedBufferHandle();
JSString *str;
if (!handle || NS_REINTERPRET_CAST(int, handle) == 1)
{
// blech, have to copy.
PRUint32 length = readable.Length();
jschar *chars = NS_REINTERPRET_CAST(jschar *,
JS_malloc(cx, (length + 1) *
sizeof(jschar)));
if (!chars)
return NULL;
if (length && !CopyUnicodeTo(readable, 0,
NS_REINTERPRET_CAST(PRUnichar *, chars),
length))
{
JS_free(cx, chars);
return NULL;
}
chars[length] = 0;
str = JS_NewUCString(cx, chars, length);
if (!str)
JS_free(cx, chars);
return str;
}
if (DOMStringFinalizerIndex == -1 && !InitializeDOMStringFinalizer())
return NULL;
str = JS_NewExternalString(cx,
NS_CONST_CAST(jschar *,
NS_REINTERPRET_CAST(const jschar *,
handle->DataStart())),
handle->DataLength(),
DOMStringFinalizerIndex);
if (!str)
return NULL;
SharedStringEntry *entry =
NS_STATIC_CAST(SharedStringEntry *,
JS_DHashTableOperate(&DOMStringTable, str,
JS_DHASH_ADD));
if (!entry)
return NULL; // str will be cleaned up by GC
entry->handle = handle;
entry->key = str;
handle->AcquireReference();
return str;
}
// static
XPCReadableJSStringWrapper *
XPCStringConvert::JSStringToReadable(JSString *str)
{
return new XPCReadableJSStringWrapper(str);
}

View File

@ -59,6 +59,7 @@ CPPSRCS = \
xpctest_inout.cpp \
xpctest_multiple.cpp \
xpctest_out.cpp \
xpctest_domstring.cpp \
$(NULL)
EXTRA_DSO_LDOPTS += \

View File

@ -57,6 +57,7 @@ OBJS= \
.\$(OBJDIR)\xpctest_inout.obj \
.\$(OBJDIR)\xpctest_calljs.obj \
.\$(OBJDIR)\xpctest_multiple.obj\
.\$(OBJDIR)\xpctest_domstring.obj \
$(NULL)
LINCS=-I$(PUBLIC)\xpconnect -I$(PUBLIC)\xpcom -I$(PUBLIC)\js \

View File

@ -0,0 +1,124 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 2001 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Mike Shaver <shaver@mozilla.org>
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU Public License (the "GPL"), in which case the
* provisions of the GPL are applicable instead of those above.
* If you wish to allow use of your version of this file only
* under the terms of the GPL and not to allow others to use your
* version of this file under the NPL, indicate your decision by
* deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete
* the provisions above, a recipient may use your version of this
* file under either the NPL or the GPL.
*/
#include "nsISupports.h"
#include "nsString.h"
#include "xpctest_domstring.h"
#include "xpctest_private.h"
class xpcTestDOMString : public nsIXPCTestDOMString {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIXPCTESTDOMSTRING
xpcTestDOMString();
virtual ~xpcTestDOMString();
private:
const nsSharedBufferHandle<PRUnichar> *mHandle;
};
NS_IMPL_ISUPPORTS1(xpcTestDOMString, nsIXPCTestDOMString);
xpcTestDOMString::xpcTestDOMString()
: mHandle(0)
{
NS_INIT_REFCNT();
NS_ADDREF_THIS();
}
xpcTestDOMString::~xpcTestDOMString()
{
if (mHandle)
mHandle->ReleaseReference();
}
NS_IMETHODIMP
xpcTestDOMString::HereHaveADOMString(const nsAReadableString &str)
{
const nsSharedBufferHandle<PRUnichar> *handle;
handle = str.GetSharedBufferHandle();
if (!handle || int(handle) == 1)
return NS_ERROR_INVALID_ARG;
if (mHandle)
mHandle->ReleaseReference();
mHandle = handle;
mHandle->AcquireReference();
return NS_OK;
}
NS_IMETHODIMP
xpcTestDOMString::DontKeepThisOne(const nsAReadableString &str)
{
nsCString c; c.AssignWithConversion(str);
fprintf(stderr, "xpcTestDOMString::DontKeepThisOne: \"%s\"\n", c.get());
return NS_OK;
}
NS_IMETHODIMP
xpcTestDOMString::GiveDOMStringTo(nsIXPCTestDOMString *recv)
{
NS_NAMED_LITERAL_STRING(myString, "A DOM String, Just For You");
return recv->HereHaveADOMString(myString);
}
NS_IMETHODIMP
xpcTestDOMString::PassDOMStringThroughTo(const nsAReadableString &str,
nsIXPCTestDOMString *recv)
{
return recv->HereHaveADOMString(str);
}
NS_IMETHODIMP
xpctest::ConstructXPCTestDOMString(nsISupports *aOuter, REFNSIID aIID,
void **aResult)
{
nsresult rv;
NS_ASSERTION(!aOuter, "no aggregation");
xpcTestDOMString *obj = new xpcTestDOMString();
if (obj)
{
rv = obj->QueryInterface(aIID, aResult);
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to find correct interface");
NS_RELEASE(obj);
}
else
{
*aResult = nsnull;
rv = NS_ERROR_OUT_OF_MEMORY;
}
return rv;
}

View File

@ -64,7 +64,8 @@ static nsModuleComponentInfo components[] = {
{nsnull, NS_XPCTESTCHILD3_CID, "@mozilla.org/js/xpc/test/Child3;1", xpctest::ConstructXPCTestChild3 },
{nsnull, NS_XPCTESTCHILD4_CID, "@mozilla.org/js/xpc/test/Child4;1", xpctest::ConstructXPCTestChild4 },
{nsnull, NS_XPCTESTCHILD5_CID, "@mozilla.org/js/xpc/test/Child5;1", xpctest::ConstructXPCTestChild5 },
{nsnull, NS_ARRAY_CID, "@mozilla.org/js/xpc/test/ArrayTest;1", xpctest::ConstructArrayTest }
{nsnull, NS_ARRAY_CID, "@mozilla.org/js/xpc/test/ArrayTest;1", xpctest::ConstructArrayTest },
{nsnull, NS_XPCTESTDOMSTRING_CID, "@mozilla.org/js/xpc/test/DOMString;1", xpctest::ConstructXPCTestDOMString }
};
NS_IMPL_NSGETMODULE("xpconnect test", components)

View File

@ -144,6 +144,11 @@
{ 0x5b9af380, 0x6569, 0x11d3, \
{ 0x98, 0x9e, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } }
// {DB569F7E-16FB-4BCB-A86C-E08AA7F97666}
#define NS_XPCTESTDOMSTRING_CID \
{0xdb569f7e, 0x16fb, 0x1bcb, \
{ 0xa8, 0x6c, 0xe0, 0x8a, 0xa7, 0xf9, 0x76, 0x66 }}
// 'namespace' class
class xpctest
{
@ -167,6 +172,7 @@ public:
static NS_METHOD ConstructXPCTestChild4(nsISupports *aOuter, REFNSIID aIID, void **aResult);
static NS_METHOD ConstructXPCTestChild5(nsISupports *aOuter, REFNSIID aIID, void **aResult);
static NS_METHOD ConstructArrayTest(nsISupports *aOuter, REFNSIID aIID, void **aResult);
static NS_METHOD ConstructXPCTestDOMString(nsISupports *aOuter, REFNSIID aIID, void **aResult);
private:
xpctest(); // not implemented

View File

@ -49,6 +49,7 @@ XPIDLSRCS = \
xpctest_in.idl \
xpctest_inout.idl \
xpctest_multiple.idl \
xpctest_domstring.idl \
xpctest_out.idl \
$(NULL)

View File

@ -45,6 +45,7 @@ XPIDLSRCS = \
.\xpctest_inout.idl \
.\xpctest_multiple.idl \
.\xpctest_out.idl \
.\xpctest_domstring.idl \
$(NULL)
include <$(DEPTH)\config\rules.mak>

View File

@ -0,0 +1,54 @@
/* -*- Mode: IDL; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 2001 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Mike Shaver <shaver@mozilla.org>
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU Public License (the "GPL"), in which case the
* provisions of the GPL are applicable instead of those above.
* If you wish to allow use of your version of this file only
* under the terms of the GPL and not to allow others to use your
* version of this file under the NPL, indicate your decision by
* deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete
* the provisions above, a recipient may use your version of this
* file under either the NPL or the GPL.
*/
#include "nsISupports.idl"
/**
* Interface for testing the DOMString-conversion infrastructure.
*/
[scriptable, uuid(646d0b6b-6872-43b9-aa73-3c6b89ac3080)]
interface nsIXPCTestDOMString : nsISupports {
// Implementation should ask for the shared buffer interface and hold
// a refcount to it.
void hereHaveADOMString(in DOMString str);
// don't hold onto this one
void dontKeepThisOne(in DOMString str);
void giveDOMStringTo(in nsIXPCTestDOMString recv);
void passDOMStringThroughTo(in DOMString str, in nsIXPCTestDOMString recv);
};