author | David Anderson <danderson@mozilla.com> |
Sun, 25 Jul 2010 20:21:14 -0700 | |
changeset 53184 | 318781041a50e747f61aecfda4d7af11819fe861 |
parent 53183 | 783991695a4dba1fd40b7faafd4ffe6bc3f4a841 (current diff) |
parent 48564 | 04393c448a0e354ac51e79550ae2c22519ea10ef (diff) |
child 53185 | f04a41b307e60cd1926be776ebecdd5249dd3dfc |
child 53186 | 71d486f747cebe25b5d86d394fed8a354297fb78 |
push id | unknown |
push user | unknown |
push date | unknown |
milestone | 2.0b2pre |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/jsd/idl/jsdIDebuggerService.idl +++ b/js/jsd/idl/jsdIDebuggerService.idl @@ -181,21 +181,21 @@ interface jsdIDebuggerService : nsISuppo * been TYPE_INTERRUPTED or TYPE_THROW. TYPE_BREAKPOINT, * TYPE_DEBUG_REQUESTED, and TYPE_DEBUGGER_KEYWORD always stop, regardless * of this setting, as long as the top frame is not disabled. * * If HIDE_DISABLED_FRAMES is set, this is effectively set as well. */ const unsigned long MASK_TOP_FRAME_ONLY = 0x20; /** - * When this flag is set, object creation will not be tracked. This will - * reduce the performance price you pay by enabling the debugger. + * This flag has been retired, do not re-use. It previously provided a hook + * for object allocation. */ - const unsigned long DISABLE_OBJECT_TRACE = 0x40; - + const unsigned long DISABLE_OBJECT_TRACE_RETIRED = 0x40; + /** * Debugger service flags. */ attribute unsigned long flags; /** * Major version number of implementation. */
--- a/js/jsd/jsd.h +++ b/js/jsd/jsd.h @@ -1040,19 +1040,16 @@ jsd_InitObjectManager(JSDContext* jsdc); extern void jsd_DestroyObjectManager(JSDContext* jsdc); extern void jsd_DestroyObjects(JSDContext* jsdc); extern void -jsd_ObjectHook(JSContext *cx, JSObject *obj, JSBool isNew, void *closure); - -extern void jsd_Constructing(JSDContext* jsdc, JSContext *cx, JSObject *obj, JSStackFrame *fp); extern JSDObject* jsd_IterateObjects(JSDContext* jsdc, JSDObject** iterp); extern JSObject* jsd_GetWrappedObject(JSDContext* jsdc, JSDObject* jsdobj);
--- a/js/jsd/jsd_high.c +++ b/js/jsd/jsd_high.c @@ -208,19 +208,16 @@ jsd_DebuggerOnForUser(JSRuntime* * the debugger is paused. The destroy hook so we'll clean up * internal data structures when scripts are destroyed, and the * newscript hook for backwards compatibility for now. We'd like * to stop doing that. */ JS_SetNewScriptHookProc(jsdc->jsrt, jsd_NewScriptHookProc, jsdc); JS_SetDestroyScriptHookProc(jsdc->jsrt, jsd_DestroyScriptHookProc, jsdc); jsd_DebuggerUnpause(jsdc); - if (!(jsdc->flags & JSD_DISABLE_OBJECT_TRACE)) { - JS_SetObjectHook(jsdc->jsrt, jsd_ObjectHook, jsdc); - } #ifdef LIVEWIRE LWDBG_SetNewScriptHookProc(jsd_NewScriptHookProc, jsdc); #endif if( jsdc->userCallbacks.setContext ) jsdc->userCallbacks.setContext(jsdc, jsdc->user); return jsdc; } @@ -234,19 +231,16 @@ jsd_DebuggerOn(void) void jsd_DebuggerOff(JSDContext* jsdc) { jsd_DebuggerPause(jsdc, JS_TRUE); /* clear hooks here */ JS_SetNewScriptHookProc(jsdc->jsrt, NULL, NULL); JS_SetDestroyScriptHookProc(jsdc->jsrt, NULL, NULL); - /* Have to unset these too, since jsd_DebuggerPause only unsets - them conditionally */ - JS_SetObjectHook(jsdc->jsrt, NULL, NULL); #ifdef LIVEWIRE LWDBG_SetNewScriptHookProc(NULL,NULL); #endif /* clean up */ JSD_LockScriptSubsystem(jsdc); jsd_DestroyScriptManager(jsdc); JSD_UnlockScriptSubsystem(jsdc); @@ -257,19 +251,17 @@ jsd_DebuggerOff(JSDContext* jsdc) if( jsdc->userCallbacks.setContext ) jsdc->userCallbacks.setContext(NULL, jsdc->user); } void jsd_DebuggerPause(JSDContext* jsdc, JSBool forceAllHooksOff) { JS_SetDebuggerHandler(jsdc->jsrt, NULL, NULL); - if (forceAllHooksOff || - (!(jsdc->flags & JSD_COLLECT_PROFILE_DATA) && - (jsdc->flags & JSD_DISABLE_OBJECT_TRACE))) { + if (forceAllHooksOff || !(jsdc->flags & JSD_COLLECT_PROFILE_DATA)) { JS_SetExecuteHook(jsdc->jsrt, NULL, NULL); JS_SetCallHook(jsdc->jsrt, NULL, NULL); } JS_SetThrowHook(jsdc->jsrt, NULL, NULL); JS_SetDebugErrorHook(jsdc->jsrt, NULL, NULL); } void
--- a/js/jsd/jsd_obj.c +++ b/js/jsd/jsd_obj.c @@ -123,72 +123,21 @@ static JSDObject* jsdobj = (JSDObject*) calloc(1, sizeof(JSDObject)); if (jsdobj) { JS_INIT_CLIST(&jsdobj->links); JS_APPEND_LINK(&jsdobj->links, &jsdc->objectsList); jsdobj->obj = obj; JS_HashTableAdd(jsdc->objectsTable, obj, jsdobj); - - if (jsdc->flags & JSD_DISABLE_OBJECT_TRACE) - return jsdobj; - - /* walk the stack to find js frame (if any) causing creation */ - while (NULL != (fp = JS_FrameIterator(cx, &iter))) - { - if( !JS_IsNativeFrame(cx, fp) ) - { - JSScript* script = JS_GetFrameScript(cx, fp); - if( !script ) - continue; - - newURL = JS_GetScriptFilename(cx, script); - if( newURL ) - jsdobj->newURL = jsd_AddAtom(jsdc, newURL); - - pc = JS_GetFramePC(cx, fp); - if( pc ) - jsdobj->newLineno = JS_PCToLineNumber(cx, script, pc); - - break; - } - } } return jsdobj; } void -jsd_ObjectHook(JSContext *cx, JSObject *obj, JSBool isNew, void *closure) -{ - JSDObject* jsdobj; - JSDContext* jsdc = (JSDContext*) closure; - - if( ! jsdc || ! jsdc->inited ) - return; - - JSD_LOCK_OBJECTS(jsdc); - if(isNew) - { - jsdobj = _createJSDObject(jsdc, cx, obj); - TRACEOBJ(jsdc, jsdobj, 0); - } - else - { - jsdobj = jsd_GetJSDObjectForJSObject(jsdc, obj); - if( jsdobj ) - { - TRACEOBJ(jsdc, jsdobj, 1); - _destroyJSDObject(jsdc, jsdobj); - } - } - JSD_UNLOCK_OBJECTS(jsdc); -} - -void jsd_Constructing(JSDContext* jsdc, JSContext *cx, JSObject *obj, JSStackFrame *fp) { JSDObject* jsdobj; JSScript* script; JSDScript* jsdscript; const char* ctorURL; const char* ctorName;
--- a/js/jsd/jsd_step.c +++ b/js/jsd/jsd_step.c @@ -114,22 +114,20 @@ JSBool { JSDScript* jsdscript; JSScript* jsscript; JSBool hookresult = JS_TRUE; if (!jsdc || !jsdc->inited) return JS_FALSE; - if (!hook && !(jsdc->flags & JSD_COLLECT_PROFILE_DATA) && - jsdc->flags & JSD_DISABLE_OBJECT_TRACE) + if (!hook && !(jsdc->flags & JSD_COLLECT_PROFILE_DATA)) { - /* no hook to call, no profile data needs to be collected, and - * the client has object tracing disabled, so there is nothing - * to do here. + /* no hook to call, no profile data needs to be collected, + * so there is nothing to do here. */ return hookresult; } if (before && JS_IsConstructorFrame(cx, fp)) jsd_Constructing(jsdc, cx, JS_GetFrameThis(cx, fp), fp); jsscript = JS_GetFrameScript(cx, fp);
--- a/js/jsd/jsd_xpc.cpp +++ b/js/jsd/jsd_xpc.cpp @@ -3275,17 +3275,17 @@ jsdASObserver::Observe (nsISupports *aSu rts->GetRuntime (&rt); if (NS_FAILED(rv)) return rv; rv = jsds->OnForRuntime(rt); if (NS_FAILED(rv)) return rv; - return jsds->SetFlags(JSD_DISABLE_OBJECT_TRACE); + return NS_OK; } NS_GENERIC_FACTORY_CONSTRUCTOR(jsdASObserver) NS_DEFINE_NAMED_CID(JSDSERVICE_CID); NS_DEFINE_NAMED_CID(JSDASO_CID); static const mozilla::Module::CIDEntry kJSDCIDs[] = { { &kJSDSERVICE_CID, false, NULL, jsdServiceConstructor },
--- a/js/jsd/jsdebug.c +++ b/js/jsd/jsdebug.c @@ -133,32 +133,21 @@ JSD_ClearAllProfileData(JSDContext *jsdc } JSD_PUBLIC_API(void) JSD_SetContextFlags(JSDContext *jsdc, uint32 flags) { uint32 oldFlags = jsdc->flags; JSD_ASSERT_VALID_CONTEXT(jsdc); jsdc->flags = flags; - if ((flags & JSD_COLLECT_PROFILE_DATA) || - !(flags & JSD_DISABLE_OBJECT_TRACE)) { + if (flags & JSD_COLLECT_PROFILE_DATA) { /* Need to reenable our call hooks now */ JS_SetExecuteHook(jsdc->jsrt, jsd_TopLevelCallHook, jsdc); JS_SetCallHook(jsdc->jsrt, jsd_FunctionCallHook, jsdc); } - if ((oldFlags ^ flags) & JSD_DISABLE_OBJECT_TRACE) { - /* Changing our JSD_DISABLE_OBJECT_TRACE flag */ - if (!(flags & JSD_DISABLE_OBJECT_TRACE)) { - /* Need to reenable our object hooks now */ - JS_SetObjectHook(jsdc->jsrt, jsd_ObjectHook, jsdc); - } else { - jsd_DestroyObjects(jsdc); - JS_SetObjectHook(jsdc->jsrt, NULL, NULL); - } - } } JSD_PUBLIC_API(uint32) JSD_GetContextFlags(JSDContext *jsdc) { JSD_ASSERT_VALID_CONTEXT(jsdc); return jsdc->flags; }
--- a/js/jsd/jsdebug.h +++ b/js/jsd/jsdebug.h @@ -235,21 +235,22 @@ JSD_ClearAllProfileData(JSDContext* jsdc * This only applies when the reason for calling the hook would have * been JSD_HOOK_INTERRUPTED or JSD_HOOK_THROW. JSD_HOOK_BREAKPOINT, * JSD_HOOK_DEBUG_REQUESTED, and JSD_HOOK_DEBUGGER_KEYWORD always stop, * regardless of this setting, as long as the top frame is not disabled. * * If JSD_HIDE_DISABLED_FRAMES is set, this is effectively set as well. */ #define JSD_MASK_TOP_FRAME_ONLY 0x20 + /* -* When this flag is set, object creation will not be tracked. This will -* reduce the performance price you pay by enabling the debugger. +* 0x40 was formerly used to hook into object creation. */ -#define JSD_DISABLE_OBJECT_TRACE 0x40 +#define JSD_DISABLE_OBJECT_TRACE_RETIRED 0x40 + extern JSD_PUBLIC_API(void) JSD_SetContextFlags (JSDContext* jsdc, uint32 flags); extern JSD_PUBLIC_API(uint32) JSD_GetContextFlags (JSDContext* jsdc); /*
--- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -280,24 +280,16 @@ static JS_ALWAYS_INLINE void * JSVAL_TO_PRIVATE(jsval v) { jsval_layout l; JS_ASSERT(JSVAL_IS_DOUBLE(v)); l.asBits = JSVAL_BITS(v); return JSVAL_TO_PRIVATE_PTR_IMPL(l); } -static JS_ALWAYS_INLINE JSBool -JSVAL_IS_UNDERLYING_TYPE_OF_PRIVATE(jsval v) -{ - jsval_layout l; - l.asBits = JSVAL_BITS(v); - return JSVAL_IS_UNDERLYING_TYPE_OF_PRIVATE_IMPL(l); -} - /************************************************************************/ /* * A jsid is an identifier for a property or method of an object which is * either a 31-bit signed integer, interned string or object. If XML is * enabled, there is an additional singleton jsid value; see * JS_DEFAULT_XML_NAMESPACE_ID below. Finally, there is an additional jsid * value, JSID_VOID, which does not occur in JS scripts but may be used to
--- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -1240,16 +1240,19 @@ struct JSCompartment { void sweep(JSContext *cx); }; struct JSGCTracer : public JSTracer { uint32 color; js::Vector<JSObject *, 0, js::SystemAllocPolicy> arraysToSlowify; }; +extern JS_FRIEND_API(void) +js_TriggerAllOperationCallbacks(JSRuntime *rt, JSBool gcLocked); + struct JSRuntime { /* Default compartment. */ JSCompartment *defaultCompartment; /* List of compartments (protected by the GC lock). */ js::Vector<JSCompartment *, 0, js::SystemAllocPolicy> compartments; /* Runtime state, synchronized by the stateChange/gcLock condvar/lock. */ @@ -1369,18 +1372,17 @@ struct JSRuntime { /* Per runtime debug hooks -- see jsprvtd.h and jsdbgapi.h. */ JSDebugHooks globalDebugHooks; #ifdef JS_TRACER /* True if any debug hooks not supported by the JIT are enabled. */ bool debuggerInhibitsJIT() const { return (globalDebugHooks.interruptHook || - globalDebugHooks.callHook || - globalDebugHooks.objectHook); + globalDebugHooks.callHook); } #endif /* More debugging state, see jsdbgapi.c. */ JSCList trapList; JSCList watchPointList; /* Client opaque pointers */ @@ -1602,25 +1604,34 @@ struct JSRuntime { JSWrapObjectCallback wrapObjectCallback; JSRuntime(); ~JSRuntime(); bool init(uint32 maxbytes); + inline void triggerGC(bool gcLocked) { + if (!gcIsNeeded) { + gcIsNeeded = true; + js_TriggerAllOperationCallbacks(this, gcLocked); + } + } + void setGCTriggerFactor(uint32 factor); void setGCLastBytes(size_t lastBytes); bool gcQuotaReached() { return gcBytes >= gcMaxBytes; } void updateMallocCounter(size_t bytes) { - gcMallocBytes -= bytes; /* We tolerate races and lost counts here. */ + /* We tolerate races and lost counts here. */ + if ((gcMallocBytes -= bytes) <= 0) + triggerGC(false); } bool mallocQuotaReached() { return gcMallocBytes <= 0; } bool overQuota() { return gcQuotaReached() || mallocQuotaReached(); @@ -2003,31 +2014,20 @@ struct JSContext #ifdef JS_THREADSAFE /* * The sweep task for this context. */ js::BackgroundSweepTask *gcSweepTask; #endif - inline void triggerGC() { - if (!runtime->gcIsNeeded) { - runtime->gcIsNeeded = true; - JS_TriggerAllOperationCallbacks(runtime); - } - } - inline void *reportIfOutOfMemory(void *p) { - if (p) { - if (runtime->mallocQuotaReached()) - triggerGC(); - return p; - } - js_ReportOutOfMemory(this); - return NULL; + if (!p) + js_ReportOutOfMemory(this); + return p; } inline void* malloc(size_t bytes) { JS_ASSERT(bytes != 0); return reportIfOutOfMemory(runtime->malloc(bytes)); } inline void* mallocNoReport(size_t bytes) { @@ -2959,24 +2959,16 @@ extern JSErrorFormatString js_ErrorForma /* * Invoke the operation callback and return false if the current execution * is to be terminated. */ extern JSBool js_InvokeOperationCallback(JSContext *cx); -#ifndef JS_THREADSAFE -# define js_TriggerAllOperationCallbacks(rt, gcLocked) \ - js_TriggerAllOperationCallbacks (rt) -#endif - -void -js_TriggerAllOperationCallbacks(JSRuntime *rt, JSBool gcLocked); - extern JSBool js_HandleExecutionInterrupt(JSContext *cx); extern JSStackFrame * js_GetScriptedCaller(JSContext *cx, JSStackFrame *fp); extern jsbytecode* js_GetCurrentBytecodePC(JSContext* cx);
--- a/js/src/jsdbgapi.cpp +++ b/js/src/jsdbgapi.cpp @@ -1670,35 +1670,16 @@ JS_SetCallHook(JSRuntime *rt, JSInterpre } if (hook) LeaveTraceRT(rt); #endif return JS_TRUE; } JS_PUBLIC_API(JSBool) -JS_SetObjectHook(JSRuntime *rt, JSObjectHook hook, void *closure) -{ -#ifdef JS_TRACER - { - AutoLockGC lock(rt); - bool wasInhibited = rt->debuggerInhibitsJIT(); -#endif - rt->globalDebugHooks.objectHook = hook; - rt->globalDebugHooks.objectHookData = closure; -#ifdef JS_TRACER - JITInhibitingHookChange(rt, wasInhibited); - } - if (hook) - LeaveTraceRT(rt); -#endif - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) JS_SetThrowHook(JSRuntime *rt, JSThrowHook hook, void *closure) { rt->globalDebugHooks.throwHook = hook; rt->globalDebugHooks.throwHookData = closure; return JS_TRUE; } JS_PUBLIC_API(JSBool)
--- a/js/src/jsdbgapi.h +++ b/js/src/jsdbgapi.h @@ -386,19 +386,16 @@ JS_SetSourceHandler(JSRuntime *rt, JSSou extern JS_PUBLIC_API(JSBool) JS_SetExecuteHook(JSRuntime *rt, JSInterpreterHook hook, void *closure); extern JS_PUBLIC_API(JSBool) JS_SetCallHook(JSRuntime *rt, JSInterpreterHook hook, void *closure); extern JS_PUBLIC_API(JSBool) -JS_SetObjectHook(JSRuntime *rt, JSObjectHook hook, void *closure); - -extern JS_PUBLIC_API(JSBool) JS_SetThrowHook(JSRuntime *rt, JSThrowHook hook, void *closure); extern JS_PUBLIC_API(JSBool) JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure); /************************************************************************/ extern JS_PUBLIC_API(size_t)
--- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -745,48 +745,50 @@ FreeGCChunks(JSRuntime *rt) } static inline size_t GetFinalizableThingSize(unsigned thingKind) { JS_STATIC_ASSERT(JS_EXTERNAL_STRING_LIMIT == 8); static const uint8 map[FINALIZE_LIMIT] = { - sizeof(JSObject), /* FINALIZE_OBJECT */ - sizeof(JSFunction), /* FINALIZE_FUNCTION */ + sizeof(JSObject), /* FINALIZE_OBJECT */ + sizeof(JSFunction), /* FINALIZE_FUNCTION */ #if JS_HAS_XML_SUPPORT - sizeof(JSXML), /* FINALIZE_XML */ + sizeof(JSXML), /* FINALIZE_XML */ #endif - sizeof(JSString), /* FINALIZE_STRING */ - sizeof(JSString), /* FINALIZE_EXTERNAL_STRING0 */ - sizeof(JSString), /* FINALIZE_EXTERNAL_STRING1 */ - sizeof(JSString), /* FINALIZE_EXTERNAL_STRING2 */ - sizeof(JSString), /* FINALIZE_EXTERNAL_STRING3 */ - sizeof(JSString), /* FINALIZE_EXTERNAL_STRING4 */ - sizeof(JSString), /* FINALIZE_EXTERNAL_STRING5 */ - sizeof(JSString), /* FINALIZE_EXTERNAL_STRING6 */ - sizeof(JSString), /* FINALIZE_EXTERNAL_STRING7 */ + sizeof(JSShortString), /* FINALIZE_SHORT_STRING */ + sizeof(JSString), /* FINALIZE_STRING */ + sizeof(JSString), /* FINALIZE_EXTERNAL_STRING0 */ + sizeof(JSString), /* FINALIZE_EXTERNAL_STRING1 */ + sizeof(JSString), /* FINALIZE_EXTERNAL_STRING2 */ + sizeof(JSString), /* FINALIZE_EXTERNAL_STRING3 */ + sizeof(JSString), /* FINALIZE_EXTERNAL_STRING4 */ + sizeof(JSString), /* FINALIZE_EXTERNAL_STRING5 */ + sizeof(JSString), /* FINALIZE_EXTERNAL_STRING6 */ + sizeof(JSString), /* FINALIZE_EXTERNAL_STRING7 */ }; JS_ASSERT(thingKind < FINALIZE_LIMIT); return map[thingKind]; } static inline size_t GetFinalizableTraceKind(size_t thingKind) { JS_STATIC_ASSERT(JS_EXTERNAL_STRING_LIMIT == 8); static const uint8 map[FINALIZE_LIMIT] = { JSTRACE_OBJECT, /* FINALIZE_OBJECT */ JSTRACE_OBJECT, /* FINALIZE_FUNCTION */ #if JS_HAS_XML_SUPPORT /* FINALIZE_XML */ JSTRACE_XML, -#endif /* FINALIZE_STRING */ - JSTRACE_STRING, +#endif + JSTRACE_STRING, /* FINALIZE_SHORT_STRING */ + JSTRACE_STRING, /* FINALIZE_STRING */ JSTRACE_STRING, /* FINALIZE_EXTERNAL_STRING0 */ JSTRACE_STRING, /* FINALIZE_EXTERNAL_STRING1 */ JSTRACE_STRING, /* FINALIZE_EXTERNAL_STRING2 */ JSTRACE_STRING, /* FINALIZE_EXTERNAL_STRING3 */ JSTRACE_STRING, /* FINALIZE_EXTERNAL_STRING4 */ JSTRACE_STRING, /* FINALIZE_EXTERNAL_STRING5 */ JSTRACE_STRING, /* FINALIZE_EXTERNAL_STRING6 */ JSTRACE_STRING, /* FINALIZE_EXTERNAL_STRING7 */ @@ -1370,16 +1372,17 @@ JS_FRIEND_API(void) js_DumpGCStats(JSRuntime *rt, FILE *fp) { static const char *const GC_ARENA_NAMES[] = { "object", "function", #if JS_HAS_XML_SUPPORT "xml", #endif + "short string", "string", "external_string_0", "external_string_1", "external_string_2", "external_string_3", "external_string_4", "external_string_5", "external_string_6", @@ -1740,17 +1743,17 @@ RefillFinalizableFreeList(JSContext *cx, } } /* * If we have to allocate a new arena, check whether are bumping * against our GC heap quota. If so, request a GC to happen soon. */ if (rt->gcQuotaReached()) - cx->triggerGC(); + cx->runtime->triggerGC(true); a = NewGCArena(cx); if (!a) return NULL; /* * Do only minimal initialization of the arena inside the GC lock. We * can do the rest outside the lock because no other threads will see @@ -2263,16 +2266,17 @@ JSWeakRoots::mark(JSTracer *trc) { #ifdef DEBUG const char * const newbornNames[] = { "newborn_object", /* FINALIZE_OBJECT */ "newborn_function", /* FINALIZE_FUNCTION */ #if JS_HAS_XML_SUPPORT "newborn_xml", /* FINALIZE_XML */ #endif + "newborn_short_string", /* FINALIZE_SHORT_STRING */ "newborn_string", /* FINALIZE_STRING */ "newborn_external_string0", /* FINALIZE_EXTERNAL_STRING0 */ "newborn_external_string1", /* FINALIZE_EXTERNAL_STRING1 */ "newborn_external_string2", /* FINALIZE_EXTERNAL_STRING2 */ "newborn_external_string3", /* FINALIZE_EXTERNAL_STRING3 */ "newborn_external_string4", /* FINALIZE_EXTERNAL_STRING4 */ "newborn_external_string5", /* FINALIZE_EXTERNAL_STRING5 */ "newborn_external_string6", /* FINALIZE_EXTERNAL_STRING6 */ @@ -2509,35 +2513,16 @@ FinalizeObject(JSContext *cx, JSObject * } inline void FinalizeFunction(JSContext *cx, JSFunction *fun, unsigned thingKind) { FinalizeObject(cx, FUN_OBJECT(fun), thingKind); } -inline void -FinalizeHookedObject(JSContext *cx, JSObject *obj, unsigned thingKind) -{ - if (!obj->map) - return; - - if (cx->debugHooks->objectHook) { - cx->debugHooks->objectHook(cx, obj, JS_FALSE, - cx->debugHooks->objectHookData); - } - FinalizeObject(cx, obj, thingKind); -} - -inline void -FinalizeHookedFunction(JSContext *cx, JSFunction *fun, unsigned thingKind) -{ - FinalizeHookedObject(cx, FUN_OBJECT(fun), thingKind); -} - #if JS_HAS_XML_SUPPORT inline void FinalizeXML(JSContext *cx, JSXML *xml, unsigned thingKind) { js_FinalizeXML(cx, xml); } #endif @@ -2555,16 +2540,25 @@ js_ChangeExternalStringFinalizer(JSStrin str_finalizers[i] = newop; return intN(i); } } return -1; } inline void +FinalizeShortString(JSContext *cx, JSShortString *str, unsigned thingKind) +{ + JS_ASSERT(FINALIZE_SHORT_STRING == thingKind); + JS_ASSERT(!JSString::isStatic(str->header())); + JS_ASSERT(str->header()->isFlat()); + JS_RUNTIME_UNMETER(cx->runtime, liveStrings); +} + +inline void FinalizeString(JSContext *cx, JSString *str, unsigned thingKind) { JS_ASSERT(FINALIZE_STRING == thingKind); JS_ASSERT(!JSString::isStatic(str)); JS_RUNTIME_UNMETER(cx->runtime, liveStrings); if (str->isDependent()) { JS_ASSERT(str->dependentBase()); JS_RUNTIME_UNMETER(cx->runtime, liveDependentStrings); @@ -2620,17 +2614,17 @@ js_FinalizeStringRT(JSRuntime *rt, JSStr JS_ASSERT(IsFinalizableStringKind(thingKind)); /* A stillborn string has null chars, so is not valid. */ jschar *chars = str->flatChars(); if (!chars) return; if (thingKind == FINALIZE_STRING) { rt->free(chars); - } else { + } else if (thingKind != FINALIZE_SHORT_STRING) { unsigned type = thingKind - FINALIZE_EXTERNAL_STRING0; JS_ASSERT(type < JS_ARRAY_LENGTH(str_finalizers)); JSStringFinalizeOp finalizer = str_finalizers[type]; if (finalizer) { /* * Assume that the finalizer for the permanently interned * string knows how to deal with null context. */ @@ -3026,40 +3020,32 @@ GC(JSContext *cx GCTIMER_PARAM) #endif /* * We finalize iterators before other objects so the iterator can use the * object which properties it enumerates over to finalize the enumeration * state. We finalize objects before string, double and other GC things * things to ensure that object's finalizer can access them even if they * will be freed. - * - * To minimize the number of checks per each to be freed object and - * function we use separated list finalizers when a debug hook is - * installed. */ JS_ASSERT(!rt->gcEmptyArenaList); - if (!cx->debugHooks->objectHook) { - FinalizeArenaList<JSObject, FinalizeObject>(cx, FINALIZE_OBJECT); - FinalizeArenaList<JSFunction, FinalizeFunction>(cx, FINALIZE_FUNCTION); - } else { - FinalizeArenaList<JSObject, FinalizeHookedObject>(cx, FINALIZE_OBJECT); - FinalizeArenaList<JSFunction, FinalizeHookedFunction>(cx, FINALIZE_FUNCTION); - } + FinalizeArenaList<JSObject, FinalizeObject>(cx, FINALIZE_OBJECT); + FinalizeArenaList<JSFunction, FinalizeFunction>(cx, FINALIZE_FUNCTION); #if JS_HAS_XML_SUPPORT FinalizeArenaList<JSXML, FinalizeXML>(cx, FINALIZE_XML); #endif TIMESTAMP(sweepObjectEnd); /* * We sweep the deflated cache before we finalize the strings so the * cache can safely use js_IsAboutToBeFinalized.. */ rt->deflatedStringCache->sweep(cx); + FinalizeArenaList<JSShortString, FinalizeShortString>(cx, FINALIZE_SHORT_STRING); FinalizeArenaList<JSString, FinalizeString>(cx, FINALIZE_STRING); for (unsigned i = FINALIZE_EXTERNAL_STRING0; i <= FINALIZE_EXTERNAL_STRING_LAST; ++i) { FinalizeArenaList<JSString, FinalizeExternalString>(cx, i); } TIMESTAMP(sweepStringEnd);
--- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -210,33 +210,34 @@ js_WaitForGC(JSRuntime *rt); * ordinary string to simplify js_GetExternalStringGCType. */ enum JSFinalizeGCThingKind { FINALIZE_OBJECT, FINALIZE_FUNCTION, #if JS_HAS_XML_SUPPORT FINALIZE_XML, #endif + FINALIZE_SHORT_STRING, FINALIZE_STRING, FINALIZE_EXTERNAL_STRING0, FINALIZE_EXTERNAL_STRING1, FINALIZE_EXTERNAL_STRING2, FINALIZE_EXTERNAL_STRING3, FINALIZE_EXTERNAL_STRING4, FINALIZE_EXTERNAL_STRING5, FINALIZE_EXTERNAL_STRING6, FINALIZE_EXTERNAL_STRING7, FINALIZE_EXTERNAL_STRING_LAST = FINALIZE_EXTERNAL_STRING7, FINALIZE_LIMIT }; static inline bool IsFinalizableStringKind(unsigned thingKind) { - return unsigned(FINALIZE_STRING) <= thingKind && + return unsigned(FINALIZE_SHORT_STRING) <= thingKind && thingKind <= unsigned(FINALIZE_EXTERNAL_STRING_LAST); } /* * Allocates a new GC thing. After a successful allocation the caller must * fully initialize the thing before calling any function that can potentially * trigger GC. This will ensure that GC tracing never sees junk values stored * in the partially initialized thing. @@ -251,16 +252,24 @@ js_NewGCObject(JSContext *cx) } static inline JSString * js_NewGCString(JSContext *cx) { return (JSString *) js_NewFinalizableGCThing(cx, FINALIZE_STRING); } +struct JSShortString; + +static inline JSShortString * +js_NewGCShortString(JSContext *cx) +{ + return (JSShortString *) js_NewFinalizableGCThing(cx, FINALIZE_SHORT_STRING); +} + static inline JSString * js_NewGCExternalString(JSContext *cx, uintN type) { JS_ASSERT(type < JS_EXTERNAL_STRING_LIMIT); type += FINALIZE_EXTERNAL_STRING0; return (JSString *) js_NewFinalizableGCThing(cx, type); }
--- a/js/src/jsnum.cpp +++ b/js/src/jsnum.cpp @@ -285,17 +285,16 @@ Class js_NumberClass = { PropertyStub, PropertyStub, PropertyStub, PropertyStub, EnumerateStub, ResolveStub, ConvertStub, NULL, JSCLASS_NO_OPTIONAL_MEMBERS }; static JSBool Number(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval) { - Value v; if (argc != 0) { if (!ValueToNumber(cx, &argv[0])) return JS_FALSE; } else { argv[0].setInt32(0); } if (!JS_IsConstructing(cx)) *rval = argv[0]; @@ -318,17 +317,17 @@ num_toSource(JSContext *cx, uintN argc, double d = primp->toNumber(); numStr = js_dtostr(JS_THREAD_DATA(cx)->dtoaState, numBuf, sizeof numBuf, DTOSTR_STANDARD, 0, d); if (!numStr) { JS_ReportOutOfMemory(cx); return JS_FALSE; } JS_snprintf(buf, sizeof buf, "(new %s(%s))", js_NumberClass.name, numStr); - str = JS_NewStringCopyZ(cx, buf); + str = js_NewStringCopyZ(cx, buf); if (!str) return JS_FALSE; vp->setString(str); return JS_TRUE; } #endif /* The buf must be big enough for MIN_INT to fit including '-' and '\0'. */ @@ -568,17 +567,17 @@ num_to(JSContext *cx, JSDToStrMode zeroA } numStr = js_dtostr(JS_THREAD_DATA(cx)->dtoaState, buf, sizeof buf, oneArgMode, (jsint)precision + precisionOffset, d); if (!numStr) { JS_ReportOutOfMemory(cx); return JS_FALSE; } - JSString *str = JS_NewStringCopyZ(cx, numStr); + JSString *str = js_NewStringCopyZ(cx, numStr); if (!str) return JS_FALSE; vp->setString(str); return JS_TRUE; } /* * In the following three implementations, we allow a larger range of precision @@ -803,17 +802,17 @@ NumberToCString(JSContext *cx, jsdouble JSString * JS_FASTCALL js_IntToString(JSContext *cx, jsint i) { if (jsuint(i) < INT_STRING_LIMIT) return JSString::intString(i); char buf[12]; - return JS_NewStringCopyZ(cx, IntToCString(i, 10, buf, sizeof buf)); + return js_NewStringCopyZ(cx, IntToCString(i, 10, buf, sizeof buf)); } static JSString * JS_FASTCALL js_NumberToStringWithBase(JSContext *cx, jsdouble d, jsint base) { /* * The longest possible result here that would need to fit in buf is * (-0x80000000).toString(2), which has length 33. (This can produce @@ -843,17 +842,17 @@ js_NumberToStringWithBase(JSContext *cx, } } JSThreadData *data = JS_THREAD_DATA(cx); if (data->dtoaCache.s && data->dtoaCache.base == base && data->dtoaCache.d == d) return data->dtoaCache.s; numStr = NumberToCString(cx, d, base, buf, sizeof buf); if (!numStr) return NULL; - s = JS_NewStringCopyZ(cx, numStr); + s = js_NewStringCopyZ(cx, numStr); if (!(numStr >= buf && numStr < buf + sizeof buf)) js_free(numStr); data->dtoaCache.base = base; data->dtoaCache.d = d; data->dtoaCache.s = s; return s; }
--- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -219,17 +219,16 @@ MarkSharpObjects(JSContext *cx, JSObject ida = JS_Enumerate(cx, obj); if (!ida) return NULL; ok = JS_TRUE; for (i = 0, length = ida->length; i < length; i++) { id = ida->vector[i]; - js::Value val; ok = obj->lookupProperty(cx, id, &obj2, &prop); if (!ok) break; if (!prop) continue; bool hasGetter, hasSetter; AutoValueRooter v(cx); AutoValueRooter setter(cx);
--- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -554,28 +554,16 @@ NewNativeClassInstance(JSContext *cx, Cl JS_ASSERT(scope->canProvideEmptyScope(&js_ObjectOps, clasp)); scope = scope->getEmptyScope(cx, clasp); JS_UNLOCK_OBJ(cx, proto); if (!scope) { obj = NULL; } else { obj->map = scope; - - /* - * Do not call debug hooks on trace, because we might be in a non-_FAIL - * builtin. See bug 481444. - */ - if (cx->debugHooks->objectHook && !JS_ON_TRACE(cx)) { - AutoObjectRooter tvr(cx, obj); - AutoKeepAtoms keep(cx->runtime); - cx->debugHooks->objectHook(cx, obj, JS_TRUE, - cx->debugHooks->objectHookData); - cx->weakRoots.finalizableNewborns[FINALIZE_OBJECT] = obj; - } } } objectCreationScope.handleCreation(obj); return obj; } bool @@ -670,28 +658,16 @@ NewObjectWithGivenProto(JSContext *cx, C obj = NULL; goto out; } } else { JS_ASSERT(ops->objectMap->ops == ops); obj->map = const_cast<JSObjectMap *>(ops->objectMap); } - /* - * Do not call debug hooks on trace, because we might be in a non-_FAIL - * builtin. See bug 481444. - */ - if (cx->debugHooks->objectHook && !JS_ON_TRACE(cx)) { - AutoObjectRooter tvr(cx, obj); - AutoKeepAtoms keep(cx->runtime); - cx->debugHooks->objectHook(cx, obj, JS_TRUE, - cx->debugHooks->objectHookData); - cx->weakRoots.finalizableNewborns[FINALIZE_OBJECT] = obj; - } - out: objectCreationScope.handleCreation(obj); return obj; } static inline JSProtoKey GetClassProtoKey(js::Class *clasp) {
--- a/js/src/jsprvtd.h +++ b/js/src/jsprvtd.h @@ -258,19 +258,16 @@ typedef void * * Returning NULL in the 'before' hook will cause the 'after' hook *not* to * be called. */ typedef void * (* JSInterpreterHook)(JSContext *cx, JSStackFrame *fp, JSBool before, JSBool *ok, void *closure); -typedef void -(* JSObjectHook)(JSContext *cx, JSObject *obj, JSBool isNew, void *closure); - typedef JSBool (* JSDebugErrorHook)(JSContext *cx, const char *message, JSErrorReport *report, void *closure); typedef struct JSDebugHooks { JSInterruptHook interruptHook; void *interruptHookData; JSNewScriptHook newScriptHook; @@ -280,18 +277,16 @@ typedef struct JSDebugHooks { JSDebuggerHandler debuggerHandler; void *debuggerHandlerData; JSSourceHandler sourceHandler; void *sourceHandlerData; JSInterpreterHook executeHook; void *executeHookData; JSInterpreterHook callHook; void *callHookData; - JSObjectHook objectHook; - void *objectHookData; JSThrowHook throwHook; void *throwHookData; JSDebugErrorHook debugErrorHook; void *debugErrorHookData; } JSDebugHooks; /* JSObjectOps function pointer typedefs. */
--- a/js/src/jsscope.cpp +++ b/js/src/jsscope.cpp @@ -81,17 +81,17 @@ js_GenerateShape(JSContext *cx, bool gcL /* * FIXME bug 440834: The shape id space has overflowed. Currently we * cope badly with this and schedule the GC on the every call. But * first we make sure that increments from other threads would not * have a chance to wrap around shapeGen to zero. */ rt->shapeGen = SHAPE_OVERFLOW_BIT; shape = SHAPE_OVERFLOW_BIT; - cx->triggerGC(); + cx->runtime->triggerGC(gcLocked); } return shape; } JSScope * js_GetMutableScope(JSContext *cx, JSObject *obj) { JSScope *scope = obj->scope();
--- a/js/src/jsscope.h +++ b/js/src/jsscope.h @@ -893,20 +893,16 @@ JSScope::insertDictionaryProperty(JSScop */ #define SPROP_USERID(sprop) \ ((sprop)->hasShortID() ? INT_TO_JSID((sprop)->shortid) \ : (sprop)->id) #define SLOT_IN_SCOPE(slot,scope) ((slot) < (scope)->freeslot) #define SPROP_HAS_VALID_SLOT(sprop,scope) SLOT_IN_SCOPE((sprop)->slot, scope) -#ifndef JS_THREADSAFE -# define js_GenerateShape(cx, gcLocked) js_GenerateShape (cx) -#endif - extern uint32 js_GenerateShape(JSContext *cx, bool gcLocked); #ifdef DEBUG struct JSScopeStats { jsrefcount searches; jsrefcount hits; jsrefcount misses;
--- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -78,58 +78,19 @@ #include "jscntxtinlines.h" #include "jsobjinlines.h" #include "jsstrinlines.h" #include "jscntxtinlines.h" using namespace js; -#define JSSTRDEP_RECURSION_LIMIT 100 - JS_STATIC_ASSERT(size_t(JSString::MAX_LENGTH) <= size_t(JSVAL_INT_MAX)); JS_STATIC_ASSERT(JSString::MAX_LENGTH <= JSVAL_INT_MAX); -static size_t -MinimizeDependentStrings(JSString *str, int level, JSString **basep) -{ - JSString *base; - size_t start, length; - - JS_ASSERT(str->isDependent()); - base = str->dependentBase(); - start = str->dependentStart(); - if (base->isDependent()) { - if (level < JSSTRDEP_RECURSION_LIMIT) { - start += MinimizeDependentStrings(base, level + 1, &base); - } else { - do { - start += base->dependentStart(); - base = base->dependentBase(); - } while (base->isDependent()); - } - length = str->dependentLength(); - str->initDependent(base, start, length); - } - *basep = base; - return start; -} - -jschar * -js_GetDependentStringChars(JSString *str) -{ - size_t start; - JSString *base; - - start = MinimizeDependentStrings(str, 0, &base); - JS_ASSERT(base->isFlat()); - JS_ASSERT(start < base->flatLength()); - return base->flatChars() + start; -} - const jschar * js_GetStringChars(JSContext *cx, JSString *str) { if (!js_MakeStringImmutable(cx, str)) return NULL; return str->flatChars(); } @@ -174,17 +135,17 @@ JSString::flatten() case 0: next = str->ropeLeft(); /* * We know the "offset" field for the new dependent string now, but * not later, so store it early. We have to be careful with this: * mLeft is replaced by mOffset. */ - str->startTraversalConversion(pos); + str->startTraversalConversion(chars, pos); str->ropeIncrementTraversalCount(); if (next->isInteriorNode()) { str = next; } else { js_strncpy(chars + pos, next->chars(), next->length()); pos += next->length(); } break; @@ -196,17 +157,17 @@ JSString::flatten() } else { js_strncpy(chars + pos, next->chars(), next->length()); pos += next->length(); } break; case 2: next = str->interiorNodeParent(); /* Make the string a dependent string dependent with the right fields. */ - str->finishTraversalConversion(topNode, pos); + str->finishTraversalConversion(topNode, chars, pos); str = next; break; default: JS_NOT_REACHED("bad traversal count"); } } JS_ASSERT(pos == length); @@ -301,16 +262,28 @@ js_ConcatStrings(JSContext *cx, JSString if (leftLen == 0) return right; rightLen = right->length(); if (rightLen == 0) return left; length = leftLen + rightLen; + if (JSShortString::fitsIntoShortString(length)) { + JSShortString *shortStr = js_NewGCShortString(cx); + if (!shortStr) + return NULL; + + jschar *buf = shortStr->init(length); + js_short_strncpy(buf, left->chars(), leftLen); + js_short_strncpy(buf + leftLen, right->chars(), rightLen); + buf[length] = 0; + return shortStr->header(); + } + /* * We need to enforce a tree structure in ropes: every node needs to have a * unique parent. So, we can't have the left or right child be in the middle * of a rope tree. One potential solution is to traverse the subtree for the * argument string and create a new flat string, but that would add * complexity and is a rare case, so we simply flatten the entire rope that * contains it. The case where left and right are part of the same rope is * handled implicitly. @@ -330,17 +303,17 @@ js_ConcatStrings(JSContext *cx, JSString */ jschar *chars = left->chars(); js_strncpy(chars + leftLen, right->chars(), rightLen); chars[length] = 0; JSString *res = js_NewString(cx, chars, length); if (!res) return NULL; res->initFlatMutable(chars, length, left->flatCapacity()); - left->initDependent(res, 0, leftLen); + left->initDependent(res, res->flatChars(), leftLen); return res; } if (length > JSString::MAX_LENGTH) { if (JS_ON_TRACE(cx)) { if (!CanLeaveTrace(cx)) return NULL; LeaveTrace(cx); @@ -1170,17 +1143,16 @@ str_charAt(JSContext *cx, uintN argc, Va out_of_range: vp->setString(cx->runtime->emptyString); return JS_TRUE; } static JSBool str_charCodeAt(JSContext *cx, uintN argc, Value *vp) { - Value t; JSString *str; jsint i; jsdouble d; if (vp[1].isString() && argc != 0 && vp[2].isInt32()) { str = vp[1].toString(); i = vp[2].toInt32(); if ((size_t)i >= str->length()) @@ -2987,19 +2959,19 @@ static const jschar UnitStringData[] = { C(0xd0), C(0xd1), C(0xd2), C(0xd3), C(0xd4), C(0xd5), C(0xd6), C(0xd7), C(0xd8), C(0xd9), C(0xda), C(0xdb), C(0xdc), C(0xdd), C(0xde), C(0xdf), C(0xe0), C(0xe1), C(0xe2), C(0xe3), C(0xe4), C(0xe5), C(0xe6), C(0xe7), C(0xe8), C(0xe9), C(0xea), C(0xeb), C(0xec), C(0xed), C(0xee), C(0xef), C(0xf0), C(0xf1), C(0xf2), C(0xf3), C(0xf4), C(0xf5), C(0xf6), C(0xf7), C(0xf8), C(0xf9), C(0xfa), C(0xfb), C(0xfc), C(0xfd), C(0xfe), C(0xff) }; -#define U(c) { {0}, {0}, \ +#define U(c) { \ JSString::FLAT | JSString::ATOMIZED | (1 << JSString::FLAGS_LENGTH_SHIFT), \ - {(jschar *)UnitStringData + (c) * 2} } + {(jschar *)UnitStringData + (c) * 2}, {0}, {0} } #ifdef __SUNPRO_CC #pragma pack(8) #else #pragma pack(push, 8) #endif JSString JSString::unitStringTable[] @@ -3096,27 +3068,27 @@ static const jschar Hundreds[] = { O20(0x30), O20(0x31), O20(0x32), O20(0x33), O20(0x34), O20(0x35), O20(0x36), O20(0x37), O20(0x38), O20(0x39), O21(0x30), O21(0x31), O21(0x32), O21(0x33), O21(0x34), O21(0x35), O21(0x36), O21(0x37), O21(0x38), O21(0x39), O22(0x30), O22(0x31), O22(0x32), O22(0x33), O22(0x34), O22(0x35), O22(0x36), O22(0x37), O22(0x38), O22(0x39), O23(0x30), O23(0x31), O23(0x32), O23(0x33), O23(0x34), O23(0x35), O23(0x36), O23(0x37), O23(0x38), O23(0x39), O24(0x30), O24(0x31), O24(0x32), O24(0x33), O24(0x34), O24(0x35), O24(0x36), O24(0x37), O24(0x38), O24(0x39), O25(0x30), O25(0x31), O25(0x32), O25(0x33), O25(0x34), O25(0x35) }; -#define L1(c) { {0}, {0}, \ +#define L1(c) { \ JSString::FLAT | JSString::ATOMIZED | (1 << JSString::FLAGS_LENGTH_SHIFT), \ - {(jschar *)Hundreds + 2 + (c) * 4} } /* length 1: 0..9 */ - -#define L2(c) { {0}, {0}, \ + {(jschar *)Hundreds + 2 + (c) * 4}, {0}, {0} } /* length 1: 0..9 */ + +#define L2(c) { \ JSString::FLAT | JSString::ATOMIZED | (2 << JSString::FLAGS_LENGTH_SHIFT), \ - {(jschar *)Hundreds + 41 + (c - 10) * 4} } /* length 2: 10..99 */ + {(jschar *)Hundreds + 41 + (c - 10) * 4}, {0}, {0} } /* length 2: 10..99 */ -#define L3(c) { {0}, {0}, \ +#define L3(c) { \ JSString::FLAT | JSString::ATOMIZED | (3 << JSString::FLAGS_LENGTH_SHIFT), \ - {(jschar *)Hundreds + (c - 100) * 4} } /* length 3: 100..255 */ + {(jschar *)Hundreds + (c - 100) * 4}, {0}, {0} } /* length 3: 100..255 */ #ifdef __SUNPRO_CC #pragma pack(8) #else #pragma pack(push, 8) #endif JSString JSString::intStringTable[] @@ -3474,26 +3446,28 @@ js_NewDependentString(JSContext *cx, JSS JSString *ds; if (length == 0) return cx->runtime->emptyString; if (start == 0 && length == base->length()) return base; - /* - * To avoid weird recursion cases, we're not allowed to have a string - * depend on a rope. - */ - base->ensureNotRope(); + jschar *chars = base->chars() + start; + + /* Try to avoid long chains of dependent strings. */ + while (base->isDependent()) + base = base->dependentBase(); + + JS_ASSERT(base->isFlat()); ds = js_NewGCString(cx); if (!ds) return NULL; - ds->initDependent(base, start, length); + ds->initDependent(base, chars, length); #ifdef DEBUG { JSRuntime *rt = cx->runtime; JS_RUNTIME_METER(rt, liveDependentStrings); JS_RUNTIME_METER(rt, totalDependentStrings); JS_RUNTIME_METER(rt, liveStrings); JS_RUNTIME_METER(rt, totalStrings); JS_LOCK_RUNTIME_VOID(rt, @@ -3524,51 +3498,113 @@ void printJSStringStats(JSRuntime *rt) rt->strdepLengthSquaredSum, &sigma); fprintf(stderr, "%lu total dependent strings, mean length %g (sigma %g)\n", (unsigned long)rt->totalDependentStrings, mean, sigma); } #endif JSString * +NewShortString(JSContext *cx, const jschar *chars, size_t length) +{ + JS_ASSERT(JSShortString::fitsIntoShortString(length)); + JSShortString *str = js_NewGCShortString(cx); + if (!str) + return NULL; + jschar *storage = str->init(length); + js_short_strncpy(storage, chars, length); + storage[length] = 0; + return str->header(); +} + +JSString * +NewShortString(JSContext *cx, const char *chars, size_t length) +{ + JS_ASSERT(JSShortString::fitsIntoShortString(length)); + JSShortString *str = js_NewGCShortString(cx); + if (!str) + return NULL; + jschar *storage = str->init(length); + + if (js_CStringsAreUTF8) { +#ifdef DEBUG + size_t oldLength = length; +#endif + if (!js_InflateStringToBuffer(cx, chars, length, storage, &length)) + return NULL; + JS_ASSERT(length <= oldLength); + storage[length] = 0; + str->resetLength(length); + } else { + size_t n = length; + jschar *p = storage; + while (n--) + *p++ = jschar(*chars++); + *p = 0; + } + return str->header(); +} + +JSString * js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n) { + if (JSShortString::fitsIntoShortString(n)) + return NewShortString(cx, s, n); + jschar *news; JSString *str; news = (jschar *) cx->malloc((n + 1) * sizeof(jschar)); if (!news) return NULL; js_strncpy(news, s, n); news[n] = 0; str = js_NewString(cx, news, n); if (!str) cx->free(news); return str; } JSString * +js_NewStringCopyN(JSContext *cx, const char *s, size_t n) +{ + if (JSShortString::fitsIntoShortString(n)) + return NewShortString(cx, s, n); + return JS_NewStringCopyN(cx, s, n); +} + +JSString * js_NewStringCopyZ(JSContext *cx, const jschar *s) { size_t n, m; jschar *news; JSString *str; n = js_strlen(s); + + if (JSShortString::fitsIntoShortString(n)) + return NewShortString(cx, s, n); + m = (n + 1) * sizeof(jschar); news = (jschar *) cx->malloc(m); if (!news) return NULL; memcpy(news, s, m); str = js_NewString(cx, news, n); if (!str) cx->free(news); return str; } +JSString * +js_NewStringCopyZ(JSContext *cx, const char *s) +{ + return js_NewStringCopyN(cx, s, strlen(s)); +} + JS_FRIEND_API(const char *) js_ValueToPrintable(JSContext *cx, const Value &v, JSValueToStringFun v2sfun) { JSString *str; str = v2sfun(cx, v); if (!str) return NULL;
--- a/js/src/jsstr.h +++ b/js/src/jsstr.h @@ -114,28 +114,28 @@ struct JSRopeBufferInfo { struct JSString { friend class js::TraceRecorder; friend JSAtom * js_AtomizeString(JSContext *cx, JSString *str, uintN flags); // Not private because we want to be able to use static // initializers for them. Don't use these directly! + size_t mLengthAndFlags; /* in all strings */ union { - size_t mCapacity; /* in flat strings (optional) */ - JSString *mParent; /* in rope interior nodes */ - JSRopeBufferInfo *mBufferWithInfo; /* in rope top nodes */ + jschar *mChars; /* in flat and dependent strings */ + JSString *mLeft; /* in rope interior and top nodes */ }; union { - size_t mOffset; /* in dependent strings */ - JSString *mLeft; /* in rope interior and top nodes */ + size_t mCapacity; /* in mutable flat strings (optional) */ + JSString *mParent; /* in rope interior nodes */ + JSRopeBufferInfo *mBufferWithInfo; /* in rope top nodes */ + jschar mInlineStorage[1]; /* In short strings. */ }; - size_t mLengthAndFlags; /* in all strings */ union { - jschar *mChars; /* in flat strings */ JSString *mBase; /* in dependent strings */ JSString *mRight; /* in rope interior and top nodes */ }; /* * The mLengthAndFlags field in string headers has data arranged in the * following way: * @@ -208,30 +208,19 @@ struct JSString { return type() == INTERIOR_NODE; } inline bool isTopNode() const { return type() == TOP_NODE; } JS_ALWAYS_INLINE jschar *chars() { - return isFlat() ? flatChars() : nonFlatChars(); - } - - JS_ALWAYS_INLINE jschar *nonFlatChars() { - if (isDependent()) - return dependentChars(); - else { + if (JS_UNLIKELY(isRope())) flatten(); - JS_ASSERT(isFlat() || isDependent()); - if (isFlat()) - return flatChars(); - else - return dependentChars(); - } + return mChars; } JS_ALWAYS_INLINE size_t length() const { return mLengthAndFlags >> FLAGS_LENGTH_SHIFT; } JS_ALWAYS_INLINE bool empty() const { return length() == 0; @@ -241,28 +230,33 @@ struct JSString { chars = this->chars(); length = this->length(); } JS_ALWAYS_INLINE void getCharsAndEnd(const jschar *&chars, const jschar *&end) { end = length() + (chars = this->chars()); } + JS_ALWAYS_INLINE jschar *inlineStorage() { + JS_ASSERT(isFlat()); + return mInlineStorage; + } + /* Specific flat string initializer and accessor methods. */ JS_ALWAYS_INLINE void initFlat(jschar *chars, size_t length) { JS_ASSERT(length <= MAX_LENGTH); - mOffset = 0; + mBase = NULL; mCapacity = 0; mLengthAndFlags = (length << FLAGS_LENGTH_SHIFT) | FLAT; mChars = chars; } JS_ALWAYS_INLINE void initFlatMutable(jschar *chars, size_t length, size_t cap) { JS_ASSERT(length <= MAX_LENGTH); - mOffset = 0; + mBase = NULL; mCapacity = cap; mLengthAndFlags = (length << FLAGS_LENGTH_SHIFT) | FLAT | MUTABLE; mChars = chars; } JS_ALWAYS_INLINE jschar *flatChars() const { JS_ASSERT(isFlat()); return mChars; @@ -316,37 +310,35 @@ struct JSString { mLengthAndFlags |= MUTABLE; } inline void flatClearMutable() { JS_ASSERT(isFlat()); mLengthAndFlags &= ~MUTABLE; } - inline void initDependent(JSString *bstr, size_t off, size_t len) { + /* + * The chars pointer should point somewhere inside the buffer owned by bstr. + * The caller still needs to pass bstr for GC purposes. + */ + inline void initDependent(JSString *bstr, jschar *chars, size_t len) { JS_ASSERT(len <= MAX_LENGTH); mParent = NULL; - mOffset = off; + mChars = chars; mLengthAndFlags = DEPENDENT | (len << FLAGS_LENGTH_SHIFT); mBase = bstr; } inline JSString *dependentBase() const { JS_ASSERT(isDependent()); return mBase; } JS_ALWAYS_INLINE jschar *dependentChars() { - return dependentBase()->isFlat() - ? dependentBase()->flatChars() + dependentStart() - : js_GetDependentStringChars(this); - } - - inline size_t dependentStart() const { - return mOffset; + return mChars; } inline size_t dependentLength() const { JS_ASSERT(isDependent()); return length(); } /* Rope-related initializers and accessors. */ @@ -390,32 +382,33 @@ struct JSString { return mBufferWithInfo; } inline void nullifyTopNodeBuffer() { JS_ASSERT(isTopNode()); mBufferWithInfo = NULL; } - /* + /* * When flattening a rope, we need to convert a rope node to a dependent * string in two separate parts instead of calling initDependent. */ - inline void startTraversalConversion(size_t offset) { + inline void startTraversalConversion(jschar *chars, size_t offset) { JS_ASSERT(isInteriorNode()); - mOffset = offset; + mChars = chars + offset; } - inline void finishTraversalConversion(JSString *base, size_t end) { + inline void finishTraversalConversion(JSString *base, jschar *chars, + size_t end) { JS_ASSERT(isInteriorNode()); /* Note that setting flags also clears the traversal count. */ mLengthAndFlags = JSString::DEPENDENT | - ((end - mOffset) << JSString::FLAGS_LENGTH_SHIFT); + ((chars + end - mChars) << JSString::FLAGS_LENGTH_SHIFT); mBase = base; - } + } inline void ropeClearTraversalCount() { JS_ASSERT(isRope()); mLengthAndFlags &= ~ROPE_TRAVERSAL_COUNT_MASK; } inline size_t ropeTraversalCount() const { JS_ASSERT(isRope()); @@ -478,16 +471,63 @@ struct JSString { static const char deflatedUnitStringTable[]; static JSString *unitString(jschar c); static JSString *getUnitString(JSContext *cx, JSString *str, size_t index); static JSString *intString(jsint i); }; /* + * Short strings should be created in cases where it's worthwhile to avoid + * mallocing the string buffer for a small string. We keep 2 string headers' + * worth of space in short strings so that more strings can be stored this way. + */ +struct JSShortString { + JSString mHeader; + JSString mDummy; + + /* + * Set the length of the string, and return a buffer for the caller to write + * to. This buffer must be written immediately, and should not be modified + * afterward. + */ + inline jschar *init(size_t length) { + JS_ASSERT(length <= MAX_SHORT_STRING_LENGTH); + mHeader.initFlat(mHeader.inlineStorage(), length); + return mHeader.inlineStorage(); + } + + inline void resetLength(size_t length) { + mHeader.initFlat(mHeader.flatChars(), length); + } + + inline JSString *header() { + return &mHeader; + } + + static const size_t MAX_SHORT_STRING_LENGTH = + ((sizeof(JSString) + 2 * sizeof(size_t)) / sizeof(jschar)) - 1; + + static inline bool fitsIntoShortString(size_t length) { + return length <= MAX_SHORT_STRING_LENGTH; + } +}; + +/* + * We're doing some tricks to give us more space for short strings, so make + * sure that space is ordered in the way we expect. + */ +JS_STATIC_ASSERT(offsetof(JSString, mInlineStorage) == 2 * sizeof(void *)); +JS_STATIC_ASSERT(offsetof(JSString, mBase) == 3 * sizeof(void *)); +JS_STATIC_ASSERT(offsetof(JSShortString, mDummy) == sizeof(JSString)); +JS_STATIC_ASSERT(offsetof(JSString, mInlineStorage) + + sizeof(jschar) * (JSShortString::MAX_SHORT_STRING_LENGTH + 1) == + sizeof(JSShortString)); + +/* * An iterator that iterates through all nodes in a rope (the top node, the * interior nodes, and the leaves) without writing to any of the nodes. * * It is safe to iterate through a rope in this way, even when something else is * already iterating through it. * * To use, pass any node of the rope into the constructor. The first call should * be to init, which returns the first node, and each subsequent call should @@ -823,20 +863,26 @@ js_NewStringFromCharBuffer(JSContext *cx extern JSString * js_NewDependentString(JSContext *cx, JSString *base, size_t start, size_t length); /* Copy a counted string and GC-allocate a descriptor for it. */ extern JSString * js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n); +extern JSString * +js_NewStringCopyN(JSContext *cx, const char *s, size_t n); + /* Copy a C string and GC-allocate a descriptor for it. */ extern JSString * js_NewStringCopyZ(JSContext *cx, const jschar *s); +extern JSString * +js_NewStringCopyZ(JSContext *cx, const char *s); + /* * Convert a value to a printable C string. */ typedef JSString *(*JSValueToStringFun)(JSContext *cx, const js::Value &v); extern JS_FRIEND_API(const char *) js_ValueToPrintable(JSContext *cx, const js::Value &, JSValueToStringFun v2sfun); @@ -909,16 +955,42 @@ js_strlen(const jschar *s); extern jschar * js_strchr(const jschar *s, jschar c); extern jschar * js_strchr_limit(const jschar *s, jschar c, const jschar *limit); #define js_strncpy(t, s, n) memcpy((t), (s), (n) * sizeof(jschar)) +inline void +js_short_strncpy(jschar *dest, const jschar *src, size_t num) +{ + /* + * It isn't strictly necessary here for |num| to be small, but this function + * is currently only called on buffers for short strings. + */ + JS_ASSERT(JSShortString::fitsIntoShortString(num)); + switch (num) { + case 1: + *dest = *src; + break; + case 2: + JS_ASSERT(sizeof(uint32) == 2 * sizeof(jschar)); + *(uint32 *)dest = *(uint32 *)src; + break; + case 4: + JS_ASSERT(sizeof(uint64) == 4 * sizeof(jschar)); + *(uint64 *)dest = *(uint64 *)src; + break; + default: + for (size_t i = 0; i < num; i++) + dest[i] = src[i]; + } +} + /* * Return s advanced past any Unicode white space characters. */ static inline const jschar * js_SkipWhiteSpace(const jschar *s, const jschar *end) { JS_ASSERT(s <= end); while (s != end && JS_ISSPACE(*s))
--- a/js/src/jsval.h +++ b/js/src/jsval.h @@ -122,17 +122,17 @@ JS_ENUM_HEADER(JSValueTag, uint32) JSVAL_TAG_CLEAR = 0xFFFF0000, JSVAL_TAG_INT32 = JSVAL_TAG_CLEAR | JSVAL_TYPE_INT32, JSVAL_TAG_UNDEFINED = JSVAL_TAG_CLEAR | JSVAL_TYPE_UNDEFINED, JSVAL_TAG_STRING = JSVAL_TAG_CLEAR | JSVAL_TYPE_STRING, JSVAL_TAG_BOOLEAN = JSVAL_TAG_CLEAR | JSVAL_TYPE_BOOLEAN, JSVAL_TAG_MAGIC = JSVAL_TAG_CLEAR | JSVAL_TYPE_MAGIC, JSVAL_TAG_NULL = JSVAL_TAG_CLEAR | JSVAL_TYPE_NULL, JSVAL_TAG_OBJECT = JSVAL_TAG_CLEAR | JSVAL_TYPE_OBJECT -} JS_ENUM_FOOTER(JSValueType); +} JS_ENUM_FOOTER(JSValueTag); JS_STATIC_ASSERT(sizeof(JSValueTag) == 4); #elif JS_BITS_PER_WORD == 64 /* Remember to propagate changes to the C defines below. */ JS_ENUM_HEADER(JSValueTag, uint32) { @@ -286,20 +286,23 @@ typedef union jsval_layout JSValueTag tag; } s; double asDouble; } jsval_layout; # elif JS_BITS_PER_WORD == 64 typedef union jsval_layout { uint64 asBits; +#ifndef _MSC_VER + /* MSVC does not pack these correctly :-( */ struct { uint64 payload47 : 47; JSValueTag tag : 17; } debugView; +#endif struct { union { int32 i32; uint32 u32; JSWhyMagic why; } payload; } s; double asDouble; @@ -691,22 +694,16 @@ static JS_ALWAYS_INLINE void * JSVAL_TO_PRIVATE_PTR_IMPL(jsval_layout l) { JS_ASSERT((l.asBits & 0x8000000000000000LL) == 0); return (void *)(l.asBits << 1); } #endif -static JS_ALWAYS_INLINE JSBool -JSVAL_IS_UNDERLYING_TYPE_OF_PRIVATE_IMPL(jsval_layout l) -{ - return JSVAL_IS_DOUBLE_IMPL(l); -} - /* See JS_USE_JSVAL_JSID_STRUCT_TYPES comment in jsapi.h. */ #if defined(DEBUG) && !defined(JS_NO_JSVAL_JSID_STRUCT_TYPES) # define JS_USE_JSVAL_JSID_STRUCT_TYPES #endif #ifdef JS_USE_JSVAL_JSID_STRUCT_TYPES typedef JSVAL_ALIGNMENT jsval_layout jsval;
--- a/js/src/jsvalue.h +++ b/js/src/jsvalue.h @@ -310,20 +310,20 @@ UNBOX_NON_DOUBLE_JSVAL(jsval_layout l, u /******************************************************************************/ namespace js { class Value { public: - /*** Constructors ***/ - - /* N.B. the default constructor creates a double. */ - Value() { data.asBits = 0; } + /* + * N.B. the default constructor leaves Value unitialized. Adding a default + * constructor prevents Value from being stored in a union. + */ /*** Mutatators ***/ void setNull() { data.asBits = JSVAL_BITS(JSVAL_NULL); } void setUndefined() { @@ -597,35 +597,31 @@ class Value * Private API * * Private setters/getters allow the caller to read/write arbitrary types * that fit in the 64-bit payload. It is the caller's responsibility, after * storing to a value with setPrivateX to only read with getPrivateX. * Privates values are given a type type which ensures they are not marked. */ - bool isUnderlyingTypeOfPrivate() const { - return JSVAL_IS_UNDERLYING_TYPE_OF_PRIVATE_IMPL(data); - } - void setPrivate(void *ptr) { data = PRIVATE_PTR_TO_JSVAL_IMPL(ptr); } void *toPrivate() const { - JS_ASSERT(JSVAL_IS_UNDERLYING_TYPE_OF_PRIVATE_IMPL(data)); + JS_ASSERT(JSVAL_IS_DOUBLE_IMPL(data)); return JSVAL_TO_PRIVATE_PTR_IMPL(data); } void setPrivateUint32(uint32 ui) { data = PRIVATE_UINT32_TO_JSVAL_IMPL(ui); } uint32 toPrivateUint32() const { - JS_ASSERT(JSVAL_IS_UNDERLYING_TYPE_OF_PRIVATE_IMPL(data)); + JS_ASSERT(JSVAL_IS_DOUBLE_IMPL(data)); return JSVAL_TO_PRIVATE_UINT32_IMPL(data); } uint32 &getPrivateUint32Ref() { JS_ASSERT(isDouble()); return data.s.payload.u32; }
--- a/js/src/jsvector.h +++ b/js/src/jsvector.h @@ -406,36 +406,36 @@ class Vector : AllocPolicy /* Helper functions */ /* * This helper function is specialized for appending the characters of a string * literal to a vector. This could not be done generically since one must take * care not to append the terminating '\0'. */ template <class T, size_t N, class AP, size_t ArrayLength> -bool +JS_ALWAYS_INLINE bool js_AppendLiteral(Vector<T,N,AP> &v, const char (&array)[ArrayLength]) { return v.append(array, array + ArrayLength - 1); } /* Vector Implementation */ template <class T, size_t N, class AllocPolicy> -inline +JS_ALWAYS_INLINE Vector<T,N,AllocPolicy>::Vector(AllocPolicy ap) : AllocPolicy(ap), mLengthOrCapacity(0) #ifdef DEBUG , entered(false) #endif {} template <class T, size_t N, class AP> -inline +JS_ALWAYS_INLINE Vector<T,N,AP>::~Vector() { ReentrancyGuard g(*this); if (usingInlineStorage()) { Impl::destroy(inlineBegin(), inlineEnd()); } else { Impl::destroy(heapBegin(), heapEnd()); this->free(heapBegin()); @@ -477,17 +477,17 @@ Vector<T,N,AP>::calculateNewCapacity(siz return true; } /* * This function will grow the current heap capacity to have capacity * (heapLength() + lengthInc) and fail on OOM or integer overflow. */ template <class T, size_t N, class AP> -inline bool +JS_ALWAYS_INLINE bool Vector<T,N,AP>::growHeapStorageBy(size_t lengthInc) { size_t newCap; return calculateNewCapacity(heapLength(), lengthInc, newCap) && Impl::growTo(*this, newCap); } /* @@ -583,24 +583,24 @@ Vector<T,N,AP>::growByImpl(size_t incr) T *newend = heapEnd() + incr; if (InitNewElems) Impl::initialize(heapEnd(), newend); heapEnd() = newend; return true; } template <class T, size_t N, class AP> -inline bool +JS_ALWAYS_INLINE bool Vector<T,N,AP>::growBy(size_t incr) { return growByImpl<true>(incr); } template <class T, size_t N, class AP> -inline bool +JS_ALWAYS_INLINE bool Vector<T,N,AP>::growByUninitialized(size_t incr) { return growByImpl<false>(incr); } template <class T, size_t N, class AP> inline bool Vector<T,N,AP>::resize(size_t newLength) @@ -623,17 +623,17 @@ Vector<T,N,AP>::clear() } else { Impl::destroy(heapBegin(), heapEnd()); heapEnd() = heapBegin(); } } template <class T, size_t N, class AP> -inline bool +JS_ALWAYS_INLINE bool Vector<T,N,AP>::append(const T &t) { ReentrancyGuard g(*this); if (usingInlineStorage()) { if (inlineLength() < sInlineCapacity) { new(inlineEnd()) T(t); ++inlineLength(); JS_ASSERT(usingInlineStorage()); @@ -648,17 +648,17 @@ Vector<T,N,AP>::append(const T &t) /* We are !usingInlineStorage(). Initialize new elements. */ JS_ASSERT(heapLength() <= heapCapacity() && heapCapacity() - heapLength() >= 1); new(heapEnd()++) T(t); return true; } template <class T, size_t N, class AP> -inline bool +JS_ALWAYS_INLINE bool Vector<T,N,AP>::appendN(const T &t, size_t needed) { ReentrancyGuard g(*this); if (usingInlineStorage()) { size_t freespace = sInlineCapacity - inlineLength(); if (needed <= freespace) { Impl::copyConstructN(inlineEnd(), needed, t); inlineLength() += needed; @@ -677,17 +677,17 @@ Vector<T,N,AP>::appendN(const T &t, size JS_ASSERT(heapLength() <= heapCapacity() && heapCapacity() - heapLength() >= needed); Impl::copyConstructN(heapEnd(), needed, t); heapEnd() += needed; return true; } template <class T, size_t N, class AP> template <class U> -inline bool +JS_ALWAYS_INLINE bool Vector<T,N,AP>::append(const U *insBegin, const U *insEnd) { ReentrancyGuard g(*this); size_t needed = PointerRangeSize(insBegin, insEnd); if (usingInlineStorage()) { size_t freespace = sInlineCapacity - inlineLength(); if (needed <= freespace) { Impl::copyConstruct(inlineEnd(), insBegin, insEnd); @@ -707,24 +707,24 @@ Vector<T,N,AP>::append(const U *insBegin JS_ASSERT(heapLength() <= heapCapacity() && heapCapacity() - heapLength() >= needed); Impl::copyConstruct(heapEnd(), insBegin, insEnd); heapEnd() += needed; return true; } template <class T, size_t N, class AP> template <class U> -inline bool +JS_ALWAYS_INLINE bool Vector<T,N,AP>::append(const U *insBegin, size_t length) { return this->append(insBegin, insBegin + length); } template <class T, size_t N, class AP> -inline void +JS_ALWAYS_INLINE void Vector<T,N,AP>::popBack() { ReentrancyGuard g(*this); JS_ASSERT(!empty()); if (usingInlineStorage()) { --inlineLength(); inlineEnd()->~T(); } else {
--- a/js/src/jsxml.cpp +++ b/js/src/jsxml.cpp @@ -7250,54 +7250,41 @@ js_EscapeAttributeValue(JSContext *cx, J JSCharBuffer cb(cx); return EscapeAttributeValue(cx, cb, str, quote); } JSString * js_AddAttributePart(JSContext *cx, JSBool isName, JSString *str, JSString *str2) { size_t len, len2, newlen; - jschar *chars; - const jschar *chars2; - - str->getCharsAndLength(const_cast<const jschar *&>(chars), len); - if (!str->isMutable()) { - str = js_NewStringCopyN(cx, chars, len); - if (!str) - return NULL; - chars = str->chars(); - } else { - /* - * Reallocating str (because we know it has no other references) - * requires purging any deflated string cached for it. - */ - cx->runtime->deflatedStringCache->remove(str); - } - + const jschar *chars, *chars2; + jschar *newchars; + + str->getCharsAndLength(chars, len); str2->getCharsAndLength(chars2, len2); newlen = (isName) ? len + 1 + len2 : len + 2 + len2 + 1; - chars = (jschar *) cx->realloc(chars, (newlen+1) * sizeof(jschar)); - if (!chars) + newchars = (jschar *) cx->malloc((newlen+1) * sizeof(jschar)); + if (!newchars) return NULL; - str->initFlat(chars, newlen); - chars += len; + js_strncpy(newchars, chars, len); + newchars += len; if (isName) { - *chars++ = ' '; - js_strncpy(chars, chars2, len2); - chars += len2; + *newchars++ = ' '; + js_strncpy(newchars, chars2, len2); + newchars += len2; } else { - *chars++ = '='; - *chars++ = '"'; - js_strncpy(chars, chars2, len2); - chars += len2; - *chars++ = '"'; - } - *chars = 0; - return str; + *newchars++ = '='; + *newchars++ = '"'; + js_strncpy(newchars, chars2, len2); + newchars += len2; + *newchars++ = '"'; + } + *newchars = 0; + return js_NewString(cx, newchars - newlen, newlen); } JSString * js_EscapeElementValue(JSContext *cx, JSString *str) { JSCharBuffer cb(cx); return EscapeElementValue(cx, cb, str, 0); }
--- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -483,17 +483,16 @@ stubs::GetElem(VMFrame &f) Value rval = regs.sp[-1]; const Value *copyFrom; JSObject *obj; jsid id; int i; if (lval.isString() && rval.isInt32()) { - Value retval; JSString *str = lval.toString(); i = rval.toInt32(); if ((size_t)i >= str->length()) THROW(); str = JSString::getUnitString(cx, str, (size_t)i); if (!str)
--- a/js/src/xpconnect/src/XPCDispObject.cpp +++ b/js/src/xpconnect/src/XPCDispObject.cpp @@ -395,22 +395,22 @@ JSBool XPCDispObject::Invoke(XPCCallCont } static JSBool GetMember(XPCCallContext& ccx, JSObject* funobj, XPCNativeInterface*& iface, XPCDispInterface::Member*& member) { jsval val; if(!JS_GetReservedSlot(ccx, funobj, 1, &val)) return JS_FALSE; - if(!JSVAL_IS_UNDERLYING_TYPE_OF_PRIVATE(val)) + if(JSVAL_IS_VOID(val)) return JS_FALSE; iface = reinterpret_cast<XPCNativeInterface*>(JSVAL_TO_PRIVATE(val)); if(!JS_GetReservedSlot(ccx, funobj, 0, &val)) return JS_FALSE; - if(!JSVAL_IS_UNDERLYING_TYPE_OF_PRIVATE(val)) + if(JSVAL_IS_VOID(val)) return JS_FALSE; member = reinterpret_cast<XPCDispInterface::Member*>(JSVAL_TO_PRIVATE(val)); return JS_TRUE; } // Handy macro used in callbacks below. #define THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper) \ PR_BEGIN_MACRO \
--- a/js/src/xpconnect/src/xpcwrappednativeinfo.cpp +++ b/js/src/xpconnect/src/xpcwrappednativeinfo.cpp @@ -92,19 +92,19 @@ XPCNativeMember::GetCallInfo(XPCCallCont JSObject* funobj, XPCNativeInterface** pInterface, XPCNativeMember** pMember) { jsval ifaceVal; jsval memberVal; if(!JS_GetReservedSlot(ccx, funobj, 0, &ifaceVal) || + JSVAL_IS_VOID(ifaceVal) || !JS_GetReservedSlot(ccx, funobj, 1, &memberVal) || - !JSVAL_IS_UNDERLYING_TYPE_OF_PRIVATE(ifaceVal) || - !JSVAL_IS_UNDERLYING_TYPE_OF_PRIVATE(memberVal)) + JSVAL_IS_VOID(memberVal)) { return JS_FALSE; } *pInterface = (XPCNativeInterface*) JSVAL_TO_PRIVATE(ifaceVal); *pMember = (XPCNativeMember*) JSVAL_TO_PRIVATE(memberVal); return JS_TRUE;