diff --git a/caps/src/nsScriptSecurityManager.cpp b/caps/src/nsScriptSecurityManager.cpp index 9b08c196190..fd486326f4a 100644 --- a/caps/src/nsScriptSecurityManager.cpp +++ b/caps/src/nsScriptSecurityManager.cpp @@ -114,7 +114,7 @@ static inline const PRUnichar * IDToString(JSContext *cx, jsid id) { if (JSID_IS_STRING(id)) - return reinterpret_cast(JS_GetStringChars(JSID_TO_STRING(id))); + return JS_GetInternedStringChars(JSID_TO_STRING(id)); JSAutoRequest ar(cx); jsval idval; @@ -123,7 +123,7 @@ IDToString(JSContext *cx, jsid id) JSString *str = JS_ValueToString(cx, idval); if(!str) return nsnull; - return reinterpret_cast(JS_GetStringChars(str)); + return JS_GetStringCharsZ(cx, str); } class nsAutoInPrincipalDomainOriginSetter { diff --git a/caps/src/nsSecurityManagerFactory.cpp b/caps/src/nsSecurityManagerFactory.cpp index e0713946831..8a466b41812 100644 --- a/caps/src/nsSecurityManagerFactory.cpp +++ b/caps/src/nsSecurityManagerFactory.cpp @@ -95,9 +95,10 @@ static void getUTF8StringArgument(JSContext *cx, JSObject *obj, PRUint16 argNum, uintN argc, jsval *argv, nsCString& aRetval) { + aRetval.Truncate(); + if (argc <= argNum || !JSVAL_IS_STRING(argv[argNum])) { JS_ReportError(cx, "String argument expected"); - aRetval.Truncate(); return; } @@ -106,12 +107,13 @@ getUTF8StringArgument(JSContext *cx, JSObject *obj, PRUint16 argNum, * to have an object to represent a target in subsequent versions. */ JSString *str = JSVAL_TO_STRING(argv[argNum]); - if (!str) { - aRetval.Truncate(); + if (!str) + return; + + const PRUnichar *data = JS_GetStringCharsZ(cx, str); + if (!data) return; - } - PRUnichar *data = (PRUnichar*)JS_GetStringChars(str); CopyUTF16toUTF8(data, aRetval); } diff --git a/content/base/src/nsFrameMessageManager.cpp b/content/base/src/nsFrameMessageManager.cpp index 8cb2f58ae35..df59b6fb97a 100644 --- a/content/base/src/nsFrameMessageManager.cpp +++ b/content/base/src/nsFrameMessageManager.cpp @@ -208,7 +208,11 @@ nsFrameMessageManager::GetParamsForMessage(nsAString& aMessageName, JSAutoRequest ar(ctx); JSString* str; if (argc && (str = JS_ValueToString(ctx, argv[0])) && str) { - aMessageName.Assign(nsDependentJSString(str)); + nsDependentJSString depStr; + if (!depStr.init(ctx, str)) { + return NS_ERROR_OUT_OF_MEMORY; + } + aMessageName.Assign(depStr); } if (argc >= 2) { diff --git a/content/base/src/nsWebSocket.cpp b/content/base/src/nsWebSocket.cpp index 95077d664a0..efbfa21486d 100644 --- a/content/base/src/nsWebSocket.cpp +++ b/content/base/src/nsWebSocket.cpp @@ -2957,17 +2957,27 @@ nsWebSocket::Initialize(nsISupports* aOwner, if (!jsstr) { return NS_ERROR_DOM_SYNTAX_ERR; } - urlParam.Assign(reinterpret_cast(JS_GetStringChars(jsstr)), - JS_GetStringLength(jsstr)); + + size_t length; + const jschar *chars = JS_GetStringCharsAndLength(aContext, jsstr, &length); + if (!chars) { + return NS_ERROR_OUT_OF_MEMORY; + } + + urlParam.Assign(chars, length); if (aArgc == 2) { jsstr = JS_ValueToString(aContext, aArgv[1]); if (!jsstr) { return NS_ERROR_DOM_SYNTAX_ERR; } - protocolParam. - Assign(reinterpret_cast(JS_GetStringChars(jsstr)), - JS_GetStringLength(jsstr)); + + chars = JS_GetStringCharsAndLength(aContext, jsstr, &length); + if (!chars) { + return NS_ERROR_OUT_OF_MEMORY; + } + + protocolParam.Assign(chars, length); if (protocolParam.IsEmpty()) { return NS_ERROR_DOM_SYNTAX_ERR; } diff --git a/content/events/src/nsEventListenerService.cpp b/content/events/src/nsEventListenerService.cpp index 49abd9d9bce..2da2de6bf34 100644 --- a/content/events/src/nsEventListenerService.cpp +++ b/content/events/src/nsEventListenerService.cpp @@ -140,7 +140,10 @@ nsEventListenerInfo::ToSource(nsAString& aResult) if (GetJSVal(&v)) { JSString* str = JS_ValueToSource(cx, v); if (str) { - aResult.Assign(nsDependentJSString(str)); + nsDependentJSString depStr; + if (depStr.init(cx, str)) { + aResult.Assign(depStr); + } } } } diff --git a/content/html/content/src/nsHTMLAudioElement.cpp b/content/html/content/src/nsHTMLAudioElement.cpp index 250354ec1e8..2cec15d8749 100644 --- a/content/html/content/src/nsHTMLAudioElement.cpp +++ b/content/html/content/src/nsHTMLAudioElement.cpp @@ -140,7 +140,10 @@ nsHTMLAudioElement::Initialize(nsISupports* aOwner, JSContext* aContext, if (!jsstr) return NS_ERROR_FAILURE; - nsDependentJSString str(jsstr); + nsDependentJSString str; + if (!str.init(aContext, jsstr)) + return NS_ERROR_FAILURE; + rv = SetAttr(kNameSpaceID_None, nsGkAtoms::src, str, PR_TRUE); if (NS_FAILED(rv)) return rv; diff --git a/content/html/content/src/nsHTMLCanvasElement.cpp b/content/html/content/src/nsHTMLCanvasElement.cpp index 51e02dd2d89..f06a0abc7c7 100644 --- a/content/html/content/src/nsHTMLCanvasElement.cpp +++ b/content/html/content/src/nsHTMLCanvasElement.cpp @@ -46,6 +46,7 @@ #include "nsIScriptSecurityManager.h" #include "nsIXPConnect.h" #include "jsapi.h" +#include "nsJSUtils.h" #include "nsFrameManager.h" #include "nsDisplayList.h" @@ -502,8 +503,11 @@ nsHTMLCanvasElement::GetContext(const nsAString& aContextId, } JSString *propnameString = JS_ValueToString(cx, propname); - - nsDependentString pstr(JS_GetStringChars(propnameString), JS_GetStringLength(propnameString)); + nsDependentJSString pstr; + if (!propnameString || !pstr.init(cx, propnameString)) { + mCurrentContext = nsnull; + return NS_ERROR_FAILURE; + } if (JSVAL_IS_BOOLEAN(propval)) { newProps->SetPropertyAsBool(pstr, propval == JSVAL_TRUE ? PR_TRUE : PR_FALSE); @@ -512,8 +516,14 @@ nsHTMLCanvasElement::GetContext(const nsAString& aContextId, } else if (JSVAL_IS_DOUBLE(propval)) { newProps->SetPropertyAsDouble(pstr, JSVAL_TO_DOUBLE(propval)); } else if (JSVAL_IS_STRING(propval)) { - newProps->SetPropertyAsAString(pstr, nsDependentString(JS_GetStringChars(JS_ValueToString(cx, propval)), - JS_GetStringLength(JS_ValueToString(cx, propval)))); + JSString *propvalString = JS_ValueToString(cx, propval); + nsDependentJSString vstr; + if (!propvalString || !vstr.init(cx, propvalString)) { + mCurrentContext = nsnull; + return NS_ERROR_FAILURE; + } + + newProps->SetPropertyAsAString(pstr, vstr); } } } diff --git a/content/html/content/src/nsHTMLOptionElement.cpp b/content/html/content/src/nsHTMLOptionElement.cpp index fcd63ec43f0..8532461b554 100644 --- a/content/html/content/src/nsHTMLOptionElement.cpp +++ b/content/html/content/src/nsHTMLOptionElement.cpp @@ -412,10 +412,13 @@ nsHTMLOptionElement::Initialize(nsISupports* aOwner, return result; } - textContent->SetText(reinterpret_cast - (JS_GetStringChars(jsstr)), - JS_GetStringLength(jsstr), - PR_FALSE); + size_t length; + const jschar *chars = JS_GetStringCharsAndLength(aContext, jsstr, &length); + if (!chars) { + return NS_ERROR_FAILURE; + } + + textContent->SetText(chars, length, PR_FALSE); result = AppendChildTo(textContent, PR_FALSE); if (NS_FAILED(result)) { @@ -429,9 +432,14 @@ nsHTMLOptionElement::Initialize(nsISupports* aOwner, return NS_ERROR_FAILURE; } + size_t length; + const jschar *chars = JS_GetStringCharsAndLength(aContext, jsstr, &length); + if (!chars) { + return NS_ERROR_FAILURE; + } + // Set the value attribute for this element - nsAutoString value(reinterpret_cast - (JS_GetStringChars(jsstr))); + nsAutoString value(chars, length); result = SetAttr(kNameSpaceID_None, nsGkAtoms::value, value, PR_FALSE); diff --git a/content/xbl/src/nsXBLBinding.cpp b/content/xbl/src/nsXBLBinding.cpp index 55b975afaf5..8da7d5c5b72 100644 --- a/content/xbl/src/nsXBLBinding.cpp +++ b/content/xbl/src/nsXBLBinding.cpp @@ -1094,78 +1094,85 @@ nsXBLBinding::ChangeDocument(nsIDocument* aOldDocument, nsIDocument* aNewDocumen nsCxPusher pusher; pusher.Push(cx); - nsCOMPtr wrapper; - jsval v; + nsCOMPtr wrapper; + nsIXPConnect *xpc = nsContentUtils::XPConnect(); nsresult rv = - nsContentUtils::WrapNative(cx, scope, mBoundElement, &v, - getter_AddRefs(wrapper)); + xpc->GetWrappedNativeOfNativeObject(cx, scope, mBoundElement, + NS_GET_IID(nsISupports), + getter_AddRefs(wrapper)); if (NS_FAILED(rv)) return; - JSObject* scriptObject = JSVAL_TO_OBJECT(v); + JSObject* scriptObject; + if (wrapper) + wrapper->GetJSObject(&scriptObject); + else + scriptObject = nsnull; - // XXX Stay in sync! What if a layered binding has an - // ?! - // XXXbz what does that comment mean, really? It seems to date - // back to when there was such a thing as an , whever - // that was... + if (scriptObject) { + // XXX Stay in sync! What if a layered binding has an + // ?! + // XXXbz what does that comment mean, really? It seems to date + // back to when there was such a thing as an , whever + // that was... - // Find the right prototype. - JSObject* base = scriptObject; - JSObject* proto; - JSAutoRequest ar(cx); - JSAutoEnterCompartment ac; - if (!ac.enter(cx, scriptObject)) { - return; - } + // Find the right prototype. + JSObject* base = scriptObject; + JSObject* proto; + JSAutoRequest ar(cx); + JSAutoEnterCompartment ac; + if (!ac.enter(cx, scriptObject)) { + return; + } - for ( ; true; base = proto) { // Will break out on null proto - proto = ::JS_GetPrototype(cx, base); - if (!proto) { + for ( ; true; base = proto) { // Will break out on null proto + proto = ::JS_GetPrototype(cx, base); + if (!proto) { + break; + } + + JSClass* clazz = ::JS_GET_CLASS(cx, proto); + if (!clazz || + (~clazz->flags & + (JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS)) || + JSCLASS_RESERVED_SLOTS(clazz) != 1 || + clazz->resolve != (JSResolveOp)XBLResolve || + clazz->finalize != XBLFinalize) { + // Clearly not the right class + continue; + } + + nsRefPtr docInfo = + static_cast(::JS_GetPrivate(cx, proto)); + if (!docInfo) { + // Not the proto we seek + continue; + } + + jsval protoBinding; + if (!::JS_GetReservedSlot(cx, proto, 0, &protoBinding)) { + NS_ERROR("Really shouldn't happen"); + continue; + } + + if (JSVAL_TO_PRIVATE(protoBinding) != mPrototypeBinding) { + // Not the right binding + continue; + } + + // Alright! This is the right prototype. Pull it out of the + // proto chain. + JSObject* grandProto = ::JS_GetPrototype(cx, proto); + ::JS_SetPrototype(cx, base, grandProto); break; } - JSClass* clazz = ::JS_GET_CLASS(cx, proto); - if (!clazz || - (~clazz->flags & - (JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS)) || - JSCLASS_RESERVED_SLOTS(clazz) != 1 || - clazz->resolve != (JSResolveOp)XBLResolve || - clazz->finalize != XBLFinalize) { - // Clearly not the right class - continue; - } + mPrototypeBinding->UndefineFields(cx, scriptObject); - nsRefPtr docInfo = - static_cast(::JS_GetPrivate(cx, proto)); - if (!docInfo) { - // Not the proto we seek - continue; - } - - jsval protoBinding; - if (!::JS_GetReservedSlot(cx, proto, 0, &protoBinding)) { - NS_ERROR("Really shouldn't happen"); - continue; - } - - if (JSVAL_TO_PRIVATE(protoBinding) != mPrototypeBinding) { - // Not the right binding - continue; - } - - // Alright! This is the right prototype. Pull it out of the - // proto chain. - JSObject* grandProto = ::JS_GetPrototype(cx, proto); - ::JS_SetPrototype(cx, base, grandProto); - break; + // Don't remove the reference from the document to the + // wrapper here since it'll be removed by the element + // itself when that's taken out of the document. } - - mPrototypeBinding->UndefineFields(cx, scriptObject); - - // Don't remove the reference from the document to the - // wrapper here since it'll be removed by the element - // itself when that's taken out of the document. } } } diff --git a/content/xslt/src/xslt/Makefile.in b/content/xslt/src/xslt/Makefile.in index 9ef202a2d0c..42041a5003e 100644 --- a/content/xslt/src/xslt/Makefile.in +++ b/content/xslt/src/xslt/Makefile.in @@ -89,6 +89,11 @@ CPPSRCS += txMozillaStylesheetCompiler.cpp \ txMozillaXSLTProcessor.cpp endif +# For nsDependentJSString +LOCAL_INCLUDES += \ + -I$(topsrcdir)/dom/base \ + $(NULL) + # we don't want the shared lib, but we want to force the creation of a # static lib. FORCE_STATIC_LIB = 1 diff --git a/content/xslt/src/xslt/txMozillaXSLTProcessor.cpp b/content/xslt/src/xslt/txMozillaXSLTProcessor.cpp index 3bfb05fc4c1..d77a9d29f67 100644 --- a/content/xslt/src/xslt/txMozillaXSLTProcessor.cpp +++ b/content/xslt/src/xslt/txMozillaXSLTProcessor.cpp @@ -67,6 +67,7 @@ #include "txExprParser.h" #include "nsIErrorService.h" #include "nsIScriptSecurityManager.h" +#include "nsJSUtils.h" using namespace mozilla::dom; @@ -1469,10 +1470,8 @@ txVariable::Convert(nsIVariant *aValue, txAExprResult** aResult) JSString *str = JS_ValueToString(cx, OBJECT_TO_JSVAL(jsobj)); NS_ENSURE_TRUE(str, NS_ERROR_FAILURE); - const PRUnichar *strChars = - reinterpret_cast - (::JS_GetStringChars(str)); - nsDependentString value(strChars, ::JS_GetStringLength(str)); + nsDependentJSString value; + NS_ENSURE_TRUE(value.init(cx, str), NS_ERROR_FAILURE); *aResult = new StringResult(value, nsnull); if (!*aResult) { diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index cee18010110..b20ecd4fd04 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -5086,7 +5086,6 @@ nsWindowSH::GlobalScopePolluterNewResolve(JSContext *cx, JSObject *obj, } JSObject *proto = ::JS_GetPrototype(cx, obj); - JSString *jsstr = JSID_TO_STRING(id); JSBool hasProp; if (!proto || !::JS_HasPropertyById(cx, proto, id, &hasProp) || @@ -5097,7 +5096,7 @@ nsWindowSH::GlobalScopePolluterNewResolve(JSContext *cx, JSObject *obj, return JS_TRUE; } - nsDependentJSString str(jsstr); + nsDependentJSString str(id); nsCOMPtr result; nsWrapperCache *cache; { @@ -5436,7 +5435,10 @@ nsWindowSH::SetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, vp, getter_AddRefs(holder)); NS_ENSURE_SUCCESS(rv, rv); - rv = location->SetHref(nsDependentJSString(val)); + nsDependentJSString depStr; + NS_ENSURE_TRUE(depStr.init(cx, val), NS_ERROR_UNEXPECTED); + + rv = location->SetHref(depStr); return NS_FAILED(rv) ? rv : NS_SUCCESS_I_DID_SOMETHING; } @@ -6291,14 +6293,14 @@ ResolvePrototype(nsIXPConnect *aXPConnect, nsGlobalWindow *aWin, JSContext *cx, // static nsresult nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx, - JSObject *obj, JSString *str, PRBool *did_resolve) + JSObject *obj, jsid id, PRBool *did_resolve) { *did_resolve = PR_FALSE; nsScriptNameSpaceManager *nameSpaceManager = nsJSRuntime::GetNameSpaceManager(); NS_ENSURE_TRUE(nameSpaceManager, NS_ERROR_NOT_INITIALIZED); - nsDependentJSString name(str); + nsDependentJSString name(id); const nsGlobalNameStruct *name_struct = nsnull; const PRUnichar *class_name = nsnull; @@ -6515,10 +6517,8 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx, NS_ENSURE_SUCCESS(rv, rv); - JSBool ok = ::JS_DefineUCProperty(cx, obj, ::JS_GetStringChars(str), - ::JS_GetStringLength(str), - prop_val, nsnull, nsnull, - JSPROP_ENUMERATE); + JSBool ok = ::JS_DefinePropertyById(cx, obj, id, prop_val, nsnull, nsnull, + JSPROP_ENUMERATE); *did_resolve = PR_TRUE; @@ -6677,8 +6677,6 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, // method on an interface that would let us just call into the // window code directly... - JSString *str = JSID_TO_STRING(id); - if (!xpc::WrapperFactory::IsXrayWrapper(obj) || xpc::WrapperFactory::IsPartiallyTransparent(obj)) { nsCOMPtr dsn(do_QueryInterface(win->GetDocShell())); @@ -6692,7 +6690,7 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, if (count > 0) { nsCOMPtr child; - const jschar *chars = ::JS_GetStringChars(str); + const jschar *chars = ::JS_GetInternedStringChars(JSID_TO_STRING(id)); dsn->FindChildWithName(reinterpret_cast(chars), PR_FALSE, PR_TRUE, nsnull, nsnull, @@ -6741,7 +6739,7 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, // which have been registered with the script namespace manager. JSBool did_resolve = JS_FALSE; - rv = GlobalResolve(win, cx, obj, str, &did_resolve); + rv = GlobalResolve(win, cx, obj, id, &did_resolve); NS_ENSURE_SUCCESS(rv, rv); if (did_resolve) { @@ -8414,7 +8412,10 @@ nsDocumentSH::SetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSString *val = ::JS_ValueToString(cx, *vp); NS_ENSURE_TRUE(val, NS_ERROR_UNEXPECTED); - rv = location->SetHref(nsDependentJSString(val)); + nsDependentJSString depStr; + NS_ENSURE_TRUE(depStr.init(cx, val), NS_ERROR_UNEXPECTED); + + rv = location->SetHref(depStr); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr holder; @@ -8503,7 +8504,10 @@ ResolveImpl(JSContext *cx, nsIXPConnectWrappedNative *wrapper, jsid id, JSString *str = IdToString(cx, id); NS_ENSURE_TRUE(str, NS_ERROR_UNEXPECTED); - return doc->ResolveName(nsDependentJSString(str), nsnull, result, aCache); + nsDependentJSString depStr; + NS_ENSURE_TRUE(depStr.init(cx, str), NS_ERROR_UNEXPECTED); + + return doc->ResolveName(depStr, nsnull, result, aCache); } // static @@ -8541,8 +8545,13 @@ nsHTMLDocumentSH::DocumentOpen(JSContext *cx, uintN argc, jsval *vp) nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_OUT_OF_MEMORY); return JS_FALSE; } + nsDependentJSString depStr; + if (!depStr.init(cx, jsstr)) { + nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_OUT_OF_MEMORY); + return JS_FALSE; + } nsAutoString type; - type.Assign(nsDependentJSString(jsstr)); + type.Assign(depStr); ToLowerCase(type); nsCAutoString actualType, dummy; NS_ParseContentType(NS_ConvertUTF16toUTF8(type), actualType, dummy); @@ -8560,9 +8569,13 @@ nsHTMLDocumentSH::DocumentOpen(JSContext *cx, uintN argc, jsval *vp) return JS_FALSE; } - replace = NS_LITERAL_STRING("replace"). - Equals(reinterpret_cast - (::JS_GetStringChars(jsstr))); + const jschar *chars = ::JS_GetStringCharsZ(cx, jsstr); + if (!chars) { + nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_OUT_OF_MEMORY); + return JS_FALSE; + } + + replace = NS_LITERAL_STRING("replace").Equals(chars); } nsCOMPtr retval; @@ -8883,8 +8896,13 @@ nsHTMLDocumentSH::CallToGetPropMapper(JSContext *cx, uintN argc, jsval *vp) self = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)); } - return ::JS_GetUCProperty(cx, self, ::JS_GetStringChars(str), - ::JS_GetStringLength(str), vp); + size_t length; + const jschar *chars = ::JS_GetStringCharsAndLength(cx, str, &length); + if (!chars) { + return JS_FALSE; + } + + return ::JS_GetUCProperty(cx, self, chars, length, vp); } @@ -9004,8 +9022,6 @@ nsHTMLDocumentSH::DocumentAllTagsNewResolve(JSContext *cx, JSObject *obj, if (JSID_IS_STRING(id)) { nsDocument *doc = GetDocument(cx, obj); - JSString *str = JSID_TO_STRING(id); - JSObject *proto = ::JS_GetPrototype(cx, obj); if (NS_UNLIKELY(!proto)) { return JS_TRUE; @@ -9021,7 +9037,7 @@ nsHTMLDocumentSH::DocumentAllTagsNewResolve(JSContext *cx, JSObject *obj, } nsRefPtr tags = - doc->GetElementsByTagName(nsDependentJSString(str)); + doc->GetElementsByTagName(nsDependentJSString(id)); if (tags) { jsval v; @@ -9193,11 +9209,11 @@ nsHTMLDocumentSH::GetProperty(nsIXPConnectWrappedNative *wrapper, // static nsresult -nsHTMLFormElementSH::FindNamedItem(nsIForm *aForm, JSString *str, +nsHTMLFormElementSH::FindNamedItem(nsIForm *aForm, jsid id, nsISupports **aResult, nsWrapperCache **aCache) { - nsDependentJSString name(str); + nsDependentJSString name(id); *aResult = aForm->ResolveName(name).get(); // FIXME Get the wrapper cache from nsIForm::ResolveName @@ -9231,8 +9247,7 @@ nsHTMLFormElementSH::NewResolve(nsIXPConnectWrappedNative *wrapper, nsCOMPtr result; nsWrapperCache *cache; - JSString *str = JSID_TO_STRING(id); - FindNamedItem(form, str, getter_AddRefs(result), &cache); + FindNamedItem(form, id, getter_AddRefs(result), &cache); if (result) { JSAutoRequest ar(cx); @@ -9262,8 +9277,7 @@ nsHTMLFormElementSH::GetProperty(nsIXPConnectWrappedNative *wrapper, nsCOMPtr result; nsWrapperCache *cache; - JSString *str = JSID_TO_STRING(id); - FindNamedItem(form, str, getter_AddRefs(result), &cache); + FindNamedItem(form, id, getter_AddRefs(result), &cache); if (result) { // Wrap result, result can be either an element or a list of @@ -10207,11 +10221,14 @@ nsStorageSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, if (!jsstr) return JS_FALSE; + nsDependentJSString depStr; + if (!depStr.init(cx, jsstr)) + return JS_FALSE; + // GetItem() will return null if the caller can't access the session // storage item. nsCOMPtr item; - nsresult rv = storage->GetItem(nsDependentJSString(jsstr), - getter_AddRefs(item)); + nsresult rv = storage->GetItem(depStr, getter_AddRefs(item)); NS_ENSURE_SUCCESS(rv, rv); if (item) { @@ -10246,11 +10263,16 @@ nsStorageSH::SetProperty(nsIXPConnectWrappedNative *wrapper, JSString *key = IdToString(cx, id); NS_ENSURE_TRUE(key, NS_ERROR_UNEXPECTED); + nsDependentJSString keyStr; + NS_ENSURE_TRUE(keyStr.init(cx, key), NS_ERROR_UNEXPECTED); + JSString *value = ::JS_ValueToString(cx, *vp); NS_ENSURE_TRUE(value, NS_ERROR_UNEXPECTED); - nsresult rv = storage->SetItem(nsDependentJSString(key), - nsDependentJSString(value)); + nsDependentJSString valueStr; + NS_ENSURE_TRUE(valueStr.init(cx, value), NS_ERROR_UNEXPECTED); + + nsresult rv = storage->SetItem(keyStr, valueStr); if (NS_SUCCEEDED(rv)) { rv = NS_SUCCESS_I_DID_SOMETHING; } @@ -10269,7 +10291,10 @@ nsStorageSH::DelProperty(nsIXPConnectWrappedNative *wrapper, JSString *key = IdToString(cx, id); NS_ENSURE_TRUE(key, NS_ERROR_UNEXPECTED); - nsresult rv = storage->RemoveItem(nsDependentJSString(key)); + nsDependentJSString keyStr; + NS_ENSURE_TRUE(keyStr.init(cx, key), NS_ERROR_UNEXPECTED); + + nsresult rv = storage->RemoveItem(keyStr); if (NS_SUCCEEDED(rv)) { rv = NS_SUCCESS_I_DID_SOMETHING; } @@ -10368,10 +10393,13 @@ nsStorage2SH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, nsCOMPtr storage(do_QueryWrappedNative(wrapper)); + nsDependentJSString depStr; + NS_ENSURE_TRUE(depStr.init(cx, jsstr), NS_ERROR_UNEXPECTED); + // GetItem() will return null if the caller can't access the session // storage item. nsAutoString data; - nsresult rv = storage->GetItem(nsDependentJSString(jsstr), data); + nsresult rv = storage->GetItem(depStr, data); NS_ENSURE_SUCCESS(rv, rv); if (!DOMStringIsNull(data)) { @@ -10440,11 +10468,16 @@ nsStorage2SH::SetProperty(nsIXPConnectWrappedNative *wrapper, JSString *key = IdToString(cx, id); NS_ENSURE_TRUE(key, NS_ERROR_UNEXPECTED); + nsDependentJSString keyStr; + NS_ENSURE_TRUE(keyStr.init(cx, key), NS_ERROR_UNEXPECTED); + JSString *value = ::JS_ValueToString(cx, *vp); NS_ENSURE_TRUE(value, NS_ERROR_UNEXPECTED); - nsresult rv = storage->SetItem(nsDependentJSString(key), - nsDependentJSString(value)); + nsDependentJSString valueStr; + NS_ENSURE_TRUE(valueStr.init(cx, value), NS_ERROR_UNEXPECTED); + + nsresult rv = storage->SetItem(keyStr, valueStr); if (NS_SUCCEEDED(rv)) { rv = NS_SUCCESS_I_DID_SOMETHING; } @@ -10463,7 +10496,10 @@ nsStorage2SH::DelProperty(nsIXPConnectWrappedNative *wrapper, JSString *key = IdToString(cx, id); NS_ENSURE_TRUE(key, NS_ERROR_UNEXPECTED); - nsresult rv = storage->RemoveItem(nsDependentJSString(key)); + nsDependentJSString keyStr; + NS_ENSURE_TRUE(keyStr.init(cx, key), NS_ERROR_UNEXPECTED); + + nsresult rv = storage->RemoveItem(keyStr); if (NS_SUCCEEDED(rv)) { rv = NS_SUCCESS_I_DID_SOMETHING; } diff --git a/dom/base/nsDOMClassInfo.h b/dom/base/nsDOMClassInfo.h index 09aa5eb2450..c5e78d99eb4 100644 --- a/dom/base/nsDOMClassInfo.h +++ b/dom/base/nsDOMClassInfo.h @@ -463,7 +463,7 @@ protected: { NS_ASSERTION(JSID_IS_STRING(id), "Don't pass non-string jsid's here!"); - jschar *str = ::JS_GetStringChars(JSID_TO_STRING(id)); + const jschar *str = ::JS_GetInternedStringChars(JSID_TO_STRING(id)); if (str[0] == 'o' && str[1] == 'n') { return ReallyIsEventName(id, str[2]); @@ -528,8 +528,7 @@ protected: } static nsresult GlobalResolve(nsGlobalWindow *aWin, JSContext *cx, - JSObject *obj, JSString *str, - PRBool *did_resolve); + JSObject *obj, jsid id, PRBool *did_resolve); public: NS_IMETHOD PreCreate(nsISupports *nativeObj, JSContext *cx, @@ -1046,7 +1045,7 @@ protected: { } - static nsresult FindNamedItem(nsIForm *aForm, JSString *str, + static nsresult FindNamedItem(nsIForm *aForm, jsid id, nsISupports **aResult, nsWrapperCache **aCache); public: diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index fad235cfc4e..f78aa411945 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -724,8 +724,13 @@ static JSBool ChangeCase(JSContext *cx, JSString *src, jsval *rval, void(* changeCaseFnc)(const nsAString&, nsAString&)) { + nsDependentJSString depStr; + if (!depStr.init(cx, src)) { + return JS_FALSE; + } + nsAutoString result; - changeCaseFnc(nsDependentJSString(src), result); + changeCaseFnc(depStr, result); JSString *ucstr = JS_NewUCStringCopyN(cx, (jschar*)result.get(), result.Length()); if (!ucstr) { @@ -779,11 +784,14 @@ LocaleCompare(JSContext *cx, JSString *src1, JSString *src2, jsval *rval) } } + nsDependentJSString depStr1, depStr2; + if (!depStr1.init(cx, src1) || !depStr2.init(cx, src2)) { + return JS_FALSE; + } + PRInt32 result; rv = gCollation->CompareString(nsICollation::kCollationStrengthDefault, - nsDependentJSString(src1), - nsDependentJSString(src2), - &result); + depStr1, depStr2, &result); if (NS_FAILED(rv)) { nsDOMClassInfo::ThrowJSException(cx, rv); @@ -1591,27 +1599,36 @@ JSValueToAString(JSContext *cx, jsval val, nsAString *result, } JSString* jsstring = ::JS_ValueToString(cx, val); - if (jsstring) { - result->Assign(reinterpret_cast - (::JS_GetStringChars(jsstring)), - ::JS_GetStringLength(jsstring)); - } else { - result->Truncate(); + if (!jsstring) { + goto error; + } - // We failed to convert val to a string. We're either OOM, or the - // security manager denied access to .toString(), or somesuch, on - // an object. Treat this case as if the result were undefined. + size_t length; + const jschar *chars; + chars = ::JS_GetStringCharsAndLength(cx, jsstring, &length); + if (!chars) { + goto error; + } - if (isUndefined) { - *isUndefined = PR_TRUE; - } + result->Assign(chars, length); + return NS_OK; - if (!::JS_IsExceptionPending(cx)) { - // JS_ValueToString() returned null w/o an exception - // pending. That means we're OOM. +error: + // We failed to convert val to a string. We're either OOM, or the + // security manager denied access to .toString(), or somesuch, on + // an object. Treat this case as if the result were undefined. - return NS_ERROR_OUT_OF_MEMORY; - } + result->Truncate(); + + if (isUndefined) { + *isUndefined = PR_TRUE; + } + + if (!::JS_IsExceptionPending(cx)) { + // JS_ValueToString()/JS_GetStringCharsAndLength returned null w/o an + // exception pending. That means we're OOM. + + return NS_ERROR_OUT_OF_MEMORY; } return NS_OK; diff --git a/dom/base/nsJSTimeoutHandler.cpp b/dom/base/nsJSTimeoutHandler.cpp index fde88b51218..e11f7217a58 100644 --- a/dom/base/nsJSTimeoutHandler.cpp +++ b/dom/base/nsJSTimeoutHandler.cpp @@ -109,7 +109,7 @@ private: nsCOMPtr mArgv; // The JS expression to evaluate or function to call, if !mExpr - JSString *mExpr; + JSFlatString *mExpr; JSObject *mFunObj; }; @@ -134,10 +134,10 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsJSScriptTimeoutHandler) else if (tmp->mFunObj) { JSFunction* fun = (JSFunction*)tmp->mFunObj->getPrivate(); if (fun->atom) { - size_t size = 1 + JS_PutEscapedString(NULL, 0, ATOM_TO_STRING(fun->atom), 0); + size_t size = 1 + JS_PutEscapedFlatString(NULL, 0, ATOM_TO_STRING(fun->atom), 0); char *name = new char[size]; if (name) { - JS_PutEscapedString(name, size, ATOM_TO_STRING(fun->atom), 0); + JS_PutEscapedFlatString(name, size, ATOM_TO_STRING(fun->atom), 0); foo.AppendLiteral(" ["); foo.Append(name); delete[] name; @@ -232,7 +232,7 @@ nsJSScriptTimeoutHandler::Init(nsGlobalWindow *aWindow, PRBool *aIsInterval, ncc->GetArgc(&argc); ncc->GetArgvPtr(&argv); - JSString *expr = nsnull; + JSFlatString *expr = nsnull; JSObject *funobj = nsnull; int32 interval = 0; @@ -264,10 +264,17 @@ nsJSScriptTimeoutHandler::Init(nsGlobalWindow *aWindow, PRBool *aIsInterval, case JSTYPE_STRING: case JSTYPE_OBJECT: - expr = ::JS_ValueToString(cx, argv[0]); - if (!expr) - return NS_ERROR_OUT_OF_MEMORY; - argv[0] = STRING_TO_JSVAL(expr); + { + JSString *str = ::JS_ValueToString(cx, argv[0]); + if (!str) + return NS_ERROR_OUT_OF_MEMORY; + + expr = ::JS_FlattenString(cx, str); + if (!expr) + return NS_ERROR_OUT_OF_MEMORY; + + argv[0] = STRING_TO_JSVAL(str); + } break; default: @@ -370,8 +377,7 @@ const PRUnichar * nsJSScriptTimeoutHandler::GetHandlerText() { NS_ASSERTION(mExpr, "No expression, so no handler text!"); - return reinterpret_cast - (::JS_GetStringChars(mExpr)); + return ::JS_GetFlatStringChars(mExpr); } nsresult NS_CreateJSTimeoutHandler(nsGlobalWindow *aWindow, diff --git a/dom/base/nsJSUtils.h b/dom/base/nsJSUtils.h index c44a3975e35..d6a0e6106e8 100644 --- a/dom/base/nsJSUtils.h +++ b/dom/base/nsJSUtils.h @@ -85,23 +85,44 @@ public: class nsDependentJSString : public nsDependentString { public: - explicit nsDependentJSString(jsval v) - : nsDependentString((PRUnichar *)::JS_GetStringChars(JSVAL_TO_STRING(v)), - ::JS_GetStringLength(JSVAL_TO_STRING(v))) - { - } - + /** + * In the case of string ids, getting the string's chars is infallible, so + * the dependent string can be constructed directly. + */ explicit nsDependentJSString(jsid id) - : nsDependentString((PRUnichar *)::JS_GetStringChars(JSID_TO_STRING(id)), - ::JS_GetStringLength(JSID_TO_STRING(id))) + : nsDependentString(JS_GetInternedStringChars(JSID_TO_STRING(id)), + JS_GetStringLength(JSID_TO_STRING(id))) { } - explicit nsDependentJSString(JSString *str) - : nsDependentString((PRUnichar *)::JS_GetStringChars(str), ::JS_GetStringLength(str)) + /** + * For all other strings, the nsDependentJSString object should be default + * constructed, which leaves it empty (this->IsEmpty()), and initialized with + * one of the fallible init() methods below. + */ + + nsDependentJSString() { } + JSBool init(JSContext* aContext, JSString* str) + { + size_t length; + const jschar* chars = JS_GetStringCharsZAndLength(aContext, str, &length); + if (!chars) + return JS_FALSE; + + NS_ASSERTION(IsEmpty(), "init() on initialized string"); + nsDependentString* base = this; + new(base) nsDependentString(chars, length); + return JS_TRUE; + } + + JSBool init(JSContext* aContext, const jsval &v) + { + return init(aContext, JSVAL_TO_STRING(v)); + } + ~nsDependentJSString() { } diff --git a/dom/indexedDB/IDBCursor.cpp b/dom/indexedDB/IDBCursor.cpp index 5bcc4da1ffe..effc4d876e4 100644 --- a/dom/indexedDB/IDBCursor.cpp +++ b/dom/indexedDB/IDBCursor.cpp @@ -447,7 +447,7 @@ IDBCursor::Continue(const jsval &aKey, } Key key; - nsresult rv = IDBObjectStore::GetKeyFromJSVal(aKey, key); + nsresult rv = IDBObjectStore::GetKeyFromJSVal(aKey, aCx, key); NS_ENSURE_SUCCESS(rv, rv); if (!key.IsUnset()) { @@ -555,7 +555,7 @@ IDBCursor::Update(const jsval& aValue, NS_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); Key key; - rv = IDBObjectStore::GetKeyFromJSVal(prop, key); + rv = IDBObjectStore::GetKeyFromJSVal(prop, aCx, key); if (NS_FAILED(rv)) { return rv; } diff --git a/dom/indexedDB/IDBDatabase.cpp b/dom/indexedDB/IDBDatabase.cpp index 42a73b57402..ddf3223436a 100644 --- a/dom/indexedDB/IDBDatabase.cpp +++ b/dom/indexedDB/IDBDatabase.cpp @@ -590,7 +590,12 @@ IDBDatabase::CreateObjectStore(const nsAString& aName, NS_WARNING("JS_ValueToString failed!"); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } - keyPath = nsDependentJSString(str); + nsDependentJSString dependentKeyPath; + if (!dependentKeyPath.init(aCx, str)) { + NS_WARNING("Initializing keyPath failed!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + keyPath = dependentKeyPath; } else if (id == nsDOMClassInfo::sAutoIncrement_id) { JSBool boolVal; diff --git a/dom/indexedDB/IDBObjectStore.cpp b/dom/indexedDB/IDBObjectStore.cpp index 1b7be7fdd65..4a29f45974b 100644 --- a/dom/indexedDB/IDBObjectStore.cpp +++ b/dom/indexedDB/IDBObjectStore.cpp @@ -366,7 +366,7 @@ GetKeyFromObject(JSContext* aCx, JSBool ok = JS_GetUCProperty(aCx, aObj, keyPathChars, keyPathLen, &key); NS_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - nsresult rv = IDBObjectStore::GetKeyFromJSVal(key, aKey); + nsresult rv = IDBObjectStore::GetKeyFromJSVal(key, aCx, aKey); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; @@ -448,13 +448,18 @@ IDBObjectStore::GetKeyFromVariant(nsIVariant* aKeyVariant, // static nsresult IDBObjectStore::GetKeyFromJSVal(jsval aKeyVal, + JSContext* aCx, Key& aKey) { if (JSVAL_IS_VOID(aKeyVal)) { aKey = Key::UNSETKEY; } else if (JSVAL_IS_STRING(aKeyVal)) { - aKey = nsDependentJSString(aKeyVal); + nsDependentJSString depStr; + if (!depStr.init(aCx, JSVAL_TO_STRING(aKeyVal))) { + return NS_ERROR_OUT_OF_MEMORY; + } + aKey = depStr; } else if (JSVAL_IS_INT(aKeyVal)) { aKey = JSVAL_TO_INT(aKeyVal); @@ -548,7 +553,7 @@ IDBObjectStore::GetKeyPathValueFromStructuredData(const PRUint8* aData, JSBool ok = JS_GetUCProperty(cx, obj, keyPathChars, keyPathLen, &keyVal); NS_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - rv = GetKeyFromJSVal(keyVal, aValue); + rv = GetKeyFromJSVal(keyVal, *aCx, aValue); if (NS_FAILED(rv)) { // If the object doesn't have a value that we can use for our index then we // leave it unset. @@ -589,7 +594,7 @@ IDBObjectStore::GetIndexUpdateInfo(ObjectStoreInfo* aObjectStoreInfo, NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE); Key value; - nsresult rv = GetKeyFromJSVal(keyPathValue, value); + nsresult rv = GetKeyFromJSVal(keyPathValue, aCx, value); if (NS_FAILED(rv) || value.IsUnset()) { // Not a value we can do anything with, ignore it. continue; @@ -819,7 +824,7 @@ IDBObjectStore::GetAddInfo(JSContext* aCx, if (mKeyPath.IsEmpty()) { // Out-of-line keys must be passed in. - rv = GetKeyFromJSVal(aKeyVal, aKey); + rv = GetKeyFromJSVal(aKeyVal, aCx, aKey); NS_ENSURE_SUCCESS(rv, rv); } else { @@ -1140,7 +1145,7 @@ IDBObjectStore::Delete(const jsval& aKey, } Key key; - nsresult rv = GetKeyFromJSVal(aKey, key); + nsresult rv = GetKeyFromJSVal(aKey, aCx, key); if (NS_FAILED(rv)) { return rv; } diff --git a/dom/indexedDB/IDBObjectStore.h b/dom/indexedDB/IDBObjectStore.h index 49a314677a6..94ec227d0e5 100644 --- a/dom/indexedDB/IDBObjectStore.h +++ b/dom/indexedDB/IDBObjectStore.h @@ -78,6 +78,7 @@ public: static nsresult GetKeyFromJSVal(jsval aKeyVal, + JSContext* aCx, Key& aKey); static nsresult diff --git a/dom/src/jsurl/nsJSProtocolHandler.cpp b/dom/src/jsurl/nsJSProtocolHandler.cpp index ab1a30b31a6..57c6904e060 100644 --- a/dom/src/jsurl/nsJSProtocolHandler.cpp +++ b/dom/src/jsurl/nsJSProtocolHandler.cpp @@ -335,7 +335,14 @@ nsresult nsJSThunk::EvaluateScript(nsIChannel *aChannel, if (!isUndefined && NS_SUCCEEDED(rv)) { NS_ASSERTION(JSVAL_IS_STRING(rval), "evalInSandbox is broken"); - result = nsDependentJSString(JSVAL_TO_STRING(rval)); + + nsDependentJSString depStr; + if (!depStr.init(cx, JSVAL_TO_STRING(rval))) { + JS_ReportPendingException(cx); + isUndefined = PR_TRUE; + } else { + result = depStr; + } } stack->Pop(nsnull); diff --git a/dom/src/threads/nsDOMWorker.cpp b/dom/src/threads/nsDOMWorker.cpp index c3f95b5bbf3..3e036f33143 100644 --- a/dom/src/threads/nsDOMWorker.cpp +++ b/dom/src/threads/nsDOMWorker.cpp @@ -142,9 +142,11 @@ nsDOMWorkerFunctions::Dump(JSContext* aCx, JSString* str; if (aArgc && (str = JS_ValueToString(aCx, JS_ARGV(aCx, aVp)[0])) && str) { - nsDependentJSString string(str); - fputs(NS_ConvertUTF16toUTF8(nsDependentJSString(str)).get(), stderr); - fflush(stderr); + nsDependentJSString depStr; + if (depStr.init(aCx, str)) { + fputs(NS_ConvertUTF16toUTF8(depStr).get(), stderr); + fflush(stderr); + } } return JS_TRUE; } @@ -268,7 +270,12 @@ nsDOMWorkerFunctions::LoadScripts(JSContext* aCx, nsString* newURL = urls.AppendElement(); NS_ASSERTION(newURL, "Shouldn't fail if SetCapacity succeeded above!"); - newURL->Assign(nsDependentJSString(str)); + nsDependentJSString depStr; + if (!depStr.init(aCx, str)) { + return JS_FALSE; + } + + newURL->Assign(depStr); } nsRefPtr loader = @@ -507,8 +514,7 @@ nsDOMWorkerFunctions::CTypesLazyGetter(JSContext* aCx, { NS_ASSERTION(JS_GetGlobalForObject(aCx, aObj) == aObj, "Bad object!"); NS_ASSERTION(JSID_IS_STRING(aId), "Not a string!"); - JSString* str = JSID_TO_STRING(aId); - NS_ASSERTION(nsDependentJSString(str).EqualsLiteral("ctypes"), "Bad id!"); + NS_ASSERTION(nsDependentJSString(aId).EqualsLiteral("ctypes"), "Bad id!"); } #endif nsDOMWorker* worker = static_cast(JS_GetContextPrivate(aCx)); @@ -674,14 +680,14 @@ nsDOMWorkerScope::AddProperty(nsIXPConnectWrappedNative* aWrapper, return NS_OK; } - JSString *str = JSID_TO_STRING(aId); + JSFlatString *str = JSID_TO_FLAT_STRING(aId); // Figure out which listener we're setting. SetListenerFunc func; - if (JS_MatchStringAndAscii(str, "onmessage")) { + if (JS_FlatStringEqualsAscii(str, "onmessage")) { func = &nsDOMWorkerScope::SetOnmessage; } - else if (JS_MatchStringAndAscii(str, "onerror")) { + else if (JS_FlatStringEqualsAscii(str, "onerror")) { func = &nsDOMWorkerScope::SetOnerror; } else { @@ -1302,7 +1308,10 @@ nsDOMWorker::InitializeInternal(nsIScriptGlobalObject* aOwner, JSString* str = JS_ValueToString(aCx, aArgv[0]); NS_ENSURE_TRUE(str, NS_ERROR_XPC_BAD_CONVERT_JS); - mScriptURL.Assign(nsDependentJSString(str)); + nsDependentJSString depStr; + NS_ENSURE_TRUE(depStr.init(aCx, str), NS_ERROR_OUT_OF_MEMORY); + + mScriptURL.Assign(depStr); NS_ENSURE_FALSE(mScriptURL.IsEmpty(), NS_ERROR_INVALID_ARG); mLock = nsAutoLock::NewLock("nsDOMWorker::mLock"); diff --git a/dom/src/threads/nsDOMWorkerTimeout.cpp b/dom/src/threads/nsDOMWorkerTimeout.cpp index 8726c75ee60..2345eaa4f1d 100644 --- a/dom/src/threads/nsDOMWorkerTimeout.cpp +++ b/dom/src/threads/nsDOMWorkerTimeout.cpp @@ -196,11 +196,10 @@ nsDOMWorkerTimeout::ExpressionCallback::Run(nsDOMWorkerTimeout* aTimeout, JSString* expression = JS_ValueToString(aCx, mExpression); NS_ENSURE_TRUE(expression, NS_ERROR_FAILURE); - jschar* string = JS_GetStringChars(expression); + size_t stringLength; + const jschar* string = JS_GetStringCharsAndLength(aCx, expression, &stringLength); NS_ENSURE_TRUE(string, NS_ERROR_FAILURE); - size_t stringLength = JS_GetStringLength(expression); - jsval rval; PRBool success = JS_EvaluateUCScriptForPrincipals(aCx, global, principal, string, stringLength, diff --git a/ipc/testshell/XPCShellEnvironment.cpp b/ipc/testshell/XPCShellEnvironment.cpp index da632015d0a..a00851d9137 100644 --- a/ipc/testshell/XPCShellEnvironment.cpp +++ b/ipc/testshell/XPCShellEnvironment.cpp @@ -1253,10 +1253,13 @@ XPCShellEnvironment::EvaluateString(const nsString& aString, if (ok && result != JSVAL_VOID) { JSErrorReporter old = JS_SetErrorReporter(mCx, NULL); JSString* str = JS_ValueToString(mCx, result); + nsDependentJSString depStr; + if (str) + depStr.init(mCx, str); JS_SetErrorReporter(mCx, old); - if (str && aResult) { - aResult->Assign(nsDependentJSString(str)); + if (!depStr.IsEmpty() && aResult) { + aResult->Assign(depStr); } } } diff --git a/js/ipc/Makefile.in b/js/ipc/Makefile.in index d7388551b2a..84d67457719 100644 --- a/js/ipc/Makefile.in +++ b/js/ipc/Makefile.in @@ -63,6 +63,11 @@ CPPSRCS = \ ObjectWrapperChild.cpp \ $(NULL) +# For nsDependentJSString +LOCAL_INCLUDES += \ + -I$(topsrcdir)/dom/base \ + $(NULL) + include $(topsrcdir)/config/config.mk include $(topsrcdir)/ipc/chromium/chromium-config.mk include $(topsrcdir)/config/rules.mk diff --git a/js/ipc/ObjectWrapperChild.cpp b/js/ipc/ObjectWrapperChild.cpp index 2fa59db7c9b..24918c32272 100644 --- a/js/ipc/ObjectWrapperChild.cpp +++ b/js/ipc/ObjectWrapperChild.cpp @@ -50,6 +50,7 @@ #include "nsTArray.h" #include "nsContentUtils.h" #include "nsIJSContextStack.h" +#include "nsJSUtils.h" using namespace mozilla::jsipc; using namespace js; @@ -188,8 +189,12 @@ ObjectWrapperChild::jsval_to_JSVariant(JSContext* cx, jsval from, JSVariant* to) case JSTYPE_OBJECT: return JSObject_to_JSVariant(cx, JSVAL_TO_OBJECT(from), to); case JSTYPE_STRING: - *to = nsDependentString((PRUnichar*)JS_GetStringChars(JSVAL_TO_STRING(from)), - JS_GetStringLength(JSVAL_TO_STRING(from))); + { + nsDependentJSString depStr; + if (!depStr.init(cx, from)) + return false; + *to = depStr; + } return true; case JSTYPE_NUMBER: if (JSVAL_IS_INT(from)) @@ -282,10 +287,10 @@ ObjectWrapperChild::Manager() static bool jsid_to_nsString(JSContext* cx, jsid from, nsString* to) { - jsval v; - if (JS_IdToValue(cx, from, &v) && JSVAL_IS_STRING(v)) { - *to = nsDependentString((PRUnichar*)JS_GetStringChars(JSVAL_TO_STRING(v)), - JS_GetStringLength(JSVAL_TO_STRING(v))); + if (JSID_IS_STRING(from)) { + size_t length; + const jschar* chars = JS_GetInternedStringCharsAndLength(JSID_TO_STRING(from), &length); + *to = nsDependentString(chars, length); return true; } return false; diff --git a/js/ipc/ObjectWrapperParent.cpp b/js/ipc/ObjectWrapperParent.cpp index c55aac04c17..1e295355fe4 100644 --- a/js/ipc/ObjectWrapperParent.cpp +++ b/js/ipc/ObjectWrapperParent.cpp @@ -42,6 +42,7 @@ #include "mozilla/jsipc/ContextWrapperParent.h" #include "mozilla/jsipc/CPOWTypes.h" #include "mozilla/unused.h" +#include "nsJSUtils.h" #include "jsobj.h" #include "jsfun.h" @@ -265,8 +266,12 @@ ObjectWrapperParent::jsval_to_JSVariant(JSContext* cx, jsval from, } return true; case JSTYPE_STRING: - *to = nsDependentString((PRUnichar*)JS_GetStringChars(JSVAL_TO_STRING(from)), - JS_GetStringLength(JSVAL_TO_STRING(from))); + { + nsDependentJSString depStr; + if (!depStr.init(cx, from)) + return false; + *to = depStr; + } return true; case JSTYPE_NUMBER: if (JSVAL_IS_INT(from)) @@ -379,10 +384,12 @@ static bool jsval_to_nsString(JSContext* cx, jsid from, nsString* to) { JSString* str; + const jschar* chars; jsval idval; if (JS_IdToValue(cx, from, &idval) && - (str = JS_ValueToString(cx, idval))) { - *to = JS_GetStringChars(str); + (str = JS_ValueToString(cx, idval)) && + (chars = JS_GetStringCharsZ(cx, str))) { + *to = chars; return true; } return false; diff --git a/js/jetpack/JetpackActorCommon.cpp b/js/jetpack/JetpackActorCommon.cpp index b95b62e2a5a..de979243a37 100644 --- a/js/jetpack/JetpackActorCommon.cpp +++ b/js/jetpack/JetpackActorCommon.cpp @@ -49,6 +49,8 @@ #include "mozilla/jetpack/PHandleChild.h" #include "mozilla/jetpack/Handle.h" +#include "nsJSUtils.h" + using mozilla::jetpack::JetpackActorCommon; using mozilla::jetpack::PHandleParent; using mozilla::jetpack::HandleParent; @@ -138,8 +140,12 @@ JetpackActorCommon::jsval_to_PrimVariant(JSContext* cx, JSType type, jsval from, } case JSTYPE_STRING: - *to = nsDependentString((PRUnichar*)JS_GetStringChars(JSVAL_TO_STRING(from)), - JS_GetStringLength(JSVAL_TO_STRING(from))); + { + nsDependentJSString depStr; + if (!depStr.init(cx, from)) + return false; + *to = depStr; + } return true; case JSTYPE_NUMBER: @@ -223,8 +229,10 @@ JetpackActorCommon::jsval_to_CompVariant(JSContext* cx, JSType type, jsval from, KeyValue kv; // Silently drop properties that can't be converted. if (jsval_to_Variant(cx, val, &kv.value(), seen)) { - kv.key() = nsDependentString((PRUnichar*)JS_GetStringChars(idStr), - JS_GetStringLength(idStr)); + nsDependentJSString depStr; + if (!depStr.init(cx, idStr)) + return false; + kv.key() = depStr; // If AppendElement fails, we lose this property, no big deal. kvs.AppendElement(kv); } diff --git a/js/jetpack/JetpackChild.cpp b/js/jetpack/JetpackChild.cpp index bd47fe0d4f6..b47cfdc916c 100644 --- a/js/jetpack/JetpackChild.cpp +++ b/js/jetpack/JetpackChild.cpp @@ -96,7 +96,7 @@ static char* UnicodeToNative(JSContext *cx, const jschar *source, size_t slen) { nsCAutoString native; - nsDependentString unicode(reinterpret_cast(source), slen); + nsDependentString unicode(source, slen); nsresult rv = NS_CopyUnicodeToNative(unicode, native); if (NS_FAILED(rv)) { JS_ReportError(cx, "could not convert string to native charset"); @@ -251,8 +251,12 @@ MessageCommon(JSContext* cx, uintN argc, jsval* vp, return JS_FALSE; } - result->msgName.Assign((PRUnichar*)JS_GetStringChars(msgNameStr), - JS_GetStringLength(msgNameStr)); + size_t length; + const jschar* chars = JS_GetStringCharsAndLength(cx, msgNameStr, &length); + if (!chars) + return JS_FALSE; + + result->msgName.Assign(chars, length); result->data.Clear(); @@ -355,8 +359,12 @@ ReceiverCommon(JSContext* cx, uintN argc, jsval* vp, return JS_FALSE; } - result->msgName.Assign((PRUnichar*)JS_GetStringChars(str), - JS_GetStringLength(str)); + size_t length; + const jschar* chars = JS_GetStringCharsAndLength(cx, str, &length); + if (!chars) + return JS_FALSE; + + result->msgName.Assign(chars, length); if (arity < 2) return JS_TRUE; @@ -497,9 +505,13 @@ JetpackChild::EvalInSandbox(JSContext* cx, uintN argc, jsval* vp) return JS_FALSE; } + size_t length; + const jschar* chars = JS_GetStringCharsAndLength(cx, str, &length); + if (!chars) + return JS_FALSE; + js::AutoValueRooter ignored(cx); - return JS_EvaluateUCScript(cx, obj, JS_GetStringChars(str), JS_GetStringLength(str), "", 1, - ignored.jsval_addr()); + return JS_EvaluateUCScript(cx, obj, chars, length, "", 1, ignored.jsval_addr()); } bool JetpackChild::sReportingError; diff --git a/js/jetpack/Makefile.in b/js/jetpack/Makefile.in index dc851ad86a6..460df1a5f3c 100644 --- a/js/jetpack/Makefile.in +++ b/js/jetpack/Makefile.in @@ -74,6 +74,11 @@ CPPSRCS = \ JetpackActorCommon.cpp \ $(NULL) +# For nsDependentJSString +LOCAL_INCLUDES += \ + -I$(topsrcdir)/dom/base \ + $(NULL) + ifdef ENABLE_TESTS TOOL_DIRS += tests endif diff --git a/js/jsd/idl/jsdIDebuggerService.idl b/js/jsd/idl/jsdIDebuggerService.idl index c9c2c3b03f9..4dec26923b8 100644 --- a/js/jsd/idl/jsdIDebuggerService.idl +++ b/js/jsd/idl/jsdIDebuggerService.idl @@ -78,7 +78,7 @@ interface jsdIActivationCallback; * Debugger service. It's not a good idea to have more than one active client of * the debugger service. */ -[scriptable, uuid(01769775-c77c-47f9-8848-0abbab404215)] +[scriptable, uuid(1ad86ef3-5eca-4ed7-81c5-a757d1957dff)] interface jsdIDebuggerService : nsISupports { /** Internal use only. */ @@ -512,7 +512,7 @@ interface jsdIFilterEnumerator : nsISupports /** * Pass an instance of one of these to jsdIDebuggerService::enumerateScripts. */ -[scriptable, uuid(5ba76b99-acb1-4ed8-a4e4-a716a7d9097e)] +[scriptable, uuid(4eef60c2-9bbc-48fa-b196-646a832c6c81)] interface jsdIScriptEnumerator : nsISupports { /** @@ -525,7 +525,7 @@ interface jsdIScriptEnumerator : nsISupports /** * Pass an instance of one of these to jsdIDebuggerService::enumerateContexts. */ -[scriptable, uuid(d96af02e-3379-4db5-885d-fee28d178701)] +[scriptable, uuid(57d18286-550c-4ca9-ac33-56f12ebba91e)] interface jsdIContextEnumerator : nsISupports { /** @@ -538,7 +538,7 @@ interface jsdIContextEnumerator : nsISupports /** * Set jsdIDebuggerService::scriptHook to an instance of one of these. */ -[scriptable, uuid(cf7ecc3f-361b-44af-84a7-4b0d6cdca204)] +[scriptable, uuid(bb722893-0f63-45c5-b547-7a0947c7b6b6)] interface jsdIScriptHook : nsISupports { /** @@ -556,7 +556,7 @@ interface jsdIScriptHook : nsISupports * Hook instances of this interface up to the * jsdIDebuggerService::functionHook and toplevelHook properties. */ -[scriptable, uuid(191d2738-22e8-4756-b366-6c878c87d73b)] +[scriptable, uuid(3eff1314-7ae3-4cf8-833b-c33c24a55633)] interface jsdICallHook : nsISupports { /** @@ -588,7 +588,7 @@ interface jsdICallHook : nsISupports void onCall (in jsdIStackFrame frame, in unsigned long type); }; -[scriptable, uuid(cea9ab1a-4b5d-416f-a197-9ffa7046f2ce)] +[scriptable, uuid(e6b45eee-d974-4d85-9d9e-f5a67218deb4)] interface jsdIErrorHook : nsISupports { /** @@ -880,7 +880,7 @@ interface jsdIStackFrame : jsdIEphemeral * Script object. In JavaScript engine terms, there's a single script for each * function, and one for the top level script. */ -[scriptable, uuid(7e6fb9ed-4382-421d-9a14-c80a486e983b)] +[scriptable, uuid(e7935220-7def-4c8e-832f-fbc948a97490)] interface jsdIScript : jsdIEphemeral { /** Internal use only. */ @@ -1038,6 +1038,10 @@ interface jsdIScript : jsdIEphemeral * Clear all breakpoints set in this script. */ void clearAllBreakpoints (); + /** + * Call interrupt hook at least once per source line + */ + void enableSingleStepInterrupts (in PRBool mode); }; /** @@ -1046,7 +1050,7 @@ interface jsdIScript : jsdIEphemeral * jsdIValue adds a root for the underlying JavaScript value, so don't keep it * if you don't need to. */ -[scriptable, uuid(9cab158f-dc78-41dd-9d11-79e05cb3f2bd)] +[scriptable, uuid(fd1311f7-096c-44a3-847b-9d478c8176c3)] interface jsdIValue : jsdIEphemeral { /** Internal use only. */ @@ -1192,7 +1196,7 @@ interface jsdIValue : jsdIEphemeral * functions from jsdIValue should move to this interface. We could inherit from * jsdIValue or use interface flattening or something. */ -[scriptable, uuid(a735a94c-9d41-4997-8fcb-cfa8b649a5b7)] +[scriptable, uuid(87d86308-7a27-4255-b23c-ce2394f02473)] interface jsdIObject : nsISupports { /** Internal use only. */ @@ -1228,7 +1232,7 @@ interface jsdIObject : nsISupports * Representation of a property of an object. When an instance is invalid, all * method and property access will result in a NS_UNAVAILABLE error. */ -[scriptable, uuid(4491ecd4-fb6b-43fb-bd6f-5d1473f1df24)] +[scriptable, uuid(09332485-1419-42bc-ba1f-070815ed4b82)] interface jsdIProperty : jsdIEphemeral { /** Internal use only. */ diff --git a/js/jsd/jsd_scpt.c b/js/jsd/jsd_scpt.c index 4b4c3297086..80b8d1540c5 100644 --- a/js/jsd/jsd_scpt.c +++ b/js/jsd/jsd_scpt.c @@ -40,6 +40,7 @@ */ #include "jsd.h" +#include "jsfriendapi.h" /* Comment this out to disable (NT specific) dumping as we go */ /* @@ -214,7 +215,8 @@ _dumpJSDScript(JSDContext* jsdc, JSDScript* jsdscript, const char* leadingtext) if (fun) { n += size_t(snprintf(Buf + n, sizeof(Buf) - n, "%s", "no fun")); } else { - n += JS_PutEscapedString(Buf + n, sizeof(Buf) - n, fun, 0); + n += JS_PutEscapedFlatString(Buf + n, sizeof(Buf) - n, + JS_ASSERT_STRING_IS_FLAT(fun), 0); Buf[sizeof(Buf) - 1] = '\0'; } if (n + 1 < sizeof(Buf)) @@ -503,7 +505,9 @@ jsd_GetScriptFunctionName(JSDContext* jsdc, JSDScript *jsdscript) if( ! jsdscript->function ) return NULL; str = JS_GetFunctionId(jsdscript->function); - return str ? str : JS_GetEmptyString(jsdc->jsrt); + + /* For compatibility we return "anonymous", not an empty string here. */ + return str ? str : JS_GetAnonymousString(jsdc->jsrt); } uintN @@ -587,6 +591,17 @@ jsd_GetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc* hook, void** callerdata) return JS_TRUE; } +JSBool +jsd_EnableSingleStepInterrupts(JSDContext* jsdc, JSDScript* jsdscript, JSBool enable) +{ + JSBool rv; + JSD_LOCK(); + rv = JS_SetSingleStepMode(jsdc->dumbContext, jsdscript->script, enable); + JSD_UNLOCK(); + return rv; +} + + /***************************************************************************/ void @@ -751,7 +766,7 @@ jsd_TrapHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, } JSD_ASSERT_VALID_EXEC_HOOK(jsdhook); - JS_ASSERT(jsdhook->pc == (jsuword)pc); + JS_ASSERT(!jsdhook->pc || jsdhook->pc == (jsuword)pc); JS_ASSERT(jsdhook->jsdscript->script == script); JS_ASSERT(jsdhook->jsdscript->jsdc == jsdc); diff --git a/js/jsd/jsd_stak.c b/js/jsd/jsd_stak.c index a2642aa16ba..5e1561f043e 100644 --- a/js/jsd/jsd_stak.c +++ b/js/jsd/jsd_stak.c @@ -40,6 +40,7 @@ */ #include "jsd.h" +#include "jsfriendapi.h" #ifdef DEBUG void JSD_ASSERT_VALID_THREAD_STATE(JSDThreadState* jsdthreadstate) @@ -369,8 +370,17 @@ jsd_GetNameForStackFrame(JSDContext* jsdc, { JSFunction *fun = JS_GetFrameFunction (jsdthreadstate->context, jsdframe->fp); - if (fun) + if( fun ) + { rv = JS_GetFunctionId (fun); + + /* + * For compatibility we return "anonymous", not an empty string + * here. + */ + if( !rv ) + rv = JS_GetAnonymousString(jsdc->jsrt); + } } JSD_UNLOCK_THREADSTATES(jsdc); diff --git a/js/jsd/jsd_val.c b/js/jsd/jsd_val.c index 01fbe85c37c..71e1e297ca8 100644 --- a/js/jsd/jsd_val.c +++ b/js/jsd/jsd_val.c @@ -41,8 +41,7 @@ #include "jsd.h" #include "jsapi.h" -#include "jspubtd.h" -#include "jsprvtd.h" +#include "jsfriendapi.h" #ifdef DEBUG void JSD_ASSERT_VALID_VALUE(JSDValue* jsdval) @@ -273,8 +272,10 @@ jsd_GetValueFunctionName(JSDContext* jsdc, JSDValue* jsdval) if(!fun) return NULL; jsdval->funName = JS_GetFunctionId(fun); + + /* For compatibility we return "anonymous", not an empty string here. */ if (!jsdval->funName) - jsdval->funName = JS_GetEmptyString(jsdc->jsrt); + jsdval->funName = JS_GetAnonymousString(jsdc->jsrt); } return jsdval->funName; } @@ -298,7 +299,7 @@ jsd_NewValue(JSDContext* jsdc, jsval val) call = JS_EnterCrossCompartmentCall(jsdc->dumbContext, jsdc->glob); if(!call) { JS_EndRequest(jsdc->dumbContext); - + free(jsdval); return NULL; } @@ -562,8 +563,11 @@ jsd_GetValueProperty(JSDContext* jsdc, JSDValue* jsdval, JSString* name) while(NULL != (jsdprop = jsd_IterateProperties(jsdc, jsdval, &iter))) { JSString* propName = jsd_GetValueString(jsdc, jsdprop->name); - if(propName && !JS_CompareStrings(propName, name)) - return jsdprop; + if(propName) { + intN result; + if (JS_CompareStrings(cx, propName, name, &result) && !result) + return jsdprop; + } JSD_DropProperty(jsdc, jsdprop); } /* Not found in property list, look it up explicitly */ @@ -571,8 +575,8 @@ jsd_GetValueProperty(JSDContext* jsdc, JSDValue* jsdval, JSString* name) if(!(obj = JSVAL_TO_OBJECT(jsdval->val))) return NULL; - nameChars = JS_GetStringChars(name); - nameLen = JS_GetStringLength(name); + if (!(nameChars = JS_GetStringCharsZAndLength(cx, name, &nameLen))) + return NULL; JS_BeginRequest(cx); call = JS_EnterCrossCompartmentCall(cx, obj); diff --git a/js/jsd/jsd_xpc.cpp b/js/jsd/jsd_xpc.cpp index d9a27073a59..a958ce1598c 100644 --- a/js/jsd/jsd_xpc.cpp +++ b/js/jsd/jsd_xpc.cpp @@ -197,7 +197,7 @@ jsds_InvalidateAllEphemerals (LiveEphemeral **listHead) LiveEphemeral *lv_record = reinterpret_cast (PR_NEXT_LINK(&(*listHead)->links)); - while (*listHead) + do { LiveEphemeral *next = reinterpret_cast @@ -205,6 +205,7 @@ jsds_InvalidateAllEphemerals (LiveEphemeral **listHead) lv_record->value->Invalidate(); lv_record = next; } + while (*listHead); } void @@ -1033,36 +1034,61 @@ jsdScript::CreatePPLineMap() JSObject *obj = JS_NewObject(cx, NULL, NULL, NULL); JSFunction *fun = JSD_GetJSFunction (mCx, mScript); JSScript *script; + JSString *jsstr; PRUint32 baseLine; PRBool scriptOwner = PR_FALSE; if (fun) { - uintN nargs = JS_GetFunctionArgumentCount(cx, fun); - if (nargs > 12) - return nsnull; - JSString *jsstr = JS_DecompileFunctionBody (cx, fun, 4); - if (!jsstr) + uintN nargs; + + /* Enter a new block so we can leave before the end of this block */ + do { + JSAutoEnterCompartment ac; + if (!ac.enter(cx, JS_GetFunctionObject(fun))) + return nsnull; + + nargs = JS_GetFunctionArgumentCount(cx, fun); + if (nargs > 12) + return nsnull; + jsstr = JS_DecompileFunctionBody (cx, fun, 4); + if (!jsstr) + return nsnull; + } while(false); + + size_t length; + const jschar *chars = JS_GetStringCharsAndLength(cx, jsstr, &length); + if (!chars) return nsnull; const char *argnames[] = {"arg1", "arg2", "arg3", "arg4", "arg5", "arg6", "arg7", "arg8", "arg9", "arg10", "arg11", "arg12" }; - fun = JS_CompileUCFunction (cx, obj, "ppfun", nargs, argnames, - JS_GetStringChars(jsstr), - JS_GetStringLength(jsstr), - "x-jsd:ppbuffer?type=function", 3); + fun = JS_CompileUCFunction (cx, obj, "ppfun", nargs, argnames, chars, + length, "x-jsd:ppbuffer?type=function", 3); if (!fun || !(script = JS_GetFunctionScript(cx, fun))) return nsnull; baseLine = 3; } else { - JSString *jsstr = JS_DecompileScript (cx, JSD_GetJSScript(mCx, mScript), - "ppscript", 4); - if (!jsstr) + /* Enter a new block so we can leave before the end of this block */ + do { + script = JSD_GetJSScript(mCx, mScript); + + JSAutoEnterCompartment ac; + if (!ac.enter(cx, script)) + return nsnull; + + jsstr = JS_DecompileScript (cx, JSD_GetJSScript(mCx, mScript), + "ppscript", 4); + if (!jsstr) + return nsnull; + } while(false); + + size_t length; + const jschar *chars = JS_GetStringCharsAndLength(cx, jsstr, &length); + if (!chars) return nsnull; - script = JS_CompileUCScript (cx, obj, - JS_GetStringChars(jsstr), - JS_GetStringLength(jsstr), + script = JS_CompileUCScript (cx, obj, chars, length, "x-jsd:ppbuffer?type=script", 1); if (!script) return nsnull; @@ -1284,8 +1310,7 @@ jsdScript::GetParameterNames(PRUint32* count, PRUnichar*** paramNames) ret[i] = 0; } else { JSString *str = JS_AtomKey(atom); - ret[i] = NS_strndup(reinterpret_cast(JS_GetStringChars(str)), - JS_GetStringLength(str)); + ret[i] = NS_strndup(JS_GetInternedStringChars(str), JS_GetStringLength(str)); if (!ret[i]) { NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(i, ret); rv = NS_ERROR_OUT_OF_MEMORY; @@ -1343,23 +1368,26 @@ jsdScript::GetFunctionSource(nsAString & aFunctionSource) JSAutoRequest ar(cx); JSString *jsstr; + JSAutoEnterCompartment ac; if (fun) { - JSAutoEnterCompartment ac; if (!ac.enter(cx, JS_GetFunctionObject(fun))) return NS_ERROR_FAILURE; jsstr = JS_DecompileFunction (cx, fun, 4); } else { JSScript *script = JSD_GetJSScript (mCx, mScript); - js::SwitchToCompartment sc(cx, script->compartment); + if (!ac.enter(cx, script)) + return NS_ERROR_FAILURE; jsstr = JS_DecompileScript (cx, script, "ppscript", 4); } if (!jsstr) return NS_ERROR_FAILURE; - aFunctionSource = - nsDependentString( - reinterpret_cast(JS_GetStringChars(jsstr)), - JS_GetStringLength(jsstr)); + size_t length; + const jschar *chars = JS_GetStringCharsZAndLength(cx, jsstr, &length); + if (!chars) + return NS_ERROR_FAILURE; + + aFunctionSource = nsDependentString(chars, length); return NS_OK; } @@ -1480,6 +1508,20 @@ jsdScript::LineToPc(PRUint32 aLine, PRUint32 aPcmap, PRUint32 *_rval) return NS_OK; } +NS_IMETHODIMP +jsdScript::EnableSingleStepInterrupts(PRBool enable) +{ + ASSERT_VALID_EPHEMERAL; + + /* Must have set interrupt hook before enabling */ + if (enable && !jsdService::GetService()->CheckInterruptHook()) + return NS_ERROR_NOT_INITIALIZED; + + JSD_EnableSingleStepInterrupts(mCx, mScript, enable); + + return NS_OK; +} + NS_IMETHODIMP jsdScript::IsLineExecutable(PRUint32 aLine, PRUint32 aPcmap, PRBool *_rval) { @@ -2240,12 +2282,19 @@ NS_IMETHODIMP jsdValue::GetStringValue(nsACString &_rval) { ASSERT_VALID_EPHEMERAL; + JSContext *cx = JSD_GetDefaultJSContext (mCx); + if (!cx) { + NS_WARNING("No default context !?"); + return NS_ERROR_FAILURE; + } JSString *jstr_val = JSD_GetValueString(mCx, mValue); if (jstr_val) { - nsDependentString chars( - reinterpret_cast(JS_GetStringChars(jstr_val)), - JS_GetStringLength(jstr_val)); - CopyUTF16toUTF8(chars, _rval); + size_t length; + const jschar *chars = JS_GetStringCharsZAndLength(cx, jstr_val, &length); + if (!chars) + return NS_ERROR_FAILURE; + nsDependentString depStr(chars, length); + CopyUTF16toUTF8(depStr, _rval); } else { _rval.Truncate(); } diff --git a/js/jsd/jsd_xpc.h b/js/jsd/jsd_xpc.h index 06e5c2aeab8..ed759f48c84 100644 --- a/js/jsd/jsd_xpc.h +++ b/js/jsd/jsd_xpc.h @@ -288,6 +288,8 @@ class jsdService : public jsdIDebuggerService virtual ~jsdService(); static jsdService *GetService (); + + PRBool CheckInterruptHook() { return !!mInterruptHook; } private: PRBool mOn; diff --git a/js/jsd/jsdebug.c b/js/jsd/jsdebug.c index 05638f6f26e..2317f86f7ac 100644 --- a/js/jsd/jsdebug.c +++ b/js/jsd/jsdebug.c @@ -576,6 +576,14 @@ JSD_SetInterruptHook(JSDContext* jsdc, return jsd_SetInterruptHook(jsdc, hook, callerdata); } +JSD_PUBLIC_API(JSBool) +JSD_EnableSingleStepInterrupts(JSDContext* jsdc, JSDScript* jsdscript, JSBool enable) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_SCRIPT(jsdscript); + return jsd_EnableSingleStepInterrupts(jsdc, jsdscript, enable); +} + JSD_PUBLIC_API(JSBool) JSD_ClearInterruptHook(JSDContext* jsdc) { diff --git a/js/jsd/jsdebug.h b/js/jsd/jsdebug.h index 7f1f3114fdb..e65028468f5 100644 --- a/js/jsd/jsdebug.h +++ b/js/jsd/jsdebug.h @@ -803,6 +803,12 @@ JSD_SetInterruptHook(JSDContext* jsdc, JSD_ExecutionHookProc hook, void* callerdata); +/* +* Call the interrupt hook at least once per source line +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_EnableSingleStepInterrupts(JSDContext* jsdc, JSDScript *jsdscript, JSBool enable); + /* * Clear the current interrupt hook. */ diff --git a/js/src/Makefile.in b/js/src/Makefile.in index 674d5ffeae8..c33c259fade 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -134,17 +134,18 @@ CPPSRCS = \ jsbool.cpp \ jsclone.cpp \ jscntxt.cpp \ + jscompartment.cpp \ jsdate.cpp \ jsdbgapi.cpp \ jsdhash.cpp \ jsdtoa.cpp \ jsemit.cpp \ jsexn.cpp \ + jsfriendapi.cpp \ jsfun.cpp \ jsgc.cpp \ jsgcchunk.cpp \ jsgcstats.cpp \ - jscompartment.cpp \ jshash.cpp \ jsinterp.cpp \ jsinvoke.cpp \ @@ -199,6 +200,7 @@ INSTALLED_HEADERS = \ jsdtoa.h \ jsemit.h \ jsfun.h \ + jsfriendapi.h \ jsgc.h \ jscell.h \ jsgcchunk.h \ @@ -572,11 +574,11 @@ ifdef ENABLE_TRACEJIT ifndef WINCE check:: $(wildcard $(RUN_TEST_PROGRAM)) $(PYTHON) -u $(srcdir)/jit-test/jit_test.py \ - --no-slow --no-progress --tinderbox $(DIST)/bin/js$(BIN_SUFFIX) + --no-slow --no-progress --tinderbox --jitflags=m,j,mj,mjp,mjd $(DIST)/bin/js$(BIN_SUFFIX) check-valgrind:: $(wildcard $(RUN_TEST_PROGRAM)) $(PYTHON) -u $(srcdir)/jit-test/jit_test.py \ - --valgrind --no-slow --no-progress --tinderbox $(DIST)/bin/js$(BIN_SUFFIX) + --valgrind --no-slow --no-progress --tinderbox --jitflags=m,j,mj,mjp,mjd $(DIST)/bin/js$(BIN_SUFFIX) endif endif diff --git a/js/src/assembler/jit/ExecutableAllocator.h b/js/src/assembler/jit/ExecutableAllocator.h index 8a5238830ed..16ee0725939 100644 --- a/js/src/assembler/jit/ExecutableAllocator.h +++ b/js/src/assembler/jit/ExecutableAllocator.h @@ -170,6 +170,13 @@ public: size_t available() const { return (m_pools.length() > 1) ? 0 : m_end - m_freePtr; } + // Flag for downstream use, whether to try to release references to this pool. + bool m_destroy; + + // GC number in which the m_destroy flag was most recently set. Used downstream to + // remember whether m_destroy was computed for the currently active GC. + size_t m_gcNumber; + private: // On OOM, this will return an Allocation where pages is NULL. static Allocation systemAlloc(size_t n); @@ -393,7 +400,7 @@ private: // This constructor can fail due to OOM. If it does, m_freePtr will be // set to NULL. -inline ExecutablePool::ExecutablePool(size_t n) : m_refCount(1) +inline ExecutablePool::ExecutablePool(size_t n) : m_refCount(1), m_destroy(false), m_gcNumber(0) { size_t allocSize = roundUpAllocationSize(n, JIT_ALLOCATOR_PAGE_SIZE); if (allocSize == OVERSIZE_ALLOCATION) { diff --git a/js/src/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp index 8286110d6e0..c14c5755a4b 100644 --- a/js/src/ctypes/CTypes.cpp +++ b/js/src/ctypes/CTypes.cpp @@ -1292,7 +1292,10 @@ StringToInteger(JSContext* cx, JSString* string, IntegerType* result) { JS_STATIC_ASSERT(numeric_limits::is_exact); - const jschar* cp = string->chars(); + const jschar* cp = string->getChars(NULL); + if (!cp) + return false; + const jschar* end = cp + string->length(); if (cp == end) return false; @@ -1780,9 +1783,10 @@ ImplicitConvert(JSContext* cx, JSString* str = JSVAL_TO_STRING(val); \ if (str->length() != 1) \ return TypeError(cx, #name, val); \ - \ - result = str->chars()[0]; \ - \ + const jschar *chars = str->getChars(cx); \ + if (!chars) \ + return false; \ + result = chars[0]; \ } else if (!jsvalToInteger(cx, val, &result)) { \ return TypeError(cx, #name, val); \ } \ @@ -1824,8 +1828,10 @@ ImplicitConvert(JSContext* cx, // which the caller assumes ownership of. // TODO: Extend this so we can safely convert strings at other times also. JSString* sourceString = JSVAL_TO_STRING(val); - const jschar* sourceChars = sourceString->chars(); size_t sourceLength = sourceString->length(); + const jschar* sourceChars = sourceString->getChars(cx); + if (!sourceChars) + return false; switch (CType::GetTypeCode(cx, baseType)) { case TYPE_char: @@ -1879,8 +1885,10 @@ ImplicitConvert(JSContext* cx, if (JSVAL_IS_STRING(val)) { JSString* sourceString = JSVAL_TO_STRING(val); - const jschar* sourceChars = sourceString->chars(); size_t sourceLength = sourceString->length(); + const jschar* sourceChars = sourceString->getChars(cx); + if (!sourceChars) + return false; switch (CType::GetTypeCode(cx, baseType)) { case TYPE_char: @@ -1989,21 +1997,18 @@ ImplicitConvert(JSContext* cx, if (JSID_IS_VOID(id)) break; - js::AutoValueRooter fieldVal(cx); - JS_IdToValue(cx, id, fieldVal.jsval_addr()); - if (!JSVAL_IS_STRING(fieldVal.jsval_value())) { + if (!JSID_IS_STRING(id)) { JS_ReportError(cx, "property name is not a string"); return false; } - const FieldInfo* field = StructType::LookupField(cx, targetType, - JSVAL_TO_STRING(fieldVal.jsval_value())); + JSFlatString *name = JSID_TO_FLAT_STRING(id); + const FieldInfo* field = StructType::LookupField(cx, targetType, name); if (!field) return false; - JSString* name = JSVAL_TO_STRING(fieldVal.jsval_value()); js::AutoValueRooter prop(cx); - if (!JS_GetUCProperty(cx, obj, name->chars(), name->length(), prop.jsval_addr())) + if (!JS_GetPropertyById(cx, obj, id, prop.jsval_addr())) return false; // Convert the field via ImplicitConvert(). @@ -3567,8 +3572,10 @@ ArrayType::ConstructData(JSContext* cx, // We were given a string. Size the array to the appropriate length, // including space for the terminator. JSString* sourceString = JSVAL_TO_STRING(argv[0]); - const jschar* sourceChars = sourceString->chars(); size_t sourceLength = sourceString->length(); + const jschar* sourceChars = sourceString->getChars(cx); + if (!sourceChars) + return false; switch (CType::GetTypeCode(cx, baseType)) { case TYPE_char: @@ -3871,7 +3878,7 @@ ArrayType::AddressOfElement(JSContext* cx, uintN argc, jsval* vp) // For a struct field descriptor 'val' of the form { name : type }, extract // 'name' and 'type'. -static JSString* +static JSFlatString* ExtractStructField(JSContext* cx, jsval val, JSObject** typeObj) { if (JSVAL_IS_PRIMITIVE(val)) { @@ -3885,23 +3892,21 @@ ExtractStructField(JSContext* cx, jsval val, JSObject** typeObj) return NULL; js::AutoObjectRooter iterroot(cx, iter); - jsid id; - if (!JS_NextProperty(cx, iter, &id)) + jsid nameid; + if (!JS_NextProperty(cx, iter, &nameid)) return NULL; - if (JSID_IS_VOID(id)) { + if (JSID_IS_VOID(nameid)) { JS_ReportError(cx, "struct field descriptors require a valid name and type"); return NULL; } - js::AutoValueRooter nameVal(cx); - JS_IdToValue(cx, id, nameVal.jsval_addr()); - if (!JSVAL_IS_STRING(nameVal.jsval_value())) { + if (!JSID_IS_STRING(nameid)) { JS_ReportError(cx, "struct field descriptors require a valid name and type"); return NULL; } - JSString* name = JSVAL_TO_STRING(nameVal.jsval_value()); // make sure we have one, and only one, property + jsid id; if (!JS_NextProperty(cx, iter, &id)) return NULL; if (!JSID_IS_VOID(id)) { @@ -3910,7 +3915,7 @@ ExtractStructField(JSContext* cx, jsval val, JSObject** typeObj) } js::AutoValueRooter propVal(cx); - if (!JS_GetUCProperty(cx, obj, name->chars(), name->length(), propVal.jsval_addr())) + if (!JS_GetPropertyById(cx, obj, nameid, propVal.jsval_addr())) return NULL; if (propVal.value().isPrimitive() || @@ -3929,7 +3934,7 @@ ExtractStructField(JSContext* cx, jsval val, JSObject** typeObj) return NULL; } - return name; + return JSID_TO_FLAT_STRING(nameid); } // For a struct field with 'name' and 'type', add an element of the form @@ -3937,7 +3942,7 @@ ExtractStructField(JSContext* cx, jsval val, JSObject** typeObj) static JSBool AddFieldToArray(JSContext* cx, jsval* element, - JSString* name, + JSFlatString* name, JSObject* typeObj) { JSObject* fieldObj = JS_NewObject(cx, NULL, NULL, NULL); @@ -4048,7 +4053,7 @@ StructType::DefineInternal(JSContext* cx, JSObject* typeObj, JSObject* fieldsObj return JS_FALSE; JSObject* fieldType = NULL; - JSString* name = ExtractStructField(cx, item.jsval_value(), &fieldType); + JSFlatString* name = ExtractStructField(cx, item.jsval_value(), &fieldType); if (!name) return JS_FALSE; fieldRootsArray[i] = OBJECT_TO_JSVAL(fieldType); @@ -4321,7 +4326,7 @@ StructType::GetFieldInfo(JSContext* cx, JSObject* obj) } const FieldInfo* -StructType::LookupField(JSContext* cx, JSObject* obj, JSString *name) +StructType::LookupField(JSContext* cx, JSObject* obj, JSFlatString *name) { JS_ASSERT(CType::IsCType(cx, obj)); JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_struct); @@ -4417,7 +4422,7 @@ StructType::FieldGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp) return JS_FALSE; } - const FieldInfo* field = LookupField(cx, typeObj, JSID_TO_STRING(idval)); + const FieldInfo* field = LookupField(cx, typeObj, JSID_TO_FLAT_STRING(idval)); if (!field) return JS_FALSE; @@ -4439,7 +4444,7 @@ StructType::FieldSetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp) return JS_FALSE; } - const FieldInfo* field = LookupField(cx, typeObj, JSID_TO_STRING(idval)); + const FieldInfo* field = LookupField(cx, typeObj, JSID_TO_FLAT_STRING(idval)); if (!field) return JS_FALSE; @@ -4467,8 +4472,11 @@ StructType::AddressOfField(JSContext* cx, uintN argc, jsval* vp) return JS_FALSE; } - const FieldInfo* field = LookupField(cx, typeObj, - JSVAL_TO_STRING(JS_ARGV(cx, vp)[0])); + JSFlatString *str = JS_FlattenString(cx, JSVAL_TO_STRING(JS_ARGV(cx, vp)[0])); + if (!str) + return JS_FALSE; + + const FieldInfo* field = LookupField(cx, typeObj, str); if (!field) return JS_FALSE; @@ -4611,18 +4619,23 @@ PrepareReturnType(JSContext* cx, jsval type) return result; } -static JS_ALWAYS_INLINE bool -IsEllipsis(jsval v) +static JS_ALWAYS_INLINE JSBool +IsEllipsis(JSContext* cx, jsval v, bool* isEllipsis) { + *isEllipsis = false; if (!JSVAL_IS_STRING(v)) - return false; + return true; JSString* str = JSVAL_TO_STRING(v); if (str->length() != 3) + return true; + const jschar* chars = str->getChars(cx); + if (!chars) return false; - const jschar* chars = str->chars(), dot('.'); - return (chars[0] == dot && - chars[1] == dot && - chars[2] == dot); + jschar dot = '.'; + *isEllipsis = (chars[0] == dot && + chars[1] == dot && + chars[2] == dot); + return true; } static JSBool @@ -4737,7 +4750,10 @@ NewFunctionInfo(JSContext* cx, fninfo->mIsVariadic = false; for (JSUint32 i = 0; i < argLength; ++i) { - if (IsEllipsis(argTypes[i])) { + bool isEllipsis; + if (!IsEllipsis(cx, argTypes[i], &isEllipsis)) + return false; + if (isEllipsis) { fninfo->mIsVariadic = true; if (i < 1) { JS_ReportError(cx, "\"...\" may not be the first and only parameter " diff --git a/js/src/ctypes/CTypes.h b/js/src/ctypes/CTypes.h index 238dad269c2..76cf9fa8112 100644 --- a/js/src/ctypes/CTypes.h +++ b/js/src/ctypes/CTypes.h @@ -141,7 +141,10 @@ void AppendString(Vector &v, JSString* str) { JS_ASSERT(str); - v.append(str->chars(), str->length()); + const jschar *chars = str->getChars(NULL); + if (!chars) + return; + v.append(chars, str->length()); } template @@ -154,8 +157,12 @@ AppendString(Vector &v, JSString* str) if (!v.resize(vlen + alen)) return; + const jschar *chars = str->getChars(NULL); + if (!chars) + return; + for (size_t i = 0; i < alen; ++i) - v[i + vlen] = char(str->chars()[i]); + v[i + vlen] = char(chars[i]); } template @@ -186,33 +193,15 @@ PrependString(Vector &v, JSString* str) if (!v.resize(vlen + alen)) return; + const jschar *chars = str->getChars(NULL); + if (!chars) + return; + // Move vector data forward. This is safe since we've already resized. memmove(v.begin() + alen, v.begin(), vlen * sizeof(jschar)); // Copy data to insert. - memcpy(v.begin(), str->chars(), alen * sizeof(jschar)); -} - -template -bool -StringsEqual(Vector &v, Vector &w) -{ - if (v.length() != w.length()) - return false; - - return memcmp(v.begin(), w.begin(), v.length() * sizeof(T)) == 0; -} - -template -bool -StringsEqual(Vector &v, JSString* str) -{ - JS_ASSERT(str); - size_t length = str->length(); - if (v.length() != length) - return false; - - return memcmp(v.begin(), str->chars(), length * sizeof(jschar)) == 0; + memcpy(v.begin(), chars, alen * sizeof(jschar)); } /******************************************************************************* @@ -274,7 +263,7 @@ struct FieldInfo // Hash policy for FieldInfos. struct FieldHashPolicy { - typedef JSString* Key; + typedef JSFlatString* Key; typedef Key Lookup; static uint32 hash(const Lookup &l) { @@ -297,7 +286,7 @@ struct FieldHashPolicy } }; -typedef HashMap FieldInfoHash; +typedef HashMap FieldInfoHash; // Descriptor of ABI, return type, argument types, and variadicity for a // FunctionType. @@ -482,7 +471,7 @@ namespace StructType { JSBool DefineInternal(JSContext* cx, JSObject* typeObj, JSObject* fieldsObj); const FieldInfoHash* GetFieldInfo(JSContext* cx, JSObject* obj); - const FieldInfo* LookupField(JSContext* cx, JSObject* obj, JSString *name); + const FieldInfo* LookupField(JSContext* cx, JSObject* obj, JSFlatString *name); JSObject* BuildFieldsArray(JSContext* cx, JSObject* obj); ffi_type* BuildFFIType(JSContext* cx, JSObject* obj); } diff --git a/js/src/ctypes/Library.cpp b/js/src/ctypes/Library.cpp index 5b85e0ceb86..ae021634f51 100644 --- a/js/src/ctypes/Library.cpp +++ b/js/src/ctypes/Library.cpp @@ -133,12 +133,13 @@ Library::Create(JSContext* cx, jsval path, JSCTypesCallbacks* callbacks) } PRLibSpec libSpec; - JSString* pathStr = JSVAL_TO_STRING(path); + JSFlatString* pathStr = JS_FlattenString(cx, JSVAL_TO_STRING(path)); + if (!pathStr) + return NULL; #ifdef XP_WIN // On Windows, converting to native charset may corrupt path string. // So, we have to use Unicode path directly. - const PRUnichar* pathChars = reinterpret_cast( - JS_GetStringCharsZ(cx, pathStr)); + const PRUnichar* pathChars = JS_GetFlatStringChars(pathStr); if (!pathChars) return NULL; diff --git a/js/src/dtoa.c b/js/src/dtoa.c index df842f498b1..3940ccf184a 100644 --- a/js/src/dtoa.c +++ b/js/src/dtoa.c @@ -502,7 +502,9 @@ newdtoa(void) DtoaState *state = (DtoaState *) MALLOC(sizeof(DtoaState)); if (state) { memset(state, 0, sizeof(DtoaState)); +#ifndef Omit_Private_Memory state->pmem_next = state->private_mem; +#endif } return state; } diff --git a/js/src/jit-test/jit_test.py b/js/src/jit-test/jit_test.py old mode 100644 new mode 100755 index c614273056a..489b1659e4c --- a/js/src/jit-test/jit_test.py +++ b/js/src/jit-test/jit_test.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python + # jit_test.py -- Python harness for JavaScript trace tests. import datetime, os, re, sys, tempfile, traceback @@ -349,8 +351,8 @@ def main(argv): help='Enable the |valgrind| flag, if valgrind is in $PATH.') op.add_option('--valgrind-all', dest='valgrind_all', action='store_true', help='Run all tests with valgrind, if valgrind is in $PATH.') - op.add_option('--jitflags', dest='jitflags', default='j', - help='Example: --jitflags=j,mj to run each test with -j and -m -j') + op.add_option('--jitflags', dest='jitflags', default='mjp', + help='Example: --jitflags=j,mj,mjp to run each test with -j, -m -j, -m -j -p [default=%default]') op.add_option('--avoid-stdio', dest='avoid_stdio', action='store_true', help='Use js-shell file indirection instead of piping stdio.') op.add_option('--write-failure-output', dest='write_failure_output', action='store_true', diff --git a/js/src/jit-test/tests/arguments/strict-args.js b/js/src/jit-test/tests/arguments/strict-args.js index c85df37579f..82682ff2aa7 100644 --- a/js/src/jit-test/tests/arguments/strict-args.js +++ b/js/src/jit-test/tests/arguments/strict-args.js @@ -13,7 +13,7 @@ function strictArgs(a) } var a1, a2, a3; -for (var i = 0; i < 5; i++) +for (var i = 0; i < HOTLOOP+1; i++) { a1 = strictArgs(); a2 = strictArgs(1); diff --git a/js/src/jit-test/tests/basic/bug618350.js b/js/src/jit-test/tests/basic/bug618350.js new file mode 100644 index 00000000000..61e72d0a0e2 --- /dev/null +++ b/js/src/jit-test/tests/basic/bug618350.js @@ -0,0 +1,24 @@ + +var global = 0; + +Object.defineProperty(Object.prototype, 0, {set: function() { global++; }}); + +for (var x = 0; x < 20; ++x) + [1,2]; +assertEq(global, 0); + +Object.defineProperty(Object.prototype, 1, {set: function() { global++; }}); + +for (var x = 0; x < 20; ++x) + [1,2]; +assertEq(global, 0); + +Object.defineProperty(Object.prototype, "b", {set: function() { global++; }}); + +for (var x = 0; x < 20; ++x) { + var s = { a:0, b:1, 0: 2, 1: 3 }; +} +assertEq(global, 0); + +assertEq([42][0], 42); +assertEq([42].length, 1); diff --git a/js/src/jit-test/tests/basic/bug620838.js b/js/src/jit-test/tests/basic/bug620838.js new file mode 100644 index 00000000000..c82933ae806 --- /dev/null +++ b/js/src/jit-test/tests/basic/bug620838.js @@ -0,0 +1,22 @@ +function g() { + return "global"; +} + +function q(fun) { + return fun(); +} + +function f(x) { + if (x) { + function g() { + return "local"; + } + var ans = q(function() { + return g(); + }); + } + g = null; + return ans; +} + +assertEq(f(true), "local"); diff --git a/js/src/jit-test/tests/basic/testBug616119.js b/js/src/jit-test/tests/basic/testBug616119.js new file mode 100644 index 00000000000..c41ab54cff4 --- /dev/null +++ b/js/src/jit-test/tests/basic/testBug616119.js @@ -0,0 +1,6 @@ +// don't assert +for (a = 0; a < 9; ++a) { + M: for (let c in >) { + break M + } +} diff --git a/js/src/jit-test/tests/basic/testChangingTypeDuringRecording.js b/js/src/jit-test/tests/basic/testChangingTypeDuringRecording.js new file mode 100644 index 00000000000..3dc190254ce --- /dev/null +++ b/js/src/jit-test/tests/basic/testChangingTypeDuringRecording.js @@ -0,0 +1,19 @@ +/* Touch/init early so global shape doesn't change in loop */ +var SetOnIter = HOTLOOP - 1; +var x = 3; +var i = 0; +assertEq(true, true); + +for (i = 0; i < SetOnIter + 10; ++i) { + x = 3; + setGlobalPropIf(i == SetOnIter, 'x', 'pretty'); + assertEq(x == 'pretty', i == SetOnIter); + x = 3; +} + +for (i = 0; i < SetOnIter + 10; ++i) { + x = 3; + defGlobalPropIf(i == SetOnIter, 'x', { value:'pretty' }); + assertEq(x == 'pretty', i == SetOnIter); + x = 3; +} diff --git a/js/src/jit-test/tests/basic/testNegativeArrayLength.js b/js/src/jit-test/tests/basic/testNegativeArrayLength.js index 7199c7a4033..77f1335feb9 100644 --- a/js/src/jit-test/tests/basic/testNegativeArrayLength.js +++ b/js/src/jit-test/tests/basic/testNegativeArrayLength.js @@ -1,6 +1,6 @@ function f() { try { - for ( var i = 1; i > -2; i-- ) + for ( var i = HOTLOOP-1; i > -2; i-- ) new Array(i).join('*'); } catch (e) { return e instanceof RangeError; diff --git a/js/src/jit-test/tests/basic/testStringPropIncrement.js b/js/src/jit-test/tests/basic/testStringPropIncrement.js new file mode 100644 index 00000000000..4832d90c6cf --- /dev/null +++ b/js/src/jit-test/tests/basic/testStringPropIncrement.js @@ -0,0 +1,16 @@ +function f() { + var o = { n: "" }; + for (var i = 0; i < 2*RUNLOOP; ++i) { + o.n = ""; + if (o.n++) { } + } +} + +f(); + +checkStats({ + recorderStarted: 1, + recorderAborted: 0, + traceCompleted: 1, + traceTriggered: 1 +}); \ No newline at end of file diff --git a/js/src/jit-test/tests/basic/testToLocaleString.js b/js/src/jit-test/tests/basic/testToLocaleString.js new file mode 100644 index 00000000000..158e08b462f --- /dev/null +++ b/js/src/jit-test/tests/basic/testToLocaleString.js @@ -0,0 +1,2 @@ +var str = (3.14).toLocaleString(); +assertEq(str[str.length-1], "4"); \ No newline at end of file diff --git a/js/src/jit-test/tests/jaeger/bug620643.js b/js/src/jit-test/tests/jaeger/bug620643.js new file mode 100644 index 00000000000..fcb096e953d --- /dev/null +++ b/js/src/jit-test/tests/jaeger/bug620643.js @@ -0,0 +1 @@ +var a = new Int32Array(); +(a[0]={}); diff --git a/js/src/jsapi-tests/Makefile.in b/js/src/jsapi-tests/Makefile.in index e70c7adf24d..05eaa965011 100644 --- a/js/src/jsapi-tests/Makefile.in +++ b/js/src/jsapi-tests/Makefile.in @@ -76,11 +76,12 @@ CPPSRCS = \ testUTF8.cpp \ testVersion.cpp \ testXDR.cpp \ + testCustomIterator.cpp \ $(NULL) DEFINES += -DEXPORT_JS_API -LIBS = $(NSPR_LIBS) $(DEPTH)/$(LIB_PREFIX)js_static.$(LIB_SUFFIX) +LIBS = $(DEPTH)/$(LIB_PREFIX)js_static.$(LIB_SUFFIX) $(NSPR_LIBS) LOCAL_INCLUDES += -I$(topsrcdir) -I.. diff --git a/js/src/jsapi-tests/testCustomIterator.cpp b/js/src/jsapi-tests/testCustomIterator.cpp new file mode 100644 index 00000000000..d495d334098 --- /dev/null +++ b/js/src/jsapi-tests/testCustomIterator.cpp @@ -0,0 +1,81 @@ +#include "tests.h" + +#include "jsvalue.h" + +int count = 0; + +static JSBool +IterNext(JSContext *cx, uintN argc, jsval *vp) +{ + if (count++ == 100) + return JS_ThrowStopIteration(cx); + JS_SET_RVAL(cx, vp, INT_TO_JSVAL(count)); + return true; +} + +static JSObject * +IterHook(JSContext *cx, JSObject *obj, JSBool keysonly) +{ + JSObject *iterObj = JS_NewObject(cx, NULL, NULL, NULL); + if (!iterObj) + return NULL; + if (!JS_DefineFunction(cx, iterObj, "next", IterNext, 0, 0)) + return NULL; + return iterObj; +} + +js::Class HasCustomIterClass = { + "HasCustomIter", + 0, + js::PropertyStub, + js::PropertyStub, + js::PropertyStub, + js::PropertyStub, + js::EnumerateStub, + js::ResolveStub, + js::ConvertStub, + NULL, + NULL, /* reserved0 */ + NULL, /* checkAccess */ + NULL, /* call */ + NULL, /* construct */ + NULL, /* xdrObject */ + NULL, /* hasInstance */ + NULL, /* mark */ + { + NULL, + NULL, + NULL, + IterHook, + NULL + } +}; + +JSBool +IterClassConstructor(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *obj = JS_NewObjectForConstructor(cx, vp); + if (!obj) + return false; + JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(obj)); + return true; +} + +BEGIN_TEST(testCustomIterator_bug612523) +{ + CHECK(JS_InitClass(cx, JS_GetGlobalObject(cx), NULL, Jsvalify(&HasCustomIterClass), + IterClassConstructor, 0, NULL, NULL, NULL, NULL)); + + jsval result; + EVAL("var o = new HasCustomIter(); \n" + "var j = 0; \n" + "for (var i in o) { ++j; }; \n" + "j;", &result); + + CHECK(JSVAL_IS_INT(result)); + CHECK(JSVAL_TO_INT(result) == 100); + CHECK(count == 101); + + return true; +} +END_TEST(testCustomIterator_bug612523) diff --git a/js/src/jsapi-tests/testIntString.cpp b/js/src/jsapi-tests/testIntString.cpp index bcb698866ff..c46c43f5cf5 100644 --- a/js/src/jsapi-tests/testIntString.cpp +++ b/js/src/jsapi-tests/testIntString.cpp @@ -12,28 +12,28 @@ BEGIN_TEST(testIntString_bug515273) EVAL("'1';", v.addr()); JSString *str = JSVAL_TO_STRING(v.value()); CHECK(JSString::isStatic(str)); - CHECK(JS_MatchStringAndAscii(str, "1")); + CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(str), "1")); EVAL("'42';", v.addr()); str = JSVAL_TO_STRING(v.value()); CHECK(JSString::isStatic(str)); - CHECK(JS_MatchStringAndAscii(str, "42")); + CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(str), "42")); EVAL("'111';", v.addr()); str = JSVAL_TO_STRING(v.value()); CHECK(JSString::isStatic(str)); - CHECK(JS_MatchStringAndAscii(str, "111")); + CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(str), "111")); /* Test other types of static strings. */ EVAL("'a';", v.addr()); str = JSVAL_TO_STRING(v.value()); CHECK(JSString::isStatic(str)); - CHECK(JS_MatchStringAndAscii(str, "a")); + CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(str), "a")); EVAL("'bc';", v.addr()); str = JSVAL_TO_STRING(v.value()); CHECK(JSString::isStatic(str)); - CHECK(JS_MatchStringAndAscii(str, "bc")); + CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(str), "bc")); return true; } diff --git a/js/src/jsapi-tests/testLookup.cpp b/js/src/jsapi-tests/testLookup.cpp index dbf52551a06..1fd219e9455 100644 --- a/js/src/jsapi-tests/testLookup.cpp +++ b/js/src/jsapi-tests/testLookup.cpp @@ -41,7 +41,10 @@ document_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject ** return false; if (JSVAL_IS_STRING(v.value())) { JSString *str = JSVAL_TO_STRING(v.value()); - if (JS_MatchStringAndAscii(str, "all") && !(flags & JSRESOLVE_DETECTING)) { + JSFlatString *flatStr = JS_FlattenString(cx, str); + if (!flatStr) + return false; + if (JS_FlatStringEqualsAscii(flatStr, "all") && !(flags & JSRESOLVE_DETECTING)) { JSBool ok = JS_DefinePropertyById(cx, obj, id, JSVAL_TRUE, NULL, NULL, 0); *objp = ok ? obj : NULL; return ok; diff --git a/js/src/jsapi-tests/testSameValue.cpp b/js/src/jsapi-tests/testSameValue.cpp index 52c910dc26f..22096744bd4 100644 --- a/js/src/jsapi-tests/testSameValue.cpp +++ b/js/src/jsapi-tests/testSameValue.cpp @@ -16,7 +16,9 @@ BEGIN_TEST(testSameValue) */ jsval v1 = DOUBLE_TO_JSVAL(0.0); jsval v2 = DOUBLE_TO_JSVAL(-0.0); - CHECK(!JS_SameValue(cx, v1, v2)); + JSBool same; + CHECK(JS_SameValue(cx, v1, v2, &same)); + CHECK(!same); return true; } END_TEST(testSameValue) diff --git a/js/src/jsapi-tests/testTrap.cpp b/js/src/jsapi-tests/testTrap.cpp index 8ffbd537101..5cd0466534f 100644 --- a/js/src/jsapi-tests/testTrap.cpp +++ b/js/src/jsapi-tests/testTrap.cpp @@ -62,7 +62,7 @@ BEGIN_TEST(testTrap_gc) JS_GC(cx); - CHECK(JS_MatchStringAndAscii(trapClosure, trapClosureText)); + CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(trapClosure), trapClosureText)); // execute CHECK(JS_ExecuteScript(cx, global, script, v2.addr())); @@ -70,7 +70,7 @@ BEGIN_TEST(testTrap_gc) JS_GC(cx); - CHECK(JS_MatchStringAndAscii(trapClosure, trapClosureText)); + CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(trapClosure), trapClosureText)); return true; } diff --git a/js/src/jsapi-tests/tests.h b/js/src/jsapi-tests/tests.h index b8a21e300bf..e4a669a7715 100644 --- a/js/src/jsapi-tests/tests.h +++ b/js/src/jsapi-tests/tests.h @@ -192,7 +192,8 @@ class JSAPITest bool checkSame(jsval actual, jsval expected, const char *actualExpr, const char *expectedExpr, const char *filename, int lineno) { - return JS_SameValue(cx, actual, expected) || + JSBool same; + return (JS_SameValue(cx, actual, expected, &same) && same) || fail(JSAPITestString("CHECK_SAME failed: expected JS_SameValue(cx, ") + actualExpr + ", " + expectedExpr + "), got !JS_SameValue(cx, " + toSource(actual) + ", " + toSource(expected) + ")", filename, lineno); diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 730a3411d11..70da353921a 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -111,6 +111,16 @@ using namespace js; using namespace js::gc; +static JSClass dummy_class = { + "jdummy", + JSCLASS_GLOBAL_FLAGS, + JS_PropertyStub, JS_PropertyStub, + JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, + JS_ConvertStub, NULL, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + class AutoVersionAPI { JSContext * const cx; @@ -571,17 +581,17 @@ JS_GetTypeName(JSContext *cx, JSType type) } JS_PUBLIC_API(JSBool) -JS_StrictlyEqual(JSContext *cx, jsval v1, jsval v2) +JS_StrictlyEqual(JSContext *cx, jsval v1, jsval v2, JSBool *equal) { assertSameCompartment(cx, v1, v2); - return StrictlyEqual(cx, Valueify(v1), Valueify(v2)); + return StrictlyEqual(cx, Valueify(v1), Valueify(v2), equal); } JS_PUBLIC_API(JSBool) -JS_SameValue(JSContext *cx, jsval v1, jsval v2) +JS_SameValue(JSContext *cx, jsval v1, jsval v2, JSBool *same) { assertSameCompartment(cx, v1, v2); - return SameValue(Valueify(v1), Valueify(v2), cx); + return SameValue(cx, Valueify(v1), Valueify(v2), same); } /************************************************************************/ @@ -1169,6 +1179,22 @@ JS_EnterCrossCompartmentCall(JSContext *cx, JSObject *target) return reinterpret_cast(call); } +JS_PUBLIC_API(JSCrossCompartmentCall *) +JS_EnterCrossCompartmentCallScript(JSContext *cx, JSScript *target) +{ + CHECK_REQUEST(cx); + + JS_ASSERT(target); + JSObject *scriptObject = target->u.object; + if (!scriptObject) { + SwitchToCompartment sc(cx, target->compartment); + scriptObject = JS_NewGlobalObject(cx, &dummy_class); + if (!scriptObject) + return NULL; + } + return JS_EnterCrossCompartmentCall(cx, scriptObject); +} + JS_PUBLIC_API(void) JS_LeaveCrossCompartmentCall(JSCrossCompartmentCall *call) { @@ -1190,6 +1216,18 @@ JSAutoEnterCompartment::enter(JSContext *cx, JSObject *target) return call != NULL; } +bool +JSAutoEnterCompartment::enter(JSContext *cx, JSScript *target) +{ + JS_ASSERT(!call); + if (cx->compartment == target->compartment) { + call = reinterpret_cast(1); + return true; + } + call = JS_EnterCrossCompartmentCallScript(cx, target); + return call != NULL; +} + void JSAutoEnterCompartment::enterAndIgnoreErrors(JSContext *cx, JSObject *target) { @@ -2199,8 +2237,14 @@ JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, void *thing, ui } case JSTRACE_STRING: - PutEscapedString(buf, bufsize, (JSString *)thing, 0); + { + JSString *str = (JSString *)thing; + if (str->isLinear()) + PutEscapedString(buf, bufsize, str->assertIsLinear(), 0); + else + JS_snprintf(buf, bufsize, "", (int)str->length()); break; + } #if JS_HAS_XML_SUPPORT case JSTRACE_XML: @@ -2662,7 +2706,7 @@ JS_GetGCParameterForThread(JSContext *cx, JSGCParamKey key) { JS_ASSERT(key == JSGC_MAX_CODE_CACHE_BYTES); #ifdef JS_TRACER - return JS_THREAD_DATA(cx)->traceMonitor.maxCodeCacheBytes; + return JS_THREAD_DATA(cx)->maxCodeCacheBytes; #else return 0; #endif @@ -5168,17 +5212,8 @@ JS_RestoreFrameChain(JSContext *cx, JSStackFrame *fp) JS_PUBLIC_API(JSString *) JS_NewStringCopyN(JSContext *cx, const char *s, size_t n) { - jschar *js; - JSString *str; - CHECK_REQUEST(cx); - js = js_InflateString(cx, s, &n); - if (!js) - return NULL; - str = js_NewString(cx, js, n); - if (!str) - cx->free(js); - return str; + return js_NewStringCopyN(cx, s, n); } JS_PUBLIC_API(JSString *) @@ -5207,6 +5242,16 @@ JS_StringHasBeenInterned(JSString *str) return str->isAtomized(); } +JS_PUBLIC_API(JSString *) +JS_InternJSString(JSContext *cx, JSString *str) +{ + CHECK_REQUEST(cx); + JSAtom *atom = js_AtomizeString(cx, str, 0); + if (!atom) + return NULL; + return ATOM_TO_STRING(atom); +} + JS_PUBLIC_API(JSString *) JS_InternString(JSContext *cx, const char *s) { @@ -5260,45 +5305,6 @@ JS_InternUCString(JSContext *cx, const jschar *s) return JS_InternUCStringN(cx, s, js_strlen(s)); } -JS_PUBLIC_API(jschar *) -JS_GetStringChars(JSString *str) -{ - size_t n, size; - jschar *s; - - str->ensureNotRope(); - - /* - * API botch: we have no cx to report out-of-memory when undepending - * strings, so we replace JSString::undepend with explicit malloc call and - * ignore its errors. - * - * If we fail to convert a dependent string into an independent one, our - * caller will not be guaranteed a \u0000 terminator as a backstop. This - * may break some clients who already misbehave on embedded NULs. - * - * The gain of dependent strings, which cure quadratic and cubic growth - * rate bugs in string concatenation, is worth this slight loss in API - * compatibility. - */ - if (str->isDependent()) { - n = str->dependentLength(); - size = (n + 1) * sizeof(jschar); - s = (jschar *) js_malloc(size); - if (s) { - memcpy(s, str->dependentChars(), n * sizeof *s); - s[n] = 0; - str->initFlat(s, n); - } else { - s = str->dependentChars(); - } - } else { - str->flatClearExtensible(); - s = str->flatChars(); - } - return s; -} - JS_PUBLIC_API(size_t) JS_GetStringLength(JSString *str) { @@ -5306,41 +5312,102 @@ JS_GetStringLength(JSString *str) } JS_PUBLIC_API(const jschar *) -JS_GetStringCharsAndLength(JSString *str, size_t *lengthp) +JS_GetStringCharsZ(JSContext *cx, JSString *str) { - *lengthp = str->length(); - return str->chars(); + CHECK_REQUEST(cx); + assertSameCompartment(cx, str); + return str->getCharsZ(cx); } JS_PUBLIC_API(const jschar *) -JS_GetStringCharsZ(JSContext *cx, JSString *str) +JS_GetStringCharsZAndLength(JSContext *cx, JSString *str, size_t *plength) { + CHECK_REQUEST(cx); assertSameCompartment(cx, str); - return str->undepend(cx); + *plength = str->length(); + return str->getCharsZ(cx); } -JS_PUBLIC_API(intN) -JS_CompareStrings(JSString *str1, JSString *str2) +JS_PUBLIC_API(const jschar *) +JS_GetStringCharsAndLength(JSContext *cx, JSString *str, size_t *plength) { - return js_CompareStrings(str1, str2); + CHECK_REQUEST(cx); + assertSameCompartment(cx, str); + *plength = str->length(); + return str->getChars(cx); +} + +JS_PUBLIC_API(const jschar *) +JS_GetInternedStringChars(JSString *str) +{ + JS_ASSERT(str->isAtomized()); + return str->flatChars(); +} + +JS_PUBLIC_API(const jschar *) +JS_GetInternedStringCharsAndLength(JSString *str, size_t *plength) +{ + JS_ASSERT(str->isAtomized()); + *plength = str->flatLength(); + return str->flatChars(); +} + +extern JS_PUBLIC_API(JSFlatString *) +JS_FlattenString(JSContext *cx, JSString *str) +{ + CHECK_REQUEST(cx); + assertSameCompartment(cx, str); + return str->getCharsZ(cx) ? (JSFlatString *)str : NULL; +} + +extern JS_PUBLIC_API(const jschar *) +JS_GetFlatStringChars(JSFlatString *str) +{ + return str->chars(); } JS_PUBLIC_API(JSBool) -JS_MatchStringAndAscii(JSString *str, const char *asciiBytes) +JS_CompareStrings(JSContext *cx, JSString *str1, JSString *str2, int32 *result) { - return MatchStringAndAscii(str, asciiBytes); + return CompareStrings(cx, str1, str2, result); +} + +JS_PUBLIC_API(JSBool) +JS_StringEqualsAscii(JSContext *cx, JSString *str, const char *asciiBytes, JSBool *match) +{ + JSLinearString *linearStr = str->ensureLinear(cx); + if (!linearStr) + return false; + *match = StringEqualsAscii(linearStr, asciiBytes); + return true; +} + +JS_PUBLIC_API(JSBool) +JS_FlatStringEqualsAscii(JSFlatString *str, const char *asciiBytes) +{ + return StringEqualsAscii(str, asciiBytes); } JS_PUBLIC_API(size_t) -JS_PutEscapedString(char *buffer, size_t size, JSString *str, char quote) +JS_PutEscapedFlatString(char *buffer, size_t size, JSFlatString *str, char quote) { return PutEscapedString(buffer, size, str, quote); } +JS_PUBLIC_API(size_t) +JS_PutEscapedString(JSContext *cx, char *buffer, size_t size, JSString *str, char quote) +{ + JSLinearString *linearStr = str->ensureLinear(cx); + if (!linearStr) + return size_t(-1); + return PutEscapedString(buffer, size, linearStr, quote); +} + JS_PUBLIC_API(JSBool) JS_FileEscapedString(FILE *fp, JSString *str, char quote) { - return FileEscapedString(fp, str, quote); + JSLinearString *linearStr = str->ensureLinear(NULL); + return linearStr && FileEscapedString(fp, linearStr, quote); } JS_PUBLIC_API(JSString *) @@ -5368,7 +5435,7 @@ JS_PUBLIC_API(const jschar *) JS_UndependString(JSContext *cx, JSString *str) { CHECK_REQUEST(cx); - return str->undepend(cx); + return str->getCharsZ(cx); } JS_PUBLIC_API(JSBool) @@ -5404,13 +5471,19 @@ JS_DecodeBytes(JSContext *cx, const char *src, size_t srclen, jschar *dst, size_ JS_PUBLIC_API(char *) JS_EncodeString(JSContext *cx, JSString *str) { - return js_DeflateString(cx, str->chars(), str->length()); + const jschar *chars = str->getChars(cx); + if (!chars) + return NULL; + return js_DeflateString(cx, chars, str->length()); } JS_PUBLIC_API(size_t) JS_GetStringEncodingLength(JSContext *cx, JSString *str) { - return js_GetDeflatedStringLength(cx, str->chars(), str->length()); + const jschar *chars = str->getChars(cx); + if (!chars) + return size_t(-1); + return js_GetDeflatedStringLength(cx, chars, str->length()); } JS_PUBLIC_API(size_t) @@ -5422,12 +5495,15 @@ JS_EncodeStringToBuffer(JSString *str, char *buffer, size_t length) * error. */ size_t writtenLength = length; - if (js_DeflateStringToBuffer(NULL, str->chars(), str->length(), buffer, &writtenLength)) { + const jschar *chars = str->getChars(NULL); + if (!chars) + return size_t(-1); + if (js_DeflateStringToBuffer(NULL, chars, str->length(), buffer, &writtenLength)) { JS_ASSERT(writtenLength <= length); return writtenLength; } JS_ASSERT(writtenLength <= length); - size_t necessaryLength = js_GetDeflatedStringLength(NULL, str->chars(), str->length()); + size_t necessaryLength = js_GetDeflatedStringLength(NULL, chars, str->length()); if (necessaryLength == size_t(-1)) return size_t(-1); if (writtenLength != length) { diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 4a5b76c020f..7d262916eaa 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -500,7 +500,7 @@ extern JS_PUBLIC_DATA(jsid) JSID_EMPTY; #define JSFUN_HEAVYWEIGHT_TEST(f) ((f) & JSFUN_HEAVYWEIGHT) -#define JSFUN_PRIMITIVE_THIS 0x0100 /* |this| may be a primitive value */ +/* 0x0100 is unused */ #define JSFUN_CONSTRUCTOR 0x0200 /* native that can be called as a ctor without creating a this object */ @@ -702,10 +702,10 @@ extern JS_PUBLIC_API(const char *) JS_GetTypeName(JSContext *cx, JSType type); extern JS_PUBLIC_API(JSBool) -JS_StrictlyEqual(JSContext *cx, jsval v1, jsval v2); +JS_StrictlyEqual(JSContext *cx, jsval v1, jsval v2, JSBool *equal); extern JS_PUBLIC_API(JSBool) -JS_SameValue(JSContext *cx, jsval v1, jsval v2); +JS_SameValue(JSContext *cx, jsval v1, jsval v2, JSBool *same); /************************************************************************/ @@ -979,6 +979,9 @@ JS_SetWrapObjectCallbacks(JSRuntime *rt, extern JS_PUBLIC_API(JSCrossCompartmentCall *) JS_EnterCrossCompartmentCall(JSContext *cx, JSObject *target); +extern JS_PUBLIC_API(JSCrossCompartmentCall *) +JS_EnterCrossCompartmentCallScript(JSContext *cx, JSScript *target); + extern JS_PUBLIC_API(void) JS_LeaveCrossCompartmentCall(JSCrossCompartmentCall *call); @@ -1009,6 +1012,8 @@ class JS_PUBLIC_API(JSAutoEnterCompartment) bool enter(JSContext *cx, JSObject *target); + bool enter(JSContext *cx, JSScript *target); + void enterAndIgnoreErrors(JSContext *cx, JSObject *target); bool entered() const { return call != NULL; } @@ -2931,6 +2936,9 @@ JS_NewStringCopyN(JSContext *cx, const char *s, size_t n); extern JS_PUBLIC_API(JSString *) JS_NewStringCopyZ(JSContext *cx, const char *s); +extern JS_PUBLIC_API(JSString *) +JS_InternJSString(JSContext *cx, JSString *str); + extern JS_PUBLIC_API(JSString *) JS_InternString(JSContext *cx, const char *s); @@ -2949,36 +2957,106 @@ JS_InternUCStringN(JSContext *cx, const jschar *s, size_t length); extern JS_PUBLIC_API(JSString *) JS_InternUCString(JSContext *cx, const jschar *s); +extern JS_PUBLIC_API(JSBool) +JS_CompareStrings(JSContext *cx, JSString *str1, JSString *str2, int32 *result); + +extern JS_PUBLIC_API(JSBool) +JS_StringEqualsAscii(JSContext *cx, JSString *str, const char *asciiBytes, JSBool *match); + +extern JS_PUBLIC_API(size_t) +JS_PutEscapedString(JSContext *cx, char *buffer, size_t size, JSString *str, char quote); + +extern JS_PUBLIC_API(JSBool) +JS_FileEscapedString(FILE *fp, JSString *str, char quote); + /* - * Deprecated. Use JS_GetStringCharsZ() instead. + * Extracting string characters and length. + * + * While getting the length of a string is infallible, getting the chars can + * fail. As indicated by the lack of a JSContext parameter, there are two + * special cases where getting the chars is infallible: + * + * The first case is interned strings, i.e., strings from JS_InternString or + * JSID_TO_STRING(id), using JS_GetInternedStringChars*. + * + * The second case is "flat" strings that have been explicitly prepared in a + * fallible context by JS_FlattenString. To catch errors, a separate opaque + * JSFlatString type is returned by JS_FlattenString and expected by + * JS_GetFlatStringChars. Note, though, that this is purely a syntactic + * distinction: the input and output of JS_FlattenString are the same actual + * GC-thing so only one needs to be rooted. If a JSString is known to be flat, + * JS_ASSERT_STRING_IS_FLAT can be used to make a debug-checked cast. Example: + * + * // in a fallible context + * JSFlatString *fstr = JS_FlattenString(cx, str); + * if (!fstr) + * return JS_FALSE; + * JS_ASSERT(fstr == JS_ASSERT_STRING_IS_FLAT(str)); + * + * // in an infallible context, for the same 'str' + * const jschar *chars = JS_GetFlatStringChars(fstr) + * JS_ASSERT(chars); + * + * The CharsZ APIs guarantee that the returned array has a null character at + * chars[length]. This can require additional copying so clients should prefer + * APIs without CharsZ if possible. The infallible functions also return + * null-terminated arrays. (There is no additional cost or non-Z alternative + * for the infallible functions, so 'Z' is left out of the identifier.) */ -extern JS_PUBLIC_API(jschar *) -JS_GetStringChars(JSString *str); extern JS_PUBLIC_API(size_t) JS_GetStringLength(JSString *str); -/* - * Return the char array and length for this string. The array is not - * null-terminated. - */ extern JS_PUBLIC_API(const jschar *) -JS_GetStringCharsAndLength(JSString *str, size_t *lengthp); +JS_GetStringCharsAndLength(JSContext *cx, JSString *str, size_t *length); + +extern JS_PUBLIC_API(const jschar *) +JS_GetInternedStringChars(JSString *str); + +extern JS_PUBLIC_API(const jschar *) +JS_GetInternedStringCharsAndLength(JSString *str, size_t *length); extern JS_PUBLIC_API(const jschar *) JS_GetStringCharsZ(JSContext *cx, JSString *str); -extern JS_PUBLIC_API(intN) -JS_CompareStrings(JSString *str1, JSString *str2); +extern JS_PUBLIC_API(const jschar *) +JS_GetStringCharsZAndLength(JSContext *cx, JSString *str, size_t *length); + +extern JS_PUBLIC_API(JSFlatString *) +JS_FlattenString(JSContext *cx, JSString *str); + +extern JS_PUBLIC_API(const jschar *) +JS_GetFlatStringChars(JSFlatString *str); + +static JS_ALWAYS_INLINE JSFlatString * +JSID_TO_FLAT_STRING(jsid id) +{ + JS_ASSERT(JSID_IS_STRING(id)); + return (JSFlatString *)(JSID_BITS(id)); +} + +static JS_ALWAYS_INLINE JSFlatString * +JS_ASSERT_STRING_IS_FLAT(JSString *str) +{ + JS_ASSERT(JS_GetFlatStringChars((JSFlatString *)str)); + return (JSFlatString *)str; +} + +static JS_ALWAYS_INLINE JSString * +JS_FORGET_STRING_FLATNESS(JSFlatString *fstr) +{ + return (JSString *)fstr; +} + +/* + * Additional APIs that avoid fallibility when given a flat string. + */ extern JS_PUBLIC_API(JSBool) -JS_MatchStringAndAscii(JSString *str, const char *asciiBytes); +JS_FlatStringEqualsAscii(JSFlatString *str, const char *asciiBytes); extern JS_PUBLIC_API(size_t) -JS_PutEscapedString(char *buffer, size_t size, JSString *str, char quote); - -extern JS_PUBLIC_API(JSBool) -JS_FileEscapedString(FILE *fp, JSString *str, char quote); +JS_PutEscapedFlatString(char *buffer, size_t size, JSFlatString *str, char quote); /* * This function is now obsolete and behaves the same as JS_NewUCString. Use diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 66f523e69f8..75e6cc09e6e 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -143,10 +143,10 @@ ENSURE_SLOW_ARRAY(JSContext *cx, JSObject *obj) * 'id' is passed as a jsboxedword since the given id need not necessarily hold * an atomized string. */ -JSBool -js_StringIsIndex(JSString *str, jsuint *indexp) +bool +js_StringIsIndex(JSLinearString *str, jsuint *indexp) { - jschar *cp = str->chars(); + const jschar *cp = str->chars(); if (JS7_ISDEC(*cp) && str->length() < sizeof(MAXSTR)) { jsuint index = JS7_UNDEC(*cp++); jsuint oldIndex = 0; @@ -166,10 +166,10 @@ js_StringIsIndex(JSString *str, jsuint *indexp) (oldIndex == (MAXINDEX / 10) && c < (MAXINDEX % 10)))) { *indexp = index; - return JS_TRUE; + return true; } } - return JS_FALSE; + return false; } static bool @@ -853,21 +853,35 @@ static JSBool array_defineProperty(JSContext *cx, JSObject *obj, jsid id, const Value *value, PropertyOp getter, PropertyOp setter, uintN attrs) { - uint32 i = 0; // init to shut GCC up - JSBool isIndex; - if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) return JS_TRUE; - isIndex = js_IdIsIndex(id, &i); - if (!isIndex || attrs != JSPROP_ENUMERATE) { - if (!ENSURE_SLOW_ARRAY(cx, obj)) - return JS_FALSE; + if (!obj->isDenseArray()) return js_DefineProperty(cx, obj, id, value, getter, setter, attrs); - } - Value tmp = *value; - return array_setProperty(cx, obj, id, &tmp, false); + do { + uint32 i = 0; // init to shut GCC up + bool isIndex = js_IdIsIndex(id, &i); + if (!isIndex || attrs != JSPROP_ENUMERATE) + break; + + JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, i, 1); + if (result != JSObject::ED_OK) { + if (result == JSObject::ED_FAILED) + return false; + JS_ASSERT(result == JSObject::ED_SPARSE); + break; + } + + if (i >= obj->getArrayLength()) + obj->setArrayLength(i + 1); + obj->setDenseArrayElement(i, *value); + return true; + } while (false); + + if (!obj->makeDenseArraySlow(cx)) + return false; + return js_DefineProperty(cx, obj, id, value, getter, setter, attrs); } static JSBool @@ -1149,12 +1163,13 @@ array_toSource(JSContext *cx, uintN argc, Value *vp) goto out; } vp->setString(str); - const jschar *chars; - size_t charlen; - str->getCharsAndLength(chars, charlen); + + const jschar *chars = str->getChars(cx); + if (!chars) + goto out; /* Append element to buffer. */ - if (!cb.append(chars, charlen)) + if (!cb.append(chars, chars + str->length())) goto out; if (index + 1 != length) { if (!js_AppendLiteral(cb, ", ")) @@ -1188,6 +1203,20 @@ array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale, { JS_CHECK_RECURSION(cx, return false); + /* Get characters to use for the separator. */ + static const jschar comma = ','; + const jschar *sep; + size_t seplen; + if (sepstr) { + seplen = sepstr->length(); + sep = sepstr->getChars(cx); + if (!sep) + return false; + } else { + sep = , + seplen = 1; + } + /* * Use HashTable entry as the cycle indicator. On first visit, create the * entry, and, when leaving, remove the entry. @@ -1197,10 +1226,8 @@ array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale, uint32 genBefore; if (!hashp) { /* Not in hash table, so not a cycle. */ - if (!cx->busyArrays.add(hashp, obj)) { - JS_ReportOutOfMemory(cx); + if (!cx->busyArrays.add(hashp, obj)) return false; - } genBefore = cx->busyArrays.generation(); } else { /* Cycle, so return empty string. */ @@ -1214,17 +1241,6 @@ array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale, MUST_FLOW_THROUGH("out"); bool ok = false; - /* Get characters to use for the separator. */ - static const jschar comma = ','; - const jschar *sep; - size_t seplen; - if (sepstr) { - sepstr->getCharsAndLength(sep, seplen); - } else { - sep = , - seplen = 1; - } - /* * This object will take responsibility for the jschar buffer until the * buffer is transferred to the returned JSString. @@ -1710,15 +1726,10 @@ comparator_stack_cast(JSRedComparator func) static int sort_compare_strings(void *arg, const void *a, const void *b, int *result) { - const Value *av = (const Value *)a, *bv = (const Value *)b; - - JS_ASSERT(av->isString()); - JS_ASSERT(bv->isString()); - if (!JS_CHECK_OPERATION_LIMIT((JSContext *)arg)) - return JS_FALSE; - - *result = (int) js_CompareStrings(av->toString(), bv->toString()); - return JS_TRUE; + JSContext *cx = (JSContext *)arg; + JSString *astr = ((const Value *)a)->toString(); + JSString *bstr = ((const Value *)b)->toString(); + return JS_CHECK_OPERATION_LIMIT(cx) && CompareStrings(cx, astr, bstr, result); } JSBool @@ -2615,9 +2626,14 @@ array_indexOfHelper(JSContext *cx, JSBool isLast, uintN argc, Value *vp) !GetElement(cx, obj, (jsuint)i, &hole, vp)) { return JS_FALSE; } - if (!hole && StrictlyEqual(cx, *vp, tosearch)) { - vp->setNumber(i); - return JS_TRUE; + if (!hole) { + JSBool equal; + if (!StrictlyEqual(cx, *vp, tosearch, &equal)) + return JS_FALSE; + if (equal) { + vp->setNumber(i); + return JS_TRUE; + } } if (i == stop) goto not_found; diff --git a/js/src/jsarray.h b/js/src/jsarray.h index b9953168d81..a230b50fd56 100644 --- a/js/src/jsarray.h +++ b/js/src/jsarray.h @@ -44,7 +44,9 @@ */ #include "jsprvtd.h" #include "jspubtd.h" +#include "jsatom.h" #include "jsobj.h" +#include "jsstr.h" /* Small arrays are dense, no matter what. */ const uintN MIN_SPARSE_INDEX = 256; @@ -86,8 +88,8 @@ JSObject::ensureDenseArrayElements(JSContext *cx, uintN index, uintN extra) return growSlots(cx, requiredCapacity) ? ED_OK : ED_FAILED; } -extern JSBool -js_StringIsIndex(JSString *str, jsuint *indexp); +extern bool +js_StringIsIndex(JSLinearString *str, jsuint *indexp); inline JSBool js_IdIsIndex(jsid id, jsuint *indexp) @@ -104,26 +106,36 @@ js_IdIsIndex(jsid id, jsuint *indexp) if (JS_UNLIKELY(!JSID_IS_STRING(id))) return JS_FALSE; - return js_StringIsIndex(JSID_TO_STRING(id), indexp); + return js_StringIsIndex(JSID_TO_ATOM(id), indexp); } /* XML really wants to pretend jsvals are jsids. */ -inline JSBool -js_IdValIsIndex(jsval id, jsuint *indexp) +inline bool +js_IdValIsIndex(JSContext *cx, jsval id, jsuint *indexp, bool *isIndex) { if (JSVAL_IS_INT(id)) { jsint i; i = JSVAL_TO_INT(id); - if (i < 0) - return JS_FALSE; + if (i < 0) { + *isIndex = false; + return true; + } *indexp = (jsuint)i; - return JS_TRUE; + *isIndex = true; + return true; } - if (!JSVAL_IS_STRING(id)) - return JS_FALSE; + if (!JSVAL_IS_STRING(id)) { + *isIndex = false; + return true; + } - return js_StringIsIndex(JSVAL_TO_STRING(id), indexp); + JSLinearString *str = JSVAL_TO_STRING(id)->ensureLinear(cx); + if (!str) + return false; + + *isIndex = js_StringIsIndex(str, indexp); + return true; } extern js::Class js_ArrayClass, js_SlowArrayClass; diff --git a/js/src/jsatom.cpp b/js/src/jsatom.cpp index aaf4fbefba1..ccd598339a5 100644 --- a/js/src/jsatom.cpp +++ b/js/src/jsatom.cpp @@ -457,17 +457,20 @@ js_SweepAtomState(JSContext *cx) } JSAtom * -js_AtomizeString(JSContext *cx, JSString *str, uintN flags) +js_AtomizeString(JSContext *cx, JSString *strArg, uintN flags) { JS_ASSERT(!(flags & ~(ATOM_PINNED|ATOM_INTERNED|ATOM_TMPSTR|ATOM_NOCOPY))); JS_ASSERT_IF(flags & ATOM_NOCOPY, flags & ATOM_TMPSTR); - if (str->isAtomized()) - return STRING_TO_ATOM(str); + if (strArg->isAtomized()) + return STRING_TO_ATOM(strArg); - const jschar *chars; - size_t length; - str->getCharsAndLength(chars, length); + JSLinearString *str = strArg->ensureLinear(cx); + if (!str) + return NULL; + + const jschar *chars = str->chars(); + size_t length = str->length(); JSString *staticStr = JSString::lookupStaticString(chars, length); if (staticStr) @@ -482,7 +485,7 @@ js_AtomizeString(JSContext *cx, JSString *str, uintN flags) /* Hashing the string should have flattened it if it was a rope. */ JS_ASSERT(str->isFlat() || str->isDependent()); - JSString *key; + JSLinearString *key; if (p) { key = AtomEntryToKey(*p); } else { @@ -506,15 +509,14 @@ js_AtomizeString(JSContext *cx, JSString *str, uintN flags) } else { if (needNewString) { SwitchToCompartment sc(cx, cx->runtime->defaultCompartment); - jschar *chars = str->chars(); if (flags & ATOM_NOCOPY) { - key = js_NewString(cx, chars, length); + key = js_NewString(cx, const_cast(str->flatChars()), length); if (!key) return NULL; /* Finish handing off chars to the GC'ed key string. */ JS_ASSERT(flags & ATOM_TMPSTR); - str->mChars = NULL; + str->u.chars = NULL; } else { key = js_NewStringCopyN(cx, chars, length); if (!key) @@ -537,7 +539,6 @@ js_AtomizeString(JSContext *cx, JSString *str, uintN flags) AddAtomEntryFlags(*p, flags & (ATOM_PINNED | ATOM_INTERNED)); - JS_ASSERT(key->isAtomized()); JSAtom *atom = STRING_TO_ATOM(key); return atom; } @@ -607,7 +608,7 @@ js_GetExistingStringAtom(JSContext *cx, const jschar *chars, size_t length) state = &cx->runtime->atomState; JS_LOCK(cx, &state->lock); - AtomSet::Ptr p = state->atoms.lookup(&str); + AtomSet::Ptr p = state->atoms.lookup(str.assertIsFlat()); str2 = p ? AtomEntryToKey(*p) : NULL; JS_UNLOCK(cx, &state->lock); @@ -628,7 +629,7 @@ js_DumpAtoms(JSContext *cx, FILE *fp) if (entry == 0) { fputs("", fp); } else { - JSString *key = AtomEntryToKey(entry); + JSAtom *key = AtomEntryToKey(entry); FileEscapedString(fp, key, '"'); uintN flags = AtomEntryFlags(entry); if (flags != 0) { diff --git a/js/src/jsatom.h b/js/src/jsatom.h index 9c6f0a1b448..fd897ae52cd 100644 --- a/js/src/jsatom.h +++ b/js/src/jsatom.h @@ -60,7 +60,7 @@ #define STRING_TO_ATOM(str) (JS_ASSERT(str->isAtomized()), \ (JSAtom *)str) -#define ATOM_TO_STRING(atom) ((JSString *)(atom)) +#define ATOM_TO_STRING(atom) (atom) #define ATOM_TO_JSVAL(atom) STRING_TO_JSVAL(ATOM_TO_STRING(atom)) /* Engine-internal extensions of jsid */ @@ -265,23 +265,23 @@ JS_STATIC_ASSERT(ATOM_ENTRY_FLAG_MASK < JS_GCTHING_ALIGN); typedef uintptr_t AtomEntryType; -static JS_ALWAYS_INLINE JSString * +static JS_ALWAYS_INLINE JSAtom * AtomEntryToKey(AtomEntryType entry) { JS_ASSERT(entry != 0); - return (JSString *)(entry & ~ATOM_ENTRY_FLAG_MASK); + return (JSAtom *)(entry & ~ATOM_ENTRY_FLAG_MASK); } struct AtomHasher { - typedef JSString *Lookup; + typedef JSLinearString *Lookup; - static HashNumber hash(JSString *str) { + static HashNumber hash(JSLinearString *str) { return js_HashString(str); } - static bool match(AtomEntryType entry, JSString *lookup) { - return entry ? js_EqualStrings(AtomEntryToKey(entry), lookup) : false; + static bool match(AtomEntryType entry, JSLinearString *lookup) { + return entry ? EqualStrings(AtomEntryToKey(entry), lookup) : false; } }; diff --git a/js/src/jsbool.cpp b/js/src/jsbool.cpp index 08fe963aa78..3afabdccbca 100644 --- a/js/src/jsbool.cpp +++ b/js/src/jsbool.cpp @@ -120,11 +120,11 @@ bool_valueOf(JSContext *cx, uintN argc, Value *vp) static JSFunctionSpec boolean_methods[] = { #if JS_HAS_TOSOURCE - JS_FN(js_toSource_str, bool_toSource, 0, JSFUN_PRIMITIVE_THIS), + JS_FN(js_toSource_str, bool_toSource, 0, 0), #endif - JS_FN(js_toString_str, bool_toString, 0, JSFUN_PRIMITIVE_THIS), - JS_FN(js_valueOf_str, bool_valueOf, 0, JSFUN_PRIMITIVE_THIS), - JS_FN(js_toJSON_str, bool_valueOf, 0, JSFUN_PRIMITIVE_THIS), + JS_FN(js_toString_str, bool_toString, 0, 0), + JS_FN(js_valueOf_str, bool_valueOf, 0, 0), + JS_FN(js_toJSON_str, bool_valueOf, 0, 0), JS_FS_END }; diff --git a/js/src/jsbuiltins.cpp b/js/src/jsbuiltins.cpp index f16aa8b4d80..b83237a2039 100644 --- a/js/src/jsbuiltins.cpp +++ b/js/src/jsbuiltins.cpp @@ -168,18 +168,22 @@ js_DoubleToUint32(jsdouble d) JS_DEFINE_CALLINFO_1(extern, UINT32, js_DoubleToUint32, DOUBLE, 1, ACCSET_NONE) jsdouble FASTCALL -js_StringToNumber(JSContext* cx, JSString* str) +js_StringToNumber(JSContext* cx, JSString* str, JSBool *ok) { - return StringToNumberType(cx, str); + double out = 0; /* silence warnings. */ + *ok = StringToNumberType(cx, str, &out); + return out; } -JS_DEFINE_CALLINFO_2(extern, DOUBLE, js_StringToNumber, CONTEXT, STRING, 1, ACCSET_NONE) +JS_DEFINE_CALLINFO_3(extern, DOUBLE, js_StringToNumber, CONTEXT, STRING, BOOLPTR, 1, ACCSET_NONE) int32 FASTCALL -js_StringToInt32(JSContext* cx, JSString* str) +js_StringToInt32(JSContext* cx, JSString* str, JSBool *ok) { - return StringToNumberType(cx, str); + int32 out = 0; /* silence warnings. */ + *ok = StringToNumberType(cx, str, &out); + return out; } -JS_DEFINE_CALLINFO_2(extern, INT32, js_StringToInt32, CONTEXT, STRING, 1, ACCSET_NONE) +JS_DEFINE_CALLINFO_3(extern, INT32, js_StringToInt32, CONTEXT, STRING, BOOLPTR, 1, ACCSET_NONE) /* Nb: it's always safe to set isDefinitelyAtom to false if you're unsure or don't know. */ static inline JSBool diff --git a/js/src/jsbuiltins.h b/js/src/jsbuiltins.h index 14c83e0982b..9d372a3c86c 100644 --- a/js/src/jsbuiltins.h +++ b/js/src/jsbuiltins.h @@ -547,7 +547,7 @@ struct ClosureVarInfo; #define _JS_DEFINE_CALLINFO_n(n, args) JS_DEFINE_CALLINFO_##n args jsdouble FASTCALL -js_StringToNumber(JSContext* cx, JSString* str); +js_StringToNumber(JSContext* cx, JSString* str, JSBool *ok); /* Extern version of SetBuiltinError. */ extern JS_FRIEND_API(void) @@ -623,9 +623,9 @@ JS_DECLARE_CALLINFO(js_CloneRegExpObject) /* Defined in jsstr.cpp. */ JS_DECLARE_CALLINFO(js_String_tn) -JS_DECLARE_CALLINFO(js_CompareStrings) +JS_DECLARE_CALLINFO(js_CompareStringsOnTrace) JS_DECLARE_CALLINFO(js_ConcatStrings) -JS_DECLARE_CALLINFO(js_EqualStrings) +JS_DECLARE_CALLINFO(js_EqualStringsOnTrace) JS_DECLARE_CALLINFO(js_Flatten) /* Defined in jstypedarray.cpp. */ diff --git a/js/src/jsclone.cpp b/js/src/jsclone.cpp index f49bf9cb0ae..b99d08f6b61 100644 --- a/js/src/jsclone.cpp +++ b/js/src/jsclone.cpp @@ -350,9 +350,10 @@ JS_STATIC_ASSERT(JSString::MAX_LENGTH < UINT32_MAX); bool JSStructuredCloneWriter::writeString(uint32_t tag, JSString *str) { - const jschar *chars; - size_t length; - str->getCharsAndLength(chars, length); + size_t length = str->length(); + const jschar *chars = str->getChars(context()); + if (!chars) + return false; return out.writePair(tag, uint32_t(length)) && out.writeChars(chars, length); } @@ -750,9 +751,10 @@ JSStructuredCloneReader::startRead(Value *vp) JSString *str = readString(nchars); if (!str) return false; - const jschar *chars; - size_t length; - str->getCharsAndLength(chars, length); + size_t length = str->length(); + const jschar *chars = str->getChars(context()); + if (!chars) + return false; JSObject *obj = RegExp::createObjectNoStatics(context(), chars, length, data); if (!obj) return false; diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 5d6b92c67b2..2e85a70a4e4 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -494,49 +494,28 @@ JSThreadData::init() #endif if (!stackSpace.init()) return false; -#ifdef JS_TRACER - if (!InitJIT(&traceMonitor)) { - finish(); - return false; - } -#endif dtoaState = js_NewDtoaState(); if (!dtoaState) { finish(); return false; } nativeStackBase = GetNativeStackBase(); - return true; -} -MathCache * -JSThreadData::allocMathCache(JSContext *cx) -{ - JS_ASSERT(!mathCache); - mathCache = new MathCache; - if (!mathCache) - js_ReportOutOfMemory(cx); - return mathCache; + /* Set the default size for the code cache to 16MB. */ + maxCodeCacheBytes = 16 * 1024 * 1024; + + return true; } void JSThreadData::finish() { -#ifdef DEBUG - for (size_t i = 0; i != JS_ARRAY_LENGTH(scriptsToGC); ++i) - JS_ASSERT(!scriptsToGC[i]); -#endif - if (dtoaState) js_DestroyDtoaState(dtoaState); js_FinishGSNCache(&gsnCache); propertyCache.~PropertyCache(); -#if defined JS_TRACER - FinishJIT(&traceMonitor); -#endif stackSpace.finish(); - delete mathCache; } void @@ -553,22 +532,6 @@ JSThreadData::purge(JSContext *cx) /* FIXME: bug 506341. */ propertyCache.purge(cx); -#ifdef JS_TRACER - /* - * If we are about to regenerate shapes, we have to flush the JIT cache, - * which will eventually abort any current recording. - */ - if (cx->runtime->gcRegenShapes) - traceMonitor.needFlush = JS_TRUE; -#endif - - /* Destroy eval'ed scripts. */ - js_DestroyScriptsToGC(cx, this); - - /* Purge cached native iterators. */ - memset(cachedNativeIterators, 0, sizeof(cachedNativeIterators)); - lastNativeIterator = NULL; - dtoaCache.s = NULL; } @@ -736,7 +699,6 @@ js_PurgeThreads(JSContext *cx) if (JS_CLIST_IS_EMPTY(&thread->contextList)) { JS_ASSERT(cx->thread != thread); - js_DestroyScriptsToGC(cx, &thread->data); DestroyThread(thread); e.removeFront(); @@ -921,7 +883,7 @@ DumpEvalCacheMeter(JSContext *cx) EVAL_CACHE_METER_LIST(frob) #undef frob }; - JSEvalCacheMeter *ecm = &JS_THREAD_DATA(cx)->evalCacheMeter; + JSEvalCacheMeter *ecm = &cx->compartment->evalCacheMeter; static JSAutoFile fp; if (!fp && !fp.open(filename, "w")) @@ -2316,6 +2278,15 @@ JSContext::updateJITEnabled() namespace js { +JS_FORCES_STACK JS_FRIEND_API(void) +LeaveTrace(JSContext *cx) +{ +#ifdef JS_TRACER + if (JS_ON_TRACE(cx)) + DeepBail(cx); +#endif +} + void SetPendingException(JSContext *cx, const Value &v) { diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 711ddeebda5..8d7e795afc6 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -884,147 +884,6 @@ private: JSStackFrame *curfp; }; -/* Holds the number of recording attemps for an address. */ -typedef HashMap, - SystemAllocPolicy> RecordAttemptMap; - -/* Holds the profile data for loops. */ -typedef HashMap, - SystemAllocPolicy> LoopProfileMap; - -class Oracle; - -typedef HashSet, - SystemAllocPolicy> TracedScriptSet; - -/* - * Trace monitor. Every JSThread (if JS_THREADSAFE) or JSRuntime (if not - * JS_THREADSAFE) has an associated trace monitor that keeps track of loop - * frequencies for all JavaScript code loaded into that runtime. - */ -struct TraceMonitor { - /* - * The context currently executing JIT-compiled code on this thread, or - * NULL if none. Among other things, this can in certain cases prevent - * last-ditch GC and suppress calls to JS_ReportOutOfMemory. - * - * !tracecx && !recorder: not on trace - * !tracecx && recorder: recording - * tracecx && !recorder: executing a trace - * tracecx && recorder: executing inner loop, recording outer loop - */ - JSContext *tracecx; - - /* - * Cached storage to use when executing on trace. While we may enter nested - * traces, we always reuse the outer trace's storage, so never need more - * than of these. - */ - TraceNativeStorage *storage; - - /* - * There are 4 allocators here. This might seem like overkill, but they - * have different lifecycles, and by keeping them separate we keep the - * amount of retained memory down significantly. They are flushed (ie. - * all the allocated memory is freed) periodically. - * - * - dataAlloc has the lifecycle of the monitor. It's flushed only when - * the monitor is flushed. It's used for fragments. - * - * - traceAlloc has the same flush lifecycle as the dataAlloc, but it is - * also *marked* when a recording starts and rewinds to the mark point - * if recording aborts. So you can put things in it that are only - * reachable on a successful record/compile cycle like GuardRecords and - * SideExits. - * - * - tempAlloc is flushed after each recording, successful or not. It's - * used to store LIR code and for all other elements in the LIR - * pipeline. - * - * - codeAlloc has the same lifetime as dataAlloc, but its API is - * different (CodeAlloc vs. VMAllocator). It's used for native code. - * It's also a good idea to keep code and data separate to avoid I-cache - * vs. D-cache issues. - */ - VMAllocator* dataAlloc; - VMAllocator* traceAlloc; - VMAllocator* tempAlloc; - nanojit::CodeAlloc* codeAlloc; - nanojit::Assembler* assembler; - FrameInfoCache* frameCache; - - /* This gets incremented every time the monitor is flushed. */ - uintN flushEpoch; - - Oracle* oracle; - TraceRecorder* recorder; - - /* If we are profiling a loop, this tracks the current profile. Otherwise NULL. */ - LoopProfile* profile; - - GlobalState globalStates[MONITOR_N_GLOBAL_STATES]; - TreeFragment* vmfragments[FRAGMENT_TABLE_SIZE]; - RecordAttemptMap* recordAttempts; - - /* A hashtable mapping PC values to loop profiles for those loops. */ - LoopProfileMap* loopProfiles; - - /* - * Maximum size of the code cache before we start flushing. 1/16 of this - * size is used as threshold for the regular expression code cache. - */ - uint32 maxCodeCacheBytes; - - /* - * If nonzero, do not flush the JIT cache after a deep bail. That would - * free JITted code pages that we will later return to. Instead, set the - * needFlush flag so that it can be flushed later. - */ - JSBool needFlush; - - /* - * Fragment map for the regular expression compiler. - */ - REHashMap* reFragments; - - // Cached temporary typemap to avoid realloc'ing every time we create one. - // This must be used in only one place at a given time. It must be cleared - // before use. - TypeMap* cachedTempTypeMap; - - /* Scripts with recorded fragments. */ - TracedScriptSet tracedScripts; - -#ifdef DEBUG - /* Fields needed for fragment/guard profiling. */ - nanojit::Seq* branches; - uint32 lastFragID; - /* - * profAlloc has a lifetime which spans exactly from js_InitJIT to - * js_FinishJIT. - */ - VMAllocator* profAlloc; - FragStatsMap* profTab; -#endif - - bool ontrace() const { - return !!tracecx; - } - - /* Flush the JIT cache. */ - void flush(); - - /* Sweep any cache entry pointing to dead GC things. */ - void sweep(); - - bool outOfMemory() const; -}; - } /* namespace js */ /* @@ -1038,23 +897,6 @@ struct TraceMonitor { # define JS_ON_TRACE(cx) false #endif -/* Number of potentially reusable scriptsToGC to search for the eval cache. */ -#ifndef JS_EVAL_CACHE_SHIFT -# define JS_EVAL_CACHE_SHIFT 6 -#endif -#define JS_EVAL_CACHE_SIZE JS_BIT(JS_EVAL_CACHE_SHIFT) - -#ifdef DEBUG -# define EVAL_CACHE_METER_LIST(_) _(probe), _(hit), _(step), _(noscope) -# define identity(x) x - -struct JSEvalCacheMeter { - uint64 EVAL_CACHE_METER_LIST(identity); -}; - -# undef identity -#endif - #ifdef DEBUG # define FUNCTION_KIND_METER_LIST(_) \ _(allfun), _(heavy), _(nofreeupvar), _(onlyfreevar), \ @@ -1077,10 +919,6 @@ struct JSFunctionMeter { #endif -#define NATIVE_ITER_CACHE_LOG2 8 -#define NATIVE_ITER_CACHE_MASK JS_BITMASK(NATIVE_ITER_CACHE_LOG2) -#define NATIVE_ITER_CACHE_SIZE JS_BIT(NATIVE_ITER_CACHE_LOG2) - struct JSPendingProxyOperation { JSPendingProxyOperation *next; JSObject *object; @@ -1118,18 +956,8 @@ struct JSThreadData { js::PropertyCache propertyCache; #ifdef JS_TRACER - /* Trace-tree JIT recorder/interpreter state. */ - js::TraceMonitor traceMonitor; - - /* Counts the number of iterations run by a trace. */ - unsigned iterationCounter; -#endif - - /* Lock-free hashed lists of scripts created by eval to garbage-collect. */ - JSScript *scriptsToGC[JS_EVAL_CACHE_SIZE]; - -#ifdef DEBUG - JSEvalCacheMeter evalCacheMeter; + /* Maximum size of the tracer's code cache before we start flushing. */ + uint32 maxCodeCacheBytes; #endif /* State used by dtoa.c. */ @@ -1149,12 +977,6 @@ struct JSThreadData { JSString *s; // if s==NULL, d and base are not valid } dtoaCache; - /* Cached native iterators. */ - JSObject *cachedNativeIterators[NATIVE_ITER_CACHE_SIZE]; - - /* Native iterator most recently started. */ - JSObject *lastNativeIterator; - /* Base address of the native stack for the current thread. */ jsuword *nativeStackBase; @@ -1163,16 +985,6 @@ struct JSThreadData { js::ConservativeGCThreadData conservativeGC; - private: - js::MathCache *mathCache; - - js::MathCache *allocMathCache(JSContext *cx); - public: - - js::MathCache *getMathCache(JSContext *cx) { - return mathCache ? mathCache : allocMathCache(cx); - } - bool init(); void finish(); void mark(JSTracer *trc); @@ -1322,6 +1134,7 @@ struct JSRuntime { uint32 gcNumber; js::GCMarker *gcMarkingTracer; uint32 gcTriggerFactor; + int64 gcJitReleaseTime; volatile JSBool gcIsNeeded; /* @@ -1369,7 +1182,7 @@ struct JSRuntime { js::Value negativeInfinityValue; js::Value positiveInfinityValue; - JSString *emptyString; + JSFlatString *emptyString; /* List of active contexts sharing this runtime; protected by gcLock. */ JSCList contextList; @@ -1725,14 +1538,6 @@ struct JSRuntime { /* Common macros to access thread-local caches in JSThread or JSRuntime. */ #define JS_GSN_CACHE(cx) (JS_THREAD_DATA(cx)->gsnCache) #define JS_PROPERTY_CACHE(cx) (JS_THREAD_DATA(cx)->propertyCache) -#define JS_TRACE_MONITOR(cx) (JS_THREAD_DATA(cx)->traceMonitor) -#define JS_SCRIPTS_TO_GC(cx) (JS_THREAD_DATA(cx)->scriptsToGC) - -#ifdef DEBUG -# define EVAL_CACHE_METER(x) (JS_THREAD_DATA(cx)->evalCacheMeter.x++) -#else -# define EVAL_CACHE_METER(x) ((void) 0) -#endif #ifdef DEBUG # define JS_RUNTIME_METER(rt, which) JS_ATOMIC_INCREMENT(&(rt)->which) @@ -2369,8 +2174,7 @@ struct JSContext void assertValidStackDepth(uintN /*depth*/) {} #endif -private: - + private: /* * The allocation code calls the function to indicate either OOM failure * when p is null or that a memory pressure counter has reached some @@ -3238,50 +3042,14 @@ js_CurrentPCIsInImacro(JSContext *cx); namespace js { -#ifdef JS_TRACER -/* - * Reconstruct the JS stack and clear cx->tracecx. We must be currently in a - * _FAIL builtin from trace on cx or another context on the same thread. The - * machine code for the trace remains on the C stack when js_DeepBail returns. - * - * Implemented in jstracer.cpp. - */ -JS_FORCES_STACK JS_FRIEND_API(void) -DeepBail(JSContext *cx); -#endif - -static JS_FORCES_STACK JS_INLINE void -LeaveTrace(JSContext *cx) -{ -#ifdef JS_TRACER - if (JS_ON_TRACE(cx)) - DeepBail(cx); -#endif -} - -static JS_INLINE void -LeaveTraceIfGlobalObject(JSContext *cx, JSObject *obj) -{ - if (!obj->parent) - LeaveTrace(cx); -} - -static JS_INLINE JSBool -CanLeaveTrace(JSContext *cx) -{ - JS_ASSERT(JS_ON_TRACE(cx)); -#ifdef JS_TRACER - return cx->bailExit != NULL; -#else - return JS_FALSE; -#endif -} - extern void SetPendingException(JSContext *cx, const Value &v); class RegExpStatics; +extern JS_FORCES_STACK JS_FRIEND_API(void) +LeaveTrace(JSContext *cx); + } /* namespace js */ /* diff --git a/js/src/jscntxtinlines.h b/js/src/jscntxtinlines.h index 50d47e82148..fbdc5ebf4ce 100644 --- a/js/src/jscntxtinlines.h +++ b/js/src/jscntxtinlines.h @@ -42,6 +42,7 @@ #define jscntxtinlines_h___ #include "jscntxt.h" +#include "jscompartment.h" #include "jsparse.h" #include "jsstaticcheck.h" #include "jsxml.h" @@ -753,6 +754,36 @@ CallSetter(JSContext *cx, JSObject *obj, jsid id, PropertyOp op, uintN attrs, ui return CallJSPropertyOpSetter(cx, op, obj, id, vp); } +#ifdef JS_TRACER +/* + * Reconstruct the JS stack and clear cx->tracecx. We must be currently in a + * _FAIL builtin from trace on cx or another context on the same thread. The + * machine code for the trace remains on the C stack when js_DeepBail returns. + * + * Implemented in jstracer.cpp. + */ +JS_FORCES_STACK JS_FRIEND_API(void) +DeepBail(JSContext *cx); +#endif + +static JS_INLINE void +LeaveTraceIfGlobalObject(JSContext *cx, JSObject *obj) +{ + if (!obj->parent) + LeaveTrace(cx); +} + +static JS_INLINE JSBool +CanLeaveTrace(JSContext *cx) +{ + JS_ASSERT(JS_ON_TRACE(cx)); +#ifdef JS_TRACER + return cx->bailExit != NULL; +#else + return JS_FALSE; +#endif +} + } /* namespace js */ #endif /* jscntxtinlines_h___ */ diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index d75088c2ba5..4fba9fe3f16 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -44,6 +44,7 @@ #include "jsiter.h" #include "jsproxy.h" #include "jsscope.h" +#include "jstracer.h" #include "methodjit/MethodJIT.h" #include "methodjit/PolyIC.h" #include "methodjit/MonoIC.h" @@ -54,17 +55,36 @@ using namespace js; using namespace js::gc; JSCompartment::JSCompartment(JSRuntime *rt) - : rt(rt), principals(NULL), data(NULL), marked(false), debugMode(rt->debugMode), - anynameObject(NULL), functionNamespaceObject(NULL) + : rt(rt), + principals(NULL), + data(NULL), + marked(false), + active(false), + debugMode(rt->debugMode), + anynameObject(NULL), + functionNamespaceObject(NULL), + mathCache(NULL) { JS_INIT_CLIST(&scripts); + + PodArrayZero(scriptsToGC); } JSCompartment::~JSCompartment() { +#if defined JS_TRACER + FinishJIT(&traceMonitor); +#endif #ifdef JS_METHODJIT delete jaegerCompartment; #endif + + delete mathCache; + +#ifdef DEBUG + for (size_t i = 0; i != JS_ARRAY_LENGTH(scriptsToGC); ++i) + JS_ASSERT(!scriptsToGC[i]); +#endif } bool @@ -81,9 +101,19 @@ JSCompartment::init() if (!crossCompartmentWrappers.init()) return false; -#ifdef JS_METHODJIT - if (!(jaegerCompartment = new mjit::JaegerCompartment)) +#ifdef JS_TRACER + if (!InitJIT(&traceMonitor)) { return false; + } +#endif + +#ifdef JS_METHODJIT + if (!(jaegerCompartment = new mjit::JaegerCompartment)) { +#ifdef JS_TRACER + FinishJIT(&traceMonitor); +#endif + return false; + } return jaegerCompartment->Initialize(); #else return true; @@ -204,7 +234,10 @@ JSCompartment::wrap(JSContext *cx, Value *vp) if (vp->isString()) { Value orig = *vp; JSString *str = vp->toString(); - JSString *wrapped = js_NewStringCopyN(cx, str->chars(), str->length()); + const jschar *chars = str->getChars(cx); + if (!chars) + return false; + JSString *wrapped = js_NewStringCopyN(cx, chars, str->length()); if (!wrapped) return false; vp->setString(wrapped); @@ -328,8 +361,33 @@ JSCompartment::wrapException(JSContext *cx) return true; } +/* + * Check if the pool containing the code for jit should be destroyed, per the + * heuristics in JSCompartment::sweep. + */ +static inline bool +ScriptPoolDestroyed(JSContext *cx, mjit::JITScript *jit, + uint32 releaseInterval, uint32 &counter) +{ + JSC::ExecutablePool *pool = jit->code.m_executablePool; + if (pool->m_gcNumber != cx->runtime->gcNumber) { + /* + * The m_destroy flag may have been set in a previous GC for a pool which had + * references we did not remove (e.g. from the compartment's ExecutableAllocator) + * and is still around. Forget we tried to destroy it in such cases. + */ + pool->m_destroy = false; + pool->m_gcNumber = cx->runtime->gcNumber; + if (--counter == 0) { + pool->m_destroy = true; + counter = releaseInterval; + } + } + return pool->m_destroy; +} + void -JSCompartment::sweep(JSContext *cx) +JSCompartment::sweep(JSContext *cx, uint32 releaseInterval) { chunk = NULL; /* Remove dead wrappers from the table. */ @@ -343,13 +401,43 @@ JSCompartment::sweep(JSContext *cx) } } +#ifdef JS_TRACER + traceMonitor.sweep(); +#endif + #if defined JS_METHODJIT && defined JS_MONOIC + + /* + * The release interval is the frequency with which we should try to destroy + * executable pools by releasing all JIT code in them, zero to never destroy pools. + * Initialize counter so that the first pool will be destroyed, and eventually drive + * the amount of JIT code in never-used compartments to zero. Don't discard anything + * for compartments which currently have active stack frames. + */ + uint32 counter = 1; + bool discardScripts = !active && releaseInterval != 0; + for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) { JSScript *script = reinterpret_cast(cursor); - if (script->hasJITCode()) - mjit::ic::SweepCallICs(script); + if (script->hasJITCode()) { + mjit::ic::SweepCallICs(script, discardScripts); + if (discardScripts) { + if (script->jitNormal && + ScriptPoolDestroyed(cx, script->jitNormal, releaseInterval, counter)) { + mjit::ReleaseScriptCode(cx, script); + continue; + } + if (script->jitCtor && + ScriptPoolDestroyed(cx, script->jitCtor, releaseInterval, counter)) { + mjit::ReleaseScriptCode(cx, script); + } + } + } } -#endif + +#endif /* JS_METHODJIT && JS_MONOIC */ + + active = false; } void @@ -357,6 +445,20 @@ JSCompartment::purge(JSContext *cx) { freeLists.purge(); + /* Destroy eval'ed scripts. */ + js_DestroyScriptsToGC(cx, this); + + nativeIterCache.purge(); + +#ifdef JS_TRACER + /* + * If we are about to regenerate shapes, we have to flush the JIT cache, + * which will eventually abort any current recording. + */ + if (cx->runtime->gcRegenShapes) + traceMonitor.needFlush = JS_TRUE; +#endif + #ifdef JS_METHODJIT for (JSScript *script = (JSScript *)scripts.next; &script->links != &scripts; @@ -367,8 +469,8 @@ JSCompartment::purge(JSContext *cx) # endif # if defined JS_MONOIC /* - * MICs do not refer to data which can be GC'ed, but are sensitive - * to shape regeneration. + * MICs do not refer to data which can be GC'ed and do not generate stubs + * which might need to be discarded, but are sensitive to shape regeneration. */ if (cx->runtime->gcRegenShapes) mjit::ic::PurgeMICs(cx, script); @@ -377,3 +479,13 @@ JSCompartment::purge(JSContext *cx) } #endif } + +MathCache * +JSCompartment::allocMathCache(JSContext *cx) +{ + JS_ASSERT(!mathCache); + mathCache = new MathCache; + if (!mathCache) + js_ReportOutOfMemory(cx); + return mathCache; +} diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index 2cd3bea9e9e..5ef5768f28d 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -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 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -55,11 +55,210 @@ #endif namespace js { + +/* Holds the number of recording attemps for an address. */ +typedef HashMap, + SystemAllocPolicy> RecordAttemptMap; + +/* Holds the profile data for loops. */ +typedef HashMap, + SystemAllocPolicy> LoopProfileMap; + +class Oracle; + +typedef HashSet, + SystemAllocPolicy> TracedScriptSet; + +/* + * Trace monitor. Every JSCompartment has an associated trace monitor + * that keeps track of loop frequencies for all JavaScript code loaded + * into that runtime. + */ +struct TraceMonitor { + /* + * The context currently executing JIT-compiled code in this compartment, or + * NULL if none. Among other things, this can in certain cases prevent + * last-ditch GC and suppress calls to JS_ReportOutOfMemory. + * + * !tracecx && !recorder: not on trace + * !tracecx && recorder: recording + * tracecx && !recorder: executing a trace + * tracecx && recorder: executing inner loop, recording outer loop + */ + JSContext *tracecx; + + /* Counts the number of iterations run by the currently executing trace. */ + unsigned iterationCounter; + + /* + * Cached storage to use when executing on trace. While we may enter nested + * traces, we always reuse the outer trace's storage, so never need more + * than of these. + */ + TraceNativeStorage *storage; + + /* + * There are 4 allocators here. This might seem like overkill, but they + * have different lifecycles, and by keeping them separate we keep the + * amount of retained memory down significantly. They are flushed (ie. + * all the allocated memory is freed) periodically. + * + * - dataAlloc has the lifecycle of the monitor. It's flushed only when + * the monitor is flushed. It's used for fragments. + * + * - traceAlloc has the same flush lifecycle as the dataAlloc, but it is + * also *marked* when a recording starts and rewinds to the mark point + * if recording aborts. So you can put things in it that are only + * reachable on a successful record/compile cycle like GuardRecords and + * SideExits. + * + * - tempAlloc is flushed after each recording, successful or not. It's + * used to store LIR code and for all other elements in the LIR + * pipeline. + * + * - codeAlloc has the same lifetime as dataAlloc, but its API is + * different (CodeAlloc vs. VMAllocator). It's used for native code. + * It's also a good idea to keep code and data separate to avoid I-cache + * vs. D-cache issues. + */ + VMAllocator* dataAlloc; + VMAllocator* traceAlloc; + VMAllocator* tempAlloc; + nanojit::CodeAlloc* codeAlloc; + nanojit::Assembler* assembler; + FrameInfoCache* frameCache; + + /* This gets incremented every time the monitor is flushed. */ + uintN flushEpoch; + + Oracle* oracle; + TraceRecorder* recorder; + + /* If we are profiling a loop, this tracks the current profile. Otherwise NULL. */ + LoopProfile* profile; + + GlobalState globalStates[MONITOR_N_GLOBAL_STATES]; + TreeFragment* vmfragments[FRAGMENT_TABLE_SIZE]; + RecordAttemptMap* recordAttempts; + + /* A hashtable mapping PC values to loop profiles for those loops. */ + LoopProfileMap* loopProfiles; + + /* + * Maximum size of the code cache before we start flushing. 1/16 of this + * size is used as threshold for the regular expression code cache. + */ + uint32 maxCodeCacheBytes; + + /* + * If nonzero, do not flush the JIT cache after a deep bail. That would + * free JITted code pages that we will later return to. Instead, set the + * needFlush flag so that it can be flushed later. + */ + JSBool needFlush; + + /* + * Fragment map for the regular expression compiler. + */ + REHashMap* reFragments; + + // Cached temporary typemap to avoid realloc'ing every time we create one. + // This must be used in only one place at a given time. It must be cleared + // before use. + TypeMap* cachedTempTypeMap; + + /* Scripts with recorded fragments. */ + TracedScriptSet tracedScripts; + +#ifdef DEBUG + /* Fields needed for fragment/guard profiling. */ + nanojit::Seq* branches; + uint32 lastFragID; + /* + * profAlloc has a lifetime which spans exactly from js_InitJIT to + * js_FinishJIT. + */ + VMAllocator* profAlloc; + FragStatsMap* profTab; +#endif + + bool ontrace() const { + return !!tracecx; + } + + /* Flush the JIT cache. */ + void flush(); + + /* Sweep any cache entry pointing to dead GC things. */ + void sweep(); + + bool outOfMemory() const; +}; + namespace mjit { class JaegerCompartment; } } +/* Number of potentially reusable scriptsToGC to search for the eval cache. */ +#ifndef JS_EVAL_CACHE_SHIFT +# define JS_EVAL_CACHE_SHIFT 6 +#endif +#define JS_EVAL_CACHE_SIZE JS_BIT(JS_EVAL_CACHE_SHIFT) + +#ifdef DEBUG +# define EVAL_CACHE_METER_LIST(_) _(probe), _(hit), _(step), _(noscope) +# define identity(x) x + +struct JSEvalCacheMeter { + uint64 EVAL_CACHE_METER_LIST(identity); +}; + +# undef identity +#endif + +namespace js { + +class NativeIterCache { + static const size_t SIZE = size_t(1) << 8; + + /* Cached native iterators. */ + JSObject *data[SIZE]; + + static size_t getIndex(uint32 key) { + return size_t(key) % SIZE; + } + + public: + /* Native iterator most recently started. */ + JSObject *last; + + NativeIterCache() + : last(NULL) { + PodArrayZero(data); + } + + void purge() { + PodArrayZero(data); + last = NULL; + } + + JSObject *get(uint32 key) const { + return data[getIndex(key)]; + } + + void set(uint32 key, JSObject *iterobj) { + data[getIndex(key)] = iterobj; + } +}; + +} /* namespace js */ + struct JS_FRIEND_API(JSCompartment) { JSRuntime *rt; JSPrincipals *principals; @@ -72,8 +271,21 @@ struct JS_FRIEND_API(JSCompartment) { js::gc::JSGCArenaStats compartmentStats[js::gc::FINALIZE_LIMIT]; #endif +#ifdef JS_TRACER + /* Trace-tree JIT recorder/interpreter state. */ + js::TraceMonitor traceMonitor; +#endif + + /* Hashed lists of scripts created by eval to garbage-collect. */ + JSScript *scriptsToGC[JS_EVAL_CACHE_SIZE]; + +#ifdef DEBUG + JSEvalCacheMeter evalCacheMeter; +#endif + void *data; bool marked; + bool active; // GC flag, whether there are active frames js::WrapperMap crossCompartmentWrappers; #ifdef JS_METHODJIT @@ -93,6 +305,8 @@ struct JS_FRIEND_API(JSCompartment) { JSObject *anynameObject; JSObject *functionNamespaceObject; + js::NativeIterCache nativeIterCache; + JSCompartment(JSRuntime *cx); ~JSCompartment(); @@ -107,12 +321,39 @@ struct JS_FRIEND_API(JSCompartment) { bool wrap(JSContext *cx, js::AutoIdVector &props); bool wrapException(JSContext *cx); - void sweep(JSContext *cx); + void sweep(JSContext *cx, uint32 releaseInterval); void purge(JSContext *cx); void finishArenaLists(); bool arenaListsAreEmpty(); + + private: + js::MathCache *mathCache; + + js::MathCache *allocMathCache(JSContext *cx); + + public: + js::MathCache *getMathCache(JSContext *cx) { + return mathCache ? mathCache : allocMathCache(cx); + } }; +#define JS_TRACE_MONITOR(cx) (cx->compartment->traceMonitor) +#define JS_SCRIPTS_TO_GC(cx) (cx->compartment->scriptsToGC) + +namespace js { +static inline MathCache * +GetMathCache(JSContext *cx) +{ + return cx->compartment->getMathCache(cx); +} +} + +#ifdef DEBUG +# define EVAL_CACHE_METER(x) (cx->compartment->evalCacheMeter.x++) +#else +# define EVAL_CACHE_METER(x) ((void) 0) +#endif + #ifdef _MSC_VER #pragma warning(pop) #endif diff --git a/js/src/jsdate.cpp b/js/src/jsdate.cpp index 3d73242aec9..f25c5ad60f6 100644 --- a/js/src/jsdate.cpp +++ b/js/src/jsdate.cpp @@ -750,7 +750,7 @@ ndigits(size_t n, size_t *result, const jschar *s, size_t* i, size_t limit) */ static JSBool -date_parseISOString(JSString *str, jsdouble *result, JSContext *cx) +date_parseISOString(JSLinearString *str, jsdouble *result, JSContext *cx) { jsdouble msec; @@ -792,7 +792,8 @@ date_parseISOString(JSString *str, jsdouble *result, JSContext *cx) if (!ndigits(n, &field, s, &i, limit)) { goto syntax; } \ JS_END_MACRO - str->getCharsAndLength(s, limit); + s = str->chars(); + limit = str->length(); if (PEEK('+') || PEEK('-')) { if (PEEK('-')) @@ -883,7 +884,7 @@ date_parseISOString(JSString *str, jsdouble *result, JSContext *cx) } static JSBool -date_parseString(JSString *str, jsdouble *result, JSContext *cx) +date_parseString(JSLinearString *str, jsdouble *result, JSContext *cx) { jsdouble msec; @@ -907,7 +908,8 @@ date_parseString(JSString *str, jsdouble *result, JSContext *cx) if (date_parseISOString(str, result, cx)) return JS_TRUE; - str->getCharsAndLength(s, limit); + s = str->chars(); + limit = str->length(); if (limit == 0) goto syntax; while (i < limit) { @@ -1167,7 +1169,11 @@ date_parse(JSContext *cx, uintN argc, Value *vp) if (!str) return JS_FALSE; vp[2].setString(str); - if (!date_parseString(str, &result, cx)) { + JSLinearString *linearStr = str->ensureLinear(cx); + if (!linearStr) + return false; + + if (!date_parseString(linearStr, &result, cx)) { vp->setDouble(js_NaN); return true; } @@ -2377,8 +2383,6 @@ date_toString(JSContext *cx, uintN argc, Value *vp) static JSBool date_valueOf(JSContext *cx, uintN argc, Value *vp) { - JSString *str, *number_str; - /* It is an error to call date_valueOf on a non-date object, but we don't * need to check for that explicitly here because every path calls * GetUTCTime, which does the check. @@ -2389,11 +2393,14 @@ date_valueOf(JSContext *cx, uintN argc, Value *vp) return date_getTime(cx, argc, vp); /* Convert to number only if the hint was given, otherwise favor string. */ - str = js_ValueToString(cx, vp[2]); + JSString *str = js_ValueToString(cx, vp[2]); if (!str) return JS_FALSE; - number_str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]); - if (js_EqualStrings(str, number_str)) + JSLinearString *linear_str = str->ensureLinear(cx); + if (!linear_str) + return JS_FALSE; + JSAtom *number_str = cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]; + if (EqualStrings(linear_str, number_str)) return date_getTime(cx, argc, vp); return date_toString(cx, argc, vp); } @@ -2487,8 +2494,11 @@ js_Date(JSContext *cx, uintN argc, Value *vp) if (!str) return false; argv[0].setString(str); + JSLinearString *linearStr = str->ensureLinear(cx); + if (!linearStr) + return false; - if (!date_parseString(str, &d, cx)) + if (!date_parseString(linearStr, &d, cx)) d = js_NaN; else d = TIMECLIP(d); diff --git a/js/src/jsdbgapi.cpp b/js/src/jsdbgapi.cpp index dc5e61b95bd..cb604369e66 100644 --- a/js/src/jsdbgapi.cpp +++ b/js/src/jsdbgapi.cpp @@ -145,7 +145,7 @@ js_SetDebugMode(JSContext *cx, JSBool debug) for (JSScript *script = (JSScript *)cx->compartment->scripts.next; &script->links != &cx->compartment->scripts; script = (JSScript *)script->links.next) { - if (script->debugMode != (bool) debug && + if (script->debugMode != !!debug && script->hasJITCode() && !IsScriptLive(cx, script)) { /* @@ -182,6 +182,30 @@ JS_SetDebugMode(JSContext *cx, JSBool debug) return js_SetDebugMode(cx, debug); } +JS_FRIEND_API(JSBool) +js_SetSingleStepMode(JSContext *cx, JSScript *script, JSBool singleStep) +{ + if (!script->singleStepMode == !singleStep) + return JS_TRUE; + + JS_ASSERT_IF(singleStep, cx->compartment->debugMode); + +#ifdef JS_METHODJIT + /* request the next recompile to inject single step interrupts */ + script->singleStepMode = !!singleStep; + + js::mjit::JITScript *jit = script->jitNormal ? script->jitNormal : script->jitCtor; + if (jit && script->singleStepMode != jit->singleStepMode) { + js::mjit::Recompiler recompiler(cx, script); + if (!recompiler.recompile()) { + script->singleStepMode = !singleStep; + return JS_FALSE; + } + } +#endif + return JS_TRUE; +} + static JSBool CheckDebugMode(JSContext *cx) { @@ -198,6 +222,15 @@ CheckDebugMode(JSContext *cx) return debugMode; } +JS_PUBLIC_API(JSBool) +JS_SetSingleStepMode(JSContext *cx, JSScript *script, JSBool singleStep) +{ + if (!CheckDebugMode(cx)) + return JS_FALSE; + + return js_SetSingleStepMode(cx, script, singleStep); +} + /* * NB: FindTrap must be called with rt->debuggerLock acquired. */ @@ -485,17 +518,6 @@ JITInhibitingHookChange(JSRuntime *rt, bool wasInhibited) js_ContextFromLinkField(cl)->traceJitEnabled = false; } } - -static void -LeaveTraceRT(JSRuntime *rt) -{ - JSThreadData *data = js_CurrentThreadData(rt); - JSContext *cx = data ? data->traceMonitor.tracecx : NULL; - JS_UNLOCK_GC(rt); - - if (cx) - LeaveTrace(cx); -} #endif JS_PUBLIC_API(JSBool) @@ -511,7 +533,6 @@ JS_SetInterrupt(JSRuntime *rt, JSInterruptHook hook, void *closure) #ifdef JS_TRACER JITInhibitingHookChange(rt, wasInhibited); } - LeaveTraceRT(rt); #endif return JS_TRUE; } @@ -1672,8 +1693,6 @@ JS_SetCallHook(JSRuntime *rt, JSInterpreterHook hook, void *closure) #ifdef JS_TRACER JITInhibitingHookChange(rt, wasInhibited); } - if (hook) - LeaveTraceRT(rt); #endif return JS_TRUE; } @@ -1995,15 +2014,13 @@ JS_FRIEND_API(JSBool) js_DumpCallgrind(JSContext *cx, uintN argc, jsval *vp) { JSString *str; - char *cstr; jsval *argv = JS_ARGV(cx, vp); if (argc > 0 && JSVAL_IS_STRING(argv[0])) { str = JSVAL_TO_STRING(argv[0]); - cstr = js_DeflateString(cx, str->chars(), str->length()); - if (cstr) { - CALLGRIND_DUMP_STATS_AT(cstr); - cx->free(cstr); + JSAutoByteString bytes(cx, str); + if (!!bytes) { + CALLGRIND_DUMP_STATS_AT(bytes.ptr()); return JS_TRUE; } } @@ -2245,7 +2262,7 @@ public: JSHashNumber hash = JS_HashString(filename); JSHashEntry **hep = JS_HashTableRawLookup(traceVisScriptTable, hash, filename); if (*hep != NULL) - return JS_FALSE; + return NULL; JS_HashTableRawAdd(traceVisScriptTable, hep, hash, filename, this); diff --git a/js/src/jsdbgapi.h b/js/src/jsdbgapi.h index 0776b1eaca1..50921615545 100644 --- a/js/src/jsdbgapi.h +++ b/js/src/jsdbgapi.h @@ -78,6 +78,14 @@ js_SetDebugMode(JSContext *cx, JSBool debug); extern JS_PUBLIC_API(JSBool) JS_SetDebugMode(JSContext *cx, JSBool debug); +/* Turn on single step mode. Requires debug mode. */ +extern JS_FRIEND_API(JSBool) +js_SetSingleStepMode(JSContext *cx, JSScript *script, JSBool singleStep); + +/* Turn on single step mode. */ +extern JS_PUBLIC_API(JSBool) +JS_SetSingleStepMode(JSContext *cx, JSScript *script, JSBool singleStep); + /* * Unexported library-private helper used to unpatch all traps in a script. * Returns script->code if script has no traps, else a JS_malloc'ed copy of diff --git a/js/src/jsemit.cpp b/js/src/jsemit.cpp index 0582becfc66..f58569c6a86 100644 --- a/js/src/jsemit.cpp +++ b/js/src/jsemit.cpp @@ -2270,7 +2270,7 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) } /* Optimize accesses to undeclared globals. */ - if (!TryConvertToGname(cg, pn, &op)) + if (!cg->mightAliasLocals() && !TryConvertToGname(cg, pn, &op)) return JS_TRUE; ale = cg->atomList.add(cg->parser, atom); @@ -2500,15 +2500,28 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) JS_ASSERT((cg->fun()->flags & JSFUN_LAMBDA) && atom == cg->fun()->atom); /* - * Leave pn->pn_op == JSOP_NAME if cg->fun() is heavyweight, as we - * cannot be sure cg->fun() is not something of the form: + * Leave pn->pn_op == JSOP_NAME if cg->fun is heavyweight to + * address two cases: a new binding introduced by eval, and + * assignment to the name in strict mode. * - * var ff = (function f(s) { eval(s); return f; }); + * var fun = (function f(s) { eval(s); return f; }); + * assertEq(fun("var f = 42"), 42); * - * where a caller invokes ff("var f = 42"). The result returned for - * such an invocation must be 42, since the callee name is - * lexically bound in an outer declarative environment from the - * function's activation. See jsfun.cpp:call_resolve. + * ECMAScript specifies that a function expression's name is bound + * in a lexical environment distinct from that used to bind its + * named parameters, the arguments object, and its variables. The + * new binding for "var f = 42" shadows the binding for the + * function itself, so the name of the function will not refer to + * the function. + * + * (function f() { "use strict"; f = 12; })(); + * + * Outside strict mode, assignment to a function expression's name + * has no effect. But in strict mode, this attempt to mutate an + * immutable binding must throw a TypeError. We implement this by + * not optimizing such assignments and by marking such functions as + * heavyweight, ensuring that the function name is represented in + * the scope chain so that assignment will throw a TypeError. */ JS_ASSERT(op != JSOP_DELNAME); if (!(cg->flags & TCF_FUN_HEAVYWEIGHT)) { @@ -3764,6 +3777,13 @@ js_EmitFunctionScript(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body) CG_SWITCH_TO_MAIN(cg); } + /* + * Strict mode functions' arguments objects copy initial parameter values. + * We create arguments objects lazily -- but that doesn't work for strict + * mode functions where a parameter might be modified and arguments might + * be accessed. For such functions we synthesize an access to arguments to + * initialize it with the original parameter values. + */ if (cg->needsEagerArguments()) { CG_SWITCH_TO_PROLOG(cg); if (js_Emit1(cx, cg, JSOP_ARGUMENTS) < 0 || js_Emit1(cx, cg, JSOP_POP) < 0) @@ -4613,7 +4633,8 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) if (!cg2->init()) return JS_FALSE; - cg2->flags = pn->pn_funbox->tcflags | TCF_COMPILING | TCF_IN_FUNCTION; + cg2->flags = pn->pn_funbox->tcflags | TCF_COMPILING | TCF_IN_FUNCTION | + (cg->flags & TCF_FUN_MIGHT_ALIAS_LOCALS); #if JS_HAS_SHARP_VARS if (cg2->flags & TCF_HAS_SHARPS) { cg2->sharpSlotBase = fun->sharpSlotBase(cx); diff --git a/js/src/jsemit.h b/js/src/jsemit.h index 0e0f2e32c51..ea3696969f8 100644 --- a/js/src/jsemit.h +++ b/js/src/jsemit.h @@ -252,10 +252,8 @@ struct JSStmtInfo { #define TCF_COMPILE_FOR_EVAL 0x2000000 /* - * The function has broken or incorrect def-use information, and it cannot - * safely optimize free variables to global names. This can happen because - * of a named function statement not at the top level (emitting a DEFFUN), - * or a variable declaration inside a "with". + * The function or a function that encloses it may define new local names + * at runtime through means other than calling eval. */ #define TCF_FUN_MIGHT_ALIAS_LOCALS 0x4000000 diff --git a/js/src/jsexn.cpp b/js/src/jsexn.cpp index 893d2ab21c6..d4b26ae6f98 100644 --- a/js/src/jsexn.cpp +++ b/js/src/jsexn.cpp @@ -612,10 +612,11 @@ StackTraceToString(JSContext *cx, JSExnPrivate *priv) #define APPEND_STRING_TO_STACK(str) \ JS_BEGIN_MACRO \ JSString *str_ = str; \ - const jschar *chars_; \ - size_t length_; \ + size_t length_ = str_->length(); \ + const jschar *chars_ = str_->getChars(cx); \ + if (!chars_) \ + goto bad; \ \ - str_->getCharsAndLength(chars_, length_); \ if (length_ > stackmax - stacklen) { \ void *ptr_; \ if (stackmax >= STACK_LENGTH_LIMIT || \ @@ -813,11 +814,17 @@ exn_toString(JSContext *cx, uintN argc, Value *vp) return JS_FALSE; if (name_length) { - js_strncpy(cp, name->chars(), name_length); + const jschar *name_chars = name->getChars(cx); + if (!name_chars) + return JS_FALSE; + js_strncpy(cp, name_chars, name_length); cp += name_length; *cp++ = ':'; *cp++ = ' '; } - js_strncpy(cp, message->chars(), message_length); + const jschar *message_chars = message->getChars(cx); + if (!message_chars) + return JS_FALSE; + js_strncpy(cp, message_chars, message_length); cp += message_length; *cp = 0; @@ -917,18 +924,27 @@ exn_toSource(JSContext *cx, uintN argc, Value *vp) return false; *cp++ = '('; *cp++ = 'n'; *cp++ = 'e'; *cp++ = 'w'; *cp++ = ' '; - js_strncpy(cp, name->chars(), name_length); + const jschar *name_chars = name->getChars(cx); + if (!name_chars) + return false; + js_strncpy(cp, name_chars, name_length); cp += name_length; *cp++ = '('; + const jschar *message_chars = message->getChars(cx); + if (!message_chars) + return false; if (message_length != 0) { - js_strncpy(cp, message->chars(), message_length); + js_strncpy(cp, message_chars, message_length); cp += message_length; } if (filename_length != 0) { /* append filename as ``, {filename}'' */ *cp++ = ','; *cp++ = ' '; - js_strncpy(cp, filename->chars(), filename_length); + const jschar *filename_chars = filename->getChars(cx); + if (!filename_chars) + return false; + js_strncpy(cp, filename_chars, filename_length); cp += filename_length; } else { if (lineno_as_str) { @@ -942,7 +958,10 @@ exn_toSource(JSContext *cx, uintN argc, Value *vp) if (lineno_as_str) { /* append lineno as ``, {lineno_as_str}'' */ *cp++ = ','; *cp++ = ' '; - js_strncpy(cp, lineno_as_str->chars(), lineno_length); + const jschar *lineno_chars = lineno_as_str->getChars(cx); + if (!lineno_chars) + return false; + js_strncpy(cp, lineno_chars, lineno_length); cp += lineno_length; } diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp new file mode 100644 index 00000000000..840376d89a7 --- /dev/null +++ b/js/src/jsfriendapi.cpp @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et tw=99: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * 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 or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is SpiderMonkey code. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "jscntxt.h" +#include "jsfriendapi.h" + +JS_FRIEND_API(JSString *) +JS_GetAnonymousString(JSRuntime *rt) +{ + JS_ASSERT(rt->state == JSRTS_UP); + return ATOM_TO_STRING(rt->atomState.anonymousAtom); +} diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h new file mode 100644 index 00000000000..a609381c71b --- /dev/null +++ b/js/src/jsfriendapi.h @@ -0,0 +1,53 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * 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 or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * + * The Original Code is SpiderMonkey code. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsfriendapi_h___ +#define jsfriendapi_h___ + +#include "jspubtd.h" +#include "jsprvtd.h" + +JS_BEGIN_EXTERN_C + +extern JS_FRIEND_API(JSString *) +JS_GetAnonymousString(JSRuntime *rt); + +JS_END_EXTERN_C + +#endif /* jsfriendapi_h___ */ diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index d1d52c5f12e..9b981e7683f 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -598,27 +598,30 @@ args_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JS_ASSERT(obj->isNormalArguments()); *objp = NULL; - bool valid = false; - uintN attrs = JSPROP_SHARED; + + uintN attrs = JSPROP_SHARED | JSPROP_SHADOWABLE; if (JSID_IS_INT(id)) { uint32 arg = uint32(JSID_TO_INT(id)); - attrs = JSPROP_ENUMERATE | JSPROP_SHARED; - if (arg < obj->getArgsInitialLength() && !obj->getArgsElement(arg).isMagic(JS_ARGS_HOLE)) - valid = true; + if (arg >= obj->getArgsInitialLength() || obj->getArgsElement(arg).isMagic(JS_ARGS_HOLE)) + return true; + + attrs |= JSPROP_ENUMERATE; } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) { - if (!obj->isArgsLengthOverridden()) - valid = true; - } else if (JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom)) { - if (!obj->getArgsCallee().isMagic(JS_ARGS_HOLE)) - valid = true; + if (obj->isArgsLengthOverridden()) + return true; + } else { + if (!JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom)) + return true; + + if (obj->getArgsCallee().isMagic(JS_ARGS_HOLE)) + return true; } - if (valid) { - Value tmp = UndefinedValue(); - if (!js_DefineProperty(cx, obj, id, &tmp, ArgGetter, ArgSetter, attrs)) - return JS_FALSE; - *objp = obj; - } + Value undef = UndefinedValue(); + if (!js_DefineProperty(cx, obj, id, &undef, ArgGetter, ArgSetter, attrs)) + return JS_FALSE; + + *objp = obj; return true; } @@ -707,47 +710,35 @@ strictargs_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject JS_ASSERT(obj->isStrictArguments()); *objp = NULL; - bool valid = false; - uintN attrs = JSPROP_SHARED; + + uintN attrs = JSPROP_SHARED | JSPROP_SHADOWABLE; + PropertyOp getter = StrictArgGetter; + PropertyOp setter = StrictArgSetter; + if (JSID_IS_INT(id)) { uint32 arg = uint32(JSID_TO_INT(id)); - attrs = JSPROP_SHARED | JSPROP_ENUMERATE; - if (arg < obj->getArgsInitialLength() && !obj->getArgsElement(arg).isMagic(JS_ARGS_HOLE)) - valid = true; - } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) { - if (!obj->isArgsLengthOverridden()) - valid = true; - } else if (JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom)) { - Value tmp = UndefinedValue(); - PropertyOp throwTypeError = CastAsPropertyOp(obj->getThrowTypeError()); - uintN attrs = JSPROP_PERMANENT | JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED; - if (!js_DefineProperty(cx, obj, id, &tmp, throwTypeError, throwTypeError, attrs)) - return false; + if (arg >= obj->getArgsInitialLength() || obj->getArgsElement(arg).isMagic(JS_ARGS_HOLE)) + return true; - *objp = obj; - return true; - } else if (JSID_IS_ATOM(id, cx->runtime->atomState.callerAtom)) { - /* - * Strict mode arguments objects have an immutable poison-pill caller - * property that throws a TypeError on getting or setting. - */ - PropertyOp throwTypeError = CastAsPropertyOp(obj->getThrowTypeError()); - Value tmp = UndefinedValue(); - if (!js_DefineProperty(cx, obj, id, &tmp, throwTypeError, throwTypeError, - JSPROP_PERMANENT | JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED)) { - return false; + attrs |= JSPROP_ENUMERATE; + } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) { + if (obj->isArgsLengthOverridden()) + return true; + } else { + if (!JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom) && + !JSID_IS_ATOM(id, cx->runtime->atomState.callerAtom)) { + return true; } - *objp = obj; - return true; + attrs = JSPROP_PERMANENT | JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED; + getter = setter = CastAsPropertyOp(obj->getThrowTypeError()); } - if (valid) { - Value tmp = UndefinedValue(); - if (!js_DefineProperty(cx, obj, id, &tmp, StrictArgGetter, StrictArgSetter, attrs)) - return false; - *objp = obj; - } + Value undef = UndefinedValue(); + if (!js_DefineProperty(cx, obj, id, &undef, getter, setter, attrs)) + return false; + + *objp = obj; return true; } @@ -1589,10 +1580,13 @@ fun_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp) /* Censor the caller if it is from another compartment. */ if (caller.getCompartment() != cx->compartment) { vp->setNull(); - } else if (caller.isFunction() && caller.getFunctionPrivate()->inStrictMode()) { - JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, - JSMSG_CALLER_IS_STRICT); - return false; + } else if (caller.isFunction()) { + JSFunction *callerFun = caller.getFunctionPrivate(); + if (callerFun->isInterpreted() && callerFun->inStrictMode()) { + JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, + JSMSG_CALLER_IS_STRICT); + return false; + } } } break; @@ -1762,7 +1756,7 @@ fun_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, PropertyOp getter, setter; uintN attrs = JSPROP_PERMANENT; - if (fun->inStrictMode() || obj->isBoundFunction()) { + if (fun->isInterpreted() ? fun->inStrictMode() : obj->isBoundFunction()) { JSObject *throwTypeError = obj->getThrowTypeError(); getter = CastAsPropertyOp(throwTypeError); @@ -2060,7 +2054,7 @@ fun_finalize(JSContext *cx, JSObject *obj) * very early. */ if (FUN_INTERPRETED(fun) && fun->u.i.script) - js_DestroyScriptFromGC(cx, fun->u.i.script, NULL); + js_DestroyScriptFromGC(cx, fun->u.i.script); } int @@ -2603,7 +2597,12 @@ Function(JSContext *cx, uintN argc, Value *vp) for (uintN i = 0; i < n; i++) { JSString *arg = argv[i].toString(); size_t arg_length = arg->length(); - (void) js_strncpy(cp, arg->chars(), arg_length); + const jschar *arg_chars = arg->getChars(cx); + if (!arg_chars) { + JS_ARENA_RELEASE(&cx->tempPool, mark); + return JS_FALSE; + } + (void) js_strncpy(cp, arg_chars, arg_length); cp += arg_length; /* Add separating comma or terminating 0. */ @@ -2690,8 +2689,11 @@ Function(JSContext *cx, uintN argc, Value *vp) str = cx->runtime->emptyString; } - return Compiler::compileFunctionBody(cx, fun, principals, - str->chars(), str->length(), + size_t length = str->length(); + const jschar *chars = str->getChars(cx); + if (!chars) + return JS_FALSE; + return Compiler::compileFunctionBody(cx, fun, principals, chars, length, filename, lineno); } @@ -3143,54 +3145,21 @@ JSFunction::addLocal(JSContext *cx, JSAtom *atom, JSLocalKind kind) return false; } - Shape **listp = &u.i.names; - Shape *parent = *listp; jsid id; - - /* - * The destructuring formal parameter parser adds a null atom, which we - * encode as an INT id. The parser adds such locals after adding vars for - * the destructured-to parameter bindings -- those must be vars to avoid - * aliasing arguments[i] for any i -- so we must switch u.i.names to a - * dictionary list to cope with insertion "in the middle" of an index-named - * shape for the object or array argument. - */ - bool findArgInsertionPoint = false; if (!atom) { JS_ASSERT(kind == JSLOCAL_ARG); - if (u.i.nvars != 0) { - /* - * A dictionary list needed only if the destructing pattern wasn't - * empty, i.e., there were vars for its destructured-to bindings. - */ - if (!parent->inDictionary() && !(parent = Shape::newDictionaryList(cx, listp))) - return false; - findArgInsertionPoint = true; - } id = INT_TO_JSID(nargs); } else { - if (kind == JSLOCAL_ARG && parent->inDictionary()) - findArgInsertionPoint = true; id = ATOM_TO_JSID(atom); } - if (findArgInsertionPoint) { - while (parent->parent && parent->getter() != GetCallArg) { - ++parent->slot; - JS_ASSERT(parent->slot == parent->slotSpan); - ++parent->slotSpan; - listp = &parent->parent; - parent = *listp; - } - } - Shape child(id, getter, setter, slot, attrs, Shape::HAS_SHORTID, *indexp); - Shape *shape = parent->getChild(cx, child, listp); + Shape *shape = u.i.names->getChild(cx, child, &u.i.names); if (!shape) return false; - JS_ASSERT_IF(!shape->inDictionary(), u.i.names == shape); + JS_ASSERT(u.i.names == shape); ++*indexp; return true; } diff --git a/js/src/jsfun.h b/js/src/jsfun.h index d6affbc195c..a226bb58266 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -180,10 +180,9 @@ struct JSFunction : public JSObject_Slots2 bool isFunctionPrototype() const { return flags & JSFUN_PROTOTYPE; } + /* Returns the strictness of this function, which must be interpreted. */ inline bool inStrictMode() const; - bool acceptsPrimitiveThis() const { return flags & JSFUN_PRIMITIVE_THIS; } - uintN countVars() const { JS_ASSERT(FUN_INTERPRETED(this)); return u.i.nvars; @@ -215,6 +214,11 @@ struct JSFunction : public JSObject_Slots2 const js::Shape *lastVar() const; const js::Shape *lastUpvar() const { return u.i.names; } + /* + * The parser builds shape paths for functions, usable by Call objects at + * runtime, by calling addLocal. All locals of ARG kind must be addLocal'ed + * before any VAR kind, and VAR before UPVAR. + */ bool addLocal(JSContext *cx, JSAtom *atom, JSLocalKind kind); /* @@ -435,6 +439,14 @@ IsFunctionObject(const js::Value &v, JSFunction **fun) return b; } +extern JS_ALWAYS_INLINE bool +SameTraceType(const Value &lhs, const Value &rhs) +{ + return SameType(lhs, rhs) && + (lhs.isPrimitive() || + lhs.toObject().isFunction() == rhs.toObject().isFunction()); +} + /* * Macro to access the private slot of the function object after the slot is * initialized. diff --git a/js/src/jsfuninlines.h b/js/src/jsfuninlines.h index 9526ea7b880..4cbb744b236 100644 --- a/js/src/jsfuninlines.h +++ b/js/src/jsfuninlines.h @@ -46,7 +46,7 @@ inline bool JSFunction::inStrictMode() const { - return isInterpreted() && u.i.script->strictModeCode; + return script()->strictModeCode; } #endif /* jsfuninlines_h___ */ diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 1e062d44053..edc14f7fe48 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -492,6 +492,13 @@ js_GCThingIsMarked(void *thing, uint32 color = BLACK) return reinterpret_cast(thing)->isMarked(color); } +/* + * 1/8 life for JIT code. After this number of microseconds have passed, 1/8 of all + * JIT code is discarded in inactive compartments, regardless of how often that + * code runs. + */ +static const int64 JIT_SCRIPT_EIGHTH_LIFETIME = 120 * 1000 * 1000; + JSBool js_InitGC(JSRuntime *rt, uint32 maxbytes) { @@ -539,6 +546,8 @@ js_InitGC(JSRuntime *rt, uint32 maxbytes) */ rt->setGCLastBytes(8192); + rt->gcJitReleaseTime = PRMJ_Now() + JIT_SCRIPT_EIGHTH_LIFETIME; + METER(PodZero(&rt->gcStats)); return true; } @@ -777,16 +786,18 @@ ConservativeGCThreadData::recordStackTop() jsuword dummy; nativeStackTop = &dummy; - /* Update the register snapshot with the latest values. */ + /* + * To record and update the register snapshot for the conservative + * scanning with the latest values we use setjmp. + */ #if defined(_MSC_VER) # pragma warning(push) # pragma warning(disable: 4611) #endif - setjmp(registerSnapshot.jmpbuf); + (void) setjmp(registerSnapshot.jmpbuf); #if defined(_MSC_VER) # pragma warning(pop) #endif - } static inline void @@ -1016,7 +1027,7 @@ FreeLists::purge() *p = NULL; } -struct JSShortString; +class JSShortString; ArenaList * GetFinalizableArenaList(JSCompartment *c, unsigned thingKind) { @@ -1435,8 +1446,10 @@ js_TraceStackFrame(JSTracer *trc, JSStackFrame *fp) MarkObject(trc, fp->callObj(), "call"); if (fp->hasArgsObj()) MarkObject(trc, fp->argsObj(), "arguments"); - if (fp->isScriptFrame()) + if (fp->isScriptFrame()) { js_TraceScript(trc, fp->script()); + fp->script()->compartment->active = true; + } MarkValue(trc, fp->returnValue(), "rval"); } @@ -1732,16 +1745,16 @@ TriggerGC(JSRuntime *rt) } /* namespace js */ void -js_DestroyScriptsToGC(JSContext *cx, JSThreadData *data) +js_DestroyScriptsToGC(JSContext *cx, JSCompartment *comp) { JSScript **listp, *script; - for (size_t i = 0; i != JS_ARRAY_LENGTH(data->scriptsToGC); ++i) { - listp = &data->scriptsToGC[i]; + for (size_t i = 0; i != JS_ARRAY_LENGTH(comp->scriptsToGC); ++i) { + listp = &comp->scriptsToGC[i]; while ((script = *listp) != NULL) { *listp = script->u.nextToGC; script->u.nextToGC = NULL; - js_DestroyScriptFromGC(cx, script, data); + js_DestroyScriptFromGC(cx, script); } } } @@ -1767,7 +1780,7 @@ js_FinalizeStringRT(JSRuntime *rt, JSString *str) JS_ASSERT(IsFinalizableStringKind(thingKind)); /* A stillborn string has null chars, so is not valid. */ - jschar *chars = str->flatChars(); + jschar *chars = const_cast(str->flatChars()); if (!chars) return; if (thingKind == FINALIZE_STRING) { @@ -2029,13 +2042,29 @@ SweepCompartments(JSContext *cx, JSGCInvocationKind gckind) /* Delete defaultCompartment only during runtime shutdown */ rt->defaultCompartment->marked = true; + /* + * Figure out how much JIT code should be released from inactive compartments. + * If multiple eighth-lifes have passed, compound the release interval linearly; + * if enough time has passed, all inactive JIT code will be released. + */ + uint32 releaseInterval = 0; + int64 now = PRMJ_Now(); + if (now >= rt->gcJitReleaseTime) { + releaseInterval = 8; + while (now >= rt->gcJitReleaseTime) { + if (--releaseInterval == 1) + rt->gcJitReleaseTime = now; + rt->gcJitReleaseTime += JIT_SCRIPT_EIGHTH_LIFETIME; + } + } + while (read < end) { JSCompartment *compartment = (*read++); if (compartment->marked) { compartment->marked = false; *write++ = compartment; /* Remove dead wrappers from the compartment map. */ - compartment->sweep(cx); + compartment->sweep(cx, releaseInterval); } else { JS_ASSERT(compartment->freeLists.isEmpty()); if (compartment->arenaListsAreEmpty() || gckind == GC_LAST_CONTEXT) { @@ -2047,7 +2076,7 @@ SweepCompartments(JSContext *cx, JSGCInvocationKind gckind) } else { compartment->marked = false; *write++ = compartment; - compartment->sweep(cx); + compartment->sweep(cx, releaseInterval); } } } @@ -2177,11 +2206,6 @@ MarkAndSweep(JSContext *cx, JSGCInvocationKind gckind GCTIMER_PARAM) rt->liveObjectPropsPreSweep = rt->liveObjectProps; #endif -#ifdef JS_TRACER - for (ThreadDataIter i(rt); !i.empty(); i.popFront()) - i.threadData()->traceMonitor.sweep(); -#endif - /* * We finalize iterators before other objects so the iterator can use the * object which properties it enumerates over to finalize the enumeration @@ -2589,6 +2613,7 @@ NewCompartment(JSContext *cx, JSPrincipals *principals) JSRuntime *rt = cx->runtime; JSCompartment *compartment = new JSCompartment(rt); if (!compartment || !compartment->init()) { + delete compartment; JS_ReportOutOfMemory(cx); return NULL; } diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 99838cd66fd..59c9eb60437 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -827,7 +827,7 @@ js_WaitForGC(JSRuntime *rt); #endif extern void -js_DestroyScriptsToGC(JSContext *cx, JSThreadData *data); +js_DestroyScriptsToGC(JSContext *cx, JSCompartment *comp); namespace js { diff --git a/js/src/jsgcinlines.h b/js/src/jsgcinlines.h index 47a0a85d526..7df550fb97e 100644 --- a/js/src/jsgcinlines.h +++ b/js/src/jsgcinlines.h @@ -272,8 +272,6 @@ MarkChildren(JSTracer *trc, JSString *str) if (str->isDependent()) MarkString(trc, str->dependentBase(), "base"); else if (str->isRope()) { - if (str->isInteriorNode()) - MarkString(trc, str->interiorNodeParent(), "parent"); MarkString(trc, str->ropeLeft(), "left child"); MarkString(trc, str->ropeRight(), "right child"); } @@ -347,37 +345,116 @@ TypedMarker(JSTracer *trc, JSFunction *thing) static JS_ALWAYS_INLINE void TypedMarker(JSTracer *trc, JSShortString *thing) { - thing->asCell()->markIfUnmarked(); + /* + * A short string cannot refer to other GC things so we don't have + * anything to mark if the string was unmarked and ignore the + * markIfUnmarked result. + */ + (void) thing->asCell()->markIfUnmarked(); } +} /* namespace gc */ + +namespace detail { + +static JS_ALWAYS_INLINE JSString * +Tag(JSString *str) +{ + JS_ASSERT(!(size_t(str) & 1)); + return (JSString *)(size_t(str) | 1); +} + +static JS_ALWAYS_INLINE bool +Tagged(JSString *str) +{ + return (size_t(str) & 1) != 0; +} + +static JS_ALWAYS_INLINE JSString * +Untag(JSString *str) +{ + JS_ASSERT((size_t(str) & 1) == 1); + return (JSString *)(size_t(str) & ~size_t(1)); +} + +static JS_ALWAYS_INLINE void +NonRopeTypedMarker(JSString *str) +{ + JS_ASSERT(!str->isRope()); + if (JSString::isStatic(str) || + !str->asCell()->markIfUnmarked() || + !str->isDependent()) { + return; + } + JSString *base = str->dependentBase(); + if (JSString::isStatic(base)) + return; + base->asCell()->markIfUnmarked(); +} + +} /* namespace detail */ + +namespace gc { + static JS_ALWAYS_INLINE void TypedMarker(JSTracer *trc, JSString *str) { - /* - * When marking any node of a rope, mark the entire rope. This means if - * a given rope node is already marked, we are done. - */ - JSRopeNodeIterator iter; - if (str->isRope()) { - if (str->asCell()->isMarked()) - return; - str = iter.init(str); - goto not_static; + using namespace detail; + + if (!str->isRope()) { + NonRopeTypedMarker(str); + return; } - do { - for (;;) { - if (JSString::isStatic(str)) - break; - not_static: - JS_ASSERT(JSTRACE_STRING == GetFinalizableTraceKind(str->asCell()->arena()->header()->thingKind)); - if (!str->asCell()->markIfUnmarked()) - break; - if (!str->isDependent()) - break; - str = str->dependentBase(); + + /* + * This function must not fail, so a simple stack-based traversal must not + * be used (since it may oom if the stack grows large). Instead, strings + * are temporarily mutated to embed parent pointers as they are traversed. + * This algorithm is homomorphic to JSString::flatten. + */ + JSString *parent = NULL; + first_visit_node: { + if (!str->asCell()->markIfUnmarked()) + goto finish_node; + JSString *left = str->ropeLeft(); + if (left->isRope()) { + JS_ASSERT(!Tagged(str->u.left) && !Tagged(str->s.right)); + str->u.left = Tag(parent); + parent = str; + str = left; + goto first_visit_node; } - str = iter.next(); - } while (str); + NonRopeTypedMarker(left); + } + visit_right_child: { + JSString *right = str->ropeRight(); + if (right->isRope()) { + JS_ASSERT(!Tagged(str->u.left) && !Tagged(str->s.right)); + str->s.right = Tag(parent); + parent = str; + str = right; + goto first_visit_node; + } + NonRopeTypedMarker(right); + } + finish_node: { + if (!parent) + return; + if (Tagged(parent->u.left)) { + JS_ASSERT(!Tagged(parent->s.right)); + JSString *nextParent = Untag(parent->u.left); + parent->u.left = str; + str = parent; + parent = nextParent; + goto visit_right_child; + } + JS_ASSERT(Tagged(parent->s.right)); + JSString *nextParent = Untag(parent->s.right); + parent->s.right = str; + str = parent; + parent = nextParent; + goto finish_node; + } } static inline void diff --git a/js/src/jsgcstats.cpp b/js/src/jsgcstats.cpp index 85022aa191d..56b48ab3c98 100644 --- a/js/src/jsgcstats.cpp +++ b/js/src/jsgcstats.cpp @@ -362,9 +362,13 @@ GCMarker::dumpConservativeRoots() } case JSTRACE_STRING: { JSString *str = (JSString *) i->thing; - char buf[50]; - PutEscapedString(buf, sizeof buf, str, '"'); - fprintf(fp, "string %s", buf); + if (str->isLinear()) { + char buf[50]; + PutEscapedString(buf, sizeof buf, str->assertIsLinear(), '"'); + fprintf(fp, "string %s", buf); + } else { + fprintf(fp, "rope: length %d", (int)str->length()); + } break; } # if JS_HAS_XML_SUPPORT diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 423b221f2f4..e40fe3d21a4 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -1000,21 +1000,12 @@ Execute(JSContext *cx, JSObject *chain, JSScript *script, Probes::startExecution(cx, script); - void *hookData = NULL; - if (JSInterpreterHook hook = cx->debugHooks->executeHook) - hookData = hook(cx, frame.fp(), JS_TRUE, 0, cx->debugHooks->executeHookData); - /* Run script until JSOP_STOP or error. */ AutoPreserveEnumerators preserve(cx); JSBool ok = RunScript(cx, script, frame.fp()); if (result) *result = frame.fp()->returnValue(); - if (hookData) { - if (JSInterpreterHook hook = cx->debugHooks->executeHook) - hook(cx, frame.fp(), JS_FALSE, &ok, hookData); - } - Probes::stopExecution(cx, script); return !!ok; @@ -1143,40 +1134,44 @@ HasInstance(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp) return JS_FALSE; } -static JS_ALWAYS_INLINE bool -EqualObjects(JSContext *cx, JSObject *lobj, JSObject *robj) -{ - return lobj == robj; -} - bool -StrictlyEqual(JSContext *cx, const Value &lref, const Value &rref) +StrictlyEqual(JSContext *cx, const Value &lref, const Value &rref, JSBool *equal) { Value lval = lref, rval = rref; if (SameType(lval, rval)) { if (lval.isString()) - return js_EqualStrings(lval.toString(), rval.toString()); - if (lval.isDouble()) - return JSDOUBLE_COMPARE(lval.toDouble(), ==, rval.toDouble(), JS_FALSE); - if (lval.isObject()) - return EqualObjects(cx, &lval.toObject(), &rval.toObject()); - if (lval.isUndefined()) - return true; - return lval.payloadAsRawUint32() == rval.payloadAsRawUint32(); + return EqualStrings(cx, lval.toString(), rval.toString(), equal); + if (lval.isDouble()) { + *equal = JSDOUBLE_COMPARE(lval.toDouble(), ==, rval.toDouble(), JS_FALSE); + return true; + } + if (lval.isObject()) { + *equal = &lval.toObject() == &rval.toObject(); + return true; + } + if (lval.isUndefined()) { + *equal = true; + return true; + } + *equal = lval.payloadAsRawUint32() == rval.payloadAsRawUint32(); + return true; } if (lval.isDouble() && rval.isInt32()) { double ld = lval.toDouble(); double rd = rval.toInt32(); - return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE); + *equal = JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE); + return true; } if (lval.isInt32() && rval.isDouble()) { double ld = lval.toInt32(); double rd = rval.toDouble(); - return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE); + *equal = JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE); + return true; } - return false; + *equal = false; + return true; } static inline bool @@ -1192,15 +1187,21 @@ IsNaN(const Value &v) } bool -SameValue(const Value &v1, const Value &v2, JSContext *cx) +SameValue(JSContext *cx, const Value &v1, const Value &v2, JSBool *same) { - if (IsNegativeZero(v1)) - return IsNegativeZero(v2); - if (IsNegativeZero(v2)) - return false; - if (IsNaN(v1) && IsNaN(v2)) + if (IsNegativeZero(v1)) { + *same = IsNegativeZero(v2); return true; - return StrictlyEqual(cx, v1, v2); + } + if (IsNegativeZero(v2)) { + *same = false; + return true; + } + if (IsNaN(v1) && IsNaN(v2)) { + *same = true; + return true; + } + return StrictlyEqual(cx, v1, v2, same); } JSType @@ -1529,7 +1530,6 @@ js_LogOpcode(JSContext *cx) JSStackFrame *fp; JSFrameRegs *regs; intN ndefs, n, nuses; - JSString *str; JSOp op; logfp = (FILE *) cx->logfp; @@ -1575,12 +1575,13 @@ js_LogOpcode(JSContext *cx) */ fputs("", logfp); } else { - str = js_ValueToString(cx, *siter); - if (!str) { + JSString *str = js_ValueToString(cx, *siter); + JSLinearString *linearStr = str ? str->ensureLinear(cx) : NULL; + if (!linearStr) { fputs("", logfp); - } else { JS_ClearPendingException(cx); - FileEscapedString(logfp, str, 0); + } else { + FileEscapedString(logfp, linearStr, 0); } } fputc(' ', logfp); @@ -2203,12 +2204,14 @@ ScriptPrologue(JSContext *cx, JSStackFrame *fp) return false; fp->functionThis().setObject(*obj); } - JSInterpreterHook hook = cx->debugHooks->callHook; - if (JS_UNLIKELY(hook != NULL) && !fp->isExecuteFrame()) - fp->setHookData(hook(cx, fp, JS_TRUE, 0, cx->debugHooks->callHookData)); - - if (!fp->isExecuteFrame()) + if (fp->isExecuteFrame()) { + if (JSInterpreterHook hook = cx->debugHooks->executeHook) + fp->setHookData(hook(cx, fp, JS_TRUE, 0, cx->debugHooks->executeHookData)); + } else { + if (JSInterpreterHook hook = cx->debugHooks->callHook) + fp->setHookData(hook(cx, fp, JS_TRUE, 0, cx->debugHooks->callHookData)); Probes::enterJSFun(cx, fp->maybeFun(), fp->maybeScript()); + } return true; } @@ -3093,8 +3096,6 @@ BEGIN_CASE(JSOP_MOREITER) if (!IteratorMore(cx, ®s.sp[-2].toObject(), &cond, ®s.sp[-1])) goto error; CHECK_INTERRUPT_HANDLER(); - TRY_BRANCH_AFTER_COND(cond, 1); - JS_ASSERT(regs.pc[1] == JSOP_IFNEX); regs.sp[-1].setBoolean(cond); } END_CASE(JSOP_MOREITER) @@ -3421,7 +3422,10 @@ END_CASE(JSOP_BITAND) if (SameType(lval, rval)) { \ if (lval.isString()) { \ JSString *l = lval.toString(), *r = rval.toString(); \ - cond = js_EqualStrings(l, r) OP JS_TRUE; \ + JSBool equal; \ + if (!EqualStrings(cx, l, r, &equal)) \ + goto error; \ + cond = equal OP JS_TRUE; \ } else if (lval.isDouble()) { \ double l = lval.toDouble(), r = rval.toDouble(); \ cond = JSDOUBLE_COMPARE(l, OP, r, IFNAN); \ @@ -3450,7 +3454,10 @@ END_CASE(JSOP_BITAND) DEFAULT_VALUE(cx, -1, JSTYPE_VOID, rval); \ if (lval.isString() && rval.isString()) { \ JSString *l = lval.toString(), *r = rval.toString(); \ - cond = js_EqualStrings(l, r) OP JS_TRUE; \ + JSBool equal; \ + if (!EqualStrings(cx, l, r, &equal)) \ + goto error; \ + cond = equal OP JS_TRUE; \ } else { \ double l, r; \ if (!ValueToNumber(cx, lval, &l) || \ @@ -3482,7 +3489,10 @@ END_CASE(JSOP_NE) JS_BEGIN_MACRO \ const Value &rref = regs.sp[-1]; \ const Value &lref = regs.sp[-2]; \ - COND = StrictlyEqual(cx, lref, rref) OP true; \ + JSBool equal; \ + if (!StrictlyEqual(cx, lref, rref, &equal)) \ + goto error; \ + COND = equal OP JS_TRUE; \ regs.sp--; \ JS_END_MACRO @@ -3543,7 +3553,10 @@ END_CASE(JSOP_CASEX) DEFAULT_VALUE(cx, -1, JSTYPE_NUMBER, rval); \ if (lval.isString() && rval.isString()) { \ JSString *l = lval.toString(), *r = rval.toString(); \ - cond = js_CompareStrings(l, r) OP 0; \ + int32 result; \ + if (!CompareStrings(cx, l, r, &result)) \ + goto error; \ + cond = result OP 0; \ } else { \ double l, r; \ if (!ValueToNumber(cx, lval, &l) || \ @@ -5126,12 +5139,14 @@ BEGIN_CASE(JSOP_LOOKUPSWITCH) } if (lval.isString()) { - JSString *str = lval.toString(); - JSString *str2; + JSLinearString *str = lval.toString()->ensureLinear(cx); + if (!str) + goto error; + JSLinearString *str2; SEARCH_PAIRS( match = (rval.isString() && - ((str2 = rval.toString()) == str || - js_EqualStrings(str2, str))); + ((str2 = rval.toString()->assertIsLinear()) == str || + EqualStrings(str2, str))); ) } else if (lval.isNumber()) { double ldbl = lval.toNumber(); diff --git a/js/src/jsinterp.h b/js/src/jsinterp.h index f53eb5a3eef..74a09088b73 100644 --- a/js/src/jsinterp.h +++ b/js/src/jsinterp.h @@ -857,13 +857,6 @@ ComputeThisFromVpInPlace(JSContext *cx, js::Value *vp) return ComputeThisFromArgv(cx, vp + 2); } -/* Return true if |fun| would accept |v| as its |this|, without being wrapped. */ -JS_ALWAYS_INLINE bool -PrimitiveThisTest(JSFunction *fun, const Value &v) -{ - return !v.isPrimitive() || fun->acceptsPrimitiveThis(); -} - /* * Abstracts the layout of the stack passed to natives from the engine and from * natives to js::Invoke. @@ -1030,11 +1023,11 @@ CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs, JSObject **objp, JSProperty **propp); extern bool -StrictlyEqual(JSContext *cx, const Value &lval, const Value &rval); +StrictlyEqual(JSContext *cx, const Value &lval, const Value &rval, JSBool *equal); /* === except that NaN is the same as NaN and -0 is not the same as +0. */ extern bool -SameValue(const Value &v1, const Value &v2, JSContext *cx); +SameValue(JSContext *cx, const Value &v1, const Value &v2, JSBool *same); extern JSType TypeOfValue(JSContext *cx, const Value &v); diff --git a/js/src/jsinterpinlines.h b/js/src/jsinterpinlines.h index 6e7ce89b0c9..2d19aefd92a 100644 --- a/js/src/jsinterpinlines.h +++ b/js/src/jsinterpinlines.h @@ -49,6 +49,8 @@ #include "jsstr.h" #include "methodjit/MethodJIT.h" +#include "jsfuninlines.h" + inline void JSStackFrame::initPrev(JSContext *cx) { @@ -356,7 +358,7 @@ JSStackFrame::computeThis(JSContext *cx) if (thisv.isObject()) return true; if (isFunctionFrame()) { - if (fun()->acceptsPrimitiveThis()) + if (fun()->inStrictMode()) return true; /* * Eval function frames have their own |this| slot, which is a copy of the function's @@ -713,10 +715,11 @@ ScriptEpilogue(JSContext *cx, JSStackFrame *fp, JSBool ok) if (!fp->isExecuteFrame()) Probes::exitJSFun(cx, fp->maybeFun(), fp->maybeScript()); - JSInterpreterHook hook = cx->debugHooks->callHook; - void* hookData; + JSInterpreterHook hook = + fp->isExecuteFrame() ? cx->debugHooks->executeHook : cx->debugHooks->callHook; - if (hook && (hookData = fp->maybeHookData()) && !fp->isExecuteFrame()) + void* hookData; + if (JS_UNLIKELY(hook != NULL) && (hookData = fp->maybeHookData())) hook(cx, fp, JS_FALSE, &ok, hookData); /* diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 175919ffebd..9055b85cf68 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -626,8 +626,6 @@ UpdateNativeIterator(NativeIterator *ni, JSObject *obj) bool GetIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp) { - uint32 hash; - JSObject **hp; Vector shapes(cx); uint32 key = 0; @@ -651,7 +649,7 @@ GetIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp) * objects here, as they are not inserted into the cache and * will result in a miss. */ - JSObject *last = JS_THREAD_DATA(cx)->lastNativeIterator; + JSObject *last = cx->compartment->nativeIterCache.last; JSObject *proto = obj->getProto(); if (last) { NativeIterator *lastni = last->getNativeIterator(); @@ -689,9 +687,7 @@ GetIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp) pobj = pobj->getProto(); } while (pobj); - hash = key % JS_ARRAY_LENGTH(JS_THREAD_DATA(cx)->cachedNativeIterators); - hp = &JS_THREAD_DATA(cx)->cachedNativeIterators[hash]; - JSObject *iterobj = *hp; + JSObject *iterobj = cx->compartment->nativeIterCache.get(key); if (iterobj) { NativeIterator *ni = iterobj->getNativeIterator(); if (!(ni->flags & JSITER_ACTIVE) && @@ -703,7 +699,7 @@ GetIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp) UpdateNativeIterator(ni, obj); RegisterEnumerator(cx, iterobj, ni); if (shapes.length() == 2) - JS_THREAD_DATA(cx)->lastNativeIterator = iterobj; + cx->compartment->nativeIterCache.last = iterobj; return true; } } @@ -738,14 +734,11 @@ GetIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp) JSObject *iterobj = &vp->toObject(); /* Cache the iterator object if possible. */ - if (shapes.length()) { - uint32 hash = key % NATIVE_ITER_CACHE_SIZE; - JSObject **hp = &JS_THREAD_DATA(cx)->cachedNativeIterators[hash]; - *hp = iterobj; - } + if (shapes.length()) + cx->compartment->nativeIterCache.set(key, iterobj); if (shapes.length() == 2) - JS_THREAD_DATA(cx)->lastNativeIterator = iterobj; + cx->compartment->nativeIterCache.last = iterobj; return true; } diff --git a/js/src/jsmath.cpp b/js/src/jsmath.cpp index 9feba7c72cf..c0024ece19e 100644 --- a/js/src/jsmath.cpp +++ b/js/src/jsmath.cpp @@ -54,6 +54,7 @@ #include "jsmath.h" #include "jsnum.h" #include "jslibmath.h" +#include "jscompartment.h" using namespace js; @@ -148,7 +149,7 @@ math_acos(JSContext *cx, uintN argc, Value *vp) return JS_TRUE; } #endif - MathCache *mathCache = JS_THREAD_DATA(cx)->getMathCache(cx); + MathCache *mathCache = GetMathCache(cx); if (!mathCache) return JS_FALSE; z = mathCache->lookup(acos, x); @@ -173,7 +174,7 @@ math_asin(JSContext *cx, uintN argc, Value *vp) return JS_TRUE; } #endif - MathCache *mathCache = JS_THREAD_DATA(cx)->getMathCache(cx); + MathCache *mathCache = GetMathCache(cx); if (!mathCache) return JS_FALSE; z = mathCache->lookup(asin, x); @@ -192,7 +193,7 @@ math_atan(JSContext *cx, uintN argc, Value *vp) } if (!ValueToNumber(cx, vp[2], &x)) return JS_FALSE; - MathCache *mathCache = JS_THREAD_DATA(cx)->getMathCache(cx); + MathCache *mathCache = GetMathCache(cx); if (!mathCache) return JS_FALSE; z = mathCache->lookup(atan, x); @@ -285,7 +286,7 @@ math_cos(JSContext *cx, uintN argc, Value *vp) } if (!ValueToNumber(cx, vp[2], &x)) return JS_FALSE; - MathCache *mathCache = JS_THREAD_DATA(cx)->getMathCache(cx); + MathCache *mathCache = GetMathCache(cx); if (!mathCache) return JS_FALSE; z = mathCache->lookup(cos, x); @@ -318,7 +319,7 @@ math_exp(JSContext *cx, uintN argc, Value *vp) } if (!ValueToNumber(cx, vp[2], &x)) return JS_FALSE; - MathCache *mathCache = JS_THREAD_DATA(cx)->getMathCache(cx); + MathCache *mathCache = GetMathCache(cx); if (!mathCache) return JS_FALSE; z = mathCache->lookup(math_exp_body, x); @@ -365,7 +366,7 @@ math_log(JSContext *cx, uintN argc, Value *vp) return JS_TRUE; } #endif - MathCache *mathCache = JS_THREAD_DATA(cx)->getMathCache(cx); + MathCache *mathCache = GetMathCache(cx); if (!mathCache) return JS_FALSE; z = mathCache->lookup(log, x); @@ -612,7 +613,7 @@ math_sin(JSContext *cx, uintN argc, Value *vp) } if (!ValueToNumber(cx, vp[2], &x)) return JS_FALSE; - MathCache *mathCache = JS_THREAD_DATA(cx)->getMathCache(cx); + MathCache *mathCache = GetMathCache(cx); if (!mathCache) return JS_FALSE; z = mathCache->lookup(sin, x); @@ -631,7 +632,7 @@ math_sqrt(JSContext *cx, uintN argc, Value *vp) } if (!ValueToNumber(cx, vp[2], &x)) return JS_FALSE; - MathCache *mathCache = JS_THREAD_DATA(cx)->getMathCache(cx); + MathCache *mathCache = GetMathCache(cx); if (!mathCache) return JS_FALSE; z = mathCache->lookup(sqrt, x); @@ -650,7 +651,7 @@ math_tan(JSContext *cx, uintN argc, Value *vp) } if (!ValueToNumber(cx, vp[2], &x)) return JS_FALSE; - MathCache *mathCache = JS_THREAD_DATA(cx)->getMathCache(cx); + MathCache *mathCache = GetMathCache(cx); if (!mathCache) return JS_FALSE; z = mathCache->lookup(tan, x); diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp index ee2f9eb2cfc..c099268b2c5 100644 --- a/js/src/jsnum.cpp +++ b/js/src/jsnum.cpp @@ -315,7 +315,10 @@ num_parseFloat(JSContext *cx, uintN argc, Value *vp) str = js_ValueToString(cx, vp[2]); if (!str) return JS_FALSE; - str->getCharsAndEnd(bp, end); + bp = str->getChars(cx); + if (!bp) + return JS_FALSE; + end = bp + str->length(); if (!js_strtod(cx, bp, end, &ep, &d)) return JS_FALSE; if (ep == bp) { @@ -330,12 +333,15 @@ num_parseFloat(JSContext *cx, uintN argc, Value *vp) static jsdouble FASTCALL ParseFloat(JSContext* cx, JSString* str) { - const jschar* bp; - const jschar* end; - const jschar* ep; - jsdouble d; + const jschar *bp = str->getChars(cx); + if (!bp) { + SetBuiltinError(cx); + return js_NaN; + } + const jschar *end = bp + str->length(); - str->getCharsAndEnd(bp, end); + const jschar *ep; + double d; if (!js_strtod(cx, bp, end, &ep, &d) || ep == bp) return js_NaN; return d; @@ -451,8 +457,10 @@ num_parseInt(JSContext *cx, uintN argc, Value *vp) } /* Steps 2-5, 9-14. */ - const jschar *ws, *end; - inputString->getCharsAndEnd(ws, end); + const jschar *ws = inputString->getChars(cx); + if (!ws) + return false; + const jschar *end = ws + inputString->length(); jsdouble number; if (!ParseIntStringHelper(cx, ws, end, radix, stripPrefix, &number)) @@ -467,8 +475,12 @@ num_parseInt(JSContext *cx, uintN argc, Value *vp) static jsdouble FASTCALL ParseInt(JSContext* cx, JSString* str) { - const jschar *start, *end; - str->getCharsAndEnd(start, end); + const jschar *start = str->getChars(cx); + if (!start) { + SetBuiltinError(cx); + return js_NaN; + } + const jschar *end = start + str->length(); jsdouble d; if (!ParseIntStringHelper(cx, start, end, 0, true, &d)) { @@ -499,7 +511,7 @@ JS_DEFINE_TRCINFO_2(num_parseInt, (1, (static, DOUBLE, ParseIntDouble, DOUBLE, 1, nanojit::ACCSET_NONE))) JS_DEFINE_TRCINFO_1(num_parseFloat, - (2, (static, DOUBLE, ParseFloat, CONTEXT, STRING, 1, nanojit::ACCSET_NONE))) + (2, (static, DOUBLE_FAIL, ParseFloat, CONTEXT, STRING, 1, nanojit::ACCSET_NONE))) #endif /* JS_TRACER */ @@ -722,7 +734,7 @@ num_toLocaleString(JSContext *cx, uintN argc, Value *vp) const char *num, *end, *tmpSrc; char *buf, *tmpDest; const char *nint; - int digits, size, remainder, nrepeat; + int digits, buflen, remainder, nrepeat; /* * Create the string, move back to bytes to make string twiddling @@ -757,9 +769,9 @@ num_toLocaleString(JSContext *cx, uintN argc, Value *vp) decimalLength = strlen(rt->decimalSeparator); /* Figure out how long resulting string will be. */ - size = digits + (*nint ? strlen(nint + 1) + 1 : 0); + buflen = digits + (*nint ? strlen(nint + 1) : 0); if (*nint == '.') - size += decimalLength; + buflen += decimalLength; numGrouping = tmpGroup = rt->numGrouping; remainder = digits; @@ -769,20 +781,20 @@ num_toLocaleString(JSContext *cx, uintN argc, Value *vp) while (*tmpGroup != CHAR_MAX && *tmpGroup != '\0') { if (*tmpGroup >= remainder) break; - size += thousandsLength; + buflen += thousandsLength; remainder -= *tmpGroup; tmpGroup++; } if (*tmpGroup == '\0' && *numGrouping != '\0') { nrepeat = (remainder - 1) / tmpGroup[-1]; - size += thousandsLength * nrepeat; + buflen += thousandsLength * nrepeat; remainder -= nrepeat * tmpGroup[-1]; } else { nrepeat = 0; } tmpGroup--; - buf = (char *)cx->malloc(size + 1); + buf = (char *)cx->malloc(buflen + 1); if (!buf) return JS_FALSE; @@ -815,7 +827,7 @@ num_toLocaleString(JSContext *cx, uintN argc, Value *vp) return ok; } - str = js_NewStringCopyN(cx, buf, size); + str = js_NewStringCopyN(cx, buf, buflen); cx->free(buf); if (!str) return JS_FALSE; @@ -920,15 +932,15 @@ JS_DEFINE_TRCINFO_2(num_toString, static JSFunctionSpec number_methods[] = { #if JS_HAS_TOSOURCE - JS_FN(js_toSource_str, num_toSource, 0, JSFUN_PRIMITIVE_THIS), + JS_FN(js_toSource_str, num_toSource, 0, 0), #endif - JS_TN(js_toString_str, num_toString, 1, JSFUN_PRIMITIVE_THIS, &num_toString_trcinfo), - JS_FN(js_toLocaleString_str, num_toLocaleString, 0, JSFUN_PRIMITIVE_THIS), - JS_FN(js_valueOf_str, js_num_valueOf, 0, JSFUN_PRIMITIVE_THIS), - JS_FN(js_toJSON_str, js_num_valueOf, 0, JSFUN_PRIMITIVE_THIS), - JS_FN("toFixed", num_toFixed, 1, JSFUN_PRIMITIVE_THIS), - JS_FN("toExponential", num_toExponential, 1, JSFUN_PRIMITIVE_THIS), - JS_FN("toPrecision", num_toPrecision, 1, JSFUN_PRIMITIVE_THIS), + JS_TN(js_toString_str, num_toString, 1, 0, &num_toString_trcinfo), + JS_FN(js_toLocaleString_str, num_toLocaleString, 0, 0), + JS_FN(js_valueOf_str, js_num_valueOf, 0, 0), + JS_FN(js_toJSON_str, js_num_valueOf, 0, 0), + JS_FN("toFixed", num_toFixed, 1, 0), + JS_FN("toExponential", num_toExponential, 1, 0), + JS_FN("toPrecision", num_toPrecision, 1, 0), JS_FS_END }; @@ -1187,6 +1199,14 @@ js_NumberToString(JSContext *cx, jsdouble d) return js_NumberToStringWithBase(cx, d, 10); } +JSFlatString * +js::NumberToString(JSContext *cx, jsdouble d) +{ + if (JSString *str = js_NumberToStringWithBase(cx, d, 10)) + return str->assertIsFlat(); + return NULL; +} + JSBool JS_FASTCALL js_NumberValueToCharBuffer(JSContext *cx, const Value &v, JSCharBuffer &cb) { @@ -1235,13 +1255,8 @@ ValueToNumberSlow(JSContext *cx, Value v, double *out) return true; } skip_int_double: - if (v.isString()) { - jsdouble d = StringToNumberType(cx, v.toString()); - if (JSDOUBLE_IS_NaN(d)) - break; - *out = d; - return true; - } + if (v.isString()) + return StringToNumberType(cx, v.toString(), out); if (v.isBoolean()) { if (v.toBoolean()) { *out = 1.0; diff --git a/js/src/jsnum.h b/js/src/jsnum.h index dd1492e6f5a..0dd4c7a22bc 100644 --- a/js/src/jsnum.h +++ b/js/src/jsnum.h @@ -206,6 +206,10 @@ js_NumberValueToCharBuffer(JSContext *cx, const js::Value &v, JSCharBuffer &cb); namespace js { +/* Same as js_NumberToString, different signature. */ +extern JSFlatString * +NumberToString(JSContext *cx, jsdouble d); + /* * Usually a small amount of static storage is enough, but sometimes we need * to dynamically allocate much more. This struct encapsulates that. @@ -643,35 +647,44 @@ template<> struct NumberTraits { }; template -static JS_ALWAYS_INLINE T -StringToNumberType(JSContext *cx, JSString *str) +static JS_ALWAYS_INLINE bool +StringToNumberType(JSContext *cx, JSString *str, T *result) { - if (str->length() == 1) { - jschar c = str->chars()[0]; - if ('0' <= c && c <= '9') - return NumberTraits::toSelfType(T(c - '0')); - if (JS_ISSPACE(c)) - return NumberTraits::toSelfType(T(0)); - return NumberTraits::NaN(); + size_t length = str->length(); + const jschar *chars = str->getChars(NULL); + if (!chars) + return false; + + if (length == 1) { + jschar c = chars[0]; + if ('0' <= c && c <= '9') { + *result = NumberTraits::toSelfType(T(c - '0')); + return true; + } + if (JS_ISSPACE(c)) { + *result = NumberTraits::toSelfType(T(0)); + return true; + } + *result = NumberTraits::NaN(); + return true; } - const jschar* bp; - const jschar* end; - const jschar* ep; - jsdouble d; - - str->getCharsAndEnd(bp, end); + const jschar *bp = chars; + const jschar *end = chars + length; bp = js_SkipWhiteSpace(bp, end); /* ECMA doesn't allow signed hex numbers (bug 273467). */ if (end - bp >= 2 && bp[0] == '0' && (bp[1] == 'x' || bp[1] == 'X')) { /* Looks like a hex number. */ const jschar *endptr; + double d; if (!GetPrefixInteger(cx, bp + 2, end, 16, &endptr, &d) || js_SkipWhiteSpace(endptr, end) != end) { - return NumberTraits::NaN(); + *result = NumberTraits::NaN(); + return true; } - return NumberTraits::toSelfType(d); + *result = NumberTraits::toSelfType(d); + return true; } /* @@ -681,12 +694,14 @@ StringToNumberType(JSContext *cx, JSString *str) * that have made it here (which can only be negative ones) will * be treated as 0 without consuming the 'x' by js_strtod. */ - if (!js_strtod(cx, bp, end, &ep, &d) || - js_SkipWhiteSpace(ep, end) != end) { - return NumberTraits::NaN(); + const jschar *ep; + double d; + if (!js_strtod(cx, bp, end, &ep, &d) || js_SkipWhiteSpace(ep, end) != end) { + *result = NumberTraits::NaN(); + return true; } - - return NumberTraits::toSelfType(d); + *result = NumberTraits::toSelfType(d); + return true; } } diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index f8f8efaba5f..028884387ff 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -484,7 +484,8 @@ obj_toSource(JSContext *cx, uintN argc, Value *vp) JSProperty *prop; Value *val; JSString *gsop[2]; - JSString *idstr, *valstr, *str; + JSString *valstr, *str; + JSLinearString *idstr; JS_CHECK_RECURSION(cx, return JS_FALSE); @@ -570,8 +571,8 @@ obj_toSource(JSContext *cx, uintN argc, Value *vp) * Convert id to a value and then to a string. Decide early whether we * prefer get/set or old getter/setter syntax. */ - idstr = js_ValueToString(cx, IdToValue(id)); - if (!idstr) { + JSString *s = js_ValueToString(cx, IdToValue(id)); + if (!s || !(idstr = s->ensureLinear(cx))) { ok = JS_FALSE; goto error; } @@ -609,18 +610,23 @@ obj_toSource(JSContext *cx, uintN argc, Value *vp) * If id is a string that's not an identifier, or if it's a negative * integer, then it must be quoted. */ - bool idIsLexicalIdentifier = !!js_IsIdentifier(idstr); + bool idIsLexicalIdentifier = js_IsIdentifier(idstr); if (JSID_IS_ATOM(id) ? !idIsLexicalIdentifier : (!JSID_IS_INT(id) || JSID_TO_INT(id) < 0)) { - idstr = js_QuoteString(cx, idstr, jschar('\'')); - if (!idstr) { + s = js_QuoteString(cx, idstr, jschar('\'')); + if (!s || !(idstr = s->ensureLinear(cx))) { ok = JS_FALSE; goto error; } vp->setString(idstr); /* local root */ } - idstr->getCharsAndLength(idstrchars, idstrlength); + idstrlength = idstr->length(); + idstrchars = idstr->getChars(cx); + if (!idstrchars) { + ok = JS_FALSE; + goto error; + } for (jsint j = 0; j < valcnt; j++) { /* @@ -637,7 +643,12 @@ obj_toSource(JSContext *cx, uintN argc, Value *vp) goto error; } localroot[j].setString(valstr); /* local root */ - valstr->getCharsAndLength(vchars, vlength); + vchars = valstr->getChars(cx); + if (!vchars) { + ok = JS_FALSE; + goto error; + } + vlength = valstr->length(); /* * If val[j] is a non-sharp object, and we're not serializing an @@ -727,10 +738,8 @@ obj_toSource(JSContext *cx, uintN argc, Value *vp) /* Allocate 1 + 1 at end for closing brace and terminating 0. */ chars = (jschar *) js_realloc((ochars = chars), curlen * sizeof(jschar)); if (!chars) { - /* Save code space on error: let JS_free ignore null vsharp. */ - cx->free(vsharp); - js_free(ochars); - goto error; + chars = ochars; + goto overflow; } if (comma) { @@ -741,8 +750,10 @@ obj_toSource(JSContext *cx, uintN argc, Value *vp) if (gsop[j]) { gsoplength = gsop[j]->length(); - js_strncpy(&chars[nchars], gsop[j]->chars(), - gsoplength); + const jschar *gsopchars = gsop[j]->getChars(cx); + if (!gsopchars) + goto overflow; + js_strncpy(&chars[nchars], gsopchars, gsoplength); nchars += gsoplength; chars[nchars++] = ' '; } @@ -987,15 +998,14 @@ js_ComputeFilename(JSContext *cx, JSStackFrame *caller, #endif static inline JSScript ** -EvalCacheHash(JSContext *cx, JSString *str) +EvalCacheHash(JSContext *cx, JSLinearString *str) { - const jschar *s; - size_t n; - uint32 h; + const jschar *s = str->chars(); + size_t n = str->length(); - str->getCharsAndLength(s, n); if (n > 100) n = 100; + uint32 h; for (h = 0; n; s++, n--) h = JS_ROTATE_LEFT32(h, 4) ^ *s; @@ -1005,7 +1015,7 @@ EvalCacheHash(JSContext *cx, JSString *str) } static JS_ALWAYS_INLINE JSScript * -EvalCacheLookup(JSContext *cx, JSString *str, JSStackFrame *caller, uintN staticLevel, +EvalCacheLookup(JSContext *cx, JSLinearString *str, JSStackFrame *caller, uintN staticLevel, JSPrincipals *principals, JSObject *scopeobj, JSScript **bucket) { /* @@ -1014,8 +1024,15 @@ EvalCacheLookup(JSContext *cx, JSString *str, JSStackFrame *caller, uintN static * An eval cache entry should never be considered a hit unless its * strictness matches that of the new eval code. The existing code takes * care of this, because hits are qualified by the function from which - * eval was called, whose strictness doesn't change. Scripts produced by - * calls to eval from global code are not cached. + * eval was called, whose strictness doesn't change. (We don't cache evals + * in eval code, so the calling function corresponds to the calling script, + * and its strictness never varies.) Scripts produced by calls to eval from + * global code aren't cached. + * + * FIXME bug 620141: Qualify hits by calling script rather than function. + * Then we wouldn't need the unintuitive !isEvalFrame() hack in EvalKernel + * to avoid caching nested evals in functions (thus potentially mismatching + * on strict mode), and we could cache evals in global code if desired. */ uintN count = 0; JSScript **scriptp = bucket; @@ -1041,9 +1058,9 @@ EvalCacheLookup(JSContext *cx, JSString *str, JSStackFrame *caller, uintN static * Get the source string passed for safekeeping in the * atom map by the prior eval to Compiler::compileScript. */ - JSString *src = ATOM_TO_STRING(script->atomMap.vector[0]); + JSAtom *src = script->atomMap.vector[0]; - if (src == str || js_EqualStrings(src, str)) { + if (src == str || EqualStrings(src, str)) { /* * Source matches, qualify by comparing scopeobj to the * COMPILE_N_GO-memoized parent of the first literal @@ -1184,9 +1201,11 @@ EvalKernel(JSContext *cx, uintN argc, Value *vp, EvalType evalType, JSStackFrame if (!CheckScopeChainValidity(cx, scopeobj)) return false; - const jschar *chars; - size_t length; - str->getCharsAndLength(chars, length); + JSLinearString *linearStr = str->ensureLinear(cx); + if (!linearStr) + return false; + const jschar *chars = linearStr->chars(); + size_t length = linearStr->length(); /* * If the eval string starts with '(' and ends with ')', it may be JSON. @@ -1214,9 +1233,9 @@ EvalKernel(JSContext *cx, uintN argc, Value *vp, EvalType evalType, JSStackFrame return false; JSScript *script = NULL; - JSScript **bucket = EvalCacheHash(cx, str); - if (evalType == DIRECT_EVAL && caller->isFunctionFrame()) - script = EvalCacheLookup(cx, str, caller, staticLevel, principals, scopeobj, bucket); + JSScript **bucket = EvalCacheHash(cx, linearStr); + if (evalType == DIRECT_EVAL && caller->isFunctionFrame() && !caller->isEvalFrame()) + script = EvalCacheLookup(cx, linearStr, caller, staticLevel, principals, scopeobj, bucket); /* * We can't have a callerFrame (down in js::Execute's terms) if we're in @@ -1229,9 +1248,8 @@ EvalKernel(JSContext *cx, uintN argc, Value *vp, EvalType evalType, JSStackFrame uint32 tcflags = TCF_COMPILE_N_GO | TCF_NEED_MUTABLE_SCRIPT | TCF_COMPILE_FOR_EVAL; script = Compiler::compileScript(cx, scopeobj, callerFrame, - principals, tcflags, - chars, length, - filename, lineno, str, staticLevel); + principals, tcflags, chars, length, + filename, lineno, linearStr, staticLevel); if (!script) return false; } @@ -1992,6 +2010,23 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropDesc &desc, JS_ASSERT(!obj->getOps()->defineProperty); + /* + * If we find a shared permanent property in a different object obj2 from + * obj, then if the property is shared permanent (an old hack to optimize + * per-object properties into one prototype property), ignore that lookup + * result (null current). + * + * FIXME: bug 575997 (see also bug 607863). + */ + if (current && obj2 != obj && obj2->isNative()) { + /* See same assertion with comment further below. */ + JS_ASSERT(obj2->getClass() == obj->getClass()); + + Shape *shape = (Shape *) current; + if (shape->isSharedPermanent()) + current = NULL; + } + /* 8.12.9 steps 2-4. */ if (!current) { if (!obj->isExtensible()) @@ -2040,14 +2075,20 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropDesc &desc, if (!shape->isAccessorDescriptor()) break; - if (desc.hasGet && - !SameValue(desc.getterValue(), shape->getterOrUndefined(), cx)) { - break; + if (desc.hasGet) { + JSBool same; + if (!SameValue(cx, desc.getterValue(), shape->getterOrUndefined(), &same)) + return JS_FALSE; + if (!same) + break; } - if (desc.hasSet && - !SameValue(desc.setterValue(), shape->setterOrUndefined(), cx)) { - break; + if (desc.hasSet) { + JSBool same; + if (!SameValue(cx, desc.setterValue(), shape->setterOrUndefined(), &same)) + return JS_FALSE; + if (!same) + break; } } else { /* @@ -2096,8 +2137,13 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropDesc &desc, if (!shape->isDataDescriptor()) break; - if (desc.hasValue && !SameValue(desc.value, v, cx)) - break; + JSBool same; + if (desc.hasValue) { + if (!SameValue(cx, desc.value, v, &same)) + return JS_FALSE; + if (!same) + break; + } if (desc.hasWritable && desc.writable() != shape->writable()) break; } else { @@ -2144,9 +2190,14 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropDesc &desc, /* 8.12.9 step 10. */ JS_ASSERT(shape->isDataDescriptor()); if (!shape->configurable() && !shape->writable()) { - if ((desc.hasWritable && desc.writable()) || - (desc.hasValue && !SameValue(desc.value, v, cx))) { + if (desc.hasWritable && desc.writable()) return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, desc.id, rval); + if (desc.hasValue) { + JSBool same; + if (!SameValue(cx, desc.value, v, &same)) + return JS_FALSE; + if (!same) + return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, desc.id, rval); } } @@ -2155,11 +2206,20 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropDesc &desc, /* 8.12.9 step 11. */ JS_ASSERT(desc.isAccessorDescriptor() && shape->isAccessorDescriptor()); if (!shape->configurable()) { - if ((desc.hasSet && - !SameValue(desc.setterValue(), shape->setterOrUndefined(), cx)) || - (desc.hasGet && - !SameValue(desc.getterValue(), shape->getterOrUndefined(), cx))) { - return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, desc.id, rval); + if (desc.hasSet) { + JSBool same; + if (!SameValue(cx, desc.setterValue(), shape->setterOrUndefined(), &same)) + return JS_FALSE; + if (!same) + return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, desc.id, rval); + } + + if (desc.hasGet) { + JSBool same; + if (!SameValue(cx, desc.getterValue(), shape->getterOrUndefined(), &same)) + return JS_FALSE; + if (!same) + return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, desc.id, rval); } } } @@ -2697,7 +2757,7 @@ static JSFunctionSpec object_methods[] = { #if JS_HAS_TOSOURCE JS_FN(js_toSource_str, obj_toSource, 0,0), #endif - JS_FN(js_toString_str, obj_toString, 0,JSFUN_PRIMITIVE_THIS), + JS_FN(js_toString_str, obj_toString, 0,0), JS_FN(js_toLocaleString_str, obj_toLocaleString, 0,0), JS_FN(js_valueOf_str, obj_valueOf, 0,0), #if JS_HAS_OBJ_WATCHPOINT @@ -4487,7 +4547,7 @@ CallAddPropertyHook(JSContext *cx, Class *clasp, JSObject *obj, const Shape *sha if (clasp->addProperty != PropertyStub) { Value nominal = *vp; - if (!CallJSPropertyOp(cx, clasp->addProperty, obj, SHAPE_USERID(shape), vp)) + if (!CallJSPropertyOp(cx, clasp->addProperty, obj, shape->id, vp)) return false; if (*vp != nominal) { if (obj->containsSlot(shape->slot)) @@ -4616,8 +4676,10 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, const Value &valu } /* Store value before calling addProperty, in case the latter GC's. */ - if (obj->containsSlot(shape->slot)) + if (obj->containsSlot(shape->slot)) { + AbortRecordingIfUnexpectedGlobalWrite(cx, obj, shape->slot); obj->nativeSetSlot(shape->slot, value); + } /* XXXbe called with lock held */ valueCopy = value; @@ -5091,6 +5153,7 @@ js_NativeSet(JSContext *cx, JSObject *obj, const Shape *shape, bool added, Value if (shape->hasDefaultSetter()) { if (!added && !obj->methodWriteBarrier(cx, *shape, *vp)) return false; + AbortRecordingIfUnexpectedGlobalWrite(cx, obj, slot); obj->nativeSetSlot(slot, *vp); return true; } @@ -5117,6 +5180,7 @@ js_NativeSet(JSContext *cx, JSObject *obj, const Shape *shape, bool added, Value obj->nativeContains(*shape))) { if (!added && !obj->methodWriteBarrier(cx, *shape, *vp)) return false; + AbortRecordingIfUnexpectedGlobalWrite(cx, obj, slot); obj->setSlot(slot, *vp); } @@ -5455,10 +5519,8 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow, if (pobj != obj) { /* * We found id in a prototype object: prepare to share or shadow. - * - * Don't clone a prototype property that doesn't have a slot. */ - if (!shape->hasSlot()) { + if (!shape->shadowable()) { if (defineHow & JSDNP_CACHE_RESULT) { #ifdef JS_TRACER JS_ASSERT_NOT_ON_TRACE(cx); @@ -5474,21 +5536,33 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow, return shape->set(cx, obj, vp); } - /* Restore attrs to the ECMA default for new properties. */ - attrs = JSPROP_ENUMERATE; - /* - * Preserve the shortid, getter, and setter when shadowing any - * property that has a shortid. An old API convention requires - * that the property's getter and setter functions receive the - * shortid, not id, when they are called on the shadow we are + * Preserve attrs except JSPROP_SHARED, getter, and setter when + * shadowing any property that has no slot (is shared). We must + * clear the shared attribute for the shadowing shape so that the + * property in obj that it defines has a slot to retain the value + * being set, in case the setter simply cannot operate on instances + * of obj's class by storing the value in some class-specific + * location. + * + * A subset of slotless shared properties is the set of properties + * with shortids, which must be preserved too. An old API requires + * that the property's getter and setter receive the shortid, not + * id, when they are called on the shadowing property that we are * about to create in obj. */ - if (shape->hasShortID()) { - flags = Shape::HAS_SHORTID; - shortid = shape->shortid; + if (!shape->hasSlot()) { + defineHow &= ~JSDNP_SET_METHOD; + if (shape->hasShortID()) { + flags = Shape::HAS_SHORTID; + shortid = shape->shortid; + } + attrs &= ~JSPROP_SHARED; getter = shape->getter(); setter = shape->setter(); + } else { + /* Restore attrs to the ECMA default for new properties. */ + attrs = JSPROP_ENUMERATE; } /* @@ -6308,7 +6382,7 @@ js_PrintObjectSlotName(JSTracer *trc, char *buf, size_t bufsize) if (JSID_IS_INT(id)) { JS_snprintf(buf, bufsize, "%ld", (long)JSID_TO_INT(id)); } else if (JSID_IS_ATOM(id)) { - PutEscapedString(buf, bufsize, JSID_TO_STRING(id), 0); + PutEscapedString(buf, bufsize, JSID_TO_ATOM(id), 0); } else { JS_snprintf(buf, bufsize, "**FINALIZED ATOM KEY**"); } @@ -6501,15 +6575,22 @@ js_DumpChars(const jschar *s, size_t n) void dumpString(JSString *str) { - dumpChars(str->chars(), str->length()); + if (const jschar *chars = str->getChars(NULL)) + dumpChars(chars, str->length()); + else + fprintf(stderr, "(oom in dumpString)"); } JS_FRIEND_API(void) js_DumpString(JSString *str) { - fprintf(stderr, "JSString* (%p) = jschar * (%p) = ", - (void *) str, (void *) str->chars()); - dumpString(str); + if (const jschar *chars = str->getChars(NULL)) { + fprintf(stderr, "JSString* (%p) = jschar * (%p) = ", + (void *) str, (void *) chars); + dumpString(str); + } else { + fprintf(stderr, "(oom in JS_DumpString)"); + } fputc('\n', stderr); } diff --git a/js/src/jsobj.h b/js/src/jsobj.h index ec8ed2a900d..65e8af9d60e 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -992,17 +992,21 @@ struct JSObject : js::gc::Cell { static const uint32 NAMESPACE_CLASS_RESERVED_SLOTS = 3; static const uint32 QNAME_CLASS_RESERVED_SLOTS = 3; - inline jsval getNamePrefix() const; - inline void setNamePrefix(jsval prefix); + inline JSLinearString *getNamePrefix() const; + inline jsval getNamePrefixVal() const; + inline void setNamePrefix(JSLinearString *prefix); + inline void clearNamePrefix(); - inline jsval getNameURI() const; - inline void setNameURI(jsval uri); + inline JSLinearString *getNameURI() const; + inline jsval getNameURIVal() const; + inline void setNameURI(JSLinearString *uri); inline jsval getNamespaceDeclared() const; inline void setNamespaceDeclared(jsval decl); - inline jsval getQNameLocalName() const; - inline void setQNameLocalName(jsval decl); + inline JSLinearString *getQNameLocalName() const; + inline jsval getQNameLocalNameVal() const; + inline void setQNameLocalName(JSLinearString *name); /* * Proxy-specific getters and setters. diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index dab7223e432..536242788ca 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -530,32 +530,55 @@ JSObject::setNativeIterator(js::NativeIterator *ni) setPrivate(ni); } -inline jsval +inline JSLinearString * JSObject::getNamePrefix() const +{ + JS_ASSERT(isNamespace() || isQName()); + const js::Value &v = getSlot(JSSLOT_NAME_PREFIX); + return !v.isUndefined() ? v.toString()->assertIsLinear() : NULL; +} + +inline jsval +JSObject::getNamePrefixVal() const { JS_ASSERT(isNamespace() || isQName()); return js::Jsvalify(getSlot(JSSLOT_NAME_PREFIX)); } inline void -JSObject::setNamePrefix(jsval prefix) +JSObject::setNamePrefix(JSLinearString *prefix) { JS_ASSERT(isNamespace() || isQName()); - setSlot(JSSLOT_NAME_PREFIX, js::Valueify(prefix)); + setSlot(JSSLOT_NAME_PREFIX, prefix ? js::StringValue(prefix) : js::UndefinedValue()); +} + +inline void +JSObject::clearNamePrefix() +{ + JS_ASSERT(isNamespace() || isQName()); + setSlot(JSSLOT_NAME_PREFIX, js::UndefinedValue()); +} + +inline JSLinearString * +JSObject::getNameURI() const +{ + JS_ASSERT(isNamespace() || isQName()); + const js::Value &v = getSlot(JSSLOT_NAME_URI); + return !v.isUndefined() ? v.toString()->assertIsLinear() : NULL; } inline jsval -JSObject::getNameURI() const +JSObject::getNameURIVal() const { JS_ASSERT(isNamespace() || isQName()); return js::Jsvalify(getSlot(JSSLOT_NAME_URI)); } inline void -JSObject::setNameURI(jsval uri) +JSObject::setNameURI(JSLinearString *uri) { JS_ASSERT(isNamespace() || isQName()); - setSlot(JSSLOT_NAME_URI, js::Valueify(uri)); + setSlot(JSSLOT_NAME_URI, uri ? js::StringValue(uri) : js::UndefinedValue()); } inline jsval @@ -572,18 +595,26 @@ JSObject::setNamespaceDeclared(jsval decl) setSlot(JSSLOT_NAMESPACE_DECLARED, js::Valueify(decl)); } -inline jsval +inline JSLinearString * JSObject::getQNameLocalName() const +{ + JS_ASSERT(isQName()); + const js::Value &v = getSlot(JSSLOT_QNAME_LOCAL_NAME); + return !v.isUndefined() ? v.toString()->assertIsLinear() : NULL; +} + +inline jsval +JSObject::getQNameLocalNameVal() const { JS_ASSERT(isQName()); return js::Jsvalify(getSlot(JSSLOT_QNAME_LOCAL_NAME)); } inline void -JSObject::setQNameLocalName(jsval name) +JSObject::setQNameLocalName(JSLinearString *name) { JS_ASSERT(isQName()); - setSlot(JSSLOT_QNAME_LOCAL_NAME, js::Valueify(name)); + setSlot(JSSLOT_QNAME_LOCAL_NAME, name ? js::StringValue(name) : js::UndefinedValue()); } inline JSObject * diff --git a/js/src/json.cpp b/js/src/json.cpp index d81cadda309..fe1566c4b8b 100644 --- a/js/src/json.cpp +++ b/js/src/json.cpp @@ -119,12 +119,15 @@ js_json_parse(JSContext *cx, uintN argc, Value *vp) if (!JS_ConvertArguments(cx, argc, Jsvalify(argv), "S / v", &s, reviver.addr())) return JS_FALSE; + JSLinearString *linearStr = s->ensureLinear(cx); + if (!linearStr) + return JS_FALSE; + JSONParser *jp = js_BeginJSONParse(cx, vp); JSBool ok = jp != NULL; if (ok) { - const jschar *chars; - size_t length; - s->getCharsAndLength(chars, length); + const jschar *chars = linearStr->chars(); + size_t length = linearStr->length(); ok = js_ConsumeJSONText(cx, jp, chars, length); ok &= !!js_FinishJSONParse(cx, jp, reviver.value()); } @@ -403,9 +406,11 @@ JO(JSContext *cx, Value *vp, StringifyContext *scx) if (!s) return JS_FALSE; - const jschar *chars; - size_t length; - s->getCharsAndLength(chars, length); + size_t length = s->length(); + const jschar *chars = s->getChars(cx); + if (!chars) + return JS_FALSE; + if (!write_string(cx, scx->cb, chars, length) || !scx->cb.append(':') || !(scx->gap.empty() || scx->cb.append(' ')) || @@ -505,9 +510,11 @@ Str(JSContext *cx, jsid id, JSObject *holder, StringifyContext *scx, Value *vp, } if (vp->isString()) { - const jschar *chars; - size_t length; - vp->toString()->getCharsAndLength(chars, length); + JSString *str = vp->toString(); + size_t length = str->length(); + const jschar *chars = str->getChars(cx); + if (!chars) + return JS_FALSE; return write_string(cx, scx->cb, chars, length); } diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index de61f2f9ed7..b5279e6ef78 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -239,6 +239,38 @@ js_GetEnterBlockStackDefs(JSContext *cx, JSScript *script, jsbytecode *pc) return OBJ_BLOCK_COUNT(cx, obj); } +class AutoScriptUntrapper { + JSContext *cx; + JSScript *script; + jsbytecode *origPC; + jsbytecode *newPC; + +public: + AutoScriptUntrapper(JSContext *cx, JSScript *script, jsbytecode **pc) + : cx(cx), script(script), origPC(*pc) + { + jsbytecode *newCode = js_UntrapScriptCode(cx, script); + if (newCode == script->code) { + // No change needed + newPC = origPC; + } else { + script->main += newCode - script->code; + *pc = newPC = origPC + (newCode - script->code); + script->code = newCode; + } + } + ~AutoScriptUntrapper() + { + ptrdiff_t delta = newPC - origPC; + if (delta) { + jsbytecode *oldCode = script->code - delta; + cx->free(script->code); + script->code = oldCode; + script->main -= delta; + } + } +}; + #ifdef DEBUG /* If pc != NULL, includes a prefix indicating whether the PC is at the current line. */ @@ -354,6 +386,8 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, jsval v; jsint i; + AutoScriptUntrapper untrapper(cx, script, &pc); + op = (JSOp)*pc; if (op >= JSOP_LIMIT) { char numBuf1[12], numBuf2[12]; @@ -372,10 +406,6 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, type = JOF_TYPE(cs->format); switch (type) { case JOF_BYTE: - if (op == JSOP_TRAP) { - op = JS_GetTrapOpcode(cx, script, pc); - len = (ptrdiff_t) js_CodeSpec[op].length; - } break; case JOF_JUMP: @@ -625,19 +655,16 @@ SprintCString(Sprinter *sp, const char *s) static ptrdiff_t SprintString(Sprinter *sp, JSString *str) { - const jschar *chars; - size_t length, size; - ptrdiff_t offset; + size_t length = str->length(); + const jschar *chars = str->getChars(sp->context); + if (!chars) + return -1; - str->getCharsAndLength(chars, length); - if (length == 0) - return sp->offset; - - size = js_GetDeflatedStringLength(sp->context, chars, length); + size_t size = js_GetDeflatedStringLength(sp->context, chars, length); if (size == (size_t)-1 || !SprintEnsureBuffer(sp, size)) return -1; - offset = sp->offset; + ptrdiff_t offset = sp->offset; sp->offset += size; js_DeflateStringToBuffer(sp->context, chars, length, sp->base + offset, &size); @@ -683,39 +710,36 @@ const char js_EscapeMap[] = { static char * QuoteString(Sprinter *sp, JSString *str, uint32 quote) { - JSBool dontEscape, ok; - jschar qc, c; - ptrdiff_t off, len; - const jschar *s, *t, *z; - const char *e; - char *bp; - /* Sample off first for later return value pointer computation. */ - dontEscape = (quote & DONT_ESCAPE) != 0; - qc = (jschar) quote; - off = sp->offset; + JSBool dontEscape = (quote & DONT_ESCAPE) != 0; + jschar qc = (jschar) quote; + ptrdiff_t off = sp->offset; if (qc && Sprint(sp, "%c", (char)qc) < 0) return NULL; + const jschar *s = str->getChars(sp->context); + if (!s) + return NULL; + const jschar *z = s + str->length(); + /* Loop control variables: z points at end of string sentinel. */ - str->getCharsAndEnd(s, z); - for (t = s; t < z; s = ++t) { + for (const jschar *t = s; t < z; s = ++t) { /* Move t forward from s past un-quote-worthy characters. */ - c = *t; + jschar c = *t; while (JS_ISPRINT(c) && c != qc && c != '\\' && c != '\t' && !(c >> 8)) { c = *++t; if (t == z) break; } - len = t - s; + ptrdiff_t len = t - s; /* Allocate space for s, including the '\0' at the end. */ if (!SprintEnsureBuffer(sp, len)) return NULL; /* Advance sp->offset and copy s into sp's buffer. */ - bp = sp->base + sp->offset; + char *bp = sp->base + sp->offset; sp->offset += len; while (--len >= 0) *bp++ = (char) *s++; @@ -725,6 +749,8 @@ QuoteString(Sprinter *sp, JSString *str, uint32 quote) break; /* Use js_EscapeMap, \u, or \x only if necessary. */ + bool ok; + const char *e; if (!(c >> 8) && (e = strchr(js_EscapeMap, (int)c)) != NULL) { ok = dontEscape ? Sprint(sp, "%c", (char)c) >= 0 @@ -1550,7 +1576,6 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc) const char *lval; JSAtom *atom; jssrcnote *sn; - JSString *str; JSBool hole; LOCAL_ASSERT(*pc == JSOP_DUP); @@ -1631,18 +1656,19 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc) case JSOP_GETPROP: LOAD_ATOM(0); do_destructure_atom: + { *OFF2STR(&ss->sprinter, head) = '{'; - str = ATOM_TO_STRING(atom); #if JS_HAS_DESTRUCTURING_SHORTHAND nameoff = ss->sprinter.offset; #endif - if (!QuoteString(&ss->sprinter, str, - js_IsIdentifier(str) ? 0 : (jschar)'\'')) { + if (!QuoteString(&ss->sprinter, atom, + js_IsIdentifier(atom) ? 0 : (jschar)'\'')) { return NULL; } if (SprintPut(&ss->sprinter, ": ", 2) < 0) return NULL; break; + } default: LOCAL_ASSERT(0); @@ -4814,14 +4840,15 @@ DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len, void *mark; JSBool ok; JSScript *oldscript; - jsbytecode *oldcode, *oldmain, *code; char *last; depth = StackDepth(script); JS_ASSERT(pcdepth <= depth); + cx = jp->sprinter.context; + + AutoScriptUntrapper untrapper(cx, script, &pc); /* Initialize a sprinter for use with the offset stack. */ - cx = jp->sprinter.context; mark = JS_ARENA_MARK(&cx->tempPool); ok = InitSprintStack(cx, &ss, jp, depth); if (!ok) @@ -4849,21 +4876,7 @@ DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len, /* Call recursive subroutine to do the hard work. */ oldscript = jp->script; jp->script = script; - oldcode = jp->script->code; - oldmain = jp->script->main; - code = js_UntrapScriptCode(cx, jp->script); - if (code != oldcode) { - jp->script->code = code; - jp->script->main = code + (oldmain - oldcode); - pc = code + (pc - oldcode); - } - ok = Decompile(&ss, pc, len, JSOP_NOP) != NULL; - if (code != oldcode) { - cx->free(jp->script->code); - jp->script->code = oldcode; - jp->script->main = oldmain; - } jp->script = oldscript; /* If the given code didn't empty the stack, do it now. */ @@ -4969,6 +4982,7 @@ js_DecompileFunction(JSPrinter *jp) /* Print the parameters. */ pc = script->main; + AutoScriptUntrapper untrapper(jp->sprinter.context, script, &pc); endpc = pc + script->length; ok = JS_TRUE; @@ -5180,14 +5194,17 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v_in, if (!fallback) return NULL; } - return js_DeflateString(cx, fallback->chars(), fallback->length()); + size_t length = fallback->length(); + const jschar *chars = fallback->getChars(cx); + if (!chars) + return NULL; + return js_DeflateString(cx, chars, length); } static char * DecompileExpression(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *pc) { - jsbytecode *code, *oldcode, *oldmain; JSOp op; const JSCodeSpec *cs; jsbytecode *begin, *end; @@ -5201,17 +5218,7 @@ DecompileExpression(JSContext *cx, JSScript *script, JSFunction *fun, JS_ASSERT(script->code <= pc && pc < script->code + script->length); pcstack = NULL; - oldcode = script->code; - oldmain = script->main; - - MUST_FLOW_THROUGH("out"); - code = js_UntrapScriptCode(cx, script); - if (code != oldcode) { - script->code = code; - script->main = code + (oldmain - oldcode); - pc = code + (pc - oldcode); - } - + AutoScriptUntrapper untrapper(cx, script, &pc); op = (JSOp) *pc; /* None of these stack-writing ops generates novel values. */ @@ -5306,13 +5313,7 @@ DecompileExpression(JSContext *cx, JSScript *script, JSFunction *fun, js_DestroyPrinter(jp); } - out: - if (code != oldcode) { - cx->free(script->code); - script->code = oldcode; - script->main = oldmain; - } - +out: cx->free(pcstack); return name; } diff --git a/js/src/jsparse.cpp b/js/src/jsparse.cpp index 25118478dd5..83cf77ea043 100644 --- a/js/src/jsparse.cpp +++ b/js/src/jsparse.cpp @@ -1788,8 +1788,6 @@ static JSBool BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc) { - JSParseNode *pn; - /* Flag tc so we don't have to lookup arguments on every use. */ if (atom == tc->parser->context->runtime->atomState.argumentsAtom) tc->flags |= TCF_FUN_PARAM_ARGUMENTS; @@ -1798,25 +1796,21 @@ BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom, JS_ASSERT(tc->inFunction()); - JSLocalKind localKind = tc->fun()->lookupLocal(cx, atom, NULL); - if (localKind != JSLOCAL_NONE) { + if (tc->decls.lookup(atom)) { ReportCompileErrorNumber(cx, TS(tc->parser), NULL, JSREPORT_ERROR, JSMSG_DESTRUCT_DUP_ARG); return JS_FALSE; } - JS_ASSERT(!tc->decls.lookup(atom)); - pn = data->pn; - if (!Define(pn, atom, tc)) - return JS_FALSE; + /* + * Distinguish destructured-to binding nodes as vars, not args, by setting + * pn_op to JSOP_SETLOCAL. Parser::functionDef checks for this pn_op value + * when processing the destructuring-assignment AST prelude induced by such + * destructuring args in Parser::functionArguments. + */ + data->pn->pn_op = JSOP_SETLOCAL; - uintN index = tc->fun()->u.i.nvars; - if (!BindLocalVariable(cx, tc->fun(), atom, JSLOCAL_VAR, true)) - return JS_FALSE; - pn->pn_op = JSOP_SETLOCAL; - pn->pn_cookie.set(tc->staticLevel, index); - pn->pn_dflags |= PND_BOUND; - return JS_TRUE; + return Define(data->pn, atom, tc); } #endif /* JS_HAS_DESTRUCTURING */ @@ -2721,6 +2715,12 @@ LeaveFunction(JSParseNode *fn, JSTreeContext *funtc, JSAtom *funAtom = NULL, static bool DefineGlobal(JSParseNode *pn, JSCodeGenerator *cg, JSAtom *atom); +/* + * FIXME? this Parser method was factored from Parser::functionDef with minimal + * change, hence the funtc ref param, funbox, and fun. It probably should match + * functionBody, etc., and use tc, tc->funbox, and tc->fun() instead of taking + * explicit parameters. + */ bool Parser::functionArguments(JSTreeContext &funtc, JSFunctionBox *funbox, JSFunction *fun, JSParseNode **listp) @@ -2766,7 +2766,7 @@ Parser::functionArguments(JSTreeContext &funtc, JSFunctionBox *funbox, JSFunctio * Adjust fun->nargs to count the single anonymous positional * parameter that is to be destructured. */ - jsint slot = fun->nargs; + uintN slot = fun->nargs; if (!fun->addLocal(context, NULL, JSLOCAL_ARG)) return false; @@ -2780,7 +2780,7 @@ Parser::functionArguments(JSTreeContext &funtc, JSFunctionBox *funbox, JSFunctio return false; rhs->pn_type = TOK_NAME; rhs->pn_op = JSOP_GETARG; - rhs->pn_cookie.set(funtc.staticLevel, uint16(slot)); + rhs->pn_cookie.set(funtc.staticLevel, slot); rhs->pn_dflags |= PND_BOUND; JSParseNode *item = @@ -2803,25 +2803,28 @@ Parser::functionArguments(JSTreeContext &funtc, JSFunctionBox *funbox, JSFunctio case TOK_NAME: { JSAtom *atom = tokenStream.currentToken().t_atom; - if (!DefineArg(funbox->node, atom, fun->nargs, &funtc)) - return false; + #ifdef JS_HAS_DESTRUCTURING /* * ECMA-262 requires us to support duplicate parameter names, but if the * parameter list includes destructuring, we consider the code to have - * opted in to higher standards, and forbid duplicates. We may see a + * "opted in" to higher standards, and forbid duplicates. We may see a * destructuring parameter later, so always note duplicates now. * * Duplicates are warned about (strict option) or cause errors (strict * mode code), but we do those tests in one place below, after having * parsed the body in case it begins with a "use strict"; directive. */ - if (fun->lookupLocal(context, atom, NULL) != JSLOCAL_NONE) { + if (funtc.decls.lookup(atom)) { duplicatedArg = atom; if (destructuringArg) goto report_dup_and_destructuring; } #endif + + if (!DefineArg(funbox->node, atom, fun->nargs, &funtc)) + return false; + if (!fun->addLocal(context, atom, JSLOCAL_ARG)) return false; break; @@ -2982,10 +2985,38 @@ Parser::functionDef(JSAtom *funAtom, FunctionType type, uintN lambda) JSFunction *fun = (JSFunction *) funbox->object; /* Now parse formal argument list and compute fun->nargs. */ - JSParseNode *prolog = NULL; - if (!functionArguments(funtc, funbox, fun, &prolog)) + JSParseNode *prelude = NULL; + if (!functionArguments(funtc, funbox, fun, &prelude)) return NULL; +#if JS_HAS_DESTRUCTURING + /* + * If there were destructuring formal parameters, bind the destructured-to + * local variables now that we've parsed all the regular and destructuring + * formal parameters. Because JSFunction::addLocal must be called first for + * all ARGs, then all VARs, finally all UPVARs, we can't bind vars induced + * by formal parameter destructuring until after Parser::functionArguments + * has returned. + */ + if (prelude) { + JSAtomListIterator iter(&funtc.decls); + + while (JSAtomListElement *ale = iter()) { + JSParseNode *apn = ALE_DEFN(ale); + + /* Filter based on pn_op -- see BindDestructuringArg, above. */ + if (apn->pn_op != JSOP_SETLOCAL) + continue; + + uintN index = fun->u.i.nvars; + if (!BindLocalVariable(context, fun, apn->pn_atom, JSLOCAL_VAR, true)) + return NULL; + apn->pn_cookie.set(funtc.staticLevel, index); + apn->pn_dflags |= PND_BOUND; + } + } +#endif + if (type == GETTER && fun->nargs > 0) { reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_ACCESSOR_WRONG_ARGS, "getter", "no", "s"); @@ -3027,14 +3058,6 @@ Parser::functionDef(JSAtom *funAtom, FunctionType type, uintN lambda) #endif pn->pn_pos.end = tokenStream.currentToken().pos.end; - /* - * Strict mode functions' arguments objects copy initial parameter values. - * We create arguments objects lazily -- but that doesn't work for strict - * mode functions where a parameter might be modified and arguments might - * be accessed. For such functions we synthesize an access to arguments to - * initialize it with the original parameter values. - */ - /* * Fruit of the poisonous tree: if a closure calls eval, we consider the * parent to call eval. We need this for two reasons: (1) the Jaegermonkey @@ -3054,11 +3077,11 @@ Parser::functionDef(JSAtom *funAtom, FunctionType type, uintN lambda) #if JS_HAS_DESTRUCTURING /* * If there were destructuring formal parameters, prepend the initializing - * comma expression that we synthesized to body. If the body is a return + * comma expression that we synthesized to body. If the body is a return * node, we must make a special TOK_SEQ node, to prepend the destructuring * code without bracing the decompilation of the function body. */ - if (prolog) { + if (prelude) { if (body->pn_arity != PN_LIST) { JSParseNode *block; @@ -3078,7 +3101,7 @@ Parser::functionDef(JSAtom *funAtom, FunctionType type, uintN lambda) item->pn_type = TOK_SEMI; item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin; - item->pn_kid = prolog; + item->pn_kid = prelude; item->pn_next = body->pn_head; body->pn_head = item; if (body->pn_tail == &body->pn_head) @@ -3163,9 +3186,6 @@ Parser::functionDef(JSAtom *funAtom, FunctionType type, uintN lambda) if (!LeaveFunction(pn, &funtc, funAtom, lambda)) return NULL; - if (funtc.inStrictMode()) - fun->flags |= JSFUN_PRIMITIVE_THIS; - /* If the surrounding function is not strict code, reset the lexer. */ if (!outertc->inStrictMode()) tokenStream.setStrictMode(false); @@ -3843,9 +3863,20 @@ NoteLValue(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, uintN dflag = PND_ pn->pn_dflags |= dflag; + /* + * Both arguments and the enclosing function's name are immutable bindings + * in ES5, so assignments to them must do nothing or throw a TypeError + * depending on code strictness. Assignment to arguments is a syntax error + * in strict mode and thus cannot happen. Outside strict mode, we optimize + * away assignment to the function name. For assignment to function name + * to fail in strict mode, we must have a binding for it in the scope + * chain; we ensure this happens by making such functions heavyweight. + */ JSAtom *lname = pn->pn_atom; - if (lname == cx->runtime->atomState.argumentsAtom) + if (lname == cx->runtime->atomState.argumentsAtom || + (tc->inFunction() && lname == tc->fun()->atom)) { tc->flags |= TCF_FUN_HEAVYWEIGHT; + } } #if JS_HAS_DESTRUCTURING @@ -5514,7 +5545,6 @@ Parser::withStatement() JSParseNode * Parser::letStatement() { - JSObject *obj; JSObjectBox *blockbox; JSParseNode *pn; @@ -5551,7 +5581,6 @@ Parser::letStatement() if (stmt && (stmt->flags & SIF_SCOPE)) { JS_ASSERT(tc->blockChainBox == stmt->blockBox); - obj = tc->blockChain(); } else { if (!stmt || (stmt->flags & SIF_BODY_BLOCK)) { /* @@ -8003,11 +8032,9 @@ Parser::xmlElementOrList(JSBool allowList) return NULL; } if (endAtom && startAtom && endAtom != startAtom) { - JSString *str = ATOM_TO_STRING(startAtom); - /* End vs. start tag name mismatch: point to the tag name. */ reportErrorNumber(pn2, JSREPORT_UC | JSREPORT_ERROR, JSMSG_XML_TAG_NAME_MISMATCH, - str->chars()); + startAtom->chars()); return NULL; } @@ -8676,15 +8703,12 @@ Parser::primaryExpr(TokenKind tt, JSBool afterDot) #if JS_HAS_XML_SUPPORT if (tokenStream.matchToken(TOK_DBLCOLON)) { if (afterDot) { - JSString *str; - /* * Here primaryExpr is called after . or .. followed by a name * followed by ::. This is the only case where a keyword after * . or .. is not treated as a property name. */ - str = ATOM_TO_STRING(pn->pn_atom); - tt = js_CheckKeyword(str->chars(), str->length()); + tt = js_CheckKeyword(pn->pn_atom->chars(), pn->pn_atom->length()); if (tt == TOK_FUNCTION) { pn->pn_arity = PN_NULLARY; pn->pn_type = TOK_FUNCTION; diff --git a/js/src/jspropertytree.cpp b/js/src/jspropertytree.cpp index 628942a40c4..7c93ed8b86c 100644 --- a/js/src/jspropertytree.cpp +++ b/js/src/jspropertytree.cpp @@ -480,13 +480,14 @@ Shape::dump(JSContext *cx, FILE *fp) const if (JSID_IS_INT(id)) { fprintf(fp, "[%ld]", (long) JSID_TO_INT(id)); } else { - JSString *str; + JSLinearString *str; if (JSID_IS_ATOM(id)) { - str = JSID_TO_STRING(id); + str = JSID_TO_ATOM(id); } else { JS_ASSERT(JSID_IS_OBJECT(id)); - str = js_ValueToString(cx, IdToValue(id)); + JSString *s = js_ValueToString(cx, IdToValue(id)); fputs("object ", fp); + str = s ? s->ensureLinear(cx) : NULL; } if (!str) fputs("", fp); diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index 79f9da414f6..b23d6f355a6 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -407,14 +407,14 @@ ArrayToIdVector(JSContext *cx, const Value &array, AutoIdVector &props) JSObject *obj = &array.toObject(); jsuint length; - if (!js_GetLengthProperty(cx, obj, &length)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH); + if (!js_GetLengthProperty(cx, obj, &length)) return false; - } AutoIdRooter idr(cx); AutoValueRooter tvr(cx); for (jsuint n = 0; n < length; ++n) { + if (!JS_CHECK_OPERATION_LIMIT(cx)) + return false; if (!js_IndexToId(cx, n, idr.addr())) return false; if (!obj->getProperty(cx, idr.id(), tvr.addr())) diff --git a/js/src/jsprvtd.h b/js/src/jsprvtd.h index e5ca7f98601..5a75de5ee22 100644 --- a/js/src/jsprvtd.h +++ b/js/src/jsprvtd.h @@ -92,6 +92,7 @@ typedef struct JSTreeContext JSTreeContext; typedef struct JSTryNote JSTryNote; /* Friend "Advanced API" typedefs. */ +typedef struct JSLinearString JSLinearString; typedef struct JSAtom JSAtom; typedef struct JSAtomList JSAtomList; typedef struct JSAtomListElement JSAtomListElement; diff --git a/js/src/jsreflect.cpp b/js/src/jsreflect.cpp index 5a42f874b3c..31d80e1186d 100644 --- a/js/src/jsreflect.cpp +++ b/js/src/jsreflect.cpp @@ -2829,7 +2829,12 @@ reflect_parse(JSContext *cx, uint32 argc, jsval *vp) if (!str) return JS_FALSE; - filename = js_DeflateString(cx, str->chars(), str->length()); + size_t length = str->length(); + const jschar *chars = str->getChars(cx); + if (!chars) + return JS_FALSE; + + filename = js_DeflateString(cx, chars, length); if (!filename) return JS_FALSE; filenamep.reset(filename); @@ -2844,10 +2849,10 @@ reflect_parse(JSContext *cx, uint32 argc, jsval *vp) } } - const jschar *chars; - size_t length; - - src->getCharsAndLength(chars, length); + size_t length = src->length(); + const jschar *chars = src->getChars(cx); + if (!chars) + return JS_FALSE; Parser parser(cx); diff --git a/js/src/jsregexp.cpp b/js/src/jsregexp.cpp index deda35feb88..e5546310e68 100644 --- a/js/src/jsregexp.cpp +++ b/js/src/jsregexp.cpp @@ -243,9 +243,11 @@ RegExp::handlePCREError(JSContext *cx, int error) bool RegExp::parseFlags(JSContext *cx, JSString *flagStr, uint32 &flagsOut) { - const jschar *s; - size_t n; - flagStr->getCharsAndLength(s, n); + size_t n = flagStr->length(); + const jschar *s = flagStr->getChars(cx); + if (!s) + return false; + flagsOut = 0; for (size_t i = 0; i < n; i++) { #define HANDLE_FLAG(__name) \ @@ -578,9 +580,12 @@ js_regexp_toString(JSContext *cx, JSObject *obj, Value *vp) return true; } - const jschar *source; - size_t length; - re->getSource()->getCharsAndLength(source, length); + JSLinearString *src = re->getSource(); + size_t length = src->length(); + const jschar *source = src->getChars(cx); + if (!source) + return false; + if (length == 0) { source = empty_regexp_ucstr; length = JS_ARRAY_LENGTH(empty_regexp_ucstr) - 1; @@ -632,9 +637,11 @@ regexp_toString(JSContext *cx, uintN argc, Value *vp) static JSString * EscapeNakedForwardSlashes(JSContext *cx, JSString *unescaped) { - const jschar *oldChars; - size_t oldLen; - unescaped->getCharsAndLength(oldChars, oldLen); + size_t oldLen = unescaped->length(); + const jschar *oldChars = unescaped->getChars(cx); + if (!oldChars) + return NULL; + js::Vector newChars(cx); for (const jschar *it = oldChars; it < oldChars + oldLen; ++it) { if (*it == '/' && (it == oldChars || it[-1] != '\\')) { diff --git a/js/src/jsregexp.h b/js/src/jsregexp.h index 399abb79bf9..1c1d061095f 100644 --- a/js/src/jsregexp.h +++ b/js/src/jsregexp.h @@ -61,7 +61,7 @@ class RegExpStatics typedef Vector MatchPairs; MatchPairs matchPairs; /* The input that was used to produce matchPairs. */ - JSString *matchPairsInput; + JSLinearString *matchPairsInput; /* The input last set on the statics. */ JSString *pendingInput; uintN flags; @@ -175,7 +175,7 @@ class RegExpStatics /* Mutators. */ - bool updateFromMatch(JSContext *cx, JSString *input, int *buf, size_t matchItemCount) { + bool updateFromMatch(JSContext *cx, JSLinearString *input, int *buf, size_t matchItemCount) { aboutToWrite(); pendingInput = input; diff --git a/js/src/jsregexpinlines.h b/js/src/jsregexpinlines.h index e31b3cfd375..43544d91b82 100644 --- a/js/src/jsregexpinlines.h +++ b/js/src/jsregexpinlines.h @@ -76,7 +76,7 @@ regexp_statics_construct(JSContext *cx, JSObject *parent) class RegExp { jsrefcount refCount; - JSString *source; + JSLinearString *source; #if ENABLE_YARR_JIT JSC::Yarr::RegexCodeBlock compiled; #else @@ -85,9 +85,9 @@ class RegExp unsigned parenCount; uint32 flags; - RegExp(JSString *source, uint32 flags) + RegExp(JSLinearString *source, uint32 flags) : refCount(1), source(source), compiled(), parenCount(0), flags(flags) {} - bool compileHelper(JSContext *cx, UString &pattern); + bool compileHelper(JSContext *cx, JSLinearString &pattern); bool compile(JSContext *cx); static const uint32 allFlags = JSREG_FOLD | JSREG_GLOB | JSREG_MULTILINE | JSREG_STICKY; void handlePCREError(JSContext *cx, int error); @@ -154,7 +154,7 @@ class RegExp void decref(JSContext *cx); /* Accessors. */ - JSString *getSource() const { return source; } + JSLinearString *getSource() const { return source; } size_t getParenCount() const { return parenCount; } bool ignoreCase() const { return flags & JSREG_FOLD; } bool global() const { return flags & JSREG_GLOB; } @@ -278,7 +278,7 @@ RegExp::createResult(JSContext *cx, JSString *input, int *buf, size_t matchItemC } inline bool -RegExp::executeInternal(JSContext *cx, RegExpStatics *res, JSString *input, +RegExp::executeInternal(JSContext *cx, RegExpStatics *res, JSString *inputstr, size_t *lastIndex, bool test, Value *rval) { #if !ENABLE_YARR_JIT @@ -303,8 +303,12 @@ RegExp::executeInternal(JSContext *cx, RegExpStatics *res, JSString *input, for (int *it = buf; it != buf + matchItemCount; ++it) *it = -1; - const jschar *chars = input->chars(); + JSLinearString *input = inputstr->ensureLinear(cx); + if (!input) + return false; + size_t len = input->length(); + const jschar *chars = input->chars(); /* * inputOffset emulates sticky mode by matching from this offset into the char buf and @@ -364,11 +368,14 @@ RegExp::executeInternal(JSContext *cx, RegExpStatics *res, JSString *input, inline RegExp * RegExp::create(JSContext *cx, JSString *source, uint32 flags) { + JSLinearString *flatSource = source->ensureLinear(cx); + if (!flatSource) + return NULL; RegExp *self; void *mem = cx->malloc(sizeof(*self)); if (!mem) return NULL; - self = new (mem) RegExp(source, flags); + self = new (mem) RegExp(flatSource, flags); if (!self->compile(cx)) { cx->destroy(self); return NULL; @@ -422,7 +429,7 @@ YarrJITIsBroken(JSContext *cx) #endif /* ANDROID */ inline bool -RegExp::compileHelper(JSContext *cx, UString &pattern) +RegExp::compileHelper(JSContext *cx, JSLinearString &pattern) { #if ENABLE_YARR_JIT bool fellBack = false; @@ -456,8 +463,13 @@ RegExp::compileHelper(JSContext *cx, UString &pattern) inline bool RegExp::compile(JSContext *cx) { + /* Flatten source early for the rest of compilation. */ + if (!source->ensureLinear(cx)) + return false; + if (!sticky()) return compileHelper(cx, *source); + /* * The sticky case we implement hackily by prepending a caret onto the front * and relying on |::execute| to pseudo-slice the string when it sees a sticky regexp. @@ -469,10 +481,10 @@ RegExp::compile(JSContext *cx) if (!cb.reserve(JS_ARRAY_LENGTH(prefix) + source->length() + JS_ARRAY_LENGTH(postfix))) return false; JS_ALWAYS_TRUE(cb.append(prefix, JS_ARRAY_LENGTH(prefix))); - JS_ALWAYS_TRUE(cb.append(source->chars(), source->length())); + JS_ALWAYS_TRUE(cb.append(source->flatChars(), source->length())); JS_ALWAYS_TRUE(cb.append(postfix, JS_ARRAY_LENGTH(postfix))); - JSString *fakeySource = js_NewStringFromCharBuffer(cx, cb); + JSLinearString *fakeySource = js_NewStringFromCharBuffer(cx, cb); if (!fakeySource) return false; return compileHelper(cx, *fakeySource); diff --git a/js/src/jsscan.cpp b/js/src/jsscan.cpp index 385db4a65ae..dfa8892e41f 100644 --- a/js/src/jsscan.cpp +++ b/js/src/jsscan.cpp @@ -147,19 +147,17 @@ js_CheckKeyword(const jschar *str, size_t length) } JSBool -js_IsIdentifier(JSString *str) +js_IsIdentifier(JSLinearString *str) { - size_t length; - jschar c; - const jschar *chars, *end; + const jschar *chars = str->chars(); + size_t length = str->length(); - str->getCharsAndLength(chars, length); if (length == 0) return JS_FALSE; - c = *chars; + jschar c = *chars; if (!JS_ISIDSTART(c)) return JS_FALSE; - end = chars + length; + const jschar *end = chars + length; while (++chars != end) { c = *chars; if (!JS_ISIDENT(c)) @@ -738,28 +736,47 @@ TokenStream::getXMLEntity() #endif /* JS_HAS_XML_SUPPORT */ /* - * We have encountered a '\': check for a Unicode escape sequence after it, - * returning the character code value if we found a Unicode escape sequence. - * Otherwise, non-destructively return the original '\'. + * We have encountered a '\': check for a Unicode escape sequence after it. + * Return 'true' and the character code value (by value) if we found a + * Unicode escape sequence. Otherwise, return 'false'. In both cases, do not + * advance along the buffer. */ -int32 -TokenStream::getUnicodeEscape() +bool +TokenStream::peekUnicodeEscape(int *result) { jschar cp[5]; - int32 c; if (peekChars(5, cp) && cp[0] == 'u' && JS7_ISHEX(cp[1]) && JS7_ISHEX(cp[2]) && JS7_ISHEX(cp[3]) && JS7_ISHEX(cp[4])) { - c = (((((JS7_UNHEX(cp[1]) << 4) + *result = (((((JS7_UNHEX(cp[1]) << 4) + JS7_UNHEX(cp[2])) << 4) + JS7_UNHEX(cp[3])) << 4) + JS7_UNHEX(cp[4]); - skipChars(5); - return c; + return true; } - return '\\'; + return false; +} + +bool +TokenStream::matchUnicodeEscapeIdStart(int32 *cp) +{ + if (peekUnicodeEscape(cp) && JS_ISIDSTART(*cp)) { + skipChars(5); + return true; + } + return false; +} + +bool +TokenStream::matchUnicodeEscapeIdent(int32 *cp) +{ + if (peekUnicodeEscape(cp) && JS_ISIDENT(*cp)) { + skipChars(5); + return true; + } + return false; } Token * @@ -795,7 +812,7 @@ TokenStream::getTokenInternal() int c, qc; Token *tp; JSAtom *atom; - JSBool hadUnicodeEscape; + bool hadUnicodeEscape; const struct keyword *kw; #if JS_HAS_XML_SUPPORT JSBool inTarget; @@ -993,11 +1010,9 @@ TokenStream::getTokenInternal() * Look for an identifier. */ - hadUnicodeEscape = JS_FALSE; + hadUnicodeEscape = false; if (JS_ISIDSTART(c) || - (c == '\\' && - (qc = getUnicodeEscape(), - hadUnicodeEscape = JS_ISIDSTART(qc)))) + (c == '\\' && (hadUnicodeEscape = matchUnicodeEscapeIdStart(&qc)))) { if (hadUnicodeEscape) c = qc; @@ -1007,11 +1022,10 @@ TokenStream::getTokenInternal() goto error; c = getChar(); if (c == '\\') { - qc = getUnicodeEscape(); - if (!JS_ISIDENT(qc)) + if (!matchUnicodeEscapeIdent(&qc)) break; c = qc; - hadUnicodeEscape = JS_TRUE; + hadUnicodeEscape = true; } else { if (!JS_ISIDENT(c)) break; diff --git a/js/src/jsscan.h b/js/src/jsscan.h index bb00532ae46..09ea86c1a03 100644 --- a/js/src/jsscan.h +++ b/js/src/jsscan.h @@ -462,7 +462,9 @@ class TokenStream void ungetChar(int32 c); void ungetCharIgnoreEOL(int32 c); Token *newToken(ptrdiff_t adjust); - int32 getUnicodeEscape(); + bool peekUnicodeEscape(int32 *c); + bool matchUnicodeEscapeIdStart(int32 *c); + bool matchUnicodeEscapeIdent(int32 *c); JSBool peekChars(intN n, jschar *cp); JSBool getXMLEntity(); jschar *findEOL(); @@ -535,7 +537,7 @@ typedef void (*JSMapKeywordFun)(const char *); * check if str is a JS keyword. */ extern JSBool -js_IsIdentifier(JSString *str); +js_IsIdentifier(JSLinearString *str); /* * Steal one JSREPORT_* bit (see jsapi.h) to tell that arguments to the error diff --git a/js/src/jsscope.cpp b/js/src/jsscope.cpp index 278498c1833..46c33576a46 100644 --- a/js/src/jsscope.cpp +++ b/js/src/jsscope.cpp @@ -1453,8 +1453,8 @@ PrintPropertyGetterOrSetter(JSTracer *trc, char *buf, size_t bufsize) name = trc->debugPrintIndex ? js_setter_str : js_getter_str; if (JSID_IS_ATOM(id)) { - n = PutEscapedString(buf, bufsize - 1, JSID_TO_STRING(id), 0); - if (n < bufsize - 1) + n = PutEscapedString(buf, bufsize, JSID_TO_ATOM(id), 0); + if (n < bufsize) JS_snprintf(buf + n, bufsize - n, " %s", name); } else if (JSID_IS_INT(shape->id)) { JS_snprintf(buf, bufsize, "%d %s", JSID_TO_INT(id), name); @@ -1476,8 +1476,8 @@ PrintPropertyMethod(JSTracer *trc, char *buf, size_t bufsize) JS_ASSERT(!JSID_IS_VOID(id)); JS_ASSERT(JSID_IS_ATOM(id)); - n = PutEscapedString(buf, bufsize - 1, JSID_TO_STRING(id), 0); - if (n < bufsize - 1) + n = PutEscapedString(buf, bufsize, JSID_TO_ATOM(id), 0); + if (n < bufsize) JS_snprintf(buf + n, bufsize - n, " method"); } #endif diff --git a/js/src/jsscope.h b/js/src/jsscope.h index dcfdecedabd..687bcebfb60 100644 --- a/js/src/jsscope.h +++ b/js/src/jsscope.h @@ -285,6 +285,11 @@ CastAsPropertyOp(js::Class *clasp) return JS_DATA_TO_FUNC_PTR(PropertyOp, clasp); } +/* + * Reuse the API-only JSPROP_INDEX attribute to mean shadowability. + */ +#define JSPROP_SHADOWABLE JSPROP_INDEX + struct Shape : public JSObjectMap { friend struct ::JSObject; @@ -598,6 +603,17 @@ struct Shape : public JSObjectMap return (attrs & (JSPROP_SETTER | JSPROP_GETTER)) != 0; } + /* + * For ES5 compatibility, we allow properties with JSPropertyOp-flavored + * setters to be shadowed when set. The "own" property thereby created in + * the directly referenced object will have the same getter and setter as + * the prototype property. See bug 552432. + */ + bool shadowable() const { + JS_ASSERT_IF(isDataDescriptor(), writable()); + return hasSlot() || (attrs & JSPROP_SHADOWABLE); + } + uint32 entryCount() const { if (table) return table->entryCount; diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index ff436525208..6327c4c7c84 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -422,7 +422,7 @@ script_finalize(JSContext *cx, JSObject *obj) { JSScript *script = (JSScript *) obj->getPrivate(); if (script) - js_DestroyScriptFromGC(cx, script, NULL); + js_DestroyScriptFromGC(cx, script); } static void @@ -1218,7 +1218,7 @@ js_CallDestroyScriptHook(JSContext *cx, JSScript *script) } static void -DestroyScript(JSContext *cx, JSScript *script, JSThreadData *data) +DestroyScript(JSContext *cx, JSScript *script) { #ifdef DEBUG if (script->isEmpty()) @@ -1277,16 +1277,7 @@ DestroyScript(JSContext *cx, JSScript *script, JSThreadData *data) } #ifdef JS_TRACER -# ifdef JS_THREADSAFE - if (data) { - PurgeScriptFragments(&data->traceMonitor, script); - } else { - for (ThreadDataIter i(cx->runtime); !i.empty(); i.popFront()) - PurgeScriptFragments(&i.threadData()->traceMonitor, script); - } -# else - PurgeScriptFragments(&JS_TRACE_MONITOR(cx), script); -# endif + PurgeScriptFragments(&script->compartment->traceMonitor, script); #endif #if defined(JS_METHODJIT) @@ -1301,14 +1292,14 @@ void js_DestroyScript(JSContext *cx, JSScript *script) { JS_ASSERT(!cx->runtime->gcRunning); - DestroyScript(cx, script, JS_THREAD_DATA(cx)); + DestroyScript(cx, script); } void -js_DestroyScriptFromGC(JSContext *cx, JSScript *script, JSThreadData *data) +js_DestroyScriptFromGC(JSContext *cx, JSScript *script) { JS_ASSERT(cx->runtime->gcRunning); - DestroyScript(cx, script, data); + DestroyScript(cx, script); } void diff --git a/js/src/jsscript.h b/js/src/jsscript.h index cd9ee325496..b685974b24c 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -242,6 +242,7 @@ struct JSScript { this script */ #ifdef JS_METHODJIT bool debugMode:1; /* script was compiled in debug mode */ + bool singleStepMode:1; /* compile script in single-step mode */ #endif jsbytecode *main; /* main entry point, after predef'ing prolog */ @@ -502,7 +503,7 @@ js_DestroyScript(JSContext *cx, JSScript *script); * from that thread. */ extern void -js_DestroyScriptFromGC(JSContext *cx, JSScript *script, JSThreadData *data); +js_DestroyScriptFromGC(JSContext *cx, JSScript *script); extern void js_TraceScript(JSTracer *trc, JSScript *script); diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index e23a7470537..0a393864bfd 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -97,90 +97,125 @@ js_GetStringChars(JSContext *cx, JSString *str) return str->flatChars(); } -void -JSString::flatten() +static JS_ALWAYS_INLINE size_t +RopeCapacityFor(size_t length) +{ + static const size_t ROPE_DOUBLING_MAX = 1024 * 1024; + + /* + * Grow by 12.5% if the buffer is very large. Otherwise, round up to the + * next power of 2. This is similar to what we do with arrays; see + * JSObject::ensureDenseArrayElements. + */ + if (length > ROPE_DOUBLING_MAX) + return length + (length / 8); + return RoundUpPow2(length); +} + +static JS_ALWAYS_INLINE jschar * +AllocChars(JSContext *maybecx, size_t wholeCapacity) +{ + /* +1 for the null char at the end. */ + JS_STATIC_ASSERT(JSString::MAX_LENGTH * sizeof(jschar) < UINT32_MAX); + size_t bytes = (wholeCapacity + 1) * sizeof(jschar); + if (maybecx) + return (jschar *)maybecx->malloc(bytes); + return (jschar *)js_malloc(bytes); +} + +const jschar * +JSString::flatten(JSContext *maybecx) { JS_ASSERT(isRope()); /* - * This can be called from any string in the rope, so first traverse to the - * top node. - */ - JSString *topNode = this; - while (topNode->isInteriorNode()) - topNode = topNode->interiorNodeParent(); - - const size_t length = topNode->length(); - const size_t capacity = topNode->topNodeCapacity(); - jschar *const chars = (jschar *) topNode->topNodeBuffer(); - - /* - * To allow a homogeneous tree traversal, transform the top node into an - * internal node with null parent (which will be the stop condition). - */ - topNode->e.mParent = NULL; -#ifdef DEBUG - topNode->mLengthAndFlags = JSString::INTERIOR_NODE; -#endif - - /* - * Perform a depth-first tree traversal, splatting each node's characters - * into a contiguous buffer. Visit each node three times: - * - the first time, record the position in the linear buffer, and recurse - * into the left child. - * - the second time, recurse into the right child. - * - the third time, transform the node into a dependent string. + * Perform a depth-first dag traversal, splatting each node's characters + * into a contiguous buffer. Visit each rope node three times: + * 1. record position in the buffer and recurse into left child; + * 2. recurse into the right child; + * 3. transform the node into a dependent string. * To avoid maintaining a stack, tree nodes are mutated to indicate how - * many times they have been visited. + * many times they have been visited. Since ropes can be dags, a node may + * be encountered multiple times during traversal. However, step 3 above + * leaves a valid dependent string, so everythings works out. This + * algorithm is homomorphic to TypedMarker(JSTracer *, JSString *). + * + * While ropes avoid all sorts of quadratic cases with string + * concatenation, they can't help when ropes are immediately flattened. + * One idiomatic case that we'd like to keep linear (and has traditionally + * been linear in SM and other JS engines) is: + * + * while (...) { + * s += ... + * s.flatten + * } + * + * To do this, when the buffer for a to-be-flattened rope is allocated, the + * allocation size is rounded up. Then, if the resulting flat string is the + * left-hand side of a new rope that gets flattened and there is enough + * capacity, the rope is flattened into the same buffer, thereby avoiding + * copying the left-hand side. Clearing the 'extensible' bit turns off this + * optimization. This is necessary, e.g., when the JSAPI hands out the raw + * null-terminated char array of a flat string. */ - JSString *str = topNode; - jschar *pos = chars; - while (true) { - /* Visiting the node for the first time. */ - JS_ASSERT(str->isInteriorNode()); - { - JSString *next = str->mLeft; - str->mChars = pos; /* N.B. aliases mLeft */ - if (next->isInteriorNode()) { - str->mLengthAndFlags = 0x200; /* N.B. flags invalidated */ - str = next; - continue; /* Visit node for the first time. */ - } else { - size_t len = next->length(); - PodCopy(pos, next->mChars, len); - pos += len; - goto visit_right_child; - } - } + const size_t wholeLength = length(); + size_t wholeCapacity; + jschar *wholeChars; + JSString *str = this; + jschar *pos; - revisit_parent: - if (str->mLengthAndFlags == 0x200) { - visit_right_child: - JSString *next = str->e.mRight; - if (next->isInteriorNode()) { - str->mLengthAndFlags = 0x300; - str = next; - continue; /* Visit 'str' for the first time. */ - } else { - size_t len = next->length(); - PodCopy(pos, next->mChars, len); - pos += len; - goto finish_node; - } - } else { - JS_ASSERT(str->mLengthAndFlags == 0x300); - finish_node: - JSString *next = str->e.mParent; - str->finishTraversalConversion(topNode, pos); - if (!next) { - JS_ASSERT(pos == chars + length); - *pos = 0; - topNode->initFlatExtensible(chars, length, capacity); - return; - } - str = next; - goto revisit_parent; /* Visit 'str' for the second or third time */ + if (u.left->isExtensible() && u.left->s.capacity >= wholeLength) { + wholeCapacity = u.left->s.capacity; + wholeChars = const_cast(u.left->u.chars); + pos = wholeChars + u.left->length(); + u.left->finishTraversalConversion(this, wholeChars, pos); + goto visit_right_child; + } + + wholeCapacity = RopeCapacityFor(wholeLength); + wholeChars = AllocChars(maybecx, wholeCapacity); + if (!wholeChars) + return NULL; + pos = wholeChars; + first_visit_node: { + JSString *left = str->u.left; /* Read before clobbered. */ + str->u.chars = pos; + if (left->isRope()) { + left->s.parent = str; /* Return to this when 'left' done, */ + left->lengthAndFlags = 0x200; /* but goto visit_right_child. */ + str = left; + goto first_visit_node; } + size_t len = left->length(); + PodCopy(pos, left->u.chars, len); + pos += len; + } + visit_right_child: { + JSString *right = str->s.right; + if (right->isRope()) { + right->s.parent = str; /* Return to this node when 'right' done, */ + right->lengthAndFlags = 0x300; /* but goto finish_node. */ + str = right; + goto first_visit_node; + } + size_t len = right->length(); + PodCopy(pos, right->u.chars, len); + pos += len; + } + finish_node: { + if (str == this) { + JS_ASSERT(pos == wholeChars + wholeLength); + *pos = '\0'; + initFlatExtensible(wholeChars, wholeLength, wholeCapacity); + return wholeChars; + } + size_t progress = str->lengthAndFlags; /* Read before clobbered. */ + JSString *parent = str->s.parent; + str->finishTraversalConversion(this, wholeChars, pos); + str = parent; + if (progress == 0x200) + goto visit_right_child; + goto finish_node; } } @@ -191,153 +226,47 @@ JSStringFinalizeOp JSExternalString::str_finalizers[JSExternalString::TYPE_LIMIT #ifdef JS_TRACER -int32 JS_FASTCALL -js_Flatten(JSString* str) +JSBool JS_FASTCALL +js_Flatten(JSContext *cx, JSString* str) { - str->flatten(); - return 0; + return !!str->flatten(cx); } -JS_DEFINE_CALLINFO_1(extern, INT32, js_Flatten, STRING, 0, nanojit::ACCSET_STORE_ANY) +JS_DEFINE_CALLINFO_2(extern, BOOL, js_Flatten, CONTEXT, STRING, 0, nanojit::ACCSET_STORE_ANY) #endif /* !JS_TRACER */ -static JS_ALWAYS_INLINE size_t -RopeAllocSize(const size_t length, size_t *capacity) -{ - static const size_t ROPE_DOUBLING_MAX = 1024 * 1024; - - size_t size; - size_t minCap = (length + 1) * sizeof(jschar); - - /* - * Grow by 12.5% if the buffer is very large. Otherwise, round up to the - * next power of 2. This is similar to what we do with arrays; see - * JSObject::ensureDenseArrayElements. - */ - if (length > ROPE_DOUBLING_MAX) - size = minCap + (minCap / 8); - else - size = RoundUpPow2(minCap); - *capacity = (size / sizeof(jschar)) - 1; - JS_ASSERT(size >= sizeof(JSRopeBufferInfo)); - return size; -} - -static JS_ALWAYS_INLINE JSRopeBufferInfo * -ObtainRopeBuffer(JSContext *cx, bool usingLeft, bool usingRight, - JSRopeBufferInfo *sourceBuffer, size_t length, - JSString *left, JSString *right) -{ - JSRopeBufferInfo *buf; - size_t capacity; - - /* - * We need to survive a GC upon failure and in case creating a new - * string header triggers a GC, but we've broken the invariant that - * rope top nodes always point to freeable JSRopeBufferInfo - * objects, so make them point to NULL. - */ - if (usingLeft) - left->nullifyTopNodeBuffer(); - if (usingRight) - right->nullifyTopNodeBuffer(); - - /* - * Try to reuse sourceBuffer. If it's not suitable, free it and create a - * suitable buffer. - */ - if (length <= sourceBuffer->capacity) { - buf = sourceBuffer; - } else { - size_t allocSize = RopeAllocSize(length, &capacity); - cx->free(sourceBuffer); - buf = (JSRopeBufferInfo *) cx->malloc(allocSize); - if (!buf) - return NULL; - buf->capacity = capacity; - } - return buf; -} - -static JS_ALWAYS_INLINE JSString * -FinishConcat(JSContext *cx, bool usingLeft, bool usingRight, - JSString *left, JSString *right, size_t length, - JSRopeBufferInfo *buf) -{ - JSString *res = js_NewGCString(cx); - if (!res) { - cx->free(buf); - return NULL; - } - res->initTopNode(left, right, length, buf); - if (usingLeft) - left->convertToInteriorNode(res); - if (usingRight) - right->convertToInteriorNode(res); - return res; -} - JSString * JS_FASTCALL js_ConcatStrings(JSContext *cx, JSString *left, JSString *right) { - size_t length, leftLen, rightLen; - bool leftRopeTop, rightRopeTop; - - leftLen = left->length(); + size_t leftLen = left->length(); if (leftLen == 0) return right; - rightLen = right->length(); + + size_t rightLen = right->length(); if (rightLen == 0) return left; - length = leftLen + rightLen; + size_t wholeLength = leftLen + rightLen; - if (JSShortString::fitsIntoShortString(length)) { + if (JSShortString::fitsIntoShortString(wholeLength)) { JSShortString *shortStr = js_NewGCShortString(cx); if (!shortStr) return NULL; + const jschar *leftChars = left->getChars(cx); + if (!leftChars) + return NULL; + const jschar *rightChars = right->getChars(cx); + if (!rightChars) + 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; + jschar *buf = shortStr->init(wholeLength); + js_short_strncpy(buf, leftChars, leftLen); + js_short_strncpy(buf + leftLen, rightChars, rightLen); + buf[wholeLength] = 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. - */ - if (left->isInteriorNode()) - left->flatten(); - if (right->isInteriorNode()) - right->flatten(); - - if (left->isExtensible() && !right->isRope() && - left->flatCapacity() >= length) { - JS_ASSERT(left->isFlat()); - - /* - * If left has enough unused space at the end of its buffer that we can - * fit the entire new string there, just write there. - */ - 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->initFlatExtensible(chars, length, left->flatCapacity()); - left->initDependent(res, res->flatChars(), leftLen); - return res; - } - - if (length > JSString::MAX_LENGTH) { + if (wholeLength > JSString::MAX_LENGTH) { if (JS_ON_TRACE(cx)) { if (!CanLeaveTrace(cx)) return NULL; @@ -347,67 +276,12 @@ js_ConcatStrings(JSContext *cx, JSString *left, JSString *right) return NULL; } - leftRopeTop = left->isTopNode(); - rightRopeTop = right->isTopNode(); + JSString *newRoot = js_NewGCString(cx); + if (!newRoot) + return NULL; - /* - * To make traversal more manageable, we enforce that, unless the children - * are leaves, the two children of a rope node must be distinct. - */ - if (left == right && leftRopeTop) { - left->flatten(); - leftRopeTop = false; - rightRopeTop = false; - JS_ASSERT(leftLen == left->length()); - JS_ASSERT(rightLen == right->length()); - JS_ASSERT(!left->isTopNode()); - JS_ASSERT(!right->isTopNode()); - } - - /* - * There are 4 cases, based on whether on whether the left or right is a - * rope or non-rope string. - */ - JSRopeBufferInfo *buf = NULL; - - if (leftRopeTop) { - /* Left child is a rope. */ - JSRopeBufferInfo *leftBuf = left->topNodeBuffer(); - - /* If both children are ropes, steal the larger buffer. */ - if (JS_UNLIKELY(rightRopeTop)) { - JSRopeBufferInfo *rightBuf = right->topNodeBuffer(); - - /* Put the larger buffer into 'leftBuf'. */ - if (leftBuf->capacity >= rightBuf->capacity) { - cx->free(rightBuf); - } else { - cx->free(leftBuf); - leftBuf = rightBuf; - } - } - - buf = ObtainRopeBuffer(cx, true, rightRopeTop, leftBuf, length, left, right); - if (!buf) - return NULL; - } else if (JS_UNLIKELY(rightRopeTop)) { - /* Right child is a rope: steal its buffer if big enough. */ - JSRopeBufferInfo *rightBuf = right->topNodeBuffer(); - - buf = ObtainRopeBuffer(cx, false, true, rightBuf, length, left, right); - if (!buf) - return NULL; - } else { - /* Neither child is a rope: need to make a new buffer. */ - size_t capacity; - size_t allocSize = RopeAllocSize(length, &capacity); - buf = (JSRopeBufferInfo *) cx->malloc(allocSize); - if (!buf) - return NULL; - buf->capacity = capacity; - } - - return FinishConcat(cx, leftRopeTop, rightRopeTop, left, right, length, buf); + newRoot->initRopeNode(left, right, wholeLength); + return newRoot; } const jschar * @@ -416,7 +290,8 @@ JSString::undepend(JSContext *cx) size_t n, size; jschar *s; - ensureNotRope(); + if (!ensureLinear(cx)) + return NULL; if (isDependent()) { n = dependentLength(); @@ -451,8 +326,7 @@ js_MakeStringImmutable(JSContext *cx, JSString *str) * Flattening a rope may result in a dependent string, so we need to flatten * before undepending the string. */ - str->ensureNotRope(); - if (!str->ensureNotDependent(cx)) { + if (!str->isFlat() && !str->undepend(cx)) { JS_RUNTIME_METER(cx->runtime, badUndependStrings); return JS_FALSE; } @@ -460,7 +334,7 @@ js_MakeStringImmutable(JSContext *cx, JSString *str) return JS_TRUE; } -static JSString * +static JSLinearString * ArgToRootedString(JSContext *cx, uintN argc, Value *vp, uintN arg) { if (arg >= argc) @@ -470,9 +344,9 @@ ArgToRootedString(JSContext *cx, uintN argc, Value *vp, uintN arg) if (vp->isObject() && !DefaultValue(cx, &vp->toObject(), JSTYPE_STRING, vp)) return NULL; - JSString *str; + JSLinearString *str; if (vp->isString()) { - str = vp->toString(); + str = vp->toString()->ensureLinear(cx); } else if (vp->isBoolean()) { str = ATOM_TO_STRING(cx->runtime->atomState.booleanAtoms[ (int)vp->toBoolean()]); @@ -482,9 +356,10 @@ ArgToRootedString(JSContext *cx, uintN argc, Value *vp, uintN arg) str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]); } else { - str = js_NumberToString(cx, vp->toNumber()); - if (str) - vp->setString(str); + str = NumberToString(cx, vp->toNumber()); + if (!str) + return NULL; + vp->setString(str); } return str; } @@ -552,18 +427,12 @@ static const uint8 urlCharType[256] = JSBool js_str_escape(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval) { - JSString *str; - size_t i, ni, length, newlength; - const jschar *chars; - jschar *newchars; - jschar ch; - jsint mask; - jsdouble d; const char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; - mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH; + jsint mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH; if (argc > 1) { + double d; if (!ValueToNumber(cx, argv[1], &d)) return JS_FALSE; if (!JSDOUBLE_IS_FINITE(d) || @@ -578,15 +447,17 @@ js_str_escape(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval } } - str = ArgToRootedString(cx, argc, argv - 2, 0); + JSLinearString *str = ArgToRootedString(cx, argc, argv - 2, 0); if (!str) return JS_FALSE; - str->getCharsAndLength(chars, length); - newlength = length; + size_t length = str->length(); + const jschar *chars = str->chars(); /* Take a first pass and see how big the result string will need to be. */ - for (i = 0; i < length; i++) { + size_t newlength = length; + for (size_t i = 0; i < length; i++) { + jschar ch; if ((ch = chars[i]) < 128 && IS_OK(ch, mask)) continue; if (ch < 256) { @@ -612,10 +483,12 @@ js_str_escape(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval return JS_FALSE; } - newchars = (jschar *) cx->malloc((newlength + 1) * sizeof(jschar)); + jschar *newchars = (jschar *) cx->malloc((newlength + 1) * sizeof(jschar)); if (!newchars) return JS_FALSE; + size_t i, ni; for (i = 0, ni = 0; i < length; i++) { + jschar ch; if ((ch = chars[i]) < 128 && IS_OK(ch, mask)) { newchars[ni++] = ch; } else if (ch < 256) { @@ -638,12 +511,12 @@ js_str_escape(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval JS_ASSERT(ni == newlength); newchars[newlength] = 0; - str = js_NewString(cx, newchars, newlength); - if (!str) { + JSString *retstr = js_NewString(cx, newchars, newlength); + if (!retstr) { cx->free(newchars); return JS_FALSE; } - rval->setString(str); + rval->setString(retstr); return JS_TRUE; } #undef IS_OK @@ -659,25 +532,20 @@ str_escape(JSContext *cx, uintN argc, Value *vp) static JSBool str_unescape(JSContext *cx, uintN argc, Value *vp) { - JSString *str; - size_t i, ni, length; - const jschar *chars; - jschar *newchars; - jschar ch; - - str = ArgToRootedString(cx, argc, vp, 0); + JSLinearString *str = ArgToRootedString(cx, argc, vp, 0); if (!str) - return JS_FALSE; + return false; - str->getCharsAndLength(chars, length); + size_t length = str->length(); + const jschar *chars = str->chars(); /* Don't bother allocating less space for the new string. */ - newchars = (jschar *) cx->malloc((length + 1) * sizeof(jschar)); + jschar *newchars = (jschar *) cx->malloc((length + 1) * sizeof(jschar)); if (!newchars) - return JS_FALSE; - ni = i = 0; + return false; + size_t ni = 0, i = 0; while (i < length) { - ch = chars[i++]; + jschar ch = chars[i++]; if (ch == '%') { if (i + 1 < length && JS7_ISHEX(chars[i]) && JS7_ISHEX(chars[i + 1])) @@ -699,12 +567,12 @@ str_unescape(JSContext *cx, uintN argc, Value *vp) } newchars[ni] = 0; - str = js_NewString(cx, newchars, ni); - if (!str) { + JSString *retstr = js_NewString(cx, newchars, ni); + if (!retstr) { cx->free(newchars); return JS_FALSE; } - vp->setString(str); + vp->setString(retstr); return JS_TRUE; } @@ -908,9 +776,10 @@ str_toSource(JSContext *cx, uintN argc, Value *vp) char buf[16]; size_t j = JS_snprintf(buf, sizeof buf, "(new String("); - const jschar *s; - size_t k; - str->getCharsAndLength(s, k); + size_t k = str->length(); + const jschar *s = str->getChars(cx); + if (!s) + return false; size_t n = j + k + 2; jschar *t = (jschar *) cx->malloc((n + 1) * sizeof(jschar)); @@ -950,70 +819,87 @@ js_str_toString(JSContext *cx, uintN argc, Value *vp) /* * Java-like string native methods. */ - -static JSString * -SubstringTail(JSContext *cx, JSString *str, jsdouble length, jsdouble begin, jsdouble end) + +JS_ALWAYS_INLINE bool +ValueToIntegerRange(JSContext *cx, const Value &v, int32 *out) { - if (begin < 0) - begin = 0; - else if (begin > length) - begin = length; + if (v.isInt32()) { + *out = v.toInt32(); + } else { + double d; - if (end < 0) - end = 0; - else if (end > length) - end = length; - if (end < begin) { - /* ECMA emulates old JDK1.0 java.lang.String.substring. */ - jsdouble tmp = begin; - begin = end; - end = tmp; + if (!ValueToNumber(cx, v, &d)) + return false; + + d = js_DoubleToInteger(d); + if (d > INT32_MAX) + *out = INT32_MAX; + else if (d < INT32_MIN) + *out = INT32_MIN; + else + *out = int32(d); } - return js_NewDependentString(cx, str, (size_t)begin, (size_t)(end - begin)); + return true; } static JSBool str_substring(JSContext *cx, uintN argc, Value *vp) { JSString *str; - jsdouble d; - jsdouble length, begin, end; + int32 length, begin, end; NORMALIZE_THIS(cx, vp, str); - if (argc != 0) { - if (!ValueToNumber(cx, vp[2], &d)) - return JS_FALSE; - length = str->length(); - begin = js_DoubleToInteger(d); - if (argc == 1 || vp[3].isUndefined()) { - end = length; - } else { - if (!ValueToNumber(cx, vp[3], &d)) - return JS_FALSE; - end = js_DoubleToInteger(d); + + if (argc > 0) { + end = length = int32(str->length()); + + if (!ValueToIntegerRange(cx, vp[2], &begin)) + return false; + + if (begin < 0) + begin = 0; + else if (begin > length) + begin = length; + + if (argc > 1 && !vp[3].isUndefined()) { + if (!ValueToIntegerRange(cx, vp[3], &end)) + return false; + + if (end > length) { + end = length; + } else { + if (end < 0) + end = 0; + if (end < begin) { + int32_t tmp = begin; + begin = end; + end = tmp; + } + } } - str = SubstringTail(cx, str, length, begin, end); + str = js_NewDependentString(cx, str, size_t(begin), size_t(end - begin)); if (!str) - return JS_FALSE; + return false; } + vp->setString(str); - return JS_TRUE; + return true; } JSString* JS_FASTCALL js_toLowerCase(JSContext *cx, JSString *str) { - size_t i, n; - const jschar *s; - jschar *news; + size_t n = str->length(); + const jschar *s = str->getChars(cx); + if (!s) + return NULL; - str->getCharsAndLength(s, n); - news = (jschar *) cx->malloc((n + 1) * sizeof(jschar)); + jschar *news = (jschar *) cx->malloc((n + 1) * sizeof(jschar)); if (!news) return NULL; - for (i = 0; i < n; i++) + for (size_t i = 0; i < n; i++) news[i] = JS_TOLOWER(s[i]); news[n] = 0; str = js_NewString(cx, news, n); @@ -1056,15 +942,14 @@ str_toLocaleLowerCase(JSContext *cx, uintN argc, Value *vp) JSString* JS_FASTCALL js_toUpperCase(JSContext *cx, JSString *str) { - size_t i, n; - const jschar *s; - jschar *news; - - str->getCharsAndLength(s, n); - news = (jschar *) cx->malloc((n + 1) * sizeof(jschar)); + size_t n = str->length(); + const jschar *s = str->getChars(cx); + if (!s) + return NULL; + jschar *news = (jschar *) cx->malloc((n + 1) * sizeof(jschar)); if (!news) return NULL; - for (i = 0; i < n; i++) + for (size_t i = 0; i < n; i++) news[i] = JS_TOUPPER(s[i]); news[n] = 0; str = js_NewString(cx, news, n); @@ -1120,7 +1005,10 @@ str_localeCompare(JSContext *cx, uintN argc, Value *vp) vp[2].setString(thatStr); return cx->localeCallbacks->localeCompare(cx, str, thatStr, Jsvalify(vp)); } - vp->setInt32(js_CompareStrings(str, thatStr)); + int32 result; + if (!CompareStrings(cx, str, thatStr, &result)) + return JS_FALSE; + vp->setInt32(result); } return JS_TRUE; } @@ -1169,8 +1057,6 @@ js_str_charCodeAt(JSContext *cx, uintN argc, Value *vp) { JSString *str; jsint i; - jsdouble d; - if (vp[1].isString() && argc != 0 && vp[2].isInt32()) { str = vp[1].toString(); i = vp[2].toInt32(); @@ -1179,11 +1065,12 @@ js_str_charCodeAt(JSContext *cx, uintN argc, Value *vp) } else { NORMALIZE_THIS(cx, vp, str); + double d; if (argc == 0) { d = 0.0; } else { if (!ValueToNumber(cx, vp[2], &d)) - return JS_FALSE; + return false; d = js_DoubleToInteger(d); } @@ -1192,12 +1079,17 @@ js_str_charCodeAt(JSContext *cx, uintN argc, Value *vp) i = (jsint) d; } - vp->setInt32(str->chars()[i]); - return JS_TRUE; + const jschar *chars; + chars = str->getChars(cx); + if (!chars) + return false; + + vp->setInt32(chars[i]); + return true; out_of_range: vp->setDouble(js_NaN); - return JS_TRUE; + return true; } jsint @@ -1359,15 +1251,24 @@ StringMatch(const jschar *text, jsuint textlen, static const size_t sRopeMatchThresholdRatioLog2 = 5; -static jsint -RopeMatch(JSString *textstr, const jschar *pat, jsuint patlen) +/* + * RopeMatch takes the text to search, the patern to search for in the text. + * RopeMatch returns false on OOM and otherwise returns the match index through + * the 'match' outparam (-1 for not found). + */ +static bool +RopeMatch(JSContext *cx, JSString *textstr, const jschar *pat, jsuint patlen, jsint *match) { - JS_ASSERT(textstr->isTopNode()); + JS_ASSERT(textstr->isRope()); - if (patlen == 0) - return 0; - if (textstr->length() < patlen) - return -1; + if (patlen == 0) { + *match = 0; + return true; + } + if (textstr->length() < patlen) { + *match = -1; + return true; + } /* * List of leaf nodes in the rope. If we run out of memory when trying to @@ -1382,12 +1283,23 @@ RopeMatch(JSString *textstr, const jschar *pat, jsuint patlen) * need to build the list of leaf nodes. Do both here: iterate over the * nodes so long as there are not too many. */ - size_t textstrlen = textstr->length(); - size_t threshold = textstrlen >> sRopeMatchThresholdRatioLog2; - JSRopeLeafIterator iter; - for (JSString *str = iter.init(textstr); str; str = iter.next()) { - if (threshold-- == 0 || !strs.append(str)) - return StringMatch(textstr->chars(), textstrlen, pat, patlen); + { + size_t textstrlen = textstr->length(); + size_t threshold = textstrlen >> sRopeMatchThresholdRatioLog2; + StringSegmentRange r(cx); + if (!r.init(textstr)) + return false; + while (!r.empty()) { + if (threshold-- == 0 || !strs.append(r.front())) { + const jschar *chars = textstr->getChars(cx); + if (!chars) + return false; + *match = StringMatch(chars, textstrlen, pat, patlen); + return true; + } + if (!r.popFront()) + return false; + } } /* Absolute offset from the beginning of the logical string textstr. */ @@ -1397,12 +1309,14 @@ RopeMatch(JSString *textstr, const jschar *pat, jsuint patlen) for (JSString **outerp = strs.begin(); outerp != strs.end(); ++outerp) { /* First try to match without spanning two nodes. */ - const jschar *chars; - size_t len; - (*outerp)->getCharsAndLength(chars, len); + JSString *outer = *outerp; + const jschar *chars = outer->nonRopeChars(); + size_t len = outer->length(); jsint matchResult = StringMatch(chars, len, pat, patlen); - if (matchResult != -1) - return pos + matchResult; + if (matchResult != -1) { + *match = pos + matchResult; + return true; + } /* Test the overlap. */ JSString **innerp = outerp; @@ -1422,16 +1336,21 @@ RopeMatch(JSString *textstr, const jschar *pat, jsuint patlen) const jschar *ttend = textend; for (const jschar *pp = p1, *tt = t; pp != patend; ++pp, ++tt) { while (tt == ttend) { - if (++innerp == strs.end()) - return -1; - (*innerp)->getCharsAndEnd(tt, ttend); + if (++innerp == strs.end()) { + *match = -1; + return true; + } + JSString *inner = *innerp; + tt = inner->nonRopeChars(); + ttend = tt + inner->length(); } if (*pp != *tt) goto break_continue; } /* Matched! */ - return pos + (t - chars) - 1; /* -1 because of *t++ above */ + *match = pos + (t - chars) - 1; /* -1 because of *t++ above */ + return true; break_continue:; } @@ -1439,7 +1358,8 @@ RopeMatch(JSString *textstr, const jschar *pat, jsuint patlen) pos += len; } - return -1; + *match = -1; + return true; } static JSBool @@ -1449,14 +1369,17 @@ str_indexOf(JSContext *cx, uintN argc, Value *vp) JSString *str; NORMALIZE_THIS(cx, vp, str); - JSString *patstr = ArgToRootedString(cx, argc, vp, 0); + JSLinearString *patstr = ArgToRootedString(cx, argc, vp, 0); if (!patstr) - return JS_FALSE; + return false; - const jschar *text = str->chars(); jsuint textlen = str->length(); - const jschar *pat = patstr->chars(); + const jschar *text = str->getChars(cx); + if (!text) + return false; + jsuint patlen = patstr->length(); + const jschar *pat = patstr->chars(); jsuint start; if (argc > 1) { @@ -1500,41 +1423,37 @@ str_indexOf(JSContext *cx, uintN argc, Value *vp) static JSBool str_lastIndexOf(JSContext *cx, uintN argc, Value *vp) { - JSString *str, *str2; - const jschar *text, *pat; - jsint i, j, textlen, patlen; - jsdouble d; + JSString *textstr; + NORMALIZE_THIS(cx, vp, textstr); + size_t textlen = textstr->length(); + const jschar *text = textstr->getChars(cx); + if (!text) + return false; - NORMALIZE_THIS(cx, vp, str); - text = str->chars(); - textlen = (jsint) str->length(); + JSLinearString *patstr = ArgToRootedString(cx, argc, vp, 0); + if (!patstr) + return false; - if (argc != 0 && vp[2].isString()) { - str2 = vp[2].toString(); - } else { - str2 = ArgToRootedString(cx, argc, vp, 0); - if (!str2) - return JS_FALSE; - } - pat = str2->chars(); - patlen = (jsint) str2->length(); + size_t patlen = patstr->length(); + const jschar *pat = patstr->chars(); - i = textlen - patlen; // Start searching here + jsint i = textlen - patlen; // Start searching here if (i < 0) { vp->setInt32(-1); - return JS_TRUE; + return true; } if (argc > 1) { if (vp[3].isInt32()) { - j = vp[3].toInt32(); + jsint j = vp[3].toInt32(); if (j <= 0) i = 0; else if (j < i) i = j; } else { + double d; if (!ValueToNumber(cx, vp[3], &d)) - return JS_FALSE; + return false; if (!JSDOUBLE_IS_NaN(d)) { d = js_DoubleToInteger(d); if (d <= 0) @@ -1547,7 +1466,7 @@ str_lastIndexOf(JSContext *cx, uintN argc, Value *vp) if (patlen == 0) { vp->setInt32(i); - return JS_TRUE; + return true; } const jschar *t = text + i; @@ -1564,26 +1483,27 @@ str_lastIndexOf(JSContext *cx, uintN argc, Value *vp) goto break_continue; } vp->setInt32(t - text); - return JS_TRUE; + return true; } break_continue:; } vp->setInt32(-1); - return JS_TRUE; + return true; } static JSBool js_TrimString(JSContext *cx, Value *vp, JSBool trimLeft, JSBool trimRight) { JSString *str; - const jschar *chars; - size_t length, begin, end; - NORMALIZE_THIS(cx, vp, str); - str->getCharsAndLength(chars, length); - begin = 0; - end = length; + size_t length = str->length(); + const jschar *chars = str->getChars(cx); + if (!chars) + return false; + + size_t begin = 0; + size_t end = length; if (trimLeft) { while (begin < length && JS_ISSPACE(chars[begin])) @@ -1597,10 +1517,10 @@ js_TrimString(JSContext *cx, Value *vp, JSBool trimLeft, JSBool trimRight) str = js_NewDependentString(cx, str, begin, end - begin); if (!str) - return JS_FALSE; + return false; vp->setString(str); - return JS_TRUE; + return true; } static JSBool @@ -1628,7 +1548,7 @@ str_trimRight(JSContext *cx, uintN argc, Value *vp) /* Result of a successfully performed flat match. */ class FlatMatch { - JSString *patstr; + JSLinearString *patstr; const jschar *pat; size_t patlen; int32 match_; @@ -1684,7 +1604,7 @@ class RegExpGuard */ static const size_t MAX_FLAT_PAT_LEN = 256; - static JSString *flattenPattern(JSContext *cx, JSString *patstr) { + static JSString *flattenPattern(JSContext *cx, JSLinearString *patstr) { JSCharBuffer cb(cx); if (!cb.reserve(patstr->length())) return NULL; @@ -1734,14 +1654,18 @@ class RegExpGuard * * @param checkMetaChars Look for regexp metachars in the pattern string. * @return Whether flat matching could be used. + * + * N.B. tryFlatMatch returns NULL on OOM, so the caller must check cx->throwing. */ const FlatMatch * - tryFlatMatch(JSString *textstr, uintN optarg, uintN argc, bool checkMetaChars = true) + tryFlatMatch(JSContext *cx, JSString *textstr, uintN optarg, uintN argc, + bool checkMetaChars = true) { if (rep.re_) return NULL; - fm.patstr->getCharsAndLength(fm.pat, fm.patlen); + fm.pat = fm.patstr->chars(); + fm.patlen = fm.patstr->length(); if (optarg < argc) return NULL; @@ -1755,12 +1679,12 @@ class RegExpGuard * textstr could be a rope, so we want to avoid flattening it for as * long as possible. */ - if (textstr->isTopNode()) { - fm.match_ = RopeMatch(textstr, fm.pat, fm.patlen); + if (textstr->isRope()) { + if (!RopeMatch(cx, textstr, fm.pat, fm.patlen, &fm.match_)) + return NULL; } else { - const jschar *text; - size_t textlen; - textstr->getCharsAndLength(text, textlen); + const jschar *text = textstr->nonRopeChars(); + size_t textlen = textstr->length(); fm.match_ = StringMatch(text, textlen, fm.pat, fm.patlen); } return &fm; @@ -1918,8 +1842,10 @@ str_match(JSContext *cx, uintN argc, Value *vp) RegExpGuard g(cx); if (!g.init(argc, vp)) return false; - if (const FlatMatch *fm = g.tryFlatMatch(str, 1, argc)) + if (const FlatMatch *fm = g.tryFlatMatch(cx, str, 1, argc)) return BuildFlatMatchArray(cx, str, *fm, vp); + if (cx->throwing) /* from tryFlatMatch */ + return false; const RegExpPair *rep = g.normalizeRegExp(false, 1, argc, vp); if (!rep) @@ -1946,10 +1872,12 @@ str_search(JSContext *cx, uintN argc, Value *vp) RegExpGuard g(cx); if (!g.init(argc, vp)) return false; - if (const FlatMatch *fm = g.tryFlatMatch(str, 1, argc)) { + if (const FlatMatch *fm = g.tryFlatMatch(cx, str, 1, argc)) { vp->setInt32(fm->match()); return true; } + if (cx->throwing) /* from tryFlatMatch */ + return false; const RegExpPair *rep = g.normalizeRegExp(false, 1, argc, vp); if (!rep) return false; @@ -1976,9 +1904,9 @@ struct ReplaceData RegExpGuard g; /* regexp parameter object and private data */ JSObject *lambda; /* replacement function object or null */ JSObject *elembase; /* object for function(a){return b[a]} replace */ - JSString *repstr; /* replacement string */ - jschar *dollar; /* null or pointer to first $ in repstr */ - jschar *dollarEnd; /* limit pointer for js_strchr_limit */ + JSLinearString *repstr; /* replacement string */ + const jschar *dollar; /* null or pointer to first $ in repstr */ + const jschar *dollarEnd; /* limit pointer for js_strchr_limit */ jsint index; /* index in result of next replacement */ jsint leftIndex; /* left context index in str->chars */ JSSubString dollarStr; /* for "$$" InterpretDollar result */ @@ -1989,8 +1917,8 @@ struct ReplaceData }; static bool -InterpretDollar(JSContext *cx, RegExpStatics *res, jschar *dp, jschar *ep, ReplaceData &rdata, - JSSubString *out, size_t *skip) +InterpretDollar(JSContext *cx, RegExpStatics *res, const jschar *dp, const jschar *ep, + ReplaceData &rdata, JSSubString *out, size_t *skip) { JS_ASSERT(*dp == '$'); @@ -2006,7 +1934,7 @@ InterpretDollar(JSContext *cx, RegExpStatics *res, jschar *dp, jschar *ep, Repla if (num > res->parenCount()) return false; - jschar *cp = dp + 2; + const jschar *cp = dp + 2; if (cp < ep && (dc = *cp, JS7_ISDEC(dc))) { uintN tmp = 10 * num + JS7_UNDEC(dc); if (tmp <= res->parenCount()) { @@ -2093,7 +2021,9 @@ FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t if (shape->slot != SHAPE_INVALID_SLOT && shape->hasDefaultGetter()) { Value value = base->getSlot(shape->slot); if (value.isString()) { - rdata.repstr = value.toString(); + rdata.repstr = value.toString()->ensureLinear(cx); + if (!rdata.repstr) + return false; *sizep = rdata.repstr->length(); return true; } @@ -2149,17 +2079,20 @@ FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t return false; /* root repstr: rdata is on the stack, so scanned by conservative gc. */ - rdata.repstr = ValueToString_TestForStringInline(cx, session.rval()); + JSString *repstr = ValueToString_TestForStringInline(cx, session.rval()); + if (!repstr) + return false; + rdata.repstr = repstr->ensureLinear(cx); if (!rdata.repstr) return false; - *sizep = rdata.repstr->length(); return true; } JSString *repstr = rdata.repstr; size_t replen = repstr->length(); - for (jschar *dp = rdata.dollar, *ep = rdata.dollarEnd; dp; dp = js_strchr_limit(dp, '$', ep)) { + for (const jschar *dp = rdata.dollar, *ep = rdata.dollarEnd; dp; + dp = js_strchr_limit(dp, '$', ep)) { JSSubString sub; size_t skip; if (InterpretDollar(cx, res, dp, ep, rdata, &sub, &skip)) { @@ -2176,11 +2109,13 @@ FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t static void DoReplace(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, jschar *chars) { - JSString *repstr = rdata.repstr; - jschar *cp; - jschar *bp = cp = repstr->chars(); + JSLinearString *repstr = rdata.repstr; + const jschar *cp; + const jschar *bp = cp = repstr->chars(); - for (jschar *dp = rdata.dollar, *ep = rdata.dollarEnd; dp; dp = js_strchr_limit(dp, '$', ep)) { + const jschar *dp = rdata.dollar; + const jschar *ep = rdata.dollarEnd; + for (; dp; dp = js_strchr_limit(dp, '$', ep)) { size_t len = dp - cp; js_strncpy(chars, cp, len); chars += len; @@ -2202,12 +2137,12 @@ DoReplace(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, jschar *chars) } static bool -ReplaceCallback(JSContext *cx, RegExpStatics *res, size_t count, void *p) +ReplaceRegExpCallback(JSContext *cx, RegExpStatics *res, size_t count, void *p) { ReplaceData &rdata = *static_cast(p); rdata.calledBack = true; - JSString *str = rdata.str; + JSLinearString *str = rdata.str->assertIsLinear(); /* flattened for regexp */ size_t leftoff = rdata.leftIndex; const jschar *left = str->chars() + leftoff; size_t leftlen = res->matchStart() - leftoff; @@ -2233,18 +2168,21 @@ static bool BuildFlatReplacement(JSContext *cx, JSString *textstr, JSString *repstr, const FlatMatch &fm, Value *vp) { - JSRopeBuilder builder(cx); - size_t match = fm.match(); /* Avoid signed/unsigned warnings. */ + RopeBuilder builder(cx); + size_t match = fm.match(); size_t matchEnd = match + fm.patternLength(); - if (textstr->isTopNode()) { + if (textstr->isRope()) { /* * If we are replacing over a rope, avoid flattening it by iterating * through it, building a new rope. */ - JSRopeLeafIterator iter; + StringSegmentRange r(cx); + if (!r.init(textstr)) + return false; size_t pos = 0; - for (JSString *str = iter.init(textstr); str; str = iter.next()) { + while (!r.empty()) { + JSString *str = r.front(); size_t len = str->length(); size_t strEnd = pos + len; if (pos < matchEnd && strEnd > match) { @@ -2282,6 +2220,8 @@ BuildFlatReplacement(JSContext *cx, JSString *textstr, JSString *repstr, return false; } pos += str->length(); + if (!r.popFront()) + return false; } } else { JSString *leftSide = js_NewDependentString(cx, textstr, 0, match); @@ -2297,7 +2237,7 @@ BuildFlatReplacement(JSContext *cx, JSString *textstr, JSString *repstr, } } - vp->setString(builder.getStr()); + vp->setString(builder.result()); return true; } @@ -2308,13 +2248,16 @@ BuildFlatReplacement(JSContext *cx, JSString *textstr, JSString *repstr, * newstring = string[:matchStart] + dollarSub(replaceValue) + string[matchLimit:] */ static inline bool -BuildDollarReplacement(JSContext *cx, JSString *textstr, JSString *repstr, +BuildDollarReplacement(JSContext *cx, JSString *textstrArg, JSLinearString *repstr, const jschar *firstDollar, const FlatMatch &fm, Value *vp) { + JSLinearString *textstr = textstrArg->ensureLinear(cx); + if (!textstr) + return NULL; + JS_ASSERT(repstr->chars() <= firstDollar && firstDollar < repstr->chars() + repstr->length()); size_t matchStart = fm.match(); size_t matchLimit = matchStart + fm.patternLength(); - JSCharBuffer newReplaceChars(cx); /* * Most probably: @@ -2323,6 +2266,7 @@ BuildDollarReplacement(JSContext *cx, JSString *textstr, JSString *repstr, * * Note that dollar vars _could_ make the resulting text smaller than this. */ + JSCharBuffer newReplaceChars(cx); if (!newReplaceChars.reserve(textstr->length() - fm.patternLength() + repstr->length())) return false; @@ -2371,13 +2315,13 @@ BuildDollarReplacement(JSContext *cx, JSString *textstr, JSString *repstr, textstr->length() - matchLimit); ENSURE(rightSide); - JSRopeBuilder builder(cx); + RopeBuilder builder(cx); ENSURE(builder.append(leftSide) && builder.append(newReplace) && builder.append(rightSide)); #undef ENSURE - vp->setString(builder.getStr()); + vp->setString(builder.result()); return true; } @@ -2393,7 +2337,7 @@ str_replace_regexp(JSContext *cx, uintN argc, Value *vp, ReplaceData &rdata) rdata.calledBack = false; RegExpStatics *res = cx->regExpStatics(); - if (!DoMatch(cx, res, vp, rdata.str, *rep, ReplaceCallback, &rdata, REPLACE_ARGS)) + if (!DoMatch(cx, res, vp, rdata.str, *rep, ReplaceRegExpCallback, &rdata, REPLACE_ARGS)) return false; if (!rdata.calledBack) { @@ -2457,14 +2401,14 @@ str_replace_flat_lambda(JSContext *cx, uintN argc, Value *vp, ReplaceData &rdata if (!rightSide) return false; - JSRopeBuilder builder(cx); + RopeBuilder builder(cx); if (!(builder.append(leftSide) && builder.append(repstr) && builder.append(rightSide))) { return false; } - vp->setString(builder.getStr()); + vp->setString(builder.result()); return true; } @@ -2543,8 +2487,10 @@ js::str_replace(JSContext *cx, uintN argc, Value *vp) * |RegExp| statics. */ - const FlatMatch *fm = rdata.g.tryFlatMatch(rdata.str, optarg, argc, false); + const FlatMatch *fm = rdata.g.tryFlatMatch(cx, rdata.str, optarg, argc, false); if (!fm) { + if (cx->throwing) /* oom in RopeMatch in tryFlatMatch */ + return false; JS_ASSERT_IF(!rdata.g.hasRegExpPair(), argc > optarg); return str_replace_regexp(cx, argc, vp, rdata); } @@ -2580,10 +2526,6 @@ static jsint find_split(JSContext *cx, RegExpStatics *res, JSString *str, js::RegExp *re, jsint *ip, JSSubString *sep) { - jsint i; - size_t length; - jschar *chars; - /* * Stop if past end of string. If at end of string, we will compare the * null char stored there (by js_NewString*) to sep->chars[j] in the while @@ -2595,12 +2537,14 @@ find_split(JSContext *cx, RegExpStatics *res, JSString *str, js::RegExp *re, jsi * However, we ape Perl and do this only if there is a sufficiently large * limit argument (see str_split). */ - i = *ip; - length = str->length(); + jsint i = *ip; + size_t length = str->length(); if ((size_t)i > length) return -1; - chars = str->chars(); + const jschar *chars = str->getChars(cx); + if (!chars) + return -2; /* * Match a regular expression against the separator at or above index i. @@ -2696,18 +2640,21 @@ str_split(JSContext *cx, uintN argc, Value *vp) sep->chars = NULL; sep->length = 0; } else { - JSString *str2 = js_ValueToString(cx, vp[2]); - if (!str2) + JSString *sepstr = js_ValueToString(cx, vp[2]); + if (!sepstr) return false; - vp[2].setString(str2); + vp[2].setString(sepstr); /* - * Point sep at a local copy of str2's header because find_split + * Point sep at a local copy of sepstr's header because find_split * will modify sep->length. */ - str2->getCharsAndLength(tmp.chars, tmp.length); - sep = &tmp; + tmp.length = sepstr->length(); + tmp.chars = sepstr->getChars(cx); + if (!tmp.chars) + return false; re = NULL; + sep = &tmp; } /* Use the second argument as the split limit, if given. */ @@ -2774,44 +2721,48 @@ static JSBool str_substr(JSContext *cx, uintN argc, Value *vp) { JSString *str; - jsdouble d; - jsdouble length, begin, end; + int32 length, len, begin; NORMALIZE_THIS(cx, vp, str); - if (argc != 0) { - if (!ValueToNumber(cx, vp[2], &d)) - return JS_FALSE; - length = str->length(); - begin = js_DoubleToInteger(d); + + if (argc > 0) { + length = int32(str->length()); + if (!ValueToIntegerRange(cx, vp[2], &begin)) + return false; + + if (begin >= length) { + str = cx->runtime->emptyString; + goto out; + } if (begin < 0) { - begin += length; + begin += length; /* length + INT_MIN will always be less then 0 */ if (begin < 0) begin = 0; - } else if (begin > length) { - begin = length; } if (argc == 1 || vp[3].isUndefined()) { - end = length; + len = length - begin; } else { - if (!ValueToNumber(cx, vp[3], &d)) - return JS_FALSE; - end = js_DoubleToInteger(d); - if (end < 0) - end = 0; - end += begin; - if (end > length) - end = length; + if (!ValueToIntegerRange(cx, vp[3], &len)) + return false; + + if (len <= 0) { + str = cx->runtime->emptyString; + goto out; + } + + if (uint32(length) < uint32(begin + len)) + len = length - begin; } - str = js_NewDependentString(cx, str, - (size_t)begin, - (size_t)(end - begin)); + str = js_NewDependentString(cx, str, size_t(begin), size_t(len)); if (!str) - return JS_FALSE; + return false; } + +out: vp->setString(str); - return JS_TRUE; + return true; } #endif /* JS_HAS_PERL_SUBSTR */ @@ -2919,42 +2870,41 @@ str_slice(JSContext *cx, uintN argc, Value *vp) /* * HTML composition aids. */ -static JSBool -tagify(JSContext *cx, const char *begin, JSString *param, const char *end, +static bool +tagify(JSContext *cx, const char *begin, JSLinearString *param, const char *end, Value *vp) { - JSString *str; - jschar *tagbuf; - size_t beglen, endlen, parlen, taglen; - size_t i, j; - - NORMALIZE_THIS(cx, vp, str); + JSString *thisstr; + NORMALIZE_THIS(cx, vp, thisstr); + JSLinearString *str = thisstr->ensureLinear(cx); + if (!str) + return false; if (!end) end = begin; - beglen = strlen(begin); - taglen = 1 + beglen + 1; /* '' */ - parlen = 0; /* Avoid warning. */ + size_t beglen = strlen(begin); + size_t taglen = 1 + beglen + 1; /* '' */ + size_t parlen = 0; /* Avoid warning. */ if (param) { parlen = param->length(); taglen += 2 + parlen + 1; /* '="param"' */ } - endlen = strlen(end); - taglen += str->length() + 2 + endlen + 1; /* 'str' */ + size_t endlen = strlen(end); + taglen += str->length() + 2 + endlen + 1; /* 'str' */ if (taglen >= ~(size_t)0 / sizeof(jschar)) { js_ReportAllocationOverflow(cx); - return JS_FALSE; + return false; } - tagbuf = (jschar *) cx->malloc((taglen + 1) * sizeof(jschar)); + jschar *tagbuf = (jschar *) cx->malloc((taglen + 1) * sizeof(jschar)); if (!tagbuf) - return JS_FALSE; + return false; - j = 0; + size_t j = 0; tagbuf[j++] = '<'; - for (i = 0; i < beglen; i++) + for (size_t i = 0; i < beglen; i++) tagbuf[j++] = (jschar)begin[i]; if (param) { tagbuf[j++] = '='; @@ -2964,32 +2914,31 @@ tagify(JSContext *cx, const char *begin, JSString *param, const char *end, tagbuf[j++] = '"'; } tagbuf[j++] = '>'; + js_strncpy(&tagbuf[j], str->chars(), str->length()); j += str->length(); tagbuf[j++] = '<'; tagbuf[j++] = '/'; - for (i = 0; i < endlen; i++) + for (size_t i = 0; i < endlen; i++) tagbuf[j++] = (jschar)end[i]; tagbuf[j++] = '>'; JS_ASSERT(j == taglen); tagbuf[j] = 0; - str = js_NewString(cx, tagbuf, taglen); - if (!str) { + JSString *retstr = js_NewString(cx, tagbuf, taglen); + if (!retstr) { js_free((char *)tagbuf); - return JS_FALSE; + return false; } - vp->setString(str); - return JS_TRUE; + vp->setString(retstr); + return true; } static JSBool tagify_value(JSContext *cx, uintN argc, Value *vp, const char *begin, const char *end) { - JSString *param; - - param = ArgToRootedString(cx, argc, vp, 0); + JSLinearString *param = ArgToRootedString(cx, argc, vp, 0); if (!param) return JS_FALSE; return tagify(cx, begin, param, end, vp); @@ -3088,60 +3037,58 @@ JS_DEFINE_TRCINFO_1(str_concat, (3, (extern, STRING_RETRY, js_ConcatStrings, CONTEXT, THIS_STRING, STRING, 1, nanojit::ACCSET_NONE))) -static const uint16 GENERIC_PRIMITIVE = JSFUN_GENERIC_NATIVE | JSFUN_PRIMITIVE_THIS; - static JSFunctionSpec string_methods[] = { #if JS_HAS_TOSOURCE - JS_FN("quote", str_quote, 0,GENERIC_PRIMITIVE), - JS_FN(js_toSource_str, str_toSource, 0,JSFUN_PRIMITIVE_THIS), + JS_FN("quote", str_quote, 0,JSFUN_GENERIC_NATIVE), + JS_FN(js_toSource_str, str_toSource, 0,0), #endif /* Java-like methods. */ - JS_FN(js_toString_str, js_str_toString, 0,JSFUN_PRIMITIVE_THIS), - JS_FN(js_valueOf_str, js_str_toString, 0,JSFUN_PRIMITIVE_THIS), - JS_FN(js_toJSON_str, js_str_toString, 0,JSFUN_PRIMITIVE_THIS), - JS_FN("substring", str_substring, 2,GENERIC_PRIMITIVE), - JS_FN("toLowerCase", str_toLowerCase, 0,GENERIC_PRIMITIVE), - JS_FN("toUpperCase", str_toUpperCase, 0,GENERIC_PRIMITIVE), - JS_FN("charAt", js_str_charAt, 1,GENERIC_PRIMITIVE), - JS_FN("charCodeAt", js_str_charCodeAt, 1,GENERIC_PRIMITIVE), - JS_FN("indexOf", str_indexOf, 1,GENERIC_PRIMITIVE), - JS_FN("lastIndexOf", str_lastIndexOf, 1,GENERIC_PRIMITIVE), - JS_FN("trim", str_trim, 0,GENERIC_PRIMITIVE), - JS_FN("trimLeft", str_trimLeft, 0,GENERIC_PRIMITIVE), - JS_FN("trimRight", str_trimRight, 0,GENERIC_PRIMITIVE), - JS_FN("toLocaleLowerCase", str_toLocaleLowerCase, 0,GENERIC_PRIMITIVE), - JS_FN("toLocaleUpperCase", str_toLocaleUpperCase, 0,GENERIC_PRIMITIVE), - JS_FN("localeCompare", str_localeCompare, 1,GENERIC_PRIMITIVE), + JS_FN(js_toString_str, js_str_toString, 0,0), + JS_FN(js_valueOf_str, js_str_toString, 0,0), + JS_FN(js_toJSON_str, js_str_toString, 0,0), + JS_FN("substring", str_substring, 2,JSFUN_GENERIC_NATIVE), + JS_FN("toLowerCase", str_toLowerCase, 0,JSFUN_GENERIC_NATIVE), + JS_FN("toUpperCase", str_toUpperCase, 0,JSFUN_GENERIC_NATIVE), + JS_FN("charAt", js_str_charAt, 1,JSFUN_GENERIC_NATIVE), + JS_FN("charCodeAt", js_str_charCodeAt, 1,JSFUN_GENERIC_NATIVE), + JS_FN("indexOf", str_indexOf, 1,JSFUN_GENERIC_NATIVE), + JS_FN("lastIndexOf", str_lastIndexOf, 1,JSFUN_GENERIC_NATIVE), + JS_FN("trim", str_trim, 0,JSFUN_GENERIC_NATIVE), + JS_FN("trimLeft", str_trimLeft, 0,JSFUN_GENERIC_NATIVE), + JS_FN("trimRight", str_trimRight, 0,JSFUN_GENERIC_NATIVE), + JS_FN("toLocaleLowerCase", str_toLocaleLowerCase, 0,JSFUN_GENERIC_NATIVE), + JS_FN("toLocaleUpperCase", str_toLocaleUpperCase, 0,JSFUN_GENERIC_NATIVE), + JS_FN("localeCompare", str_localeCompare, 1,JSFUN_GENERIC_NATIVE), /* Perl-ish methods (search is actually Python-esque). */ - JS_FN("match", str_match, 1,GENERIC_PRIMITIVE), - JS_FN("search", str_search, 1,GENERIC_PRIMITIVE), - JS_FN("replace", str_replace, 2,GENERIC_PRIMITIVE), - JS_FN("split", str_split, 2,GENERIC_PRIMITIVE), + JS_FN("match", str_match, 1,JSFUN_GENERIC_NATIVE), + JS_FN("search", str_search, 1,JSFUN_GENERIC_NATIVE), + JS_FN("replace", str_replace, 2,JSFUN_GENERIC_NATIVE), + JS_FN("split", str_split, 2,JSFUN_GENERIC_NATIVE), #if JS_HAS_PERL_SUBSTR - JS_FN("substr", str_substr, 2,GENERIC_PRIMITIVE), + JS_FN("substr", str_substr, 2,JSFUN_GENERIC_NATIVE), #endif /* Python-esque sequence methods. */ - JS_TN("concat", str_concat, 1,GENERIC_PRIMITIVE, &str_concat_trcinfo), - JS_FN("slice", str_slice, 2,GENERIC_PRIMITIVE), + JS_TN("concat", str_concat, 1,JSFUN_GENERIC_NATIVE, &str_concat_trcinfo), + JS_FN("slice", str_slice, 2,JSFUN_GENERIC_NATIVE), /* HTML string methods. */ #if JS_HAS_STR_HTML_HELPERS - JS_FN("bold", str_bold, 0,JSFUN_PRIMITIVE_THIS), - JS_FN("italics", str_italics, 0,JSFUN_PRIMITIVE_THIS), - JS_FN("fixed", str_fixed, 0,JSFUN_PRIMITIVE_THIS), - JS_FN("fontsize", str_fontsize, 1,JSFUN_PRIMITIVE_THIS), - JS_FN("fontcolor", str_fontcolor, 1,JSFUN_PRIMITIVE_THIS), - JS_FN("link", str_link, 1,JSFUN_PRIMITIVE_THIS), - JS_FN("anchor", str_anchor, 1,JSFUN_PRIMITIVE_THIS), - JS_FN("strike", str_strike, 0,JSFUN_PRIMITIVE_THIS), - JS_FN("small", str_small, 0,JSFUN_PRIMITIVE_THIS), - JS_FN("big", str_big, 0,JSFUN_PRIMITIVE_THIS), - JS_FN("blink", str_blink, 0,JSFUN_PRIMITIVE_THIS), - JS_FN("sup", str_sup, 0,JSFUN_PRIMITIVE_THIS), - JS_FN("sub", str_sub, 0,JSFUN_PRIMITIVE_THIS), + JS_FN("bold", str_bold, 0,0), + JS_FN("italics", str_italics, 0,0), + JS_FN("fixed", str_fixed, 0,0), + JS_FN("fontsize", str_fontsize, 1,0), + JS_FN("fontcolor", str_fontcolor, 1,0), + JS_FN("link", str_link, 1,0), + JS_FN("anchor", str_anchor, 1,0), + JS_FN("strike", str_strike, 0,0), + JS_FN("small", str_small, 0,0), + JS_FN("big", str_big, 0,0), + JS_FN("blink", str_blink, 0,0), + JS_FN("sup", str_sup, 0,0), + JS_FN("sub", str_sub, 0,0), #endif JS_FS_END @@ -3164,14 +3111,17 @@ static JSFunctionSpec string_methods[] = { #define R3(n) R2(n), R2((n) + (1 << 2)) #define R7(n) R6(n), R6((n) + (1 << 6)) +#define BUILD_LENGTH_AND_FLAGS(length, flags) \ + (((length) << JSString::LENGTH_SHIFT) | (flags)) + /* * Declare unit strings. Pack the string data itself into the mInlineChars * place in the header. */ #define R(c) { \ - JSString::FLAT | JSString::ATOMIZED | (1 << JSString::FLAGS_LENGTH_SHIFT),\ + BUILD_LENGTH_AND_FLAGS(1, JSString::FLAT | JSString::ATOMIZED), \ { (jschar *)(((char *)(unitStringTable + (c))) + \ - offsetof(JSString, mInlineStorage)) }, \ + offsetof(JSString, inlineStorage)) }, \ { {(c), 0x00} } } #ifdef __SUNPRO_CC @@ -3229,9 +3179,9 @@ const jschar JSString::fromSmallChar[] = { R6(0) }; * second character. */ #define R(c) { \ - JSString::FLAT | JSString::ATOMIZED | (2 << JSString::FLAGS_LENGTH_SHIFT),\ + BUILD_LENGTH_AND_FLAGS(2, JSString::FLAT | JSString::ATOMIZED), \ { (jschar *)(((char *)(length2StringTable + (c))) + \ - offsetof(JSString, mInlineStorage)) }, \ + offsetof(JSString, inlineStorage)) }, \ { {FROM_SMALL_CHAR((c) >> 6), FROM_SMALL_CHAR((c) & 0x3F), 0x00} } } #ifdef __SUNPRO_CC @@ -3262,9 +3212,9 @@ __attribute__ ((aligned (8))) * correct location of the int string. */ #define R(c) { \ - JSString::FLAT | JSString::ATOMIZED | (3 << JSString::FLAGS_LENGTH_SHIFT),\ + BUILD_LENGTH_AND_FLAGS(3, JSString::FLAT | JSString::ATOMIZED), \ { (jschar *)(((char *)(hundredStringTable + ((c) - 100))) + \ - offsetof(JSString, mInlineStorage)) }, \ + offsetof(JSString, inlineStorage)) }, \ { {((c) / 100) + '0', ((c) / 10 % 10) + '0', ((c) % 10) + '0', 0x00} } } @@ -3429,7 +3379,7 @@ js_InitStringClass(JSContext *cx, JSObject *obj) return proto; } -JSString * +JSFlatString * js_NewString(JSContext *cx, jschar *chars, size_t length) { JSString *str; @@ -3463,10 +3413,10 @@ js_NewString(JSContext *cx, jschar *chars, size_t length) rt->lengthSquaredSum += (double)length * (double)length)); } #endif - return str; + return str->assertIsFlat(); } -static JS_ALWAYS_INLINE JSString * +static JS_ALWAYS_INLINE JSFlatString * NewShortString(JSContext *cx, const jschar *chars, size_t length) { JS_ASSERT(JSShortString::fitsIntoShortString(length)); @@ -3476,10 +3426,10 @@ NewShortString(JSContext *cx, const jschar *chars, size_t length) jschar *storage = str->init(length); js_short_strncpy(storage, chars, length); storage[length] = 0; - return str->header(); + return str->header()->assertIsFlat(); } -static JSString * +static JSFlatString * NewShortString(JSContext *cx, const char *chars, size_t length) { JS_ASSERT(JSShortString::fitsIntoShortString(length)); @@ -3501,15 +3451,15 @@ NewShortString(JSContext *cx, const char *chars, size_t length) size_t n = length; jschar *p = storage; while (n--) - *p++ = jschar(*chars++); + *p++ = (unsigned char)*chars++; *p = 0; } - return str->header(); + return str->header()->assertIsFlat(); } static const size_t sMinWasteSize = 16; -JSString * +JSFlatString * js_NewStringFromCharBuffer(JSContext *cx, JSCharBuffer &cb) { if (cb.empty()) @@ -3542,14 +3492,14 @@ js_NewStringFromCharBuffer(JSContext *cx, JSCharBuffer &cb) buf = tmp; } - JSString *str = js_NewString(cx, buf, length); + JSFlatString *str = js_NewString(cx, buf, length); if (!str) cx->free(buf); return str; } -JSString * -js_NewDependentString(JSContext *cx, JSString *base, size_t start, +JSLinearString * +js_NewDependentString(JSContext *cx, JSString *baseArg, size_t start, size_t length) { JSString *ds; @@ -3557,12 +3507,16 @@ js_NewDependentString(JSContext *cx, JSString *base, size_t start, if (length == 0) return cx->runtime->emptyString; + JSLinearString *base = baseArg->ensureLinear(cx); + if (!base) + return NULL; + if (start == 0 && length == base->length()) return base; - jschar *chars = base->chars() + start; + const jschar *chars = base->chars() + start; - JSString *staticStr = JSString::lookupStaticString(chars, length); + JSLinearString *staticStr = JSString::lookupStaticString(chars, length); if (staticStr) return staticStr; @@ -3591,7 +3545,7 @@ js_NewDependentString(JSContext *cx, JSString *base, size_t start, rt->lengthSquaredSum += (double)length * (double)length)); } #endif - return ds; + return ds->assertIsLinear(); } #ifdef DEBUG @@ -3615,58 +3569,57 @@ void printJSStringStats(JSRuntime *rt) } #endif -JSString * +JSFlatString * 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)); + jschar *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); + JSFlatString *str = js_NewString(cx, news, n); if (!str) cx->free(news); return str; } -JSString * +JSFlatString * 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); + + jschar *chars = js_InflateString(cx, s, &n); + if (!chars) + return NULL; + JSFlatString *str = js_NewString(cx, chars, n); + if (!str) + cx->free(chars); + return str; } -JSString * +JSFlatString * js_NewStringCopyZ(JSContext *cx, const jschar *s) { - size_t n, m; - jschar *news; - JSString *str; - - n = js_strlen(s); - + size_t n = js_strlen(s); if (JSShortString::fitsIntoShortString(n)) return NewShortString(cx, s, n); - m = (n + 1) * sizeof(jschar); - news = (jschar *) cx->malloc(m); + size_t m = (n + 1) * sizeof(jschar); + jschar *news = (jschar *) cx->malloc(m); if (!news) return NULL; memcpy(news, s, m); - str = js_NewString(cx, news, n); + JSFlatString *str = js_NewString(cx, news, n); if (!str) cx->free(news); return str; } -JSString * +JSFlatString * js_NewStringCopyZ(JSContext *cx, const char *s) { return js_NewStringCopyN(cx, s, strlen(s)); @@ -3713,11 +3666,7 @@ js_ValueToString(JSContext *cx, const Value &arg) static inline JSBool AppendAtom(JSAtom *atom, JSCharBuffer &cb) { - JSString *str = ATOM_TO_STRING(atom); - const jschar *chars; - size_t length; - str->getCharsAndLength(chars, length); - return cb.append(chars, length); + return cb.append(atom->chars(), atom->length()); } /* This function implements E-262-3 section 9.8, toString. */ @@ -3726,12 +3675,14 @@ js_ValueToCharBuffer(JSContext *cx, const Value &arg, JSCharBuffer &cb) { Value v = arg; if (v.isObject() && !DefaultValue(cx, &v.toObject(), JSTYPE_STRING, &v)) - return JS_FALSE; + return false; if (v.isString()) { - const jschar *chars; - size_t length; - v.toString()->getCharsAndLength(chars, length); + JSString *str = v.toString(); + size_t length = str->length(); + const jschar *chars = str->getChars(cx); + if (!chars) + return false; return cb.append(chars, length); } if (v.isNumber()) @@ -3769,86 +3720,142 @@ js_ValueToSource(JSContext *cx, const Value &v) return js_ValueToString(cx, tvr.value()); } -/* - * str is not necessarily a GC thing here. - */ -uint32 -js_HashString(JSString *str) -{ - const jschar *s; - size_t n; - uint32 h; - - str->getCharsAndLength(s, n); - for (h = 0; n; s++, n--) - h = JS_ROTATE_LEFT32(h, 4) ^ *s; - return h; -} +namespace js { /* * str is not necessarily a GC thing here. */ -JSBool JS_FASTCALL -js_EqualStrings(JSString *str1, JSString *str2) +static JS_ALWAYS_INLINE bool +EqualStringsTail(JSLinearString *str1, size_t length1, JSLinearString *str2) { - size_t n; - const jschar *s1, *s2; - - JS_ASSERT(str1); - JS_ASSERT(str2); - - /* Fast case: pointer equality could be a quick win. */ - if (str1 == str2) - return JS_TRUE; - - n = str1->length(); - if (n != str2->length()) - return JS_FALSE; - - if (n == 0) - return JS_TRUE; - - s1 = str1->chars(), s2 = str2->chars(); + const jschar *s1 = str1->chars(); + const jschar *s1end = s1 + length1; + const jschar *s2 = str2->chars(); do { - if (*s1 != *s2) - return JS_FALSE; + if (*s1 != *s2) { + return false; + } ++s1, ++s2; - } while (--n != 0); + } while (s1 != s1end); - return JS_TRUE; + return true; } -JS_DEFINE_CALLINFO_2(extern, BOOL, js_EqualStrings, STRING, STRING, 1, nanojit::ACCSET_NONE) -int32 JS_FASTCALL -js_CompareStrings(JSString *str1, JSString *str2) +bool +EqualStrings(JSContext *cx, JSString *str1, JSString *str2, JSBool *result) { - size_t l1, l2, n, i; - const jschar *s1, *s2; - intN cmp; - - JS_ASSERT(str1); - JS_ASSERT(str2); - - /* Fast case: pointer equality could be a quick win. */ - if (str1 == str2) - return 0; - - str1->getCharsAndLength(s1, l1); - str2->getCharsAndLength(s2, l2); - n = JS_MIN(l1, l2); - for (i = 0; i < n; i++) { - cmp = s1[i] - s2[i]; - if (cmp != 0) - return cmp; + if (str1 == str2) { + *result = true; + return true; } - return (intN)(l1 - l2); + + size_t length1 = str1->length(); + if (length1 != str2->length()) { + *result = false; + return true; + } + + if (length1 == 0) { + *result = true; + return true; + } + + JSLinearString *linear1 = str1->ensureLinear(cx); + if (!linear1) + return false; + JSLinearString *linear2 = str2->ensureLinear(cx); + if (!linear2) + return false; + + *result = EqualStringsTail(linear1, length1, linear2); + return true; } -JS_DEFINE_CALLINFO_2(extern, INT32, js_CompareStrings, STRING, STRING, 1, nanojit::ACCSET_NONE) + +bool +EqualStrings(JSLinearString *str1, JSLinearString *str2) +{ + if (str1 == str2) + return true; + + size_t length1 = str1->length(); + if (length1 != str2->length()) + return false; + + if (length1 == 0) + return true; + + return EqualStringsTail(str1, length1, str2); +} + +} /* namespace js */ + +JSBool JS_FASTCALL +js_EqualStringsOnTrace(JSContext *cx, JSString *str1, JSString *str2) +{ + JSBool result; + return EqualStrings(cx, str1, str2, &result) ? result : JS_NEITHER; +} +JS_DEFINE_CALLINFO_3(extern, BOOL, js_EqualStringsOnTrace, + CONTEXT, STRING, STRING, 1, nanojit::ACCSET_NONE) namespace js { -JSBool -MatchStringAndAscii(JSString *str, const char *asciiBytes) +static bool +CompareStringsImpl(JSContext *cx, JSString *str1, JSString *str2, int32 *result) +{ + JS_ASSERT(str1); + JS_ASSERT(str2); + + if (str1 == str2) { + *result = 0; + return true; + } + + size_t l1 = str1->length(); + const jschar *s1 = str1->getChars(cx); + if (!s1) + return false; + + size_t l2 = str2->length(); + const jschar *s2 = str2->getChars(cx); + if (!s2) + return false; + + size_t n = JS_MIN(l1, l2); + for (size_t i = 0; i < n; i++) { + if (int32 cmp = s1[i] - s2[i]) { + *result = cmp; + return true; + } + } + *result = (int32)(l1 - l2); + return true; +} + +bool +CompareStrings(JSContext *cx, JSString *str1, JSString *str2, int32 *result) +{ + return CompareStringsImpl(cx, str1, str2, result); +} + +} /* namespace js */ + +int32 JS_FASTCALL +js_CompareStringsOnTrace(JSContext *cx, JSString *str1, JSString *str2) +{ + int32 result; + if (!CompareStringsImpl(cx, str1, str2, &result)) + return INT32_MIN; + JS_ASSERT(result != INT32_MIN); + return result; +} +JS_DEFINE_CALLINFO_3(extern, INT32, js_CompareStringsOnTrace, + CONTEXT, STRING, STRING, 1, nanojit::ACCSET_NONE) + +namespace js { + +bool +StringEqualsAscii(JSLinearString *str, const char *asciiBytes) { size_t length = strlen(asciiBytes); #ifdef DEBUG @@ -3867,8 +3874,6 @@ MatchStringAndAscii(JSString *str, const char *asciiBytes) } /* namespacejs */ - - size_t js_strlen(const jschar *s) { @@ -5574,26 +5579,24 @@ static JSBool Encode(JSContext *cx, JSString *str, const jschar *unescapedSet, const jschar *unescapedSet2, Value *rval) { - size_t length, j, k, L; - JSCharBuffer cb(cx); - const jschar *chars; - jschar c, c2; - uint32 v; - uint8 utf8buf[4]; - jschar hexBuf[4]; static const char HexDigits[] = "0123456789ABCDEF"; /* NB: uppercase */ - str->getCharsAndLength(chars, length); + size_t length = str->length(); + const jschar *chars = str->getChars(cx); + if (!chars) + return JS_FALSE; + if (length == 0) { rval->setString(cx->runtime->emptyString); return JS_TRUE; } - /* From this point the control must goto bad on failures. */ + JSCharBuffer cb(cx); + jschar hexBuf[4]; hexBuf[0] = '%'; hexBuf[3] = 0; - for (k = 0; k < length; k++) { - c = chars[k]; + for (size_t k = 0; k < length; k++) { + jschar c = chars[k]; if (js_strchr(unescapedSet, c) || (unescapedSet2 && js_strchr(unescapedSet2, c))) { if (!cb.append(c)) @@ -5604,6 +5607,7 @@ Encode(JSContext *cx, JSString *str, const jschar *unescapedSet, JSMSG_BAD_URI, NULL); return JS_FALSE; } + uint32 v; if (c < 0xD800 || c > 0xDBFF) { v = c; } else { @@ -5613,7 +5617,7 @@ Encode(JSContext *cx, JSString *str, const jschar *unescapedSet, JSMSG_BAD_URI, NULL); return JS_FALSE; } - c2 = chars[k]; + jschar c2 = chars[k]; if ((c2 < 0xDC00) || (c2 > 0xDFFF)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_URI, NULL); @@ -5621,8 +5625,9 @@ Encode(JSContext *cx, JSString *str, const jschar *unescapedSet, } v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000; } - L = js_OneUcs4ToUtf8Char(utf8buf, v); - for (j = 0; j < L; j++) { + uint8 utf8buf[4]; + size_t L = js_OneUcs4ToUtf8Char(utf8buf, v); + for (size_t j = 0; j < L; j++) { hexBuf[1] = HexDigits[utf8buf[j] >> 4]; hexBuf[2] = HexDigits[utf8buf[j] & 0xf]; if (!cb.append(hexBuf, 3)) @@ -5637,44 +5642,40 @@ Encode(JSContext *cx, JSString *str, const jschar *unescapedSet, static JSBool Decode(JSContext *cx, JSString *str, const jschar *reservedSet, Value *rval) { - size_t length, start, k; - JSCharBuffer cb(cx); - const jschar *chars; - jschar c, H; - uint32 v; - jsuint B; - uint8 octets[4]; - intN j, n; + size_t length = str->length(); + const jschar *chars = str->getChars(cx); + if (!chars) + return JS_FALSE; - str->getCharsAndLength(chars, length); if (length == 0) { rval->setString(cx->runtime->emptyString); return JS_TRUE; } - /* From this point the control must goto bad on failures. */ - for (k = 0; k < length; k++) { - c = chars[k]; + JSCharBuffer cb(cx); + for (size_t k = 0; k < length; k++) { + jschar c = chars[k]; if (c == '%') { - start = k; + size_t start = k; if ((k + 2) >= length) goto report_bad_uri; if (!JS7_ISHEX(chars[k+1]) || !JS7_ISHEX(chars[k+2])) goto report_bad_uri; - B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]); + jsuint B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]); k += 2; if (!(B & 0x80)) { c = (jschar)B; } else { - n = 1; + intN n = 1; while (B & (0x80 >> n)) n++; if (n == 1 || n > 4) goto report_bad_uri; + uint8 octets[4]; octets[0] = (uint8)B; if (k + 3 * (n - 1) >= length) goto report_bad_uri; - for (j = 1; j < n; j++) { + for (intN j = 1; j < n; j++) { k++; if (chars[k] != '%') goto report_bad_uri; @@ -5686,13 +5687,13 @@ Decode(JSContext *cx, JSString *str, const jschar *reservedSet, Value *rval) k += 2; octets[j] = (char)B; } - v = Utf8ToOneUcs4Char(octets, n); + uint32 v = Utf8ToOneUcs4Char(octets, n); if (v >= 0x10000) { v -= 0x10000; if (v > 0xFFFFF) goto report_bad_uri; c = (jschar)((v & 0x3FF) + 0xDC00); - H = (jschar)((v >> 10) + 0xD800); + jschar H = (jschar)((v >> 10) + 0xD800); if (!cb.append(H)) return JS_FALSE; } else { @@ -5724,9 +5725,7 @@ Decode(JSContext *cx, JSString *str, const jschar *reservedSet, Value *rval) static JSBool str_decodeURI(JSContext *cx, uintN argc, Value *vp) { - JSString *str; - - str = ArgToRootedString(cx, argc, vp, 0); + JSLinearString *str = ArgToRootedString(cx, argc, vp, 0); if (!str) return JS_FALSE; return Decode(cx, str, js_uriReservedPlusPound_ucstr, vp); @@ -5735,9 +5734,7 @@ str_decodeURI(JSContext *cx, uintN argc, Value *vp) static JSBool str_decodeURI_Component(JSContext *cx, uintN argc, Value *vp) { - JSString *str; - - str = ArgToRootedString(cx, argc, vp, 0); + JSLinearString *str = ArgToRootedString(cx, argc, vp, 0); if (!str) return JS_FALSE; return Decode(cx, str, js_empty_ucstr, vp); @@ -5746,9 +5743,7 @@ str_decodeURI_Component(JSContext *cx, uintN argc, Value *vp) static JSBool str_encodeURI(JSContext *cx, uintN argc, Value *vp) { - JSString *str; - - str = ArgToRootedString(cx, argc, vp, 0); + JSLinearString *str = ArgToRootedString(cx, argc, vp, 0); if (!str) return JS_FALSE; return Encode(cx, str, js_uriReservedPlusPound_ucstr, js_uriUnescaped_ucstr, @@ -5758,9 +5753,7 @@ str_encodeURI(JSContext *cx, uintN argc, Value *vp) static JSBool str_encodeURI_Component(JSContext *cx, uintN argc, Value *vp) { - JSString *str; - - str = ArgToRootedString(cx, argc, vp, 0); + JSLinearString *str = ArgToRootedString(cx, argc, vp, 0); if (!str) return JS_FALSE; return Encode(cx, str, js_uriUnescaped_ucstr, NULL, vp); @@ -5836,13 +5829,8 @@ Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length) namespace js { size_t -PutEscapedStringImpl(char *buffer, size_t bufferSize, FILE *fp, JSString *str, uint32 quote) +PutEscapedStringImpl(char *buffer, size_t bufferSize, FILE *fp, JSLinearString *str, uint32 quote) { - const jschar *chars, *charsEnd; - size_t n; - const char *escape; - char c; - uintN u, hex, shift; enum { STOP, FIRST_QUOTE, LAST_QUOTE, CHARS, ESCAPE_START, ESCAPE_MORE } state; @@ -5856,13 +5844,14 @@ PutEscapedStringImpl(char *buffer, size_t bufferSize, FILE *fp, JSString *str, u else bufferSize--; - str->getCharsAndEnd(chars, charsEnd); - n = 0; + const jschar *chars = str->chars(); + const jschar *charsEnd = chars + str->length(); + size_t n = 0; state = FIRST_QUOTE; - shift = 0; - hex = 0; - u = 0; - c = 0; /* to quell GCC warnings */ + uintN shift = 0; + uintN hex = 0; + uintN u = 0; + char c = 0; /* to quell GCC warnings */ for (;;) { switch (state) { @@ -5886,7 +5875,7 @@ PutEscapedStringImpl(char *buffer, size_t bufferSize, FILE *fp, JSString *str, u u = *chars++; if (u < ' ') { if (u != 0) { - escape = strchr(js_EscapeMap, (int)u); + const char *escape = strchr(js_EscapeMap, (int)u); if (escape) { u = escape[1]; goto do_escape; diff --git a/js/src/jsstr.h b/js/src/jsstr.h index 2eee36c6227..b4db7910024 100644 --- a/js/src/jsstr.h +++ b/js/src/jsstr.h @@ -57,9 +57,6 @@ #include "jsvalue.h" #include "jscell.h" -#define JSSTRING_BIT(n) ((size_t)1 << (n)) -#define JSSTRING_BITMASK(n) (JSSTRING_BIT(n) - 1) - enum { UNIT_STRING_LIMIT = 256U, SMALL_CHAR_LIMIT = 128U, /* Bigger chars cannot be in a length-2 string. */ @@ -86,6 +83,8 @@ namespace js { namespace mjit { class Compiler; }} +struct JSLinearString; + /* * The GC-thing "string" type. * @@ -108,98 +107,84 @@ namespace js { namespace mjit { * threads. * * When the string is DEPENDENT, the string depends on characters of another - * string strongly referenced by the mBase field. The base member may point to + * string strongly referenced by the base field. The base member may point to * another dependent string if chars() has not been called yet. * - * To optimize js_ConcatStrings and some other cases, we lazily concatenate - * strings when possible, creating concatenation trees, a.k.a. ropes. A string - * is an INTERIOR_NODE if it is a non-root, non-leaf node in a rope, and a - * string is a TOP_NODE if it is the root of a rope. In order to meet API - * requirements, chars() is not allowed to fail, so we build ropes so that they - * form a well-defined tree structure, and the top node of every rope contains - * an (almost) empty buffer that is large enough to contain the entire string. - * Whenever chars() is called on a rope, it traverses its tree and fills that - * buffer in, and when concatenating strings, we reuse these empty buffers - * whenever possible, so that we can build a string through concatenation in - * linear time, and have relatively few malloc calls when doing so. + * When a string is a ROPE, it represents the lazy concatenation of other + * strings. In general, the nodes reachable from any rope form a dag. * - * NB: Always use the length() and chars() accessor methods. + * To allow static type-based checking that a given JSString* always points + * to a flat or non-rope string, the JSFlatString and JSLinearString types may + * be used. Instead of casting, callers should use ensureX() and assertIsX(). */ -struct JSString { +struct JSString +{ friend class js::TraceRecorder; friend class js::mjit::Compiler; - friend JSAtom * - js_AtomizeString(JSContext *cx, JSString *str, uintN flags); - public: + 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! + * Not private because we want to be able to use static initializers for + * them. Don't use these directly! FIXME bug 614459. */ - size_t mLengthAndFlags; /* in all strings */ + size_t lengthAndFlags; /* in all strings */ union { - jschar *mChars; /* in flat and dependent strings */ - JSString *mLeft; /* in rope interior and top nodes */ - }; + const jschar *chars; /* in non-rope strings */ + JSString *left; /* in rope strings */ + } u; union { - /* - * We may keep more than 4 inline chars, but 4 is necessary for all of - * our static initialization. - */ - jschar mInlineStorage[4]; /* In short strings. */ + jschar inlineStorage[4]; /* in short strings */ struct { union { - size_t mCapacity; /* in extensible flat strings (optional) */ - JSString *mParent; /* in rope interior nodes */ - JSRopeBufferInfo *mBufferWithInfo; /* in rope top nodes */ + JSString *right; /* in rope strings */ + JSString *base; /* in dependent strings */ + size_t capacity; /* in extensible flat strings */ }; union { - JSString *mBase; /* in dependent strings */ - JSString *mRight; /* in rope interior and top nodes */ + JSString *parent; /* temporarily used during flatten */ + size_t reserved; /* may use for bug 615290 */ }; - } e; - uintN externalStringType; /* for external strings. */ + } s; + size_t externalStringType; /* in external strings */ }; /* - * The mLengthAndFlags field in string headers has data arranged in the + * The lengthAndFlags field in string headers has data arranged in the * following way: * * [ length (bits 4-31) ][ flags (bits 2-3) ][ type (bits 0-1) ] * - * The length is packed in mLengthAndFlags, even in string types that don't + * The length is packed in lengthAndFlags, even in string types that don't * need 3 other fields, to make the length check simpler. * * When the string type is FLAT, the flags can contain ATOMIZED or * EXTENSIBLE. - * - * When the string type is INTERIOR_NODE or TOP_NODE, the flags area is - * used to store the rope traversal count. */ - static const size_t FLAT = 0; - static const size_t DEPENDENT = 1; - static const size_t INTERIOR_NODE = 2; - static const size_t TOP_NODE = 3; + static const size_t TYPE_FLAGS_MASK = JS_BITMASK(4); + static const size_t LENGTH_SHIFT = 4; - /* Rope/non-rope can be checked by checking one bit. */ - static const size_t ROPE_BIT = JSSTRING_BIT(1); + static const size_t TYPE_MASK = JS_BITMASK(2); + static const size_t FLAT = 0x0; + static const size_t DEPENDENT = 0x1; + static const size_t ROPE = 0x2; - static const size_t ATOMIZED = JSSTRING_BIT(2); - static const size_t EXTENSIBLE = JSSTRING_BIT(3); + /* Allow checking 1 bit for dependent/rope strings. */ + static const size_t DEPENDENT_BIT = JS_BIT(0); + static const size_t ROPE_BIT = JS_BIT(1); - static const size_t FLAGS_LENGTH_SHIFT = 4; + static const size_t ATOMIZED = JS_BIT(2); + static const size_t EXTENSIBLE = JS_BIT(3); - static const size_t TYPE_MASK = JSSTRING_BITMASK(2); - static const size_t TYPE_FLAGS_MASK = JSSTRING_BITMASK(4); - inline bool hasFlag(size_t flag) const { - return (mLengthAndFlags & flag) != 0; + size_t buildLengthAndFlags(size_t length, size_t flags) { + return (length << LENGTH_SHIFT) | flags; } inline js::gc::Cell *asCell() { return reinterpret_cast(this); } - + inline js::gc::FreeCell *asFreeCell() { return reinterpret_cast(this); } @@ -210,73 +195,55 @@ struct JSString { */ static const size_t MAX_LENGTH = (1 << 28) - 1; - inline size_t type() const { - return mLengthAndFlags & TYPE_MASK; - } - JS_ALWAYS_INLINE bool isDependent() const { - return type() == DEPENDENT; + return lengthAndFlags & DEPENDENT_BIT; } JS_ALWAYS_INLINE bool isFlat() const { - return type() == FLAT; + return (lengthAndFlags & TYPE_MASK) == FLAT; } - inline bool isExtensible() const { - return isFlat() && hasFlag(EXTENSIBLE); - } - - inline bool isRope() const { - return hasFlag(ROPE_BIT); + JS_ALWAYS_INLINE bool isExtensible() const { + JS_ASSERT_IF(lengthAndFlags & EXTENSIBLE, isFlat()); + return lengthAndFlags & EXTENSIBLE; } JS_ALWAYS_INLINE bool isAtomized() const { - return isFlat() && hasFlag(ATOMIZED); + JS_ASSERT_IF(lengthAndFlags & ATOMIZED, isFlat()); + return lengthAndFlags & ATOMIZED; } - inline bool isInteriorNode() const { - return type() == INTERIOR_NODE; - } - - inline bool isTopNode() const { - return type() == TOP_NODE; - } - - JS_ALWAYS_INLINE jschar *chars() { - if (JS_UNLIKELY(isRope())) - flatten(); - return mChars; + JS_ALWAYS_INLINE bool isRope() const { + return lengthAndFlags & ROPE_BIT; } JS_ALWAYS_INLINE size_t length() const { - return mLengthAndFlags >> FLAGS_LENGTH_SHIFT; + return lengthAndFlags >> LENGTH_SHIFT; } JS_ALWAYS_INLINE bool empty() const { - return length() == 0; + return lengthAndFlags <= TYPE_FLAGS_MASK; } - JS_ALWAYS_INLINE void getCharsAndLength(const jschar *&chars, size_t &length) { - chars = this->chars(); - length = this->length(); + /* This can fail by returning null and reporting an error on cx. */ + JS_ALWAYS_INLINE const jschar *getChars(JSContext *cx) { + if (isRope()) + return flatten(cx); + return nonRopeChars(); } - 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; + /* This can fail by returning null and reporting an error on cx. */ + JS_ALWAYS_INLINE const jschar *getCharsZ(JSContext *cx) { + if (!isFlat()) + return undepend(cx); + return flatChars(); } JS_ALWAYS_INLINE void initFlatNotTerminated(jschar *chars, size_t length) { JS_ASSERT(length <= MAX_LENGTH); JS_ASSERT(!isStatic(this)); - e.mBase = NULL; - e.mCapacity = 0; - mLengthAndFlags = (length << FLAGS_LENGTH_SHIFT) | FLAT; - mChars = chars; + lengthAndFlags = buildLengthAndFlags(length, FLAT); + u.chars = chars; } /* Specific flat string initializer and accessor methods. */ @@ -285,26 +252,31 @@ struct JSString { JS_ASSERT(chars[length] == jschar(0)); } - JS_ALWAYS_INLINE void initShortString(jschar *chars, size_t length) { + JS_ALWAYS_INLINE void initShortString(const jschar *chars, size_t length) { JS_ASSERT(length <= MAX_LENGTH); + JS_ASSERT(chars >= inlineStorage && chars < (jschar *)(this + 2)); JS_ASSERT(!isStatic(this)); - mLengthAndFlags = (length << FLAGS_LENGTH_SHIFT) | FLAT; - mChars = chars; + lengthAndFlags = buildLengthAndFlags(length, FLAT); + u.chars = chars; } JS_ALWAYS_INLINE void initFlatExtensible(jschar *chars, size_t length, size_t cap) { JS_ASSERT(length <= MAX_LENGTH); JS_ASSERT(chars[length] == jschar(0)); JS_ASSERT(!isStatic(this)); - e.mBase = NULL; - e.mCapacity = cap; - mLengthAndFlags = (length << FLAGS_LENGTH_SHIFT) | FLAT | EXTENSIBLE; - mChars = chars; + lengthAndFlags = buildLengthAndFlags(length, FLAT | EXTENSIBLE); + u.chars = chars; + s.capacity = cap; } - JS_ALWAYS_INLINE jschar *flatChars() const { + JS_ALWAYS_INLINE JSFlatString *assertIsFlat() { JS_ASSERT(isFlat()); - return mChars; + return reinterpret_cast(this); + } + + JS_ALWAYS_INLINE const jschar *flatChars() const { + JS_ASSERT(isFlat()); + return u.chars; } JS_ALWAYS_INLINE size_t flatLength() const { @@ -312,81 +284,44 @@ struct JSString { return length(); } - JS_ALWAYS_INLINE size_t flatCapacity() const { - JS_ASSERT(isFlat()); - return e.mCapacity; - } - - /* - * Methods to manipulate ATOMIZED and EXTENSIBLE flags of flat strings. It is - * safe to use these without extra locking due to the following properties: - * - * * We do not have a flatClearAtomized method, as a string remains - * atomized until the GC collects it. - * - * * A thread may call flatSetExtensible only when it is the only - * thread accessing the string until a later call to - * flatClearExtensible. - * - * * Multiple threads can call flatClearExtensible but the function actually - * clears the EXTENSIBLE flag only when the flag is set -- in which case - * only one thread can access the string (see previous property). - * - * Thus, when multiple threads access the string, JSString::flatSetAtomized - * is the only function that can update the mLengthAndFlags field of the - * string by changing the EXTENSIBLE bit from 0 to 1. We call the method only - * after the string has been hashed. When some threads in js_ValueToStringId - * see that the flag is set, it knows that the string was atomized. - * - * On the other hand, if the thread sees that the flag is unset, it could - * be seeing a stale value when another thread has just atomized the string - * and set the flag. But this can lead only to an extra call to - * js_AtomizeString. This function would find that the string was already - * hashed and return it with the atomized bit set. - */ inline void flatSetAtomized() { JS_ASSERT(isFlat()); JS_ASSERT(!isStatic(this)); - JS_ATOMIC_SET_MASK((jsword *)&mLengthAndFlags, ATOMIZED); - } - - inline void flatSetExtensible() { - JS_ASSERT(isFlat()); - JS_ASSERT(!isAtomized()); - mLengthAndFlags |= EXTENSIBLE; + lengthAndFlags |= ATOMIZED; } inline void flatClearExtensible() { - JS_ASSERT(isFlat()); - /* - * We cannot eliminate the flag check before writing to mLengthAndFlags as - * static strings may reside in write-protected memory. See bug 599481. + * N.B. This may be called on static strings, which may be in read-only + * memory, so we cannot unconditionally apply the mask. */ - if (mLengthAndFlags & EXTENSIBLE) - mLengthAndFlags &= ~EXTENSIBLE; + JS_ASSERT(isFlat()); + if (lengthAndFlags & EXTENSIBLE) + lengthAndFlags &= ~EXTENSIBLE; } /* - * The chars pointer should point somewhere inside the buffer owned by bstr. - * The caller still needs to pass bstr for GC purposes. + * The chars pointer should point somewhere inside the buffer owned by base. + * The caller still needs to pass base for GC purposes. */ - inline void initDependent(JSString *bstr, jschar *chars, size_t len) { - JS_ASSERT(len <= MAX_LENGTH); + inline void initDependent(JSString *base, const jschar *chars, size_t length) { JS_ASSERT(!isStatic(this)); - e.mParent = NULL; - mChars = chars; - mLengthAndFlags = DEPENDENT | (len << FLAGS_LENGTH_SHIFT); - e.mBase = bstr; + JS_ASSERT(base->isFlat()); + JS_ASSERT(chars >= base->flatChars() && chars < base->flatChars() + base->length()); + JS_ASSERT(length <= base->length() - (chars - base->flatChars())); + lengthAndFlags = buildLengthAndFlags(length, DEPENDENT); + u.chars = chars; + s.base = base; } - inline JSString *dependentBase() const { + inline JSLinearString *dependentBase() const { JS_ASSERT(isDependent()); - return e.mBase; + return s.base->assertIsLinear(); } - JS_ALWAYS_INLINE jschar *dependentChars() { - return mChars; + JS_ALWAYS_INLINE const jschar *dependentChars() { + JS_ASSERT(isDependent()); + return u.chars; } inline size_t dependentLength() const { @@ -394,73 +329,54 @@ struct JSString { return length(); } + const jschar *undepend(JSContext *cx); + + const jschar *nonRopeChars() const { + JS_ASSERT(!isRope()); + return u.chars; + } + /* Rope-related initializers and accessors. */ - inline void initTopNode(JSString *left, JSString *right, size_t len, - JSRopeBufferInfo *buf) { - JS_ASSERT(left->length() + right->length() <= MAX_LENGTH); - JS_ASSERT(!isStatic(this)); - mLengthAndFlags = TOP_NODE | (len << FLAGS_LENGTH_SHIFT); - mLeft = left; - e.mRight = right; - e.mBufferWithInfo = buf; - } - - inline void convertToInteriorNode(JSString *parent) { - JS_ASSERT(isTopNode()); - e.mParent = parent; - mLengthAndFlags = INTERIOR_NODE | (length() << FLAGS_LENGTH_SHIFT); - } - - inline JSString *interiorNodeParent() const { - JS_ASSERT(isInteriorNode()); - return e.mParent; + inline void initRopeNode(JSString *left, JSString *right, size_t length) { + JS_ASSERT(left->length() + right->length() == length); + lengthAndFlags = buildLengthAndFlags(length, ROPE); + u.left = left; + s.right = right; } inline JSString *ropeLeft() const { JS_ASSERT(isRope()); - return mLeft; + return u.left; } inline JSString *ropeRight() const { JS_ASSERT(isRope()); - return e.mRight; + return s.right; } - inline size_t topNodeCapacity() const { - JS_ASSERT(isTopNode()); - return e.mBufferWithInfo->capacity; + inline void finishTraversalConversion(JSString *base, const jschar *baseBegin, const jschar *end) { + JS_ASSERT(baseBegin <= u.chars && u.chars <= end); + lengthAndFlags = buildLengthAndFlags(end - u.chars, DEPENDENT); + s.base = base; } - inline JSRopeBufferInfo *topNodeBuffer() const { - JS_ASSERT(isTopNode()); - return e.mBufferWithInfo; + const jschar *flatten(JSContext *maybecx); + + JSLinearString *ensureLinear(JSContext *cx) { + if (isRope() && !flatten(cx)) + return NULL; + return reinterpret_cast(this); } - inline void nullifyTopNodeBuffer() { - JS_ASSERT(isTopNode()); - e.mBufferWithInfo = NULL; + bool isLinear() const { + return !isRope(); } - inline void finishTraversalConversion(JSString *base, jschar *end) { - mLengthAndFlags = JSString::DEPENDENT | - ((end - mChars) << JSString::FLAGS_LENGTH_SHIFT); - e.mBase = base; + JSLinearString *assertIsLinear() { + JS_ASSERT(isLinear()); + return reinterpret_cast(this); } - inline bool ensureNotDependent(JSContext *cx) { - return !isDependent() || undepend(cx); - } - - inline void ensureNotRope() { - if (isRope()) - flatten(); - } - - const jschar *undepend(JSContext *cx); - - /* By design, this is not allowed to fail. */ - void flatten(); - typedef uint8 SmallChar; static inline bool fitsInSmallChar(jschar c) { @@ -521,18 +437,63 @@ struct JSString { */ static const JSString *const intStringTable[]; - static JSString *unitString(jschar c); - static JSString *getUnitString(JSContext *cx, JSString *str, size_t index); - static JSString *length2String(jschar c1, jschar c2); - static JSString *length2String(uint32 i); - static JSString *intString(jsint i); + static JSFlatString *unitString(jschar c); + static JSLinearString *getUnitString(JSContext *cx, JSString *str, size_t index); + static JSFlatString *length2String(jschar c1, jschar c2); + static JSFlatString *length2String(uint32 i); + static JSFlatString *intString(jsint i); + + static JSFlatString *lookupStaticString(const jschar *chars, size_t length); - static JSString *lookupStaticString(const jschar *chars, size_t length); - JS_ALWAYS_INLINE void finalize(JSContext *cx); + + static size_t offsetOfLengthAndFlags() { + return offsetof(JSString, lengthAndFlags); + } + + static size_t offsetOfChars() { + return offsetof(JSString, u.chars); + } + + static void staticAsserts() { + JS_STATIC_ASSERT(((JSString::MAX_LENGTH << JSString::LENGTH_SHIFT) >> + JSString::LENGTH_SHIFT) == JSString::MAX_LENGTH); + } }; -struct JSExternalString : JSString { +/* + * A "linear" string may or may not be null-terminated, but it provides + * infallible access to a linear array of characters. Namely, this means the + * string is not a rope. + */ +struct JSLinearString : JSString +{ + const jschar *chars() const { return JSString::nonRopeChars(); } +}; + +JS_STATIC_ASSERT(sizeof(JSLinearString) == sizeof(JSString)); + +/* + * A linear string where, additionally, chars()[length()] == '\0'. Namely, this + * means the string is not a dependent string or rope. + */ +struct JSFlatString : JSLinearString +{ + const jschar *charsZ() const { return chars(); } +}; + +JS_STATIC_ASSERT(sizeof(JSFlatString) == sizeof(JSString)); + +/* + * A flat string which has been "atomized", i.e., that is a unique string among + * other atomized strings and therefore allows equality via pointer comparison. + */ +struct JSAtom : JSFlatString +{ +}; + +struct JSExternalString : JSString +{ static const uintN TYPE_LIMIT = 8; static JSStringFinalizeOp str_finalizers[TYPE_LIMIT]; @@ -558,10 +519,12 @@ JS_STATIC_ASSERT(sizeof(JSString) == sizeof(JSExternalString)); * 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 : js::gc::Cell { +class JSShortString : public js::gc::Cell +{ JSString mHeader; JSString mDummy; + public: /* * 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 @@ -569,16 +532,16 @@ struct JSShortString : js::gc::Cell { */ inline jschar *init(size_t length) { JS_ASSERT(length <= MAX_SHORT_STRING_LENGTH); - mHeader.initShortString(mHeader.inlineStorage(), length); - return mHeader.inlineStorage(); + mHeader.initShortString(mHeader.inlineStorage, length); + return mHeader.inlineStorage; } inline jschar *getInlineStorageBeforeInit() { - return mHeader.mInlineStorage; + return mHeader.inlineStorage; } inline void initAtOffsetInBuffer(jschar *p, size_t length) { - JS_ASSERT(p >= mHeader.mInlineStorage && p < mHeader.mInlineStorage + MAX_SHORT_STRING_LENGTH); + JS_ASSERT(p >= mHeader.inlineStorage && p < mHeader.inlineStorage + MAX_SHORT_STRING_LENGTH); mHeader.initShortString(p, length); } @@ -590,147 +553,45 @@ struct JSShortString : js::gc::Cell { return &mHeader; } + static const size_t FREE_STRING_WORDS = 2; + static const size_t MAX_SHORT_STRING_LENGTH = - ((sizeof(JSString) + 2 * sizeof(size_t)) / sizeof(jschar)) - 1; + ((sizeof(JSString) + FREE_STRING_WORDS * sizeof(size_t)) / sizeof(jschar)) - 1; static inline bool fitsIntoShortString(size_t length) { return length <= MAX_SHORT_STRING_LENGTH; } JS_ALWAYS_INLINE void finalize(JSContext *cx); + + static void staticAsserts() { + JS_STATIC_ASSERT(offsetof(JSString, inlineStorage) == + sizeof(JSString) - JSShortString::FREE_STRING_WORDS * sizeof(void *)); + JS_STATIC_ASSERT(offsetof(JSShortString, mDummy) == sizeof(JSString)); + JS_STATIC_ASSERT(offsetof(JSString, inlineStorage) + + sizeof(jschar) * (JSShortString::MAX_SHORT_STRING_LENGTH + 1) == + sizeof(JSShortString)); + } }; -/* - * 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(JSShortString, mDummy) == sizeof(JSString)); -JS_STATIC_ASSERT(offsetof(JSString, mInlineStorage) + - sizeof(jschar) * (JSShortString::MAX_SHORT_STRING_LENGTH + 1) == - sizeof(JSShortString)); +namespace js { /* - * 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. + * When an algorithm does not need a string represented as a single linear + * array of characters, this range utility may be used to traverse the string a + * sequence of linear arrays of characters. This avoids flattening ropes. * - * 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 - * be to next. NULL is returned when there are no more nodes to return. + * Implemented in jsstrinlines.h. */ -class JSRopeNodeIterator { - private: - JSString *mStr; - size_t mUsedFlags; - - static const size_t DONE_LEFT = 0x1; - static const size_t DONE_RIGHT = 0x2; - - public: - JSRopeNodeIterator() - : mStr(NULL), mUsedFlags(0) - {} - - JSString *init(JSString *str) { - /* Move to the farthest-left leaf in the rope. */ - mStr = str; - while (mStr->isInteriorNode()) - mStr = mStr->interiorNodeParent(); - while (mStr->ropeLeft()->isInteriorNode()) - mStr = mStr->ropeLeft(); - JS_ASSERT(mUsedFlags == 0); - return mStr; - } - - JSString *next() { - if (!mStr) - return NULL; - if (!mStr->ropeLeft()->isInteriorNode() && !(mUsedFlags & DONE_LEFT)) { - mUsedFlags |= DONE_LEFT; - return mStr->ropeLeft(); - } - if (!mStr->ropeRight()->isInteriorNode() && !(mUsedFlags & DONE_RIGHT)) { - mUsedFlags |= DONE_RIGHT; - return mStr->ropeRight(); - } - if (mStr->ropeRight()->isInteriorNode()) { - /* - * If we have a right child, go right once, then left as far as - * possible. - */ - mStr = mStr->ropeRight(); - while (mStr->ropeLeft()->isInteriorNode()) - mStr = mStr->ropeLeft(); - } else { - /* - * If we have no right child, follow our parent until we move - * up-right. - */ - JSString *prev; - do { - prev = mStr; - /* Set the string to NULL if we reach the end of the tree. */ - mStr = mStr->isInteriorNode() ? mStr->interiorNodeParent() : NULL; - } while (mStr && mStr->ropeRight() == prev); - } - mUsedFlags = 0; - return mStr; - } -}; +class StringSegmentRange; +class MutatingRopeSegmentRange; /* - * An iterator that returns the leaves of a rope (which hold the actual string - * data) in order. The usage is the same as JSRopeNodeIterator. + * Utility for building a rope (lazy concatenation) of strings. */ -class JSRopeLeafIterator { - private: - JSRopeNodeIterator mNodeIterator; +class RopeBuilder; - public: - inline JSString *init(JSString *str) { - JS_ASSERT(str->isTopNode()); - str = mNodeIterator.init(str); - while (str->isRope()) { - str = mNodeIterator.next(); - JS_ASSERT(str); - } - return str; - } - - inline JSString *next() { - JSString *str; - do { - str = mNodeIterator.next(); - } while (str && str->isRope()); - return str; - } -}; - -class JSRopeBuilder { - JSContext * const cx; - JSString *mStr; - - public: - JSRopeBuilder(JSContext *cx); - - inline bool append(JSString *str) { - mStr = js_ConcatStrings(cx, mStr, str); - return !!mStr; - } - - inline JSString *getStr() { - return mStr; - } -}; - -JS_STATIC_ASSERT(JSString::INTERIOR_NODE & JSString::ROPE_BIT); -JS_STATIC_ASSERT(JSString::TOP_NODE & JSString::ROPE_BIT); - -JS_STATIC_ASSERT(((JSString::MAX_LENGTH << JSString::FLAGS_LENGTH_SHIFT) >> - JSString::FLAGS_LENGTH_SHIFT) == JSString::MAX_LENGTH); +} /* namespace js */ extern const jschar * js_GetStringChars(JSContext *cx, JSString *str); @@ -923,7 +784,7 @@ extern const char js_decodeURIComponent_str[]; extern const char js_encodeURIComponent_str[]; /* GC-allocate a string descriptor for the given malloc-allocated chars. */ -extern JSString * +extern JSFlatString * js_NewString(JSContext *cx, jschar *chars, size_t length); /* @@ -931,25 +792,25 @@ js_NewString(JSContext *cx, jschar *chars, size_t length); * This function takes responsibility for adding the terminating '\0' required * by js_NewString. */ -extern JSString * +extern JSFlatString * js_NewStringFromCharBuffer(JSContext *cx, JSCharBuffer &cb); -extern JSString * +extern JSLinearString * 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 * +extern JSFlatString * js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n); -extern JSString * +extern JSFlatString * js_NewStringCopyN(JSContext *cx, const char *s, size_t n); /* Copy a C string and GC-allocate a descriptor for it. */ -extern JSString * +extern JSFlatString * js_NewStringCopyZ(JSContext *cx, const jschar *s); -extern JSString * +extern JSFlatString * js_NewStringCopyZ(JSContext *cx, const char *s); /* @@ -1002,29 +863,42 @@ js_ValueToSource(JSContext *cx, const js::Value &v); * Compute a hash function from str. The caller can call this function even if * str is not a GC-allocated thing. */ -extern uint32 -js_HashString(JSString *str); +inline uint32 +js_HashString(JSLinearString *str) +{ + const jschar *s = str->chars(); + size_t n = str->length(); + uint32 h; + for (h = 0; n; s++, n--) + h = JS_ROTATE_LEFT32(h, 4) ^ *s; + return h; +} + +namespace js { /* * Test if strings are equal. The caller can call the function even if str1 * or str2 are not GC-allocated things. */ -extern JSBool JS_FASTCALL -js_EqualStrings(JSString *str1, JSString *str2); +extern bool +EqualStrings(JSContext *cx, JSString *str1, JSString *str2, JSBool *result); + +/* EqualStrings is infallible on linear strings. */ +extern bool +EqualStrings(JSLinearString *str1, JSLinearString *str2); /* * Return less than, equal to, or greater than zero depending on whether * str1 is less than, equal to, or greater than str2. */ -extern int32 JS_FASTCALL -js_CompareStrings(JSString *str1, JSString *str2); +extern bool +CompareStrings(JSContext *cx, JSString *str1, JSString *str2, int32 *result); -namespace js { /* * Return true if the string matches the given sequence of ASCII bytes. */ -extern JSBool -MatchStringAndAscii(JSString *str, const char *asciiBytes); +extern bool +StringEqualsAscii(JSLinearString *str, const char *asciiBytes); } /* namespacejs */ @@ -1170,7 +1044,7 @@ js_OneUcs4ToUtf8Char(uint8 *utf8Buffer, uint32 ucs4Char); namespace js { extern size_t -PutEscapedStringImpl(char *buffer, size_t size, FILE *fp, JSString *str, uint32 quote); +PutEscapedStringImpl(char *buffer, size_t size, FILE *fp, JSLinearString *str, uint32 quote); /* * Write str into buffer escaping any non-printable or non-ASCII character @@ -1182,7 +1056,7 @@ PutEscapedStringImpl(char *buffer, size_t size, FILE *fp, JSString *str, uint32 * be a single or double quote character that will quote the output. */ inline size_t -PutEscapedString(char *buffer, size_t size, JSString *str, uint32 quote) +PutEscapedString(char *buffer, size_t size, JSLinearString *str, uint32 quote) { size_t n = PutEscapedStringImpl(buffer, size, NULL, str, quote); @@ -1197,7 +1071,7 @@ PutEscapedString(char *buffer, size_t size, JSString *str, uint32 quote) * will quote the output. */ inline bool -FileEscapedString(FILE *fp, JSString *str, uint32 quote) +FileEscapedString(FILE *fp, JSLinearString *str, uint32 quote) { return PutEscapedStringImpl(NULL, 0, fp, str, quote) != size_t(-1); } diff --git a/js/src/jsstrinlines.h b/js/src/jsstrinlines.h index b1ea02cd631..39f9912c986 100644 --- a/js/src/jsstrinlines.h +++ b/js/src/jsstrinlines.h @@ -42,49 +42,53 @@ #include "jsstr.h" -inline JSString * +inline JSFlatString * JSString::unitString(jschar c) { JS_ASSERT(c < UNIT_STRING_LIMIT); - return const_cast(&unitStringTable[c]); + return const_cast(&unitStringTable[c])->assertIsFlat(); } -inline JSString * +inline JSLinearString * JSString::getUnitString(JSContext *cx, JSString *str, size_t index) { JS_ASSERT(index < str->length()); - jschar c = str->chars()[index]; + const jschar *chars = str->getChars(cx); + if (!chars) + return NULL; + jschar c = chars[index]; if (c < UNIT_STRING_LIMIT) return unitString(c); return js_NewDependentString(cx, str, index, 1); } -inline JSString * +inline JSFlatString * JSString::length2String(jschar c1, jschar c2) { JS_ASSERT(fitsInSmallChar(c1)); JS_ASSERT(fitsInSmallChar(c2)); - return const_cast - (&length2StringTable[(((size_t)toSmallChar[c1]) << 6) + toSmallChar[c2]]); + return const_cast ( + &length2StringTable[(((size_t)toSmallChar[c1]) << 6) + toSmallChar[c2]] + )->assertIsFlat(); } -inline JSString * +inline JSFlatString * JSString::length2String(uint32 i) { JS_ASSERT(i < 100); return length2String('0' + i / 10, '0' + i % 10); } -inline JSString * +inline JSFlatString * JSString::intString(jsint i) { jsuint u = jsuint(i); JS_ASSERT(u < INT_STRING_LIMIT); - return const_cast(JSString::intStringTable[u]); + return const_cast(JSString::intStringTable[u])->assertIsFlat(); } /* Get a static atomized string for chars if possible. */ -inline JSString * +inline JSFlatString * JSString::lookupStaticString(const jschar *chars, size_t length) { if (length == 1) { @@ -125,24 +129,21 @@ JSString::finalize(JSContext *cx) { JS_ASSERT(!JSString::isStatic(this)); JS_RUNTIME_UNMETER(cx->runtime, liveStrings); if (isDependent()) { - JS_ASSERT(dependentBase()); JS_RUNTIME_UNMETER(cx->runtime, liveDependentStrings); } else if (isFlat()) { /* * flatChars for stillborn string is null, but cx->free checks * for a null pointer on its own. */ - cx->free(flatChars()); - } else if (isTopNode()) { - cx->free(topNodeBuffer()); + cx->free(const_cast(flatChars())); } } inline void JSShortString::finalize(JSContext *cx) { - JS_ASSERT(!JSString::isStatic(header())); - JS_ASSERT(header()->isFlat()); + JS_ASSERT(!JSString::isStatic(&mHeader)); + JS_ASSERT(mHeader.isFlat()); JS_RUNTIME_UNMETER(cx->runtime, liveStrings); } @@ -155,7 +156,7 @@ JSExternalString::finalize(JSContext *cx) JS_RUNTIME_UNMETER(cx->runtime, liveStrings); /* A stillborn string has null chars. */ - jschar *chars = flatChars(); + jschar *chars = const_cast(flatChars()); if (!chars) return; JSStringFinalizeOp finalizer = str_finalizers[externalStringType]; @@ -177,8 +178,75 @@ JSExternalString::finalize() } } -inline -JSRopeBuilder::JSRopeBuilder(JSContext *cx) - : cx(cx), mStr(cx->runtime->emptyString) {} +namespace js { + +class RopeBuilder { + JSContext *cx; + JSString *res; + + public: + RopeBuilder(JSContext *cx) + : cx(cx), res(cx->runtime->emptyString) + {} + + inline bool append(JSString *str) { + res = js_ConcatStrings(cx, res, str); + return !!res; + } + + inline JSString *result() { + return res; + } +}; + +class StringSegmentRange +{ + /* + * If malloc() shows up in any profiles from this vector, we can add a new + * StackAllocPolicy which stashes a reusable freed-at-gc buffer in the cx. + */ + Vector stack; + JSString *cur; + + bool settle(JSString *str) { + while (str->isRope()) { + if (!stack.append(str->ropeRight())) + return false; + str = str->ropeLeft(); + } + cur = str; + return true; + } + + public: + StringSegmentRange(JSContext *cx) + : stack(cx), cur(NULL) + {} + + JS_WARN_UNUSED_RESULT bool init(JSString *str) { + JS_ASSERT(stack.empty()); + return settle(str); + } + + bool empty() const { + return cur == NULL; + } + + JSString *front() const { + JS_ASSERT(!cur->isRope()); + return cur; + } + + JS_WARN_UNUSED_RESULT bool popFront() { + JS_ASSERT(!empty()); + if (stack.empty()) { + cur = NULL; + return true; + } + return settle(stack.popCopy()); + } +}; + +} /* namespace js */ #endif /* jsstrinlines_h___ */ diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index ea1e264c6bf..d9edaa8c95d 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -60,6 +60,7 @@ #include "jsarray.h" #include "jsbool.h" #include "jscntxt.h" +#include "jscompartment.h" #include "jsdate.h" #include "jsdbgapi.h" #include "jsemit.h" @@ -437,14 +438,14 @@ jitstats_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) int index = -1; if (JSID_IS_STRING(id)) { - JSString* str = JSID_TO_STRING(id); - if (MatchStringAndAscii(str, "HOTLOOP")) { + JSAtom* str = JSID_TO_ATOM(id); + if (StringEqualsAscii(str, "HOTLOOP")) { *vp = INT_TO_JSVAL(HOTLOOP); return JS_TRUE; } #ifdef JS_METHODJIT - if (MatchStringAndAscii(str, "profiler")) { + if (StringEqualsAscii(str, "profiler")) { *vp = BOOLEAN_TO_JSVAL(cx->profilingEnabled); return JS_TRUE; } @@ -910,7 +911,11 @@ PrintOnTrace(char* format, uint32 argc, double *argv) // protect against massive spew if u.s is a bad pointer. if (length > 1 << 16) length = 1 << 16; - jschar *chars = u.s->chars(); + if (u.s->isRope()) { + fprintf(out, ""); + break; + } + const jschar *chars = u.s->nonRopeChars(); for (unsigned i = 0; i < length; ++i) { jschar co = chars[i]; if (co < 128) @@ -2237,6 +2242,7 @@ TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* anchor, VMFragment* frag pendingSpecializedNative(NULL), pendingUnboxSlot(NULL), pendingGuardCondition(NULL), + pendingGlobalSlotToSet(-1), pendingLoop(true), generatedSpecializedNative(), tempTypeMap(cx), @@ -2344,7 +2350,13 @@ TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* anchor, VMFragment* frag * thread and cannot outlive the corresponding JSThreadData. */ w.comment("begin-interruptFlags-check"); - LIns* flagptr = w.nameImmpNonGC((void *) &JS_THREAD_DATA(cx)->interruptFlags); + /* FIXME: See bug 621140 for moving interruptCounter to the compartment. */ +#ifdef JS_THREADSAFE + void *interrupt = (void*) &cx->runtime->interruptCounter; +#else + void *interrupt = (void*) &JS_THREAD_DATA(cx)->interruptFlags; +#endif + LIns* flagptr = w.nameImmpNonGC(interrupt); LIns* x = w.ldiVolatile(flagptr); guard(true, w.eqi0(x), TIMEOUT_EXIT); w.comment("end-interruptFlags-check"); @@ -2357,7 +2369,7 @@ TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* anchor, VMFragment* frag #ifdef JS_METHODJIT if (cx->methodJitEnabled) { w.comment("begin-count-loop-iterations"); - LIns* counterPtr = w.nameImmpNonGC((void *) &JS_THREAD_DATA(cx)->iterationCounter); + LIns* counterPtr = w.nameImmpNonGC((void *) &traceMonitor->iterationCounter); LIns* counterValue = w.ldiVolatile(counterPtr); LIns* test = w.ltiN(counterValue, LOOP_COUNT_MAX); LIns *branch = w.jfUnoptimizable(test); @@ -2430,7 +2442,7 @@ TraceRecorder::finishSuccessfully() delete this; /* Catch OOM that occurred during recording. */ - if (localtm->outOfMemory() || OverfullJITCache(localtm)) { + if (localtm->outOfMemory() || OverfullJITCache(localcx, localtm)) { ResetJIT(localcx, FR_OOM); return ARECORD_ABORTED; } @@ -2480,7 +2492,7 @@ TraceRecorder::finishAbort(const char* reason) localtm->recorder = NULL; delete this; - if (localtm->outOfMemory() || OverfullJITCache(localtm)) { + if (localtm->outOfMemory() || OverfullJITCache(localcx, localtm)) { ResetJIT(localcx, FR_OOM); return JIT_RESET; } @@ -2691,7 +2703,7 @@ ValueToNative(const Value &v, JSValueType type, double* slot) if (LogController.lcbits & LC_TMTracer) { char funName[40]; if (fun->atom) - JS_PutEscapedString(funName, sizeof funName, ATOM_TO_STRING(fun->atom), 0); + JS_PutEscapedFlatString(funName, sizeof funName, ATOM_TO_STRING(fun->atom), 0); else strcpy(funName, "unnamed"); LogController.printf("function<%p:%s> ", (void*)*(JSObject **)slot, funName); @@ -2889,7 +2901,7 @@ NativeToValue(JSContext* cx, Value& v, JSValueType type, double* slot) JSFunction* fun = GET_FUNCTION_PRIVATE(cx, &v.toObject()); char funName[40]; if (fun->atom) - JS_PutEscapedString(funName, sizeof funName, ATOM_TO_STRING(fun->atom), 0); + JS_PutEscapedFlatString(funName, sizeof funName, ATOM_TO_STRING(fun->atom), 0); else strcpy(funName, "unnamed"); LogController.printf("function<%p:%s> ", (void*) &v.toObject(), funName); @@ -3675,9 +3687,15 @@ TraceRecorder::writeBack(LIns* ins, LIns* base, ptrdiff_t offset, bool shouldDem JS_ASSERT(base == lirbuf->sp || base == eos_ins); if (shouldDemote && IsPromoteInt(ins)) ins = w.demote(ins); - Address addr = (base == lirbuf->sp) - ? (Address)StackAddress(base, offset) - : (Address)EosAddress(base, offset); + + Address addr; + if (base == lirbuf->sp) { + addr = StackAddress(base, offset); + } else { + addr = EosAddress(base, offset); + JS_ASSERT(pendingGlobalSlotToSet == -1); + pendingGlobalSlotToSet = offset / sizeof(double); + } return w.st(ins, addr); } @@ -5522,7 +5540,7 @@ TraceRecorder::startRecorder(JSContext* cx, VMSideExit* anchor, VMFragment* f, expectedInnerExit, outerScript, outerPC, outerArgc, speculate); - if (!tm->recorder || tm->outOfMemory() || OverfullJITCache(tm)) { + if (!tm->recorder || tm->outOfMemory() || OverfullJITCache(cx, tm)) { ResetJIT(cx, FR_OOM); return false; } @@ -5647,9 +5665,9 @@ RecordTree(JSContext* cx, TreeFragment* first, JSScript* outerScript, jsbytecode AUDIT(recorderStarted); if (tm->outOfMemory() || - OverfullJITCache(tm) || + OverfullJITCache(cx, tm) || !tm->tracedScripts.put(cx->fp()->script())) { - if (!OverfullJITCache(tm)) + if (!OverfullJITCache(cx, tm)) js_ReportOutOfMemory(cx); Backoff(cx, (jsbytecode*) f->root->ip); ResetJIT(cx, FR_OOM); @@ -6516,7 +6534,7 @@ ExecuteTree(JSContext* cx, TreeFragment* f, uintN& inlineCallCount, debug_only_stmt(*(uint64*)&tm->storage->global()[globalSlots] = 0xdeadbeefdeadbeefLL;) /* Execute trace. */ - JS_THREAD_DATA(cx)->iterationCounter = 0; + tm->iterationCounter = 0; debug_only(int64 t0 = PRMJ_Now();) #ifdef MOZ_TRACEVIS VMSideExit* lr = (TraceVisStateObj(cx, S_NATIVE), ExecuteTrace(cx, f, state)); @@ -6535,7 +6553,7 @@ ExecuteTree(JSContext* cx, TreeFragment* f, uintN& inlineCallCount, bool ok = !(state.builtinStatus & BUILTIN_ERROR); JS_ASSERT_IF(cx->throwing, !ok); - size_t iters = JS_THREAD_DATA(cx)->iterationCounter; + size_t iters = tm->iterationCounter; f->execs++; f->iters += iters; @@ -6658,7 +6676,7 @@ LeaveTree(TraceMonitor *tm, TracerState& state, VMSideExit* lr) * the interpreter stack, in pre-call state, with pc pointing to the * CALL/APPLY op, for correctness. Then we continued in native code. */ - if (!(bs & (BUILTIN_ERROR | BUILTIN_NO_FIXUP_NEEDED))) { + if (!(bs & BUILTIN_ERROR)) { /* * The builtin or native deep-bailed but finished successfully * (no exception or error). @@ -7175,6 +7193,7 @@ TraceRecorder::monitorRecording(JSOp op) */ pendingSpecializedNative = NULL; newobj_ins = NULL; + pendingGlobalSlotToSet = -1; /* Handle one-shot request from finishGetProp or INSTANCEOF to snapshot post-op state and guard. */ if (pendingGuardCondition) { @@ -7257,7 +7276,7 @@ TraceRecorder::monitorRecording(JSOp op) return status == ARECORD_ERROR ? ARECORD_ERROR : ARECORD_ABORTED; } - if (outOfMemory() || OverfullJITCache(&localtm)) { + if (outOfMemory() || OverfullJITCache(cx, &localtm)) { ResetJIT(cx, FR_OOM); /* @@ -7526,18 +7545,23 @@ disable_debugger_exceptions() { } void SetMaxCodeCacheBytes(JSContext* cx, uint32 bytes) { - TraceMonitor* tm = &JS_THREAD_DATA(cx)->traceMonitor; +#ifdef DEBUG + TraceMonitor* tm = &JS_TRACE_MONITOR(cx); JS_ASSERT(tm->codeAlloc && tm->dataAlloc && tm->traceAlloc); +#endif if (bytes > 1 G) bytes = 1 G; if (bytes < 128 K) bytes = 128 K; - tm->maxCodeCacheBytes = bytes; + JS_THREAD_DATA(cx)->maxCodeCacheBytes = bytes; } bool InitJIT(TraceMonitor *tm) { + // InitJIT expects this area to be zero'd + memset(tm, 0, sizeof(*tm)); + #if defined JS_JIT_SPEW tm->profAlloc = NULL; /* Set up debug logging. */ @@ -7581,9 +7605,6 @@ InitJIT(TraceMonitor *tm) did_we_check_processor_features = true; } - /* Set the default size for the code cache to 16MB. */ - tm->maxCodeCacheBytes = 16 M; - tm->oracle = new Oracle(); tm->profile = NULL; @@ -7598,7 +7619,6 @@ InitJIT(TraceMonitor *tm) tm->flushEpoch = 0; - JS_ASSERT(!tm->dataAlloc && !tm->traceAlloc && !tm->codeAlloc); tm->dataAlloc = new VMAllocator(); tm->traceAlloc = new VMAllocator(); tm->tempAlloc = new VMAllocator(); @@ -7793,7 +7813,7 @@ PurgeScriptFragments(TraceMonitor* tm, JSScript* script) } bool -OverfullJITCache(TraceMonitor* tm) +OverfullJITCache(JSContext *cx, TraceMonitor* tm) { /* * You might imagine the outOfMemory flag on the allocator is sufficient @@ -7829,7 +7849,7 @@ OverfullJITCache(TraceMonitor* tm) * handled by the (few) callers of this function. * */ - jsuint maxsz = tm->maxCodeCacheBytes; + jsuint maxsz = JS_THREAD_DATA(cx)->maxCodeCacheBytes; VMAllocator *dataAlloc = tm->dataAlloc; VMAllocator *traceAlloc = tm->traceAlloc; CodeAlloc *codeAlloc = tm->codeAlloc; @@ -8425,8 +8445,13 @@ TraceRecorder::d2i(LIns* d, bool resultCanBeImpreciseIfFractional) #endif } if (ci == &js_StringToNumber_ci) { - LIns* args[] = { d->callArgN(1), d->callArgN(0) }; - return w.call(&js_StringToInt32_ci, args); + LIns* ok_ins = w.allocp(sizeof(JSBool)); + LIns* args[] = { ok_ins, d->callArgN(1), d->callArgN(0) }; + LIns* ret_ins = w.call(&js_StringToInt32_ci, args); + guard(false, + w.name(w.eqi0(w.ldiAlloc(ok_ins)), "guard(oom)"), + OOM_EXIT); + return ret_ins; } } return resultCanBeImpreciseIfFractional @@ -8461,6 +8486,23 @@ TraceRecorder::makeNumberInt32(LIns* d, LIns** out) return guard(true, w.eqd(d, w.i2d(*out)), MISMATCH_EXIT, /* abortIfAlwaysExits = */true); } +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::makeNumberUint32(LIns* d, LIns** out) +{ + JS_ASSERT(d->isD()); + if (IsPromoteUint(d)) { + *out = w.demote(d); + return RECORD_CONTINUE; + } + + // This means "convert double to uint if it's integral, otherwise + // exit". We first convert the double to an unsigned int, then + // convert it back and exit if the two doubles don't match. If + // 'f' is a non-integral immediate we'll end up aborting. + *out = d2u(d); + return guard(true, w.eqd(d, w.ui2d(*out)), MISMATCH_EXIT, /* abortIfAlwaysExits = */true); +} + JS_REQUIRES_STACK LIns* TraceRecorder::stringify(const Value& v) { @@ -8473,7 +8515,7 @@ TraceRecorder::stringify(const Value& v) if (v.isNumber()) { ci = &js_NumberToString_ci; } else if (v.isUndefined()) { - return w.immpAtomGC(cx->runtime->atomState.booleanAtoms[2]); + return w.immpAtomGC(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]); } else if (v.isBoolean()) { ci = &js_BooleanIntToString_ci; } else { @@ -8642,10 +8684,13 @@ TraceRecorder::switchop() w.name(w.eqd(v_ins, w.immd(d)), "guard(switch on numeric)"), BRANCH_EXIT); } else if (v.isString()) { - LIns* args[] = { w.immpStrGC(v.toString()), v_ins }; - guard(true, - w.name(w.eqi0(w.eqi0(w.call(&js_EqualStrings_ci, args))), - "guard(switch on string)"), + LIns* args[] = { w.immpStrGC(v.toString()), v_ins, cx_ins }; + LIns* equal_rval = w.call(&js_EqualStringsOnTrace_ci, args); + guard(false, + w.name(w.eqiN(equal_rval, JS_NEITHER), "guard(oom)"), + OOM_EXIT); + guard(false, + w.name(w.eqi0(equal_rval), "guard(switch on string)"), BRANCH_EXIT); } else if (v.isBoolean()) { guard(true, @@ -8663,20 +8708,22 @@ JS_REQUIRES_STACK RecordingStatus TraceRecorder::inc(Value& v, jsint incr, bool pre) { LIns* v_ins = get(&v); - CHECK_STATUS(inc(v, v_ins, incr, pre)); + Value dummy; + CHECK_STATUS(inc(v, v_ins, dummy, incr, pre)); set(&v, v_ins); return RECORD_CONTINUE; } /* * On exit, v_ins is the incremented unboxed value, and the appropriate value - * (pre- or post-increment as described by pre) is stacked. + * (pre- or post-increment as described by pre) is stacked. v_out is set to + * the value corresponding to v_ins. */ JS_REQUIRES_STACK RecordingStatus -TraceRecorder::inc(const Value &v, LIns*& v_ins, jsint incr, bool pre) +TraceRecorder::inc(const Value &v, LIns*& v_ins, Value &v_out, jsint incr, bool pre) { LIns* v_after; - CHECK_STATUS(incHelper(v, v_ins, v_after, incr)); + CHECK_STATUS(incHelper(v, v_ins, v_out, v_after, incr)); const JSCodeSpec& cs = js_CodeSpec[*cx->regs->pc]; JS_ASSERT(cs.ndefs == 1); @@ -8687,9 +8734,13 @@ TraceRecorder::inc(const Value &v, LIns*& v_ins, jsint incr, bool pre) /* * Do an increment operation without storing anything to the stack. + * + * v_after is an out param whose value corresponds to the instruction the + * v_ins_after out param gets set to. */ JS_REQUIRES_STACK RecordingStatus -TraceRecorder::incHelper(const Value &v, LIns*& v_ins, LIns*& v_after, jsint incr) +TraceRecorder::incHelper(const Value &v, LIns*& v_ins, Value &v_after, + LIns*& v_ins_after, jsint incr) { // FIXME: Bug 606071 on making this work for objects. if (!v.isPrimitive()) @@ -8698,17 +8749,23 @@ TraceRecorder::incHelper(const Value &v, LIns*& v_ins, LIns*& v_after, jsint inc // We need to modify |v_ins| the same way relational() modifies // its RHS and LHS. if (v.isUndefined()) { - v_after = w.immd(js_NaN); + v_ins_after = w.immd(js_NaN); + v_after.setDouble(js_NaN); v_ins = w.immd(js_NaN); } else if (v.isNull()) { - v_after = w.immd(incr); + v_ins_after = w.immd(incr); + v_after.setDouble(incr); v_ins = w.immd(0.0); } else { if (v.isBoolean()) { v_ins = w.i2d(v_ins); } else if (v.isString()) { - LIns* args[] = { v_ins, cx_ins }; + LIns* ok_ins = w.allocp(sizeof(JSBool)); + LIns* args[] = { ok_ins, v_ins, cx_ins }; v_ins = w.call(&js_StringToNumber_ci, args); + guard(false, + w.name(w.eqi0(w.ldiAlloc(ok_ins)), "guard(oom)"), + OOM_EXIT); } else { JS_ASSERT(v.isNumber()); } @@ -8717,7 +8774,8 @@ TraceRecorder::incHelper(const Value &v, LIns*& v_ins, LIns*& v_after, jsint inc AutoValueRooter tvr(cx); *tvr.addr() = v; ValueToNumber(cx, tvr.value(), &num); - v_after = alu(LIR_addd, num, incr, v_ins, w.immd(incr)); + v_ins_after = alu(LIR_addd, num, incr, v_ins, w.immd(incr)); + v_after.setDouble(num + incr); } return RECORD_CONTINUE; @@ -8741,10 +8799,11 @@ TraceRecorder::incProp(jsint incr, bool pre) RETURN_STOP_A("incProp on invalid slot"); Value& v = obj->getSlotRef(slot); - CHECK_STATUS_A(inc(v, v_ins, incr, pre)); + Value v_after; + CHECK_STATUS_A(inc(v, v_ins, v_after, incr, pre)); LIns* slots_ins = NULL; - stobj_set_slot(obj, obj_ins, slot, slots_ins, v, v_ins); + stobj_set_slot(obj, obj_ins, slot, slots_ins, v_after, v_ins); return ARECORD_CONTINUE; } @@ -8762,8 +8821,9 @@ TraceRecorder::incElem(jsint incr, bool pre) CHECK_STATUS(denseArrayElement(l, r, vp, v_ins, addr_ins, snapshot(BRANCH_EXIT))); if (!addr_ins) // if we read a hole, abort return RECORD_STOP; - CHECK_STATUS(inc(*vp, v_ins, incr, pre)); - box_value_into(*vp, v_ins, DSlotsAddress(addr_ins)); + Value v_after; + CHECK_STATUS(inc(*vp, v_ins, v_after, incr, pre)); + box_value_into(v_after, v_ins, DSlotsAddress(addr_ins)); return RECORD_CONTINUE; } @@ -8800,14 +8860,18 @@ EvalCmp(LOpcode op, double l, double r) } static bool -EvalCmp(LOpcode op, JSString* l, JSString* r) +EvalCmp(JSContext *cx, LOpcode op, JSString* l, JSString* r, JSBool *ret) { if (op == LIR_eqd) - return !!js_EqualStrings(l, r); - return EvalCmp(op, js_CompareStrings(l, r), 0); + return EqualStrings(cx, l, r, ret); + JSBool cmp; + if (!CompareStrings(cx, l, r, &cmp)) + return false; + *ret = EvalCmp(op, cmp, 0); + return true; } -JS_REQUIRES_STACK void +JS_REQUIRES_STACK RecordingStatus TraceRecorder::strictEquality(bool equal, bool cmpCase) { Value& r = stackval(-1); @@ -8815,16 +8879,21 @@ TraceRecorder::strictEquality(bool equal, bool cmpCase) LIns* l_ins = get(&l); LIns* r_ins = get(&r); LIns* x; - bool cond; + JSBool cond; JSValueType ltag = getPromotedType(l); if (ltag != getPromotedType(r)) { cond = !equal; x = w.immi(cond); } else if (ltag == JSVAL_TYPE_STRING) { - LIns* args[] = { r_ins, l_ins }; - x = w.eqiN(w.call(&js_EqualStrings_ci, args), equal); - cond = !!js_EqualStrings(l.toString(), r.toString()); + LIns* args[] = { r_ins, l_ins, cx_ins }; + LIns* equal_ins = w.call(&js_EqualStringsOnTrace_ci, args); + guard(false, + w.name(w.eqiN(equal_ins, JS_NEITHER), "guard(oom)"), + OOM_EXIT); + x = w.eqiN(equal_ins, equal); + if (!EqualStrings(cx, l.toString(), r.toString(), &cond)) + RETURN_ERROR("oom"); } else { if (ltag == JSVAL_TYPE_DOUBLE) x = w.eqd(l_ins, r_ins); @@ -8838,16 +8907,17 @@ TraceRecorder::strictEquality(bool equal, bool cmpCase) ? l.toNumber() == r.toNumber() : l == r; } - cond = (cond == equal); + cond = (!!cond == equal); if (cmpCase) { /* Only guard if the same path may not always be taken. */ if (!x->isImmI()) guard(cond, x, BRANCH_EXIT); - return; + return RECORD_CONTINUE; } set(&l, x); + return RECORD_CONTINUE; } JS_REQUIRES_STACK AbortableRecordingStatus @@ -8867,8 +8937,8 @@ TraceRecorder::equalityHelper(Value& l, Value& r, LIns* l_ins, LIns* r_ins, Value& rval) { LOpcode op = LIR_eqi; - bool cond; - LIns* args[] = { NULL, NULL }; + JSBool cond; + LIns* args[] = { NULL, NULL, NULL }; /* * The if chain below closely mirrors that found in 11.9.3, in general @@ -8909,11 +8979,16 @@ TraceRecorder::equalityHelper(Value& l, Value& r, LIns* l_ins, LIns* r_ins, l_ins = w.getStringChar(l_ins, w.immpNonGC(0)); r_ins = w.getStringChar(r_ins, w.immpNonGC(0)); } else { - args[0] = r_ins, args[1] = l_ins; - l_ins = w.call(&js_EqualStrings_ci, args); + args[0] = r_ins, args[1] = l_ins, args[2] = cx_ins; + LIns *equal_ins = w.call(&js_EqualStringsOnTrace_ci, args); + guard(false, + w.name(w.eqiN(equal_ins, JS_NEITHER), "guard(oom)"), + OOM_EXIT); + l_ins = equal_ins; r_ins = w.immi(1); } - cond = !!js_EqualStrings(l.toString(), r.toString()); + if (!EqualStrings(cx, l.toString(), r.toString(), &cond)) + RETURN_ERROR_A("oom"); } else { JS_ASSERT(l.isNumber() && r.isNumber()); cond = (l.toNumber() == r.toNumber()); @@ -8926,14 +9001,30 @@ TraceRecorder::equalityHelper(Value& l, Value& r, LIns* l_ins, LIns* r_ins, r_ins = w.immiUndefined(); cond = true; } else if (l.isNumber() && r.isString()) { - args[0] = r_ins, args[1] = cx_ins; + LIns* ok_ins = w.allocp(sizeof(JSBool)); + args[0] = ok_ins, args[1] = r_ins, args[2] = cx_ins; r_ins = w.call(&js_StringToNumber_ci, args); - cond = (l.toNumber() == js_StringToNumber(cx, r.toString())); + guard(false, + w.name(w.eqi0(w.ldiAlloc(ok_ins)), "guard(oom)"), + OOM_EXIT); + JSBool ok; + double d = js_StringToNumber(cx, r.toString(), &ok); + if (!ok) + RETURN_ERROR_A("oom"); + cond = (l.toNumber() == d); op = LIR_eqd; } else if (l.isString() && r.isNumber()) { - args[0] = l_ins, args[1] = cx_ins; + LIns* ok_ins = w.allocp(sizeof(JSBool)); + args[0] = ok_ins, args[1] = l_ins, args[2] = cx_ins; l_ins = w.call(&js_StringToNumber_ci, args); - cond = (js_StringToNumber(cx, l.toString()) == r.toNumber()); + guard(false, + w.name(w.eqi0(w.ldiAlloc(ok_ins)), "guard(oom)"), + OOM_EXIT); + JSBool ok; + double d = js_StringToNumber(cx, l.toString(), &ok); + if (!ok) + RETURN_ERROR_A("oom"); + cond = (d == r.toNumber()); op = LIR_eqd; } else { // Below we may assign to l or r, which modifies the interpreter state. @@ -9007,7 +9098,7 @@ TraceRecorder::relational(LOpcode op, bool tryBranchAfterCond) Value& r = stackval(-1); Value& l = stackval(-2); LIns* x = NULL; - bool cond; + JSBool cond; LIns* l_ins = get(&l); LIns* r_ins = get(&r); bool fp = false; @@ -9033,22 +9124,31 @@ TraceRecorder::relational(LOpcode op, bool tryBranchAfterCond) /* 11.8.5 steps 3, 16-21. */ if (l.isString() && r.isString()) { - LIns* args[] = { r_ins, l_ins }; - l_ins = w.call(&js_CompareStrings_ci, args); + LIns* args[] = { r_ins, l_ins, cx_ins }; + LIns* result_ins = w.call(&js_CompareStringsOnTrace_ci, args); + guard(false, + w.name(w.eqiN(result_ins, INT32_MIN), "guard(oom)"), + OOM_EXIT); + l_ins = result_ins; r_ins = w.immi(0); - cond = EvalCmp(op, l.toString(), r.toString()); + if (!EvalCmp(cx, op, l.toString(), r.toString(), &cond)) + RETURN_ERROR_A("oom"); goto do_comparison; } /* 11.8.5 steps 4-5. */ if (!l.isNumber()) { - LIns* args[] = { l_ins, cx_ins }; if (l.isBoolean()) { l_ins = w.i2d(l_ins); } else if (l.isUndefined()) { l_ins = w.immd(js_NaN); } else if (l.isString()) { + LIns* ok_ins = w.allocp(sizeof(JSBool)); + LIns* args[] = { ok_ins, l_ins, cx_ins }; l_ins = w.call(&js_StringToNumber_ci, args); + guard(false, + w.name(w.eqi0(w.ldiAlloc(ok_ins)), "guard(oom)"), + OOM_EXIT); } else if (l.isNull()) { l_ins = w.immd(0.0); } else { @@ -9058,13 +9158,17 @@ TraceRecorder::relational(LOpcode op, bool tryBranchAfterCond) } } if (!r.isNumber()) { - LIns* args[] = { r_ins, cx_ins }; if (r.isBoolean()) { r_ins = w.i2d(r_ins); } else if (r.isUndefined()) { r_ins = w.immd(js_NaN); } else if (r.isString()) { + LIns* ok_ins = w.allocp(sizeof(JSBool)); + LIns* args[] = { ok_ins, r_ins, cx_ins }; r_ins = w.call(&js_StringToNumber_ci, args); + guard(false, + w.name(w.eqi0(w.ldiAlloc(ok_ins)), "guard(oom)"), + OOM_EXIT); } else if (r.isNull()) { r_ins = w.immd(0.0); } else { @@ -9172,16 +9276,30 @@ TraceRecorder::binary(LOpcode op) if (l.isString()) { NanoAssert(op != LIR_addd); // LIR_addd/IS_STRING case handled by record_JSOP_ADD() - LIns* args[] = { a, cx_ins }; + LIns* ok_ins = w.allocp(sizeof(JSBool)); + LIns* args[] = { ok_ins, a, cx_ins }; a = w.call(&js_StringToNumber_ci, args); - lnum = js_StringToNumber(cx, l.toString()); + guard(false, + w.name(w.eqi0(w.ldiAlloc(ok_ins)), "guard(oom)"), + OOM_EXIT); + JSBool ok; + lnum = js_StringToNumber(cx, l.toString(), &ok); + if (!ok) + RETURN_ERROR("oom"); leftIsNumber = true; } if (r.isString()) { NanoAssert(op != LIR_addd); // LIR_addd/IS_STRING case handled by record_JSOP_ADD() - LIns* args[] = { b, cx_ins }; + LIns* ok_ins = w.allocp(sizeof(JSBool)); + LIns* args[] = { ok_ins, b, cx_ins }; b = w.call(&js_StringToNumber_ci, args); - rnum = js_StringToNumber(cx, r.toString()); + guard(false, + w.name(w.eqi0(w.ldiAlloc(ok_ins)), "guard(oom)"), + OOM_EXIT); + JSBool ok; + rnum = js_StringToNumber(cx, r.toString(), &ok); + if (!ok) + RETURN_ERROR("oom"); rightIsNumber = true; } if (l.isBoolean()) { @@ -9606,7 +9724,7 @@ TraceRecorder::is_boxed_true(Address addr) LIns *tag_ins = w.ldiValueTag(addr); LIns *bool_ins = w.eqi(tag_ins, w.nameImmui(JSVAL_TAG_BOOLEAN)); LIns *payload_ins = w.ldiValuePayload(addr); - return w.andi(bool_ins, payload_ins); + return w.gtiN(w.andi(bool_ins, payload_ins), 0); } LIns* @@ -9858,7 +9976,7 @@ TraceRecorder::getThis(LIns*& this_ins) JS_ASSERT(fp->callee().getGlobal() == globalObj); Value& thisv = fp->thisValue(); - if (fp->fun()->inStrictMode() || thisv.isObject()) { + if (thisv.isObject() || fp->fun()->inStrictMode()) { /* * fp->thisValue() has already been computed. Since the * type-specialization of traces distinguishes between computed and @@ -10356,11 +10474,18 @@ TraceRecorder::record_JSOP_GOTO() */ jssrcnote* sn = js_GetSrcNote(cx->fp()->script(), cx->regs->pc); - if (sn && - (SN_TYPE(sn) == SRC_BREAK || SN_TYPE(sn) == SRC_CONT2LABEL || - SN_TYPE(sn) == SRC_BREAK2LABEL)) { - AUDIT(breakLoopExits); - return endLoop(); + if (sn) { + if (SN_TYPE(sn) == SRC_BREAK) { + AUDIT(breakLoopExits); + return endLoop(); + } + + /* + * Tracing labeled break isn't impossible, but does require potentially + * fixing up the block chain. See bug 616119. + */ + if (SN_TYPE(sn) == SRC_BREAK2LABEL || SN_TYPE(sn) == SRC_CONT2LABEL) + RETURN_STOP_A("labeled break"); } return ARECORD_CONTINUE; } @@ -10391,8 +10516,8 @@ TraceRecorder::newArguments(LIns* callee_ins, bool strict) if (strict) { LIns* argsData_ins = w.getObjPrivatizedSlot(argsobj_ins, JSObject::JSSLOT_ARGS_DATA); ptrdiff_t slotsOffset = offsetof(ArgumentsData, slots); - cx->fp()->forEachCanonicalActualArg(BoxArg(this, ArgsSlotsAddress(argsData_ins, - slotsOffset))); + cx->fp()->forEachCanonicalActualArg(BoxArg(this, ArgsSlotOffsetAddress(argsData_ins, + slotsOffset))); } return argsobj_ins; @@ -10695,8 +10820,13 @@ TraceRecorder::record_JSOP_NEG() } if (v.isString()) { - LIns* args[] = { get(&v), cx_ins }; - set(&v, w.negd(w.call(&js_StringToNumber_ci, args))); + LIns* ok_ins = w.allocp(sizeof(JSBool)); + LIns* args[] = { ok_ins, get(&v), cx_ins }; + LIns* num_ins = w.call(&js_StringToNumber_ci, args); + guard(false, + w.name(w.eqi0(w.ldiAlloc(ok_ins)), "guard(oom)"), + OOM_EXIT); + set(&v, w.negd(num_ins)); return ARECORD_CONTINUE; } @@ -10728,8 +10858,13 @@ TraceRecorder::record_JSOP_POS() } if (v.isString()) { - LIns* args[] = { get(&v), cx_ins }; - set(&v, w.call(&js_StringToNumber_ci, args)); + LIns* ok_ins = w.allocp(sizeof(JSBool)); + LIns* args[] = { ok_ins, get(&v), cx_ins }; + LIns* num_ins = w.call(&js_StringToNumber_ci, args); + guard(false, + w.name(w.eqi0(w.ldiAlloc(ok_ins)), "guard(oom)"), + OOM_EXIT); + set(&v, num_ins); return ARECORD_CONTINUE; } @@ -10859,7 +10994,9 @@ TraceRecorder::newArray(JSObject* ctor, uint32 argc, Value* argv, Value* rval) } else if (argc == 1 && argv[0].isNumber()) { /* Abort on RangeError if the double doesn't fit in a uint. */ - LIns *args[] = { proto_ins, d2i(get(argv)), cx_ins }; + LIns *len_ins; + CHECK_STATUS(makeNumberUint32(get(argv), &len_ins)); + LIns *args[] = { proto_ins, len_ins, cx_ins }; arr_ins = w.call(&js::NewDenseUnallocatedArray_ci, args); guard(false, w.eqp0(arr_ins), OOM_EXIT); @@ -11050,7 +11187,7 @@ TraceRecorder::callSpecializedNative(JSNativeTraceInfo *trcinfo, uintN argc, goto next_specialization; *argp = this_ins; } else if (argtype == 'M') { - MathCache *mathCache = JS_THREAD_DATA(cx)->getMathCache(cx); + MathCache *mathCache = GetMathCache(cx); if (!mathCache) return RECORD_ERROR; *argp = w.nameImmpNonGC(mathCache); @@ -11520,7 +11657,7 @@ TraceRecorder::record_JSOP_DELNAME() return ARECORD_STOP; } -JSBool JS_FASTCALL +static JSBool JS_FASTCALL DeleteIntKey(JSContext* cx, JSObject* obj, int32 i, JSBool strict) { LeaveTraceIfGlobalObject(cx, obj); @@ -11542,7 +11679,7 @@ DeleteIntKey(JSContext* cx, JSObject* obj, int32 i, JSBool strict) JS_DEFINE_CALLINFO_4(extern, BOOL_FAIL, DeleteIntKey, CONTEXT, OBJECT, INT32, BOOL, 0, ACCSET_STORE_ANY) -JSBool JS_FASTCALL +static JSBool JS_FASTCALL DeleteStrKey(JSContext* cx, JSObject* obj, JSString* str, JSBool strict) { LeaveTraceIfGlobalObject(cx, obj); @@ -11692,24 +11829,25 @@ TraceRecorder::incName(jsint incr, bool pre) { Value* vp; LIns* v_ins; - LIns* v_after; + LIns* v_ins_after; NameResult nr; CHECK_STATUS_A(name(vp, v_ins, nr)); Value v = nr.tracked ? *vp : nr.v; - CHECK_STATUS_A(incHelper(v, v_ins, v_after, incr)); - LIns* v_result = pre ? v_after : v_ins; + Value v_after; + CHECK_STATUS_A(incHelper(v, v_ins, v_after, v_ins_after, incr)); + LIns* v_ins_result = pre ? v_ins_after : v_ins; if (nr.tracked) { - set(vp, v_after); - stack(0, v_result); + set(vp, v_ins_after); + stack(0, v_ins_result); return ARECORD_CONTINUE; } if (!nr.obj->isCall()) RETURN_STOP_A("incName on unsupported object class"); - CHECK_STATUS_A(setCallProp(nr.obj, nr.obj_ins, nr.shape, v_after, v)); - stack(0, v_result); + CHECK_STATUS_A(setCallProp(nr.obj, nr.obj_ins, nr.shape, v_ins_after, v_after)); + stack(0, v_ins_result); return ARECORD_CONTINUE; } @@ -12414,12 +12552,14 @@ TraceRecorder::getCharCodeAt(JSString *str, LIns* str_ins, LIns* idx_ins, LIns** LIns *lengthAndFlags_ins = w.ldpStringLengthAndFlags(str_ins); if (MaybeBranch mbr = w.jt(w.eqp0(w.andp(lengthAndFlags_ins, w.nameImmw(JSString::ROPE_BIT))))) { - w.call(&js_Flatten_ci, &str_ins); + LIns *args[] = { str_ins, cx_ins }; + LIns *ok_ins = w.call(&js_Flatten_ci, args); + guard(false, w.eqi0(ok_ins), OOM_EXIT); w.label(mbr); } guard(true, - w.ltup(idx_ins, w.rshupN(lengthAndFlags_ins, JSString::FLAGS_LENGTH_SHIFT)), + w.ltup(idx_ins, w.rshupN(lengthAndFlags_ins, JSString::LENGTH_SHIFT)), snapshot(MISMATCH_EXIT)); *out = w.i2d(w.getStringChar(str_ins, idx_ins)); return RECORD_CONTINUE; @@ -12446,11 +12586,13 @@ TraceRecorder::getCharAt(JSString *str, LIns* str_ins, LIns* idx_ins, JSOp mode, if (MaybeBranch mbr = w.jt(w.eqp0(w.andp(lengthAndFlags_ins, w.nameImmw(JSString::ROPE_BIT))))) { - w.call(&js_Flatten_ci, &str_ins); + LIns *args[] = { str_ins, cx_ins }; + LIns *ok_ins = w.call(&js_Flatten_ci, args); + guard(false, w.eqi0(ok_ins), OOM_EXIT); w.label(mbr); } - LIns* inRange = w.ltup(idx_ins, w.rshupN(lengthAndFlags_ins, JSString::FLAGS_LENGTH_SHIFT)); + LIns* inRange = w.ltup(idx_ins, w.rshupN(lengthAndFlags_ins, JSString::LENGTH_SHIFT)); if (mode == JSOP_GETELEM) { guard(true, inRange, MISMATCH_EXIT); @@ -12487,7 +12629,8 @@ TraceRecorder::guardNotHole(LIns *argsobj_ins, LIns *idx_ins) LIns* vp_ins = w.addp(argsData_ins, slotOffset_ins); guard(false, - w.name(is_boxed_magic(ArgsSlotsAddress(vp_ins), JS_ARGS_HOLE), "guard(not deleted arg)"), + w.name(is_boxed_magic(ArgsSlotOffsetAddress(vp_ins), JS_ARGS_HOLE), + "guard(not deleted arg)"), MISMATCH_EXIT); } @@ -12816,8 +12959,12 @@ TraceRecorder::setElem(int lval_spindex, int idx_spindex, int v_spindex) } else if (v.isUndefined()) { typed_v_ins = w.immd(js_NaN); } else if (v.isString()) { - LIns* args[] = { typed_v_ins, cx_ins }; + LIns* ok_ins = w.allocp(sizeof(JSBool)); + LIns* args[] = { ok_ins, typed_v_ins, cx_ins }; typed_v_ins = w.call(&js_StringToNumber_ci, args); + guard(false, + w.name(w.eqi0(w.ldiAlloc(ok_ins)), "guard(oom)"), + OOM_EXIT); } else if (v.isBoolean()) { JS_ASSERT(v.isBoolean()); typed_v_ins = w.i2d(typed_v_ins); @@ -14014,14 +14161,14 @@ TraceRecorder::record_JSOP_LOOKUPSWITCH() JS_REQUIRES_STACK AbortableRecordingStatus TraceRecorder::record_JSOP_STRICTEQ() { - strictEquality(true, false); + CHECK_STATUS_A(strictEquality(true, false)); return ARECORD_CONTINUE; } JS_REQUIRES_STACK AbortableRecordingStatus TraceRecorder::record_JSOP_STRICTNE() { - strictEquality(false, false); + CHECK_STATUS_A(strictEquality(false, false)); return ARECORD_CONTINUE; } @@ -14309,13 +14456,10 @@ static JSBool FASTCALL IteratorMore(JSContext *cx, JSObject *iterobj, Value *vp) { if (!js_IteratorMore(cx, iterobj, vp)) { - SetBuiltinError(cx, BUILTIN_ERROR_NO_FIXUP_NEEDED); - return false; - } else if (cx->tracerState->builtinStatus) { - SetBuiltinError(cx, BUILTIN_NO_FIXUP_NEEDED); + SetBuiltinError(cx); return false; } - return true; + return cx->tracerState->builtinStatus == 0; } JS_DEFINE_CALLINFO_3(extern, BOOL_FAIL, IteratorMore, CONTEXT, OBJECT, VALUEPTR, 0, ACCSET_STORE_ANY) @@ -14331,22 +14475,16 @@ TraceRecorder::record_JSOP_MOREITER() JSObject* iterobj = &iterobj_val.toObject(); LIns* iterobj_ins = get(&iterobj_val); - bool cond; LIns* cond_ins; /* JSOP_FOR* already guards on this, but in certain rare cases we might record misformed loop traces. */ if (iterobj->hasClass(&js_IteratorClass)) { guardClass(iterobj_ins, &js_IteratorClass, snapshot(BRANCH_EXIT), LOAD_NORMAL); - NativeIterator *ni = (NativeIterator *) iterobj->getPrivate(); - void *cursor = ni->props_cursor; - void *end = ni->props_end; LIns *ni_ins = w.ldpObjPrivate(iterobj_ins); LIns *cursor_ins = w.ldpIterCursor(ni_ins); LIns *end_ins = w.ldpIterEnd(ni_ins); - /* Figure out whether the native iterator contains more values. */ - cond = cursor < end; cond_ins = w.ltp(cursor_ins, end_ins); } else { guardNotClass(iterobj_ins, &js_IteratorClass, snapshot(BRANCH_EXIT), LOAD_NORMAL); @@ -14357,40 +14495,12 @@ TraceRecorder::record_JSOP_MOREITER() LIns* args[] = { vp_ins, iterobj_ins, cx_ins }; LIns* ok_ins = w.call(&IteratorMore_ci, args); - /* - * We cannot use pendingGuardCondition since monitorRecording may not be - * triggered if we close the loop below with endLoop. Instead, we guard - * here with STATUS_EXIT. By default, STATUS_EXIT means "advance the pc - * and fixup the stack", so IteratorMore sets BUILTIN_NO_FIXUP_NEEDED. - * If IteratorMore fails, we will reexecute this op in the interpreter, - * but js_IteratoreMore is idempotent so this is ok. - */ - guard(false, w.eqi0(ok_ins), STATUS_EXIT); - + pendingGuardCondition = w.eqi0(ok_ins); leaveDeepBailCall(); - /* - * The interpreter will call js_IteratorMore again, but that's legal. We have to - * carefully protect ourselves against reentrancy. - */ - JSContext *localCx = cx; - AutoValueRooter rooter(cx); - if (!js_IteratorMore(cx, iterobj, rooter.addr())) - RETURN_ERROR_A("error in js_IteratorMore"); - if (!TRACE_RECORDER(localCx)) - return ARECORD_ABORTED; - - cond = (rooter.value().isTrue()); cond_ins = w.eqi0(w.eqi0(is_boxed_true(AllocSlotsAddress(vp_ins)))); } - jsbytecode* pc = cx->regs->pc; - - if (pc[1] == JSOP_IFNE) { - fuseIf(pc + 1, cond, cond_ins); - return checkTraceEnd(pc + 1); - } - stack(0, cond_ins); return ARECORD_CONTINUE; @@ -14963,7 +15073,7 @@ TraceRecorder::record_JSOP_CONDSWITCH() JS_REQUIRES_STACK AbortableRecordingStatus TraceRecorder::record_JSOP_CASE() { - strictEquality(true, true); + CHECK_STATUS_A(strictEquality(true, true)); return ARECORD_CONTINUE; } @@ -15341,7 +15451,7 @@ TraceRecorder::record_JSOP_GOSUBX() JS_REQUIRES_STACK AbortableRecordingStatus TraceRecorder::record_JSOP_CASEX() { - strictEquality(true, true); + CHECK_STATUS_A(strictEquality(true, true)); return ARECORD_CONTINUE; } @@ -15616,7 +15726,7 @@ TraceRecorder::record_JSOP_CALLPROP() if (pcval.isFunObj()) { if (l.isPrimitive()) { JSFunction* fun = GET_FUNCTION_PRIVATE(cx, &pcval.toFunObj()); - if (!PrimitiveThisTest(fun, l)) + if (fun->isInterpreted() && !fun->inStrictMode()) RETURN_STOP_A("callee does not accept primitive |this|"); } set(&l, w.immpObjGC(&pcval.toFunObj())); @@ -15673,14 +15783,14 @@ TraceRecorder::record_JSOP_CALLELEM() JS_REQUIRES_STACK AbortableRecordingStatus TraceRecorder::record_JSOP_STOP() { + JSStackFrame *fp = cx->fp(); + /* A return from callDepth 0 terminates the current loop, except for recursion. */ - if (callDepth == 0) { + if (callDepth == 0 && !fp->hasImacropc()) { AUDIT(returnLoopExits); return endLoop(); } - JSStackFrame *fp = cx->fp(); - if (fp->hasImacropc()) { /* * End of imacro, so return true to the interpreter immediately. The @@ -16889,7 +16999,7 @@ LoopProfile::decide(JSContext *cx) /* don't trace */ } else if (count(OP_EVAL)) { /* don't trace */ - } else if (numInnerLoops > 3) { + } else if (numInnerLoops > 7) { /* don't trace */ } else if (shortLoop) { /* don't trace */ diff --git a/js/src/jstracer.h b/js/src/jstracer.h index 0bac7c3ea4b..36290542fed 100644 --- a/js/src/jstracer.h +++ b/js/src/jstracer.h @@ -52,6 +52,7 @@ #include "jslock.h" #include "jsnum.h" #include "jsvector.h" +#include "jscompartment.h" #include "Writer.h" namespace js { @@ -819,16 +820,13 @@ public: */ typedef enum BuiltinStatus { BUILTIN_BAILED = 1, - BUILTIN_ERROR = 2, - BUILTIN_NO_FIXUP_NEEDED = 4, - - BUILTIN_ERROR_NO_FIXUP_NEEDED = BUILTIN_ERROR | BUILTIN_NO_FIXUP_NEEDED + BUILTIN_ERROR = 2 } BuiltinStatus; static JS_INLINE void -SetBuiltinError(JSContext *cx, BuiltinStatus status = BUILTIN_ERROR) +SetBuiltinError(JSContext *cx) { - cx->tracerState->builtinStatus |= status; + cx->tracerState->builtinStatus |= BUILTIN_ERROR; } #ifdef DEBUG_RECORDING_STATUS_NOT_BOOL @@ -1106,6 +1104,9 @@ class TraceRecorder /* Carry a guard condition to the beginning of the next monitorRecording. */ nanojit::LIns* pendingGuardCondition; + /* See AbortRecordingIfUnexpectedGlobalWrite. */ + int pendingGlobalSlotToSet; + /* Carry whether we have an always-exit from emitIf to checkTraceEnd. */ bool pendingLoop; @@ -1278,6 +1279,7 @@ class TraceRecorder nanojit::LIns* d2i(nanojit::LIns* f, bool resultCanBeImpreciseIfFractional = false); nanojit::LIns* d2u(nanojit::LIns* d); JS_REQUIRES_STACK RecordingStatus makeNumberInt32(nanojit::LIns* d, nanojit::LIns** num_ins); + JS_REQUIRES_STACK RecordingStatus makeNumberUint32(nanojit::LIns* d, nanojit::LIns** num_ins); JS_REQUIRES_STACK nanojit::LIns* stringify(const Value& v); JS_REQUIRES_STACK nanojit::LIns* newArguments(nanojit::LIns* callee_ins, bool strict); @@ -1292,15 +1294,18 @@ class TraceRecorder JS_REQUIRES_STACK AbortableRecordingStatus tableswitch(); #endif JS_REQUIRES_STACK RecordingStatus inc(Value& v, jsint incr, bool pre = true); - JS_REQUIRES_STACK RecordingStatus inc(const Value &v, nanojit::LIns*& v_ins, jsint incr, - bool pre = true); + JS_REQUIRES_STACK RecordingStatus inc(const Value &v, nanojit::LIns*& v_ins, + Value &v_out, jsint incr, + bool pre = true); JS_REQUIRES_STACK RecordingStatus incHelper(const Value &v, nanojit::LIns*& v_ins, - nanojit::LIns*& v_after, jsint incr); + Value &v_after, + nanojit::LIns*& v_ins_after, + jsint incr); JS_REQUIRES_STACK AbortableRecordingStatus incProp(jsint incr, bool pre = true); JS_REQUIRES_STACK RecordingStatus incElem(jsint incr, bool pre = true); JS_REQUIRES_STACK AbortableRecordingStatus incName(jsint incr, bool pre = true); - JS_REQUIRES_STACK void strictEquality(bool equal, bool cmpCase); + JS_REQUIRES_STACK RecordingStatus strictEquality(bool equal, bool cmpCase); JS_REQUIRES_STACK AbortableRecordingStatus equality(bool negate, bool tryBranchAfterCond); JS_REQUIRES_STACK AbortableRecordingStatus equalityHelper(Value& l, Value& r, nanojit::LIns* l_ins, nanojit::LIns* r_ins, @@ -1595,6 +1600,18 @@ class TraceRecorder JS_REQUIRES_STACK AbortableRecordingStatus record_NativeCallComplete(); void forgetGuardedShapesForObject(JSObject* obj); + bool globalSetExpected(unsigned slot) { + if (pendingGlobalSlotToSet != (int)slot) { + /* + * Do slot arithmetic manually to avoid getSlotRef assertions which + * do not need to be satisfied for this purpose. + */ + return !tracker.has(globalObj->getSlots() + slot); + } + pendingGlobalSlotToSet = -1; + return true; + } + #ifdef DEBUG /* Debug printing functionality to emit printf() on trace. */ JS_REQUIRES_STACK void tprint(const char *format, int count, nanojit::LIns *insa[]); @@ -1675,7 +1692,7 @@ extern void PurgeScriptFragments(TraceMonitor* tm, JSScript* script); extern bool -OverfullJITCache(TraceMonitor* tm); +OverfullJITCache(JSContext *cx, TraceMonitor* tm); extern void FlushJITCache(JSContext* cx); @@ -1828,4 +1845,29 @@ struct TraceVisStateObj { #endif /* !JS_TRACER */ +namespace js { + +/* + * While recording, the slots of the global object may change payload or type. + * This is fine as long as the recorder expects this change (and therefore has + * generated the corresponding LIR, snapshots, etc). The recorder indicates + * that it expects a write to a global slot by setting pendingGlobalSlotToSet + * in the recorder, before the write is made by the interpreter, and clearing + * pendingGlobalSlotToSet before recording the next op. Any global slot write + * that has not been whitelisted in this manner is therefore unexpected and, if + * the global slot is actually being tracked, recording must be aborted. + */ +static JS_INLINE void +AbortRecordingIfUnexpectedGlobalWrite(JSContext *cx, JSObject *obj, unsigned slot) +{ +#ifdef JS_TRACER + if (TraceRecorder *tr = TRACE_RECORDER(cx)) { + if (!obj->parent && !tr->globalSetExpected(slot)) + AbortRecording(cx, "Global slot written outside tracer supervision"); + } +#endif +} + +} /* namespace js */ + #endif /* jstracer_h___ */ diff --git a/js/src/jstypes.h b/js/src/jstypes.h index 33dbb5b72d2..c2103d80b34 100644 --- a/js/src/jstypes.h +++ b/js/src/jstypes.h @@ -221,6 +221,14 @@ # endif #endif +#ifndef JS_WARN_UNUSED_RESULT +# if defined __GNUC__ +# define JS_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +# else +# define JS_WARN_UNUSED_RESULT +# endif +#endif + #ifdef NS_STATIC_CHECKING /* * Attributes for static analysis. Functions declared with JS_REQUIRES_STACK diff --git a/js/src/jsutil.h b/js/src/jsutil.h index d735a4adeed..6962ba8540e 100644 --- a/js/src/jsutil.h +++ b/js/src/jsutil.h @@ -357,10 +357,11 @@ PodArrayZero(T (&t)[N]) template JS_ALWAYS_INLINE static void -PodCopy(T *dst, T *src, size_t nelem) +PodCopy(T *dst, const T *src, size_t nelem) { + JS_ASSERT(abs(dst - src) >= ptrdiff_t(nelem)); if (nelem < 128) { - for (T *srcend = src + nelem; src != srcend; ++src, ++dst) + for (const T *srcend = src + nelem; src != srcend; ++src, ++dst) *dst = *src; } else { memcpy(dst, src, nelem * sizeof(T)); diff --git a/js/src/jsval.h b/js/src/jsval.h index e095ee4c7ff..0d9a2a42faa 100644 --- a/js/src/jsval.h +++ b/js/src/jsval.h @@ -266,8 +266,9 @@ typedef enum JSWhyMagic JS_GENERIC_MAGIC /* for local use */ } JSWhyMagic; -typedef struct JSString JSString; -typedef struct JSObject JSObject; +typedef struct JSString JSString; +typedef struct JSFlatString JSFlatString; +typedef struct JSObject JSObject; #if defined(IS_LITTLE_ENDIAN) # if JS_BITS_PER_WORD == 32 @@ -351,7 +352,7 @@ typedef union jsval_layout static JS_ALWAYS_INLINE JSBool JSVAL_IS_DOUBLE_IMPL(jsval_layout l) { - return (uint32)l.s.tag < (uint32)JSVAL_TAG_CLEAR; + return (uint32)l.s.tag <= (uint32)JSVAL_TAG_CLEAR; } static JS_ALWAYS_INLINE jsval_layout diff --git a/js/src/jsvector.h b/js/src/jsvector.h index c893a471224..ae8fab42bf1 100644 --- a/js/src/jsvector.h +++ b/js/src/jsvector.h @@ -265,6 +265,10 @@ class Vector : AllocPolicy /* accessors */ + const AllocPolicy &allocPolicy() const { + return *this; + } + enum { InlineLength = N }; size_t length() const { @@ -342,6 +346,8 @@ class Vector : AllocPolicy void popBack(); + T popCopy(); + /* * Transfers ownership of the internal buffer used by Vector to the caller. * After this call, the Vector is empty. Since the returned buffer may need @@ -690,6 +696,15 @@ Vector::popBack() endNoCheck()->~T(); } +template +JS_ALWAYS_INLINE T +Vector::popCopy() +{ + T ret = back(); + popBack(); + return ret; +} + template inline T * Vector::extractRawBuffer() diff --git a/js/src/jsxdrapi.cpp b/js/src/jsxdrapi.cpp index 56c5e85fab5..2878c2dc810 100644 --- a/js/src/jsxdrapi.cpp +++ b/js/src/jsxdrapi.cpp @@ -456,13 +456,12 @@ JS_XDRString(JSXDRState *xdr, JSString **strp) if (!JS_XDRUint32(xdr, &nchars)) return JS_FALSE; - if (xdr->mode == JSXDR_DECODE) { + if (xdr->mode == JSXDR_DECODE) chars = (jschar *) xdr->cx->malloc((nchars + 1) * sizeof(jschar)); - if (!chars) - return JS_FALSE; - } else { - chars = (*strp)->chars(); - } + else + chars = const_cast((*strp)->getChars(xdr->cx)); + if (!chars) + return JS_FALSE; if (!XDRChars(xdr, chars, nchars)) goto bad; diff --git a/js/src/jsxml.cpp b/js/src/jsxml.cpp index 24fdc640224..fc79197f846 100644 --- a/js/src/jsxml.cpp +++ b/js/src/jsxml.cpp @@ -158,36 +158,6 @@ const char js_leftcurly_entity_str[] = "{"; static JSBool GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp); -static JS_INLINE JSString * -GetPrefix(const JSObject *obj) -{ - jsval v = obj->getNamePrefix(); - if (JSVAL_IS_VOID(v)) - return NULL; - JS_ASSERT(JSVAL_IS_STRING(v)); - return JSVAL_TO_STRING(v); -} - -static JSString * -GetURI(const JSObject *obj) -{ - jsval v = obj->getNameURI(); - if (JSVAL_IS_VOID(v)) - return NULL; - JS_ASSERT(JSVAL_IS_STRING(v)); - return JSVAL_TO_STRING(v); -} - -static JSString * -GetLocalName(const JSObject *obj) -{ - jsval v = obj->getQNameLocalName(); - if (JSVAL_IS_VOID(v)) - return NULL; - JS_ASSERT(JSVAL_IS_STRING(v)); - return JSVAL_TO_STRING(v); -} - static JSBool IsDeclared(const JSObject *obj) { @@ -209,9 +179,10 @@ xml_isXMLName(JSContext *cx, uintN argc, jsval *vp) static inline bool AppendString(JSCharBuffer &cb, JSString *str) { - const jschar *chars, *end; - str->getCharsAndEnd(chars, end); - return cb.append(chars, end); + const jschar *chars = str->getChars(cb.allocPolicy().context()); + if (!chars) + return false; + return cb.append(chars, str->length()); } /* @@ -240,9 +211,9 @@ NewBuiltinClassInstanceXML(JSContext *cx, Class *clasp) * Namespace class and library functions. */ DEFINE_GETTER(NamePrefix_getter, - if (obj->getClass() == &js_NamespaceClass) *vp = obj->getNamePrefix()) + if (obj->getClass() == &js_NamespaceClass) *vp = obj->getNamePrefixVal()) DEFINE_GETTER(NameURI_getter, - if (obj->getClass() == &js_NamespaceClass) *vp = obj->getNameURI()) + if (obj->getClass() == &js_NamespaceClass) *vp = obj->getNameURIVal()) static void namespace_finalize(JSContext *cx, JSObject *obj) @@ -260,7 +231,7 @@ namespace_equality(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp) obj2 = v->toObjectOrNull(); *bp = (!obj2 || obj2->getClass() != &js_NamespaceClass) ? JS_FALSE - : js_EqualStrings(GetURI(obj), GetURI(obj2)); + : EqualStrings(obj->getNameURI(), obj2->getNameURI()); return JS_TRUE; } @@ -310,7 +281,7 @@ namespace_toString(JSContext *cx, uintN argc, Value *vp) obj = ComputeThisFromVp(cx, vp); if (!JS_InstanceOf(cx, obj, Jsvalify(&js_NamespaceClass), Jsvalify(vp + 2))) return JS_FALSE; - *vp = Valueify(obj->getNameURI()); + *vp = Valueify(obj->getNameURIVal()); return JS_TRUE; } @@ -320,20 +291,20 @@ static JSFunctionSpec namespace_methods[] = { }; static JSObject * -NewXMLNamespace(JSContext *cx, JSString *prefix, JSString *uri, JSBool declared) +NewXMLNamespace(JSContext *cx, JSLinearString *prefix, JSLinearString *uri, JSBool declared) { JSObject *obj; obj = NewBuiltinClassInstanceXML(cx, &js_NamespaceClass); if (!obj) return JS_FALSE; - JS_ASSERT(JSVAL_IS_VOID(obj->getNamePrefix())); - JS_ASSERT(JSVAL_IS_VOID(obj->getNameURI())); + JS_ASSERT(JSVAL_IS_VOID(obj->getNamePrefixVal())); + JS_ASSERT(JSVAL_IS_VOID(obj->getNameURIVal())); JS_ASSERT(JSVAL_IS_VOID(obj->getNamespaceDeclared())); if (prefix) - obj->setNamePrefix(STRING_TO_JSVAL(prefix)); + obj->setNamePrefix(prefix); if (uri) - obj->setNameURI(STRING_TO_JSVAL(uri)); + obj->setNameURI(uri); if (declared) obj->setNamespaceDeclared(JSVAL_TRUE); METER(xml_stats.xmlnamespace); @@ -345,10 +316,10 @@ NewXMLNamespace(JSContext *cx, JSString *prefix, JSString *uri, JSBool declared) */ DEFINE_GETTER(QNameNameURI_getter, if (obj->getClass() == &js_QNameClass) - *vp = JSVAL_IS_VOID(obj->getNameURI()) ? JSVAL_NULL : obj->getNameURI()) + *vp = JSVAL_IS_VOID(obj->getNameURIVal()) ? JSVAL_NULL : obj->getNameURIVal()) DEFINE_GETTER(QNameLocalName_getter, if (obj->getClass() == &js_QNameClass) - *vp = obj->getQNameLocalName()) + *vp = obj->getQNameLocalNameVal()) static void anyname_finalize(JSContext* cx, JSObject* obj) @@ -361,14 +332,14 @@ anyname_finalize(JSContext* cx, JSObject* obj) static JSBool qname_identity(JSObject *qna, JSObject *qnb) { - JSString *uri1 = GetURI(qna); - JSString *uri2 = GetURI(qnb); + JSLinearString *uri1 = qna->getNameURI(); + JSLinearString *uri2 = qnb->getNameURI(); if (!uri1 ^ !uri2) return JS_FALSE; - if (uri1 && !js_EqualStrings(uri1, uri2)) + if (uri1 && !EqualStrings(uri1, uri2)) return JS_FALSE; - return js_EqualStrings(GetLocalName(qna), GetLocalName(qnb)); + return EqualStrings(qna->getQNameLocalName(), qnb->getQNameLocalName()); } static JSBool @@ -474,7 +445,7 @@ qname_toString(JSContext *cx, uintN argc, Value *vp) return JS_FALSE; } - uri = GetURI(obj); + uri = obj->getNameURI(); if (!uri) { /* No uri means wildcard qualifier. */ str = ATOM_TO_STRING(cx->runtime->atomState.starQualifierAtom); @@ -487,7 +458,7 @@ qname_toString(JSContext *cx, uintN argc, Value *vp) if (!str) return JS_FALSE; } - str = js_ConcatStrings(cx, str, GetLocalName(obj)); + str = js_ConcatStrings(cx, str, obj->getQNameLocalName()); if (!str) return JS_FALSE; @@ -497,7 +468,12 @@ qname_toString(JSContext *cx, uintN argc, Value *vp) if (!chars) return JS_FALSE; *chars = '@'; - js_strncpy(chars + 1, str->chars(), length); + const jschar *strChars = str->getChars(cx); + if (!strChars) { + cx->free(chars); + return JS_FALSE; + } + js_strncpy(chars + 1, strChars, length); chars[++length] = 0; str = js_NewString(cx, chars, length); if (!str) { @@ -517,23 +493,23 @@ static JSFunctionSpec qname_methods[] = { static void -InitXMLQName(JSObject *obj, JSString *uri, JSString *prefix, - JSString *localName) +InitXMLQName(JSObject *obj, JSLinearString *uri, JSLinearString *prefix, + JSLinearString *localName) { - JS_ASSERT(JSVAL_IS_VOID(obj->getNamePrefix())); - JS_ASSERT(JSVAL_IS_VOID(obj->getNameURI())); - JS_ASSERT(JSVAL_IS_VOID(obj->getQNameLocalName())); + JS_ASSERT(JSVAL_IS_VOID(obj->getNamePrefixVal())); + JS_ASSERT(JSVAL_IS_VOID(obj->getNameURIVal())); + JS_ASSERT(JSVAL_IS_VOID(obj->getQNameLocalNameVal())); if (uri) - obj->setNameURI(STRING_TO_JSVAL(uri)); + obj->setNameURI(uri); if (prefix) - obj->setNamePrefix(STRING_TO_JSVAL(prefix)); + obj->setNamePrefix(prefix); if (localName) - obj->setQNameLocalName(STRING_TO_JSVAL(localName)); + obj->setQNameLocalName(localName); } static JSObject * -NewXMLQName(JSContext *cx, JSString *uri, JSString *prefix, JSString *localName, - Class *clasp = &js_QNameClass) +NewXMLQName(JSContext *cx, JSLinearString *uri, JSLinearString *prefix, + JSLinearString *localName, Class *clasp = &js_QNameClass) { JSObject *obj = NewBuiltinClassInstanceXML(cx, clasp); if (!obj) @@ -585,7 +561,7 @@ IsXMLName(const jschar *cp, size_t n) JSBool js_IsXMLName(JSContext *cx, jsval v) { - JSString *name; + JSLinearString *name = NULL; JSErrorReporter older; /* @@ -596,10 +572,12 @@ js_IsXMLName(JSContext *cx, jsval v) */ if (!JSVAL_IS_PRIMITIVE(v) && JSVAL_TO_OBJECT(v)->isQName()) { - name = GetLocalName(JSVAL_TO_OBJECT(v)); + name = JSVAL_TO_OBJECT(v)->getQNameLocalName(); } else { older = JS_SetErrorReporter(cx, NULL); - name = js_ValueToString(cx, Valueify(v)); + JSString *str = js_ValueToString(cx, Valueify(v)); + if (str) + name = str->ensureLinear(cx); JS_SetErrorReporter(cx, older); if (!name) { JS_ClearPendingException(cx); @@ -622,7 +600,7 @@ NamespaceHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv, JSObject *uriobj; JSBool isNamespace, isQName; Class *clasp; - JSString *empty, *uri, *prefix; + JSLinearString *empty, *prefix, *uri; isNamespace = isQName = JS_FALSE; #ifdef __GNUC__ /* suppress bogus gcc warnings */ @@ -656,42 +634,47 @@ NamespaceHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv, METER(xml_stats.xmlnamespace); empty = cx->runtime->emptyString; - obj->setNamePrefix(STRING_TO_JSVAL(empty)); - obj->setNameURI(STRING_TO_JSVAL(empty)); + obj->setNamePrefix(empty); + obj->setNameURI(empty); if (argc == 1 || argc == -1) { if (isNamespace) { obj->setNameURI(uriobj->getNameURI()); obj->setNamePrefix(uriobj->getNamePrefix()); - } else if (isQName && (uri = GetURI(uriobj))) { - obj->setNameURI(STRING_TO_JSVAL(uri)); + } else if (isQName && (uri = uriobj->getNameURI())) { + obj->setNameURI(uri); obj->setNamePrefix(uriobj->getNamePrefix()); } else { - uri = js_ValueToString(cx, Valueify(urival)); + JSString *str = js_ValueToString(cx, Valueify(urival)); + if (!str) + return JS_FALSE; + uri = str->ensureLinear(cx); if (!uri) return JS_FALSE; - obj->setNameURI(STRING_TO_JSVAL(uri)); + obj->setNameURI(uri); if (!uri->empty()) - obj->setNamePrefix(JSVAL_VOID); + obj->clearNamePrefix(); } } else if (argc == 2) { - if (!isQName || !(uri = GetURI(uriobj))) { - uri = js_ValueToString(cx, Valueify(urival)); + if (!isQName || !(uri = uriobj->getNameURI())) { + JSString *str = js_ValueToString(cx, Valueify(urival)); + if (!str) + return JS_FALSE; + uri = str->ensureLinear(cx); if (!uri) return JS_FALSE; } - obj->setNameURI(STRING_TO_JSVAL(uri)); + obj->setNameURI(uri); prefixval = argv[0]; if (uri->empty()) { if (!JSVAL_IS_VOID(prefixval)) { - prefix = js_ValueToString(cx, Valueify(prefixval)); - if (!prefix) + JSString *str = js_ValueToString(cx, Valueify(prefixval)); + if (!str) return JS_FALSE; - if (!prefix->empty()) { - Value v = StringValue(prefix); + if (!str->empty()) { JSAutoByteString bytes; - if (js_ValueToPrintable(cx, v, &bytes)) { + if (js_ValueToPrintable(cx, StringValue(str), &bytes)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_XML_NAMESPACE, bytes.ptr()); } @@ -699,12 +682,15 @@ NamespaceHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv, } } } else if (JSVAL_IS_VOID(prefixval) || !js_IsXMLName(cx, prefixval)) { - obj->setNamePrefix(JSVAL_VOID); + obj->clearNamePrefix(); } else { - prefix = js_ValueToString(cx, Valueify(prefixval)); + JSString *str = js_ValueToString(cx, Valueify(prefixval)); + if (!str) + return JS_FALSE; + prefix = str->ensureLinear(cx); if (!prefix) return JS_FALSE; - obj->setNamePrefix(STRING_TO_JSVAL(prefix)); + obj->setNamePrefix(prefix); } } return JS_TRUE; @@ -729,7 +715,7 @@ QNameHelper(JSContext *cx, JSObject *obj, Class *clasp, intN argc, jsval nameval, nsval; JSBool isQName, isNamespace; JSObject *qn; - JSString *uri, *prefix, *name; + JSLinearString *uri, *prefix, *name; JSObject *obj2; JS_ASSERT(clasp == &js_QNameClass || @@ -767,14 +753,14 @@ QNameHelper(JSContext *cx, JSObject *obj, Class *clasp, intN argc, /* If namespace is not specified and name is a QName, clone it. */ qn = JSVAL_TO_OBJECT(nameval); if (argc == 1) { - uri = GetURI(qn); - prefix = GetPrefix(qn); - name = GetLocalName(qn); + uri = qn->getNameURI(); + prefix = qn->getNamePrefix(); + name = qn->getQNameLocalName(); goto out; } /* Namespace and qname were passed -- use the qname's localName. */ - nameval = qn->getQNameLocalName(); + nameval = qn->getQNameLocalNameVal(); } if (argc == 0) { @@ -782,7 +768,10 @@ QNameHelper(JSContext *cx, JSObject *obj, Class *clasp, intN argc, } else if (argc < 0) { name = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]); } else { - name = js_ValueToString(cx, Valueify(nameval)); + JSString *str = js_ValueToString(cx, Valueify(nameval)); + if (!str) + return JS_FALSE; + name = str->ensureLinear(cx); if (!name) return JS_FALSE; argv[argc > 1] = STRING_TO_JSVAL(name); @@ -802,7 +791,7 @@ QNameHelper(JSContext *cx, JSObject *obj, Class *clasp, intN argc, if (JSVAL_IS_NULL(nsval)) { /* NULL prefix represents *undefined* in ECMA-357 13.3.2 5(a). */ - uri = prefix = NULL; + prefix = uri = NULL; } else { /* * Inline specialization of the Namespace constructor called with @@ -823,14 +812,17 @@ QNameHelper(JSContext *cx, JSObject *obj, Class *clasp, intN argc, #endif if (isNamespace) { - uri = GetURI(obj2); - prefix = GetPrefix(obj2); - } else if (isQName && (uri = GetURI(obj2))) { + uri = obj2->getNameURI(); + prefix = obj2->getNamePrefix(); + } else if (isQName && (uri = obj2->getNameURI())) { JS_ASSERT(argc > 1); - prefix = GetPrefix(obj2); + prefix = obj2->getNamePrefix(); } else { JS_ASSERT(argc > 1); - uri = js_ValueToString(cx, Valueify(nsval)); + JSString *str = js_ValueToString(cx, Valueify(nsval)); + if (!str) + return JS_FALSE; + uri = str->ensureLinear(cx); if (!uri) return JS_FALSE; argv[0] = STRING_TO_JSVAL(uri); /* local root */ @@ -869,17 +861,17 @@ namespace_identity(const void *a, const void *b) { const JSObject *nsa = (const JSObject *) a; const JSObject *nsb = (const JSObject *) b; - JSString *prefixa = GetPrefix(nsa); - JSString *prefixb = GetPrefix(nsb); + JSLinearString *prefixa = nsa->getNamePrefix(); + JSLinearString *prefixb = nsb->getNamePrefix(); if (prefixa && prefixb) { - if (!js_EqualStrings(prefixa, prefixb)) + if (!EqualStrings(prefixa, prefixb)) return JS_FALSE; } else { if (prefixa || prefixb) return JS_FALSE; } - return js_EqualStrings(GetURI(nsa), GetURI(nsb)); + return EqualStrings(nsa->getNameURI(), nsb->getNameURI()); } static JSBool @@ -1180,16 +1172,17 @@ ParseNodeToQName(Parser *parser, JSParseNode *pn, JSXMLArray *inScopeNSes, JSBool isAttributeName) { JSContext *cx = parser->context; - JSString *str, *uri, *prefix, *localName; + JSLinearString *str, *uri, *prefix, *localName; size_t length, offset; const jschar *start, *limit, *colon; uint32 n; JSObject *ns; - JSString *nsprefix; + JSLinearString *nsprefix; JS_ASSERT(pn->pn_arity == PN_NULLARY); - str = ATOM_TO_STRING(pn->pn_atom); - str->getCharsAndLength(start, length); + str = pn->pn_atom; + start = str->chars(); + length = str->length(); JS_ASSERT(length != 0 && *start != '@'); JS_ASSERT(length != 1 || *start != '*'); @@ -1204,11 +1197,11 @@ ParseNodeToQName(Parser *parser, JSParseNode *pn, if (STARTS_WITH_XML(start, offset)) { if (offset == 3) { - uri = JS_InternString(cx, xml_namespace_str); + uri = JS_ASSERT_STRING_IS_FLAT(JS_InternString(cx, xml_namespace_str)); if (!uri) return NULL; } else if (offset == 5 && HAS_NS_AFTER_XML(start)) { - uri = JS_InternString(cx, xmlns_namespace_str); + uri = JS_ASSERT_STRING_IS_FLAT(JS_InternString(cx, xmlns_namespace_str)); if (!uri) return NULL; } else { @@ -1220,9 +1213,9 @@ ParseNodeToQName(Parser *parser, JSParseNode *pn, while (n != 0) { --n; ns = XMLARRAY_MEMBER(inScopeNSes, n, JSObject); - nsprefix = GetPrefix(ns); - if (nsprefix && js_EqualStrings(nsprefix, prefix)) { - uri = GetURI(ns); + nsprefix = ns->getNamePrefix(); + if (nsprefix && EqualStrings(nsprefix, prefix)) { + uri = ns->getNameURI(); break; } } @@ -1257,9 +1250,9 @@ ParseNodeToQName(Parser *parser, JSParseNode *pn, while (n != 0) { --n; ns = XMLARRAY_MEMBER(inScopeNSes, n, JSObject); - nsprefix = GetPrefix(ns); + nsprefix = ns->getNamePrefix(); if (!nsprefix || nsprefix->empty()) { - uri = GetURI(ns); + uri = ns->getNameURI(); break; } } @@ -1278,7 +1271,11 @@ ChompXMLWhitespace(JSContext *cx, JSString *str) const jschar *cp, *start, *end; jschar c; - str->getCharsAndLength(start, length); + length = str->length(); + start = str->getChars(cx); + if (!start) + return NULL; + for (cp = start, end = cp + length; cp < end; cp++) { c = *cp; if (!JS_ISXMLSPACE(c)) @@ -1303,7 +1300,7 @@ ParseNodeToXML(Parser *parser, JSParseNode *pn, { JSContext *cx = parser->context; JSXML *xml, *kid, *attr, *attrj; - JSString *str; + JSLinearString *str; uint32 length, n, i, j; JSParseNode *pn2, *pn3, *head, **pnp; JSObject *ns; @@ -1372,7 +1369,7 @@ ParseNodeToXML(Parser *parser, JSParseNode *pn, /* XXX where is this documented in an XML spec, or in E4X? */ if ((flags & XSF_IGNORE_WHITESPACE) && n > 1 && kid->xml_class == JSXML_CLASS_TEXT) { - str = ChompXMLWhitespace(cx, kid->xml_value); + JSString *str = ChompXMLWhitespace(cx, kid->xml_value); if (!str) goto fail; kid->xml_value = str; @@ -1461,17 +1458,18 @@ ParseNodeToXML(Parser *parser, JSParseNode *pn, } } - str = ATOM_TO_STRING(pn2->pn_atom); + JSAtom *atom = pn2->pn_atom; pn2 = pn2->pn_next; JS_ASSERT(pn2); if (pn2->pn_type != TOK_XMLATTR) goto syntax; - str->getCharsAndLength(chars, length); + chars = atom->chars(); + length = atom->length(); if (length >= 5 && IS_XMLNS_CHARS(chars) && (length == 5 || chars[5] == ':')) { - JSString *uri, *prefix; + JSLinearString *uri, *prefix; uri = ATOM_TO_STRING(pn2->pn_atom); if (length == 5) { @@ -1545,8 +1543,8 @@ ParseNodeToXML(Parser *parser, JSParseNode *pn, for (j = 0; j < i; j++) { attrj = XMLARRAY_MEMBER(&xml->xml_attrs, j, JSXML); attrjqn = attrj->name; - if (js_EqualStrings(GetURI(attrjqn), GetURI(qn)) && - js_EqualStrings(GetLocalName(attrjqn), GetLocalName(qn))) { + if (EqualStrings(attrjqn->getNameURI(), qn->getNameURI()) && + EqualStrings(attrjqn->getQNameLocalName(), qn->getQNameLocalName())) { Value v = StringValue(ATOM_TO_STRING(pn2->pn_atom)); JSAutoByteString bytes; if (js_ValueToPrintable(cx, v, &bytes)) { @@ -1701,7 +1699,7 @@ static JSXML * ParseXMLSource(JSContext *cx, JSString *src) { jsval nsval; - JSString *uri; + JSLinearString *uri; size_t urilen, srclen, length, offset, dstlen; jschar *chars; const jschar *srcp, *endp; @@ -1718,7 +1716,7 @@ ParseXMLSource(JSContext *cx, JSString *src) if (!js_GetDefaultXMLNamespace(cx, &nsval)) return NULL; - uri = GetURI(JSVAL_TO_OBJECT(nsval)); + uri = JSVAL_TO_OBJECT(nsval)->getNameURI(); uri = js_EscapeAttributeValue(cx, uri, JS_FALSE); if (!uri) return NULL; @@ -1741,7 +1739,11 @@ ParseXMLSource(JSContext *cx, JSString *src) js_InflateStringToBuffer(cx, middle, constrlen(middle), chars + offset, &dstlen); offset += dstlen; - srcp = src->chars(); + srcp = src->getChars(cx); + if (!srcp) { + cx->free(chars); + return NULL; + } js_strncpy(chars + offset, srcp, srclen); offset += srclen; dstlen = length - offset + 1; @@ -1773,8 +1775,10 @@ ParseXMLSource(JSContext *cx, JSString *src) Parser parser(cx); if (parser.init(chars, length, filename, lineno)) { JSObject *scopeChain = GetScopeChain(cx); - if (!scopeChain) + if (!scopeChain) { + cx->free(chars); return NULL; + } JSParseNode *pn = parser.parseXMLText(scopeChain, false); uintN flags; if (pn && GetXMLSettingFlags(cx, &flags)) { @@ -2071,9 +2075,10 @@ AppendAttributeValue(JSContext *cx, JSCharBuffer &cb, JSString *valstr) static JSString * EscapeElementValue(JSContext *cx, JSCharBuffer &cb, JSString *str, uint32 toSourceFlag) { - size_t length; - const jschar *start; - str->getCharsAndLength(start, length); + size_t length = str->length(); + const jschar *start = str->getChars(cx); + if (!start) + return NULL; for (const jschar *cp = start, *end = start + length; cp != end; ++cp) { jschar c = *cp; @@ -2114,13 +2119,14 @@ EscapeElementValue(JSContext *cx, JSCharBuffer &cb, JSString *str, uint32 toSour * * These functions mutate cb, leaving it empty. */ -static JSString * +static JSLinearString * EscapeAttributeValue(JSContext *cx, JSCharBuffer &cb, JSString *str, JSBool quote) { - size_t length; - const jschar *start; - str->getCharsAndLength(start, length); + size_t length = str->length(); + const jschar *start = str->getChars(cx); + if (!start) + return NULL; if (quote && !cb.append('"')) return NULL; @@ -2168,13 +2174,13 @@ EscapeAttributeValue(JSContext *cx, JSCharBuffer &cb, JSString *str, static JSObject * GetNamespace(JSContext *cx, JSObject *qn, const JSXMLArray *inScopeNSes) { - JSString *uri, *prefix, *nsprefix; + JSLinearString *uri, *prefix, *nsprefix; JSObject *match, *ns; uint32 i, n; jsval argv[2]; - uri = GetURI(qn); - prefix = GetPrefix(qn); + uri = qn->getNameURI(); + prefix = qn->getNamePrefix(); JS_ASSERT(uri); if (!uri) { JSAutoByteString bytes; @@ -2221,11 +2227,11 @@ GetNamespace(JSContext *cx, JSObject *qn, const JSXMLArray *inScopeNSes) * This spec bug leads to ToXMLString results that duplicate the * declared namespace. */ - if (js_EqualStrings(GetURI(ns), uri)) { - nsprefix = GetPrefix(ns); + if (EqualStrings(ns->getNameURI(), uri)) { + nsprefix = ns->getNamePrefix(); if (nsprefix == prefix || ((nsprefix && prefix) - ? js_EqualStrings(nsprefix, prefix) + ? EqualStrings(nsprefix, prefix) : (nsprefix ? nsprefix : prefix)->empty())) { match = ns; break; @@ -2247,8 +2253,8 @@ GetNamespace(JSContext *cx, JSObject *qn, const JSXMLArray *inScopeNSes) return match; } -static JSString * -GeneratePrefix(JSContext *cx, JSString *uri, JSXMLArray *decls) +static JSLinearString * +GeneratePrefix(JSContext *cx, JSLinearString *uri, JSXMLArray *decls) { const jschar *cp, *start, *end; size_t length, newlength, offset; @@ -2256,7 +2262,7 @@ GeneratePrefix(JSContext *cx, JSString *uri, JSXMLArray *decls) jschar *bp, *dp; JSBool done; JSObject *ns; - JSString *nsprefix, *prefix; + JSLinearString *nsprefix, *prefix; JS_ASSERT(!uri->empty()); @@ -2272,7 +2278,7 @@ GeneratePrefix(JSContext *cx, JSString *uri, JSXMLArray *decls) * This is necessary for various log10 uses below to be valid. */ if (decls->length == 0) - return JS_NewStringCopyZ(cx, "a"); + return js_NewStringCopyZ(cx, "a"); /* * Try peeling off the last filename suffix or pathname component till @@ -2280,7 +2286,8 @@ GeneratePrefix(JSContext *cx, JSString *uri, JSXMLArray *decls) * ".../there.is.only.xul", "xbl" given ".../xbl", and "xbl2" given any * likely URI of the form ".../xbl2/2005". */ - uri->getCharsAndEnd(start, end); + start = uri->chars(); + end = start + uri->length(); cp = end; while (--cp > start) { if (*cp == '.' || *cp == '/' || *cp == ':') { @@ -2323,7 +2330,7 @@ GeneratePrefix(JSContext *cx, JSString *uri, JSXMLArray *decls) done = JS_TRUE; for (i = 0, n = decls->length; i < n; i++) { ns = XMLARRAY_MEMBER(decls, i, JSObject); - if (ns && (nsprefix = GetPrefix(ns)) && + if (ns && (nsprefix = ns->getNamePrefix()) && nsprefix->length() == newlength && !memcmp(nsprefix->chars(), bp, newlength * sizeof(jschar))) { @@ -2367,13 +2374,13 @@ namespace_match(const void *a, const void *b) { const JSObject *nsa = (const JSObject *) a; const JSObject *nsb = (const JSObject *) b; - JSString *prefixa, *prefixb = GetPrefix(nsb); + JSLinearString *prefixa, *prefixb = nsb->getNamePrefix(); if (prefixb) { - prefixa = GetPrefix(nsa); - return prefixa && js_EqualStrings(prefixa, prefixb); + prefixa = nsa->getNamePrefix(); + return prefixa && EqualStrings(prefixa, prefixb); } - return js_EqualStrings(GetURI(nsa), GetURI(nsb)); + return EqualStrings(nsa->getNameURI(), nsb->getNameURI()); } /* ECMA-357 10.2.1 and 10.2.2 */ @@ -2385,7 +2392,8 @@ XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, { JSBool pretty, indentKids; JSCharBuffer cb(cx); - JSString *str, *prefix, *nsuri; + JSString *str; + JSLinearString *prefix, *nsuri; uint32 i, n, nextIndentLevel; JSObject *ns, *ns2; AutoNamespaceArray empty(cx), decls(cx), ancdecls(cx); @@ -2423,7 +2431,7 @@ XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, case JSXML_CLASS_PROCESSING_INSTRUCTION: /* Step 7. */ - return MakeXMLPIString(cx, cb, GetLocalName(xml->name), + return MakeXMLPIString(cx, cb, xml->name->getQNameLocalName(), xml->xml_value); case JSXML_CLASS_LIST: @@ -2467,7 +2475,7 @@ XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, continue; if (!XMLARRAY_HAS_MEMBER(ancestorNSes, ns, namespace_identity)) { /* NOTE: may want to exclude unused namespaces here. */ - ns2 = NewXMLNamespace(cx, GetPrefix(ns), GetURI(ns), JS_TRUE); + ns2 = NewXMLNamespace(cx, ns->getNamePrefix(), ns->getNameURI(), JS_TRUE); if (!ns2 || !XMLARRAY_APPEND(cx, &decls.array, ns2)) goto out; } @@ -2505,7 +2513,7 @@ XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, goto out; /* Step 12 (NULL means *undefined* here), plus the deferred ns cloning. */ - prefix = GetPrefix(ns); + prefix = ns->getNamePrefix(); if (!prefix) { /* * Create a namespace prefix that isn't used by any member of decls. @@ -2521,8 +2529,8 @@ XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, * This helps descendants inherit the namespace instead of redundantly * redeclaring it with generated prefixes in each descendant. */ - nsuri = GetURI(ns); - if (!GetPrefix(xml->name)) { + nsuri = ns->getNameURI(); + if (!xml->name->getNamePrefix()) { prefix = cx->runtime->emptyString; } else { prefix = GeneratePrefix(cx, nsuri, &ancdecls.array); @@ -2569,11 +2577,11 @@ XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, if (!cb.append('<')) goto out; - if (prefix && !prefix->empty()) { + if (!prefix->empty()) { if (!AppendString(cb, prefix) || !cb.append(':')) goto out; } - if (!AppendString(cb, GetLocalName(xml->name))) + if (!AppendString(cb, xml->name->getQNameLocalName())) goto out; /* @@ -2593,14 +2601,14 @@ XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, goto out; /* 17(b)(ii): NULL means *undefined* here. */ - prefix = GetPrefix(ns2); + prefix = ns2->getNamePrefix(); if (!prefix) { - prefix = GeneratePrefix(cx, GetURI(ns2), &ancdecls.array); + prefix = GeneratePrefix(cx, ns2->getNameURI(), &ancdecls.array); if (!prefix) goto out; /* Again, we avoid copying ns2 until we know it's prefix-less. */ - ns2 = NewXMLNamespace(cx, prefix, GetURI(ns2), JS_TRUE); + ns2 = NewXMLNamespace(cx, prefix, ns2->getNameURI(), JS_TRUE); if (!ns2) goto out; @@ -2624,7 +2632,7 @@ XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, } /* 17(b)(iv). */ - if (!AppendString(cb, GetLocalName(attr->name))) + if (!AppendString(cb, attr->name->getQNameLocalName())) goto out; /* 17(d-g). */ @@ -2643,12 +2651,12 @@ XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, goto out; /* 17(c)(ii): NULL means *undefined* here. */ - prefix = GetPrefix(ns3); + prefix = ns3->getNamePrefix(); if (!prefix) { - prefix = GeneratePrefix(cx, GetURI(ns3), &ancdecls.array); + prefix = GeneratePrefix(cx, ns3->getNameURI(), &ancdecls.array); if (!prefix) goto out; - ns3->setNamePrefix(STRING_TO_JSVAL(prefix)); + ns3->setNamePrefix(prefix); } /* 17(c)(iii). */ @@ -2658,7 +2666,7 @@ XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, } /* 17(d-g). */ - if (!AppendAttributeValue(cx, cb, GetURI(ns3))) + if (!AppendAttributeValue(cx, cb, ns3->getNameURI())) goto out; } } @@ -2714,14 +2722,14 @@ XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, goto out; /* Step 26. */ - prefix = GetPrefix(ns); + prefix = ns->getNamePrefix(); if (prefix && !prefix->empty()) { if (!AppendString(cb, prefix) || !cb.append(':')) goto out; } /* Step 27. */ - if (!AppendString(cb, GetLocalName(xml->name)) || !cb.append('>')) + if (!AppendString(cb, xml->name->getQNameLocalName()) || !cb.append('>')) goto out; } @@ -2773,13 +2781,15 @@ ToXMLString(JSContext *cx, jsval v, uint32 toSourceFlag) static JSObject * ToAttributeName(JSContext *cx, jsval v) { - JSString *name, *uri, *prefix; + JSLinearString *name, *uri, *prefix; JSObject *obj; Class *clasp; JSObject *qn; if (JSVAL_IS_STRING(v)) { - name = JSVAL_TO_STRING(v); + name = JSVAL_TO_STRING(v)->ensureLinear(cx); + if (!name) + return NULL; uri = prefix = cx->runtime->emptyString; } else { if (JSVAL_IS_PRIMITIVE(v)) { @@ -2795,14 +2805,17 @@ ToAttributeName(JSContext *cx, jsval v) if (clasp == &js_QNameClass) { qn = obj; - uri = GetURI(qn); - prefix = GetPrefix(qn); - name = GetLocalName(qn); + uri = qn->getNameURI(); + prefix = qn->getNamePrefix(); + name = qn->getQNameLocalName(); } else { if (clasp == &js_AnyNameClass) { name = ATOM_TO_STRING(cx->runtime->atomState.starAtom); } else { - name = js_ValueToString(cx, Valueify(v)); + JSString *str = js_ValueToString(cx, Valueify(v)); + if (!str) + return NULL; + name = str->ensureLinear(cx); if (!name) return NULL; } @@ -2826,14 +2839,14 @@ static JSBool IsFunctionQName(JSContext *cx, JSObject *qn, jsid *funidp) { JSAtom *atom; - JSString *uri; + JSLinearString *uri; atom = cx->runtime->atomState.functionNamespaceURIAtom; - uri = GetURI(qn); + uri = qn->getNameURI(); if (uri && (uri == ATOM_TO_STRING(atom) || - js_EqualStrings(uri, ATOM_TO_STRING(atom)))) { - return JS_ValueToId(cx, STRING_TO_JSVAL(GetLocalName(qn)), funidp); + EqualStrings(uri, ATOM_TO_STRING(atom)))) { + return JS_ValueToId(cx, STRING_TO_JSVAL(qn->getQNameLocalName()), funidp); } *funidp = JSID_VOID; return JS_TRUE; @@ -2898,7 +2911,7 @@ ToXMLName(JSContext *cx, jsval v, jsid *funidp) if (js_IdIsIndex(ATOM_TO_JSID(atomizedName), &index)) goto bad; - if (*name->chars() == '@') { + if (*atomizedName->chars() == '@') { name = js_NewDependentString(cx, name, 1, name->length() - 1); if (!name) return NULL; @@ -2928,7 +2941,7 @@ bad: static JSBool AddInScopeNamespace(JSContext *cx, JSXML *xml, JSObject *ns) { - JSString *prefix, *prefix2; + JSLinearString *prefix, *prefix2; JSObject *match, *ns2; uint32 i, n, m; @@ -2936,12 +2949,12 @@ AddInScopeNamespace(JSContext *cx, JSXML *xml, JSObject *ns) return JS_TRUE; /* NULL means *undefined* here -- see ECMA-357 9.1.1.13 step 2. */ - prefix = GetPrefix(ns); + prefix = ns->getNamePrefix(); if (!prefix) { match = NULL; for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject); - if (ns2 && js_EqualStrings(GetURI(ns2), GetURI(ns))) { + if (ns2 && EqualStrings(ns2->getNameURI(), ns->getNameURI())) { match = ns2; break; } @@ -2949,7 +2962,7 @@ AddInScopeNamespace(JSContext *cx, JSXML *xml, JSObject *ns) if (!match && !XMLARRAY_ADD_MEMBER(cx, &xml->xml_namespaces, n, ns)) return JS_FALSE; } else { - if (prefix->empty() && GetURI(xml->name)->empty()) + if (prefix->empty() && xml->name->getNameURI()->empty()) return JS_TRUE; match = NULL; #ifdef __GNUC__ /* suppress bogus gcc warnings */ @@ -2957,18 +2970,18 @@ AddInScopeNamespace(JSContext *cx, JSXML *xml, JSObject *ns) #endif for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject); - if (ns2 && (prefix2 = GetPrefix(ns2)) && - js_EqualStrings(prefix2, prefix)) { + if (ns2 && (prefix2 = ns2->getNamePrefix()) && + EqualStrings(prefix2, prefix)) { match = ns2; m = i; break; } } - if (match && !js_EqualStrings(GetURI(match), GetURI(ns))) { + if (match && !EqualStrings(match->getNameURI(), ns->getNameURI())) { ns2 = XMLARRAY_DELETE(cx, &xml->xml_namespaces, m, JS_TRUE, JSObject); JS_ASSERT(ns2 == match); - match->setNamePrefix(JSVAL_VOID); + match->clearNamePrefix(); if (!AddInScopeNamespace(cx, xml, match)) return JS_FALSE; } @@ -3116,7 +3129,7 @@ DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags) return NULL; qn = xml->name; if (qn) { - qn = NewXMLQName(cx, GetURI(qn), GetPrefix(qn), GetLocalName(qn)); + qn = NewXMLQName(cx, qn->getNameURI(), qn->getNamePrefix(), qn->getQNameLocalName()); if (!qn) { ok = JS_FALSE; goto out; @@ -3145,7 +3158,7 @@ DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags) ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject); if (!ns) continue; - ns2 = NewXMLNamespace(cx, GetPrefix(ns), GetURI(ns), + ns2 = NewXMLNamespace(cx, ns->getNamePrefix(), ns->getNameURI(), IsDeclared(ns)); if (!ns2) { copy->xml_namespaces.length = i; @@ -3188,27 +3201,27 @@ static JSBool MatchAttrName(JSObject *nameqn, JSXML *attr) { JSObject *attrqn = attr->name; - JSString *localName = GetLocalName(nameqn); - JSString *uri; + JSLinearString *localName = nameqn->getQNameLocalName(); + JSLinearString *uri; return (IS_STAR(localName) || - js_EqualStrings(GetLocalName(attrqn), localName)) && - (!(uri = GetURI(nameqn)) || - js_EqualStrings(GetURI(attrqn), uri)); + EqualStrings(attrqn->getQNameLocalName(), localName)) && + (!(uri = nameqn->getNameURI()) || + EqualStrings(attrqn->getNameURI(), uri)); } static JSBool MatchElemName(JSObject *nameqn, JSXML *elem) { - JSString *localName = GetLocalName(nameqn); - JSString *uri; + JSLinearString *localName = nameqn->getQNameLocalName(); + JSLinearString *uri; return (IS_STAR(localName) || (elem->xml_class == JSXML_CLASS_ELEMENT && - js_EqualStrings(GetLocalName(elem->name), localName))) && - (!(uri = GetURI(nameqn)) || + EqualStrings(elem->name->getQNameLocalName(), localName))) && + (!(uri = nameqn->getNameURI()) || (elem->xml_class == JSXML_CLASS_ELEMENT && - js_EqualStrings(GetURI(elem->name), uri))); + EqualStrings(elem->name->getNameURI(), uri))); } /* ECMA-357 9.1.1.8 XML [[Descendants]] and 9.2.1.8 XMLList [[Descendants]]. */ @@ -3325,8 +3338,8 @@ retry: vqn = vxml->name; if (qn) { *bp = vqn && - js_EqualStrings(GetLocalName(qn), GetLocalName(vqn)) && - js_EqualStrings(GetURI(qn), GetURI(vqn)); + EqualStrings(qn->getQNameLocalName(), vqn->getQNameLocalName()) && + EqualStrings(qn->getNameURI(), vqn->getNameURI()); } else { *bp = vqn == NULL; } @@ -3334,7 +3347,8 @@ retry: return JS_TRUE; if (JSXML_HAS_VALUE(xml)) { - *bp = js_EqualStrings(xml->xml_value, vxml->xml_value); + if (!EqualStrings(cx, xml->xml_value, vxml->xml_value, bp)) + return JS_FALSE; } else if (xml->xml_kids.length != vxml->xml_kids.length) { *bp = JS_FALSE; } else { @@ -3374,7 +3388,8 @@ retry: vattr = XMLARRAY_MEMBER(&vxml->xml_attrs, j, JSXML); if (!vattr) continue; - *bp = js_EqualStrings(attr->xml_value, vattr->xml_value); + if (!EqualStrings(cx, attr->xml_value, vattr->xml_value, bp)) + return JS_FALSE; } } } @@ -3853,7 +3868,6 @@ PutProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) uint32 index, i, j, k, n, q, matchIndex; jsval attrval, nsval; jsid funid; - JSString *left, *right, *space, *uri; JSObject *ns; xml = (JSXML *) GetInstancePrivate(cx, obj, &js_XMLClass, NULL); @@ -3943,7 +3957,7 @@ PutProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) /* 2(c)(ii) is distributed below as several js_NewXML calls. */ targetprop = xml->xml_targetprop; - if (!targetprop || IS_STAR(GetLocalName(targetprop))) { + if (!targetprop || IS_STAR(targetprop->getQNameLocalName())) { /* 2(c)(iv)(1-2), out of order w.r.t. 2(c)(iii). */ kid = js_NewXML(cx, JSXML_CLASS_TEXT); if (!kid) @@ -4047,8 +4061,8 @@ PutProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) { nameobj = kid->name; if (nameobj->getClass() != &js_AttributeNameClass) { - nameobj = NewXMLQName(cx, GetURI(nameobj), GetPrefix(nameobj), - GetLocalName(nameobj), + nameobj = NewXMLQName(cx, nameobj->getNameURI(), nameobj->getNamePrefix(), + nameobj->getQNameLocalName(), &js_AttributeNameClass); if (!nameobj) goto bad; @@ -4265,16 +4279,16 @@ PutProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) if (n == 0) { *vp = STRING_TO_JSVAL(cx->runtime->emptyString); } else { - left = KidToString(cx, vxml, 0); + JSString *left = KidToString(cx, vxml, 0); if (!left) goto bad; - space = ATOM_TO_STRING(cx->runtime->atomState.spaceAtom); + JSString *space = cx->runtime->atomState.spaceAtom; for (i = 1; i < n; i++) { left = js_ConcatStrings(cx, left, space); if (!left) goto bad; - right = KidToString(cx, vxml, i); + JSString *right = KidToString(cx, vxml, i); if (!right) goto bad; left = js_ConcatStrings(cx, left, right); @@ -4298,10 +4312,9 @@ PutProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) if (!attr) continue; attrqn = attr->name; - if (js_EqualStrings(GetLocalName(attrqn), - GetLocalName(nameqn))) { - uri = GetURI(nameqn); - if (!uri || js_EqualStrings(GetURI(attrqn), uri)) { + if (EqualStrings(attrqn->getQNameLocalName(), nameqn->getQNameLocalName())) { + JSLinearString *uri = nameqn->getNameURI(); + if (!uri || EqualStrings(attrqn->getNameURI(), uri)) { if (!match) { match = attr; } else { @@ -4316,14 +4329,15 @@ PutProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) attr = match; if (!attr) { /* 7(f)(i-ii). */ - uri = GetURI(nameqn); + JSLinearString *uri = nameqn->getNameURI(); + JSLinearString *left, *right; if (!uri) { left = right = cx->runtime->emptyString; } else { left = uri; - right = GetPrefix(nameqn); + right = nameqn->getNamePrefix(); } - nameqn = NewXMLQName(cx, left, right, GetLocalName(nameqn)); + nameqn = NewXMLQName(cx, left, right, nameqn->getQNameLocalName()); if (!nameqn) goto bad; @@ -4355,13 +4369,13 @@ PutProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) /* 8-9. */ if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj)) && - !IS_STAR(GetLocalName(nameqn))) { + !IS_STAR(nameqn->getQNameLocalName())) { goto out; } /* 10-11. */ id = JSID_VOID; - primitiveAssign = !vxml && !IS_STAR(GetLocalName(nameqn)); + primitiveAssign = !vxml && !IS_STAR(nameqn->getQNameLocalName()); /* 12. */ k = n = xml->xml_kids.length; @@ -4407,16 +4421,17 @@ PutProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) /* 13(b). */ if (primitiveAssign) { - uri = GetURI(nameqn); + JSLinearString *uri = nameqn->getNameURI(); + JSLinearString *left, *right; if (!uri) { ns = JSVAL_TO_OBJECT(nsval); - left = GetURI(ns); - right = GetPrefix(ns); + left = ns->getNameURI(); + right = ns->getNamePrefix(); } else { left = uri; - right = GetPrefix(nameqn); + right = nameqn->getNamePrefix(); } - nameqn = NewXMLQName(cx, left, right, GetLocalName(nameqn)); + nameqn = NewXMLQName(cx, left, right, nameqn->getQNameLocalName()); if (!nameqn) goto bad; @@ -4501,7 +4516,7 @@ ResolveValue(JSContext *cx, JSXML *list, JSXML **result) target = list->xml_target; targetprop = list->xml_targetprop; - if (!target || !targetprop || IS_STAR(GetLocalName(targetprop))) { + if (!target || !targetprop || IS_STAR(targetprop->getQNameLocalName())) { *result = NULL; return JS_TRUE; } @@ -4631,12 +4646,16 @@ static JSBool HasProperty(JSContext *cx, JSObject *obj, jsval id, JSBool *found) { JSXML *xml; + bool isIndex; uint32 i; JSObject *qn; jsid funid; xml = (JSXML *) obj->getPrivate(); - if (js_IdValIsIndex(id, &i)) { + if (!js_IdValIsIndex(cx, id, &i, &isIndex)) + return JS_FALSE; + + if (isIndex) { *found = HasIndexedProperty(xml, i); } else { qn = ToXMLName(cx, id, &funid); @@ -5039,7 +5058,7 @@ js_TestXMLEquality(JSContext *cx, const Value &v1, const Value &v2, JSBool *bp) ok = (str = js_ValueToString(cx, ObjectValue(*obj))) && (vstr = js_ValueToString(cx, Valueify(v))); if (ok) - *bp = js_EqualStrings(str, vstr); + ok = EqualStrings(cx, str, vstr, bp); js_LeaveLocalRootScope(cx); } } else { @@ -5053,13 +5072,13 @@ js_TestXMLEquality(JSContext *cx, const Value &v1, const Value &v2, JSBool *bp) ok = (str = js_ValueToString(cx, ObjectValue(*obj))) && (vstr = js_ValueToString(cx, Valueify(v))); if (ok) - *bp = js_EqualStrings(str, vstr); + ok = EqualStrings(cx, str, vstr, bp); } else if (JSVAL_IS_STRING(v) || JSVAL_IS_NUMBER(v)) { str = js_ValueToString(cx, ObjectValue(*obj)); if (!str) { ok = JS_FALSE; } else if (JSVAL_IS_STRING(v)) { - *bp = js_EqualStrings(str, JSVAL_TO_STRING(v)); + ok = EqualStrings(cx, str, JSVAL_TO_STRING(v), bp); } else { ok = JS_ValueToNumber(cx, STRING_TO_JSVAL(str), &d); if (ok) { @@ -5338,6 +5357,7 @@ static JSBool xml_child_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval name, jsval *rval) { + bool isIndex; uint32 index; JSXML *kid; JSObject *kidobj; @@ -5345,7 +5365,10 @@ xml_child_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval name, /* ECMA-357 13.4.4.6 */ JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST); - if (js_IdValIsIndex(name, &index)) { + if (!js_IdValIsIndex(cx, name, &index, &isIndex)) + return JS_FALSE; + + if (isIndex) { if (index >= JSXML_LENGTH(xml)) { *rval = JSVAL_VOID; } else { @@ -5731,7 +5754,7 @@ FindInScopeNamespaces(JSContext *cx, JSXML *xml, JSXMLArray *nsarray) { uint32 length, i, j, n; JSObject *ns, *ns2; - JSString *prefix, *prefix2; + JSLinearString *prefix, *prefix2; length = nsarray->length; do { @@ -5742,14 +5765,14 @@ FindInScopeNamespaces(JSContext *cx, JSXML *xml, JSXMLArray *nsarray) if (!ns) continue; - prefix = GetPrefix(ns); + prefix = ns->getNamePrefix(); for (j = 0; j < length; j++) { ns2 = XMLARRAY_MEMBER(nsarray, j, JSObject); if (ns2) { - prefix2 = GetPrefix(ns2); + prefix2 = ns2->getNamePrefix(); if ((prefix2 && prefix) - ? js_EqualStrings(prefix2, prefix) - : js_EqualStrings(GetURI(ns2), GetURI(ns))) { + ? EqualStrings(prefix2, prefix) + : EqualStrings(ns2->getNameURI(), ns->getNameURI())) { break; } } @@ -5885,7 +5908,7 @@ static JSBool xml_localName(JSContext *cx, uintN argc, jsval *vp) { NON_LIST_XML_METHOD_PROLOG; - *vp = xml->name ? xml->name->getQNameLocalName() : JSVAL_NULL; + *vp = xml->name ? xml->name->getQNameLocalNameVal() : JSVAL_NULL; return JS_TRUE; } @@ -5900,7 +5923,7 @@ xml_name(JSContext *cx, uintN argc, jsval *vp) static JSBool xml_namespace(JSContext *cx, uintN argc, jsval *vp) { - JSString *prefix, *nsprefix; + JSLinearString *prefix, *nsprefix; jsuint i, length; JSObject *ns; @@ -5913,7 +5936,10 @@ xml_namespace(JSContext *cx, uintN argc, jsval *vp) if (argc == 0) { prefix = NULL; } else { - prefix = js_ValueToString(cx, Valueify(vp[2])); + JSString *str = js_ValueToString(cx, Valueify(vp[2])); + if (!str) + return false; + prefix = str->ensureLinear(cx); if (!prefix) return false; vp[2] = STRING_TO_JSVAL(prefix); /* local root */ @@ -5932,8 +5958,8 @@ xml_namespace(JSContext *cx, uintN argc, jsval *vp) for (i = 0, length = inScopeNSes.array.length; i < length; i++) { ns = XMLARRAY_MEMBER(&inScopeNSes.array, i, JSObject); if (ns) { - nsprefix = GetPrefix(ns); - if (nsprefix && js_EqualStrings(nsprefix, prefix)) + nsprefix = ns->getNamePrefix(); + if (nsprefix && EqualStrings(nsprefix, prefix)) break; ns = NULL; } @@ -6119,7 +6145,6 @@ xml_processingInstructions_helper(JSContext *cx, JSObject *obj, JSXML *xml, JSObject *kidobj; jsval v; uint32 i, n; - JSString *localName; list = xml_list_helper(cx, xml, vp); if (!list) @@ -6160,9 +6185,9 @@ xml_processingInstructions_helper(JSContext *cx, JSObject *obj, JSXML *xml, for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { JSXML *kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); if (kid && kid->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) { - localName = GetLocalName(nameqn); + JSLinearString *localName = nameqn->getQNameLocalName(); if (IS_STAR(localName) || - js_EqualStrings(localName, GetLocalName(kid->name))) { + EqualStrings(localName, kid->name->getQNameLocalName())) { ok = Append(cx, list, kid); if (!ok) break; @@ -6210,17 +6235,23 @@ xml_prependChild(JSContext *cx, uintN argc, jsval *vp) static JSBool xml_propertyIsEnumerable(JSContext *cx, uintN argc, jsval *vp) { + bool isIndex; uint32 index; XML_METHOD_PROLOG; *vp = JSVAL_FALSE; - if (argc != 0 && js_IdValIsIndex(vp[2], &index)) { - if (xml->xml_class == JSXML_CLASS_LIST) { - /* 13.5.4.18. */ - *vp = BOOLEAN_TO_JSVAL(index < xml->xml_kids.length); - } else { - /* 13.4.4.30. */ - *vp = BOOLEAN_TO_JSVAL(index == 0); + if (argc != 0) { + if (!js_IdValIsIndex(cx, vp[2], &index, &isIndex)) + return JS_FALSE; + + if (isIndex) { + if (xml->xml_class == JSXML_CLASS_LIST) { + /* 13.5.4.18. */ + *vp = BOOLEAN_TO_JSVAL(index < xml->xml_kids.length); + } else { + /* 13.4.4.30. */ + *vp = BOOLEAN_TO_JSVAL(index == 0); + } } } return JS_TRUE; @@ -6231,15 +6262,15 @@ namespace_full_match(const void *a, const void *b) { const JSObject *nsa = (const JSObject *) a; const JSObject *nsb = (const JSObject *) b; - JSString *prefixa = GetPrefix(nsa); - JSString *prefixb; + JSLinearString *prefixa = nsa->getNamePrefix(); + JSLinearString *prefixb; if (prefixa) { - prefixb = GetPrefix(nsb); - if (prefixb && !js_EqualStrings(prefixa, prefixb)) + prefixb = nsb->getNamePrefix(); + if (prefixb && !EqualStrings(prefixa, prefixb)) return JS_FALSE; } - return js_EqualStrings(GetURI(nsa), GetURI(nsb)); + return EqualStrings(nsa->getNameURI(), nsb->getNameURI()); } static JSBool @@ -6338,7 +6369,15 @@ xml_replace(JSContext *cx, uintN argc, jsval *vp) if (!xml) return JS_FALSE; - if (argc == 0 || !js_IdValIsIndex(vp[2], &index)) { + bool haveIndex; + if (argc == 0) { + haveIndex = true; + } else { + if (!js_IdValIsIndex(cx, vp[2], &index, &haveIndex)) + return JS_FALSE; + } + + if (!haveIndex) { /* * Call function QName per spec, not ToXMLName, to avoid attribute * names. @@ -6395,7 +6434,7 @@ xml_setLocalName(JSContext *cx, uintN argc, jsval *vp) { jsval name; JSObject *nameqn; - JSString *namestr; + JSLinearString *namestr; NON_LIST_XML_METHOD_PROLOG; if (!JSXML_HAS_NAME(xml)) @@ -6408,19 +6447,22 @@ xml_setLocalName(JSContext *cx, uintN argc, jsval *vp) if (!JSVAL_IS_PRIMITIVE(name) && JSVAL_TO_OBJECT(name)->getClass() == &js_QNameClass) { nameqn = JSVAL_TO_OBJECT(name); - namestr = GetLocalName(nameqn); + namestr = nameqn->getQNameLocalName(); } else { if (!JS_ConvertValue(cx, name, JSTYPE_STRING, &vp[2])) return JS_FALSE; name = vp[2]; - namestr = JSVAL_TO_STRING(name); + namestr = JSVAL_TO_STRING(name)->ensureLinear(cx); + if (!namestr) + return JS_FALSE; } } xml = CHECK_COPY_ON_WRITE(cx, xml, obj); if (!xml) return JS_FALSE; - xml->name->setQNameLocalName(namestr ? STRING_TO_JSVAL(namestr) : JSVAL_VOID); + if (namestr) + xml->name->setQNameLocalName(namestr); return JS_TRUE; } @@ -6444,8 +6486,8 @@ xml_setName(JSContext *cx, uintN argc, jsval *vp) name = vp[2]; if (!JSVAL_IS_PRIMITIVE(name) && JSVAL_TO_OBJECT(name)->getClass() == &js_QNameClass && - !GetURI(nameqn = JSVAL_TO_OBJECT(name))) { - name = vp[2] = nameqn->getQNameLocalName(); + !(nameqn = JSVAL_TO_OBJECT(name))->getNameURI()) { + name = vp[2] = nameqn->getQNameLocalNameVal(); } } @@ -6455,7 +6497,7 @@ xml_setName(JSContext *cx, uintN argc, jsval *vp) /* ECMA-357 13.4.4.35 Step 4. */ if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) - nameqn->setNameURI(STRING_TO_JSVAL(cx->runtime->emptyString)); + nameqn->setNameURI(cx->runtime->emptyString); xml = CHECK_COPY_ON_WRITE(cx, xml, obj); if (!xml) @@ -6477,7 +6519,7 @@ xml_setName(JSContext *cx, uintN argc, jsval *vp) nsowner = xml->parent; } - if (GetPrefix(nameqn)) { + if (nameqn->getNamePrefix()) { /* * The name being set has a prefix, which originally came from some * namespace object (which may be the null namespace, where both the @@ -6510,18 +6552,18 @@ xml_setName(JSContext *cx, uintN argc, jsval *vp) * the constructor, because we know uri of nameqn is non-empty (so * prefix does not need to be converted from null to empty by QName). */ - JS_ASSERT(!GetURI(nameqn)->empty()); + JS_ASSERT(!nameqn->getNameURI()->empty()); nsarray = &nsowner->xml_namespaces; for (i = 0, n = nsarray->length; i < n; i++) { ns = XMLARRAY_MEMBER(nsarray, i, JSObject); - if (ns && js_EqualStrings(GetURI(ns), GetURI(nameqn))) { + if (ns && EqualStrings(ns->getNameURI(), nameqn->getNameURI())) { nameqn->setNamePrefix(ns->getNamePrefix()); return JS_TRUE; } } - ns = NewXMLNamespace(cx, NULL, GetURI(nameqn), JS_TRUE); + ns = NewXMLNamespace(cx, NULL, nameqn->getNameURI(), JS_TRUE); if (!ns) return JS_FALSE; } @@ -7175,13 +7217,13 @@ JSBool js_GetFunctionNamespace(JSContext *cx, Value *vp) { JSObject *obj; - JSString *prefix, *uri; + JSLinearString *prefix, *uri; obj = cx->compartment->functionNamespaceObject; if (!obj) { JSRuntime *rt = cx->runtime; - prefix = ATOM_TO_STRING(rt->atomState.typeAtoms[JSTYPE_FUNCTION]); - uri = ATOM_TO_STRING(rt->atomState.functionNamespaceURIAtom); + prefix = rt->atomState.typeAtoms[JSTYPE_FUNCTION]; + uri = rt->atomState.functionNamespaceURIAtom; obj = NewXMLNamespace(cx, prefix, uri, JS_FALSE); if (!obj) return false; @@ -7282,7 +7324,7 @@ js_ToAttributeName(JSContext *cx, Value *vp) return JS_TRUE; } -JSString * +JSLinearString * js_EscapeAttributeValue(JSContext *cx, JSString *str, JSBool quote) { JSCharBuffer cb(cx); @@ -7292,14 +7334,18 @@ js_EscapeAttributeValue(JSContext *cx, JSString *str, JSBool quote) JSString * js_AddAttributePart(JSContext *cx, JSBool isName, JSString *str, JSString *str2) { - size_t len, len2, newlen; - const jschar *chars, *chars2; - jschar *newchars; + size_t len = str->length(); + const jschar *chars = str->getChars(cx); + if (!chars) + return NULL; - str->getCharsAndLength(chars, len); - str2->getCharsAndLength(chars2, len2); - newlen = (isName) ? len + 1 + len2 : len + 2 + len2 + 1; - newchars = (jschar *) cx->malloc((newlen+1) * sizeof(jschar)); + size_t len2 = str2->length(); + const jschar *chars2 = str2->getChars(cx); + if (!chars2) + return NULL; + + size_t newlen = (isName) ? len + 1 + len2 : len + 2 + len2 + 1; + jschar *newchars = (jschar *) cx->malloc((newlen+1) * sizeof(jschar)); if (!newchars) return NULL; @@ -7722,7 +7768,10 @@ js_NewXMLSpecialObject(JSContext *cx, JSXMLClass xml_class, JSString *name, return NULL; xml = (JSXML *) obj->getPrivate(); if (name) { - qn = NewXMLQName(cx, cx->runtime->emptyString, NULL, name); + JSLinearString *linearName = name->ensureLinear(cx); + if (!linearName) + return NULL; + qn = NewXMLQName(cx, cx->runtime->emptyString, NULL, linearName); if (!qn) return NULL; xml->name = qn; diff --git a/js/src/jsxml.h b/js/src/jsxml.h index 58c2bb87552..fe3fa184f2f 100644 --- a/js/src/jsxml.h +++ b/js/src/jsxml.h @@ -312,7 +312,7 @@ js_IsXMLName(JSContext *cx, jsval v); extern JSBool js_ToAttributeName(JSContext *cx, js::Value *vp); -extern JSString * +extern JSLinearString * js_EscapeAttributeValue(JSContext *cx, JSString *str, JSBool quote); extern JSString * diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 37d8fac9048..bbd00b2a8b0 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -42,6 +42,7 @@ #include "MethodJIT.h" #include "jsnum.h" #include "jsbool.h" +#include "jsemit.h" #include "jsiter.h" #include "Compiler.h" #include "StubCalls.h" @@ -440,6 +441,7 @@ mjit::Compiler::finishThisUp(JITScript **jitp) jit->code = JSC::MacroAssemblerCodeRef(result, execPool, masm.size() + stubcc.size()); jit->nCallSites = callSites.length(); jit->invokeEntry = result; + jit->singleStepMode = script->singleStepMode; /* Build the pc -> ncode mapping. */ NativeMapEntry *nmap = (NativeMapEntry *)cursor; @@ -538,6 +540,12 @@ mjit::Compiler::finishThisUp(JITScript **jitp) cics[i].oolJumpOffset = offset; JS_ASSERT(cics[i].oolJumpOffset == offset); + /* Compute the start of the OOL IC call. */ + offset = stubCode.locationOf(callICs[i].icCall) - + stubCode.locationOf(callICs[i].slowPathStart); + cics[i].icCallOffset = offset; + JS_ASSERT(cics[i].icCallOffset == offset); + /* Compute the slow join point offset. */ offset = stubCode.locationOf(callICs[i].slowJoinPoint) - stubCode.locationOf(callICs[i].slowPathStart); @@ -791,6 +799,32 @@ mjit::Compiler::finishThisUp(JITScript **jitp) return Compile_Okay; } +class SrcNoteLineScanner { + ptrdiff_t offset; + jssrcnote *sn; + +public: + SrcNoteLineScanner(jssrcnote *sn) : offset(0), sn(sn) {} + + bool firstOpInLine(ptrdiff_t relpc) { + while ((offset < relpc) && !SN_IS_TERMINATOR(sn)) { + offset += SN_DELTA(sn); + sn = SN_NEXT(sn); + } + + while ((offset == relpc) && !SN_IS_TERMINATOR(sn)) { + JSSrcNoteType type = (JSSrcNoteType) SN_TYPE(sn); + if (type == SRC_SETLINE || type == SRC_NEWLINE) + return true; + + offset += SN_DELTA(sn); + sn = SN_NEXT(sn); + } + + return false; + } +}; + #ifdef DEBUG #define SPEW_OPCODE() \ JS_BEGIN_MACRO \ @@ -815,16 +849,19 @@ CompileStatus mjit::Compiler::generateMethod() { mjit::AutoScriptRetrapper trapper(cx, script); + SrcNoteLineScanner scanner(script->notes()); for (;;) { JSOp op = JSOp(*PC); - bool trap = (op == JSOP_TRAP); - - if (trap) { + int trap = stubs::JSTRAP_NONE; + if (op == JSOP_TRAP) { if (!trapper.untrap(PC)) return Compile_Error; op = JSOp(*PC); + trap |= stubs::JSTRAP_TRAP; } + if (script->singleStepMode && scanner.firstOpInLine(PC - script->code)) + trap |= stubs::JSTRAP_SINGLESTEP; analyze::Bytecode *opinfo = analysis->maybeCode(PC); @@ -850,7 +887,7 @@ mjit::Compiler::generateMethod() if (trap) { prepareStubCall(Uses(0)); - masm.move(ImmPtr(PC), Registers::ArgReg1); + masm.move(Imm32(trap), Registers::ArgReg1); Call cl = emitStubCall(JS_FUNC_TO_DATA_PTR(void *, stubs::Trap)); InternalCallSite site(masm.callReturnOffset(cl), PC, CallSite::MAGIC_TRAP_ID, true, false); @@ -2671,6 +2708,7 @@ mjit::Compiler::inlineCallHelper(uint32 callImmArgc, bool callingNew) Jump toPatch = stubcc.masm.jump(); toPatch.linkTo(stubcc.masm.label(), &stubcc.masm); callIC.oolJump = toPatch; + callIC.icCall = stubcc.masm.label(); /* * At this point the function is definitely scripted, so we try to @@ -2799,7 +2837,8 @@ mjit::Compiler::compareTwoValues(JSContext *cx, JSOp op, const Value &lhs, const JS_ASSERT(rhs.isPrimitive()); if (lhs.isString() && rhs.isString()) { - int cmp = js_CompareStrings(lhs.toString(), rhs.toString()); + int32 cmp; + CompareStrings(cx, lhs.toString(), rhs.toString(), &cmp); switch (op) { case JSOP_LT: return cmp < 0; @@ -2930,8 +2969,8 @@ mjit::Compiler::jsop_length() frame.push(v); } else { RegisterID str = frame.ownRegForData(top); - masm.loadPtr(Address(str, offsetof(JSString, mLengthAndFlags)), str); - masm.rshiftPtr(Imm32(JSString::FLAGS_LENGTH_SHIFT), str); + masm.loadPtr(Address(str, JSString::offsetOfLengthAndFlags()), str); + masm.rshiftPtr(Imm32(JSString::LENGTH_SHIFT), str); frame.pop(); frame.pushTypedPayload(JSVAL_TYPE_INT32, str); } @@ -4050,18 +4089,8 @@ mjit::Compiler::iter(uintN flags) RegisterID T2 = frame.allocReg(); frame.unpinReg(reg); - /* - * Fetch the most recent iterator. TODO: bake this pointer in when - * iterator caches become per-compartment. - */ - masm.loadPtr(FrameAddress(offsetof(VMFrame, cx)), T1); -#ifdef JS_THREADSAFE - masm.loadPtr(Address(T1, offsetof(JSContext, thread)), T1); - masm.loadPtr(Address(T1, offsetof(JSThread, data.lastNativeIterator)), ioreg); -#else - masm.loadPtr(Address(T1, offsetof(JSContext, runtime)), T1); - masm.loadPtr(Address(T1, offsetof(JSRuntime, threadData.lastNativeIterator)), ioreg); -#endif + /* Fetch the most recent iterator. */ + masm.loadPtr(&script->compartment->nativeIterCache.last, ioreg); /* Test for NULL. */ Jump nullIterator = masm.branchTest32(Assembler::Zero, ioreg, ioreg); @@ -4751,7 +4780,8 @@ mjit::Compiler::jumpAndTrace(Jump j, jsbytecode *target, Jump *slow) return false; } #else - if (!addTraceHints || target >= PC || JSOp(*target) != JSOP_TRACE + if (!addTraceHints || target >= PC || + (JSOp(*target) != JSOP_TRACE && JSOp(*target) != JSOP_NOTRACE) #ifdef JS_MONOIC || GET_UINT16(target) == BAD_TRACEIC_INDEX #endif @@ -4759,10 +4789,8 @@ mjit::Compiler::jumpAndTrace(Jump j, jsbytecode *target, Jump *slow) { if (!jumpInScript(j, target)) return false; - if (slow) { - if (!stubcc.jumpInScript(*slow, target)) - stubcc.jumpInScript(*slow, target); - } + if (slow && !stubcc.jumpInScript(*slow, target)) + return false; return true; } @@ -4784,9 +4812,21 @@ mjit::Compiler::jumpAndTrace(Jump j, jsbytecode *target, Jump *slow) Label traceStart = stubcc.masm.label(); - stubcc.linkExitDirect(j, traceStart); - if (slow) - slow->linkTo(traceStart, &stubcc.masm); + /* + * We make a trace IC even if the trace is currently disabled, in case it is + * enabled later, but set up the jumps so that InvokeTracer is initially skipped. + */ + if (JSOp(*target) == JSOP_TRACE) { + stubcc.linkExitDirect(j, traceStart); + if (slow) + slow->linkTo(traceStart, &stubcc.masm); + } else { + if (!jumpInScript(j, target)) + return false; + if (slow && !stubcc.jumpInScript(*slow, target)) + return false; + } + # if JS_MONOIC ic.addrLabel = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1); traceICs[index] = ic; diff --git a/js/src/methodjit/Compiler.h b/js/src/methodjit/Compiler.h index eb80f8066f3..e5346bbf1c4 100644 --- a/js/src/methodjit/Compiler.h +++ b/js/src/methodjit/Compiler.h @@ -140,6 +140,7 @@ class Compiler : public BaseCompiler DataLabelPtr addrLabel1; DataLabelPtr addrLabel2; Jump oolJump; + Label icCall; RegisterID funObjReg; RegisterID funPtrReg; FrameSize frameSize; diff --git a/js/src/methodjit/FastOps.cpp b/js/src/methodjit/FastOps.cpp index 3e604b362f2..11eccaedcdf 100644 --- a/js/src/methodjit/FastOps.cpp +++ b/js/src/methodjit/FastOps.cpp @@ -1562,7 +1562,8 @@ mjit::Compiler::jsop_stricteq(JSOp op) /* Constant-fold. */ if (lhs->isConstant() && rhs->isConstant()) { - bool b = StrictlyEqual(cx, lhs->getValue(), rhs->getValue()); + JSBool b; + StrictlyEqual(cx, lhs->getValue(), rhs->getValue(), &b); frame.popn(2); frame.push(BooleanValue((op == JSOP_STRICTEQ) ? b : !b)); return; diff --git a/js/src/methodjit/InvokeHelpers.cpp b/js/src/methodjit/InvokeHelpers.cpp index cd65285a2d1..45b09582ad7 100644 --- a/js/src/methodjit/InvokeHelpers.cpp +++ b/js/src/methodjit/InvokeHelpers.cpp @@ -1,4 +1,4 @@ -/* -*- mOde: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=4 sw=4 et tw=99: * * ***** BEGIN LICENSE BLOCK ***** @@ -590,9 +590,14 @@ stubs::EnterScript(VMFrame &f) JSContext *cx = f.cx; if (fp->script()->debugMode) { - JSInterpreterHook hook = cx->debugHooks->callHook; - if (JS_UNLIKELY(hook != NULL) && !fp->isExecuteFrame()) { - fp->setHookData(hook(cx, fp, JS_TRUE, 0, cx->debugHooks->callHookData)); + if (fp->isExecuteFrame()) { + JSInterpreterHook hook = cx->debugHooks->executeHook; + if (JS_UNLIKELY(hook != NULL)) + fp->setHookData(hook(cx, fp, JS_TRUE, 0, cx->debugHooks->executeHookData)); + } else { + JSInterpreterHook hook = cx->debugHooks->callHook; + if (JS_UNLIKELY(hook != NULL)) + fp->setHookData(hook(cx, fp, JS_TRUE, 0, cx->debugHooks->callHookData)); } } @@ -607,10 +612,11 @@ stubs::LeaveScript(VMFrame &f) Probes::exitJSFun(cx, fp->maybeFun(), fp->maybeScript()); if (fp->script()->debugMode) { - JSInterpreterHook hook = cx->debugHooks->callHook; void *hookData; + JSInterpreterHook hook = + fp->isExecuteFrame() ? cx->debugHooks->executeHook : cx->debugHooks->callHook; - if (hook && (hookData = fp->maybeHookData()) && !fp->isExecuteFrame()) { + if (JS_UNLIKELY(hook != NULL) && (hookData = fp->maybeHookData())) { JSBool ok = JS_TRUE; hook(cx, fp, JS_FALSE, &ok, hookData); if (!ok) diff --git a/js/src/methodjit/MethodJIT.cpp b/js/src/methodjit/MethodJIT.cpp index c4eec066ddc..1cd9658ccae 100644 --- a/js/src/methodjit/MethodJIT.cpp +++ b/js/src/methodjit/MethodJIT.cpp @@ -138,7 +138,7 @@ SetVMFrameRegs(VMFrame &f) f.cx->setCurrentRegs(&f.regs); } -#if defined(__APPLE__) || defined(XP_WIN) || defined(XP_OS2) +#if defined(__APPLE__) || (defined(XP_WIN) && !defined(JS_CPU_X64)) || defined(XP_OS2) # define SYMBOL_STRING(name) "_" #name #else # define SYMBOL_STRING(name) #name diff --git a/js/src/methodjit/MethodJIT.h b/js/src/methodjit/MethodJIT.h index 628d2156114..d32c65041f7 100644 --- a/js/src/methodjit/MethodJIT.h +++ b/js/src/methodjit/MethodJIT.h @@ -304,33 +304,43 @@ struct JITScript { .ncode values may not be NULL. */ size_t nNmapPairs; /* number of entries in nmap */ + void *invokeEntry; /* invoke address */ + void *fastEntry; /* cached entry, fastest */ + void *arityCheckEntry; /* arity check address */ + + /* To minimize the size of this struct on 64-bit, put uint32s after all pointers. */ js::mjit::CallSite *callSites; - uint32 nCallSites; #ifdef JS_MONOIC ic::MICInfo *mics; /* MICs in this script. */ - uint32 nMICs; /* number of MonoICs */ ic::CallICInfo *callICs; /* CallICs in this script. */ - uint32 nCallICs; /* number of call ICs */ ic::EqualityICInfo *equalityICs; - uint32 nEqualityICs; ic::TraceICInfo *traceICs; - uint32 nTraceICs; +#endif +#ifdef JS_POLYIC + ic::PICInfo *pics; /* PICs in this script */ + ic::GetElementIC *getElems; + ic::SetElementIC *setElems; +#endif + uint32 nCallSites:31; + bool singleStepMode:1; /* compiled in "single step mode" */ +#ifdef JS_MONOIC + uint32 nMICs; /* number of MonoICs */ + uint32 nCallICs; /* number of call ICs */ + uint32 nEqualityICs; + uint32 nTraceICs; +#endif +#ifdef JS_POLYIC + uint32 nPICs; /* number of PolyICs */ + uint32 nGetElems; + uint32 nSetElems; +#endif + +#ifdef JS_MONOIC // Additional ExecutablePools that IC stubs were generated into. typedef Vector ExecPoolVector; ExecPoolVector execPools; #endif -#ifdef JS_POLYIC - ic::PICInfo *pics; /* PICs in this script */ - uint32 nPICs; /* number of PolyICs */ - ic::GetElementIC *getElems; - uint32 nGetElems; - ic::SetElementIC *setElems; - uint32 nSetElems; -#endif - void *invokeEntry; /* invoke address */ - void *fastEntry; /* cached entry, fastest */ - void *arityCheckEntry; /* arity check address */ ~JITScript(); @@ -341,7 +351,7 @@ struct JITScript { } void nukeScriptDependentICs(); - void sweepCallICs(); + void sweepCallICs(bool purgeAll); void purgeMICs(); void purgePICs(); }; diff --git a/js/src/methodjit/MonoIC.cpp b/js/src/methodjit/MonoIC.cpp index d17b42b1bee..71a02033131 100644 --- a/js/src/methodjit/MonoIC.cpp +++ b/js/src/methodjit/MonoIC.cpp @@ -292,13 +292,13 @@ class EqualityCompiler : public BaseCompiler /* Test if lhs/rhs are atomized. */ Imm32 atomizedFlags(JSString::FLAT | JSString::ATOMIZED); - masm.load32(Address(lvr.dataReg(), offsetof(JSString, mLengthAndFlags)), tmp); + masm.load32(Address(lvr.dataReg(), JSString::offsetOfLengthAndFlags()), tmp); masm.and32(Imm32(JSString::TYPE_FLAGS_MASK), tmp); Jump lhsNotAtomized = masm.branch32(Assembler::NotEqual, tmp, atomizedFlags); linkToStub(lhsNotAtomized); if (!rvr.isConstant()) { - masm.load32(Address(rvr.dataReg(), offsetof(JSString, mLengthAndFlags)), tmp); + masm.load32(Address(rvr.dataReg(), JSString::offsetOfLengthAndFlags()), tmp); masm.and32(Imm32(JSString::TYPE_FLAGS_MASK), tmp); Jump rhsNotAtomized = masm.branch32(Assembler::NotEqual, tmp, atomizedFlags); linkToStub(rhsNotAtomized); @@ -1118,13 +1118,16 @@ JITScript::nukeScriptDependentICs() } void -JITScript::sweepCallICs() +JITScript::sweepCallICs(bool purgeAll) { - if (!nCallICs) - return; - Repatcher repatcher(this); + /* + * If purgeAll is set, purge stubs in the script except those covered by PurgePICs + * (which is always called during GC). We want to remove references which can keep + * alive pools that we are trying to destroy (see JSCompartment::sweep). + */ + for (uint32 i = 0; i < nCallICs; i++) { ic::CallICInfo &ic = callICs[i]; @@ -1133,11 +1136,10 @@ JITScript::sweepCallICs() * executing a stub generated by a guard on that object. This lets us * precisely GC call ICs while keeping the identity guard safe. */ - bool fastFunDead = ic.fastGuardedObject && IsAboutToBeFinalized(ic.fastGuardedObject); - bool nativeDead = ic.fastGuardedNative && IsAboutToBeFinalized(ic.fastGuardedNative); - - if (!fastFunDead && !nativeDead) - continue; + bool fastFunDead = ic.fastGuardedObject && + (purgeAll || IsAboutToBeFinalized(ic.fastGuardedObject)); + bool nativeDead = ic.fastGuardedNative && + (purgeAll || IsAboutToBeFinalized(ic.fastGuardedNative)); if (fastFunDead) { repatcher.repatch(ic.funGuard, NULL); @@ -1151,19 +1153,48 @@ JITScript::sweepCallICs() ic.fastGuardedNative = NULL; } - repatcher.relink(ic.funJump, ic.slowPathStart); + if (purgeAll) { + ic.releasePool(CallICInfo::Pool_ScriptStub); + JSC::CodeLocationJump oolJump = ic.slowPathStart.jumpAtOffset(ic.oolJumpOffset); + JSC::CodeLocationLabel icCall = ic.slowPathStart.labelAtOffset(ic.icCallOffset); + repatcher.relink(oolJump, icCall); + } + repatcher.relink(ic.funJump, ic.slowPathStart); ic.hit = false; } + + if (purgeAll) { + /* Purge ICs generating stubs into execPools. */ + uint32 released = 0; + + for (uint32 i = 0; i < nEqualityICs; i++) { + ic::EqualityICInfo &ic = equalityICs[i]; + if (!ic.generated) + continue; + + JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, ic::Equality)); + repatcher.relink(ic.stubCall, fptr); + repatcher.relink(ic.jumpToStub, ic.stubEntry); + + ic.generated = false; + released++; + } + + JS_ASSERT(released == execPools.length()); + for (uint32 i = 0; i < released; i++) + execPools[i]->release(); + execPools.clear(); + } } void -ic::SweepCallICs(JSScript *script) +ic::SweepCallICs(JSScript *script, bool purgeAll) { if (script->jitNormal) - script->jitNormal->sweepCallICs(); + script->jitNormal->sweepCallICs(purgeAll); if (script->jitCtor) - script->jitCtor->sweepCallICs(); + script->jitCtor->sweepCallICs(purgeAll); } #endif /* JS_MONOIC */ diff --git a/js/src/methodjit/MonoIC.h b/js/src/methodjit/MonoIC.h index 6500625da9d..55d65e21893 100644 --- a/js/src/methodjit/MonoIC.h +++ b/js/src/methodjit/MonoIC.h @@ -222,6 +222,9 @@ struct CallICInfo { /* Jump to patch for out-of-line scripted calls. */ uint32 oolJumpOffset : 16; + /* Label for out-of-line call to IC function. */ + uint32 icCallOffset : 16; + /* Offset for deep-fun check to rejoin at. */ uint32 hotPathOffset : 16; @@ -262,7 +265,7 @@ void JS_FASTCALL NativeCall(VMFrame &f, ic::CallICInfo *ic); JSBool JS_FASTCALL SplatApplyArgs(VMFrame &f); void PurgeMICs(JSContext *cx, JSScript *script); -void SweepCallICs(JSScript *script); +void SweepCallICs(JSScript *script, bool purgeAll); } /* namespace ic */ } /* namespace mjit */ diff --git a/js/src/methodjit/PolyIC.cpp b/js/src/methodjit/PolyIC.cpp index 90a12f43a1c..1d8774c4711 100644 --- a/js/src/methodjit/PolyIC.cpp +++ b/js/src/methodjit/PolyIC.cpp @@ -79,7 +79,7 @@ class PICLinker : public LinkerHelper JSC::ExecutablePool *pool = LinkerHelper::init(cx); if (!pool) return false; - if (!ic.execPools.append(pool)) { + if (!ic.addPool(cx, pool)) { pool->release(); js_ReportOutOfMemory(cx); return false; @@ -977,9 +977,9 @@ class GetPropCompiler : public PICStubCompiler Assembler masm; Jump notString = masm.branchPtr(Assembler::NotEqual, pic.typeReg(), ImmType(JSVAL_TYPE_STRING)); - masm.loadPtr(Address(pic.objReg, offsetof(JSString, mLengthAndFlags)), pic.objReg); + masm.loadPtr(Address(pic.objReg, JSString::offsetOfLengthAndFlags()), pic.objReg); // String length is guaranteed to be no more than 2**28, so the 32-bit operation is OK. - masm.urshift32(Imm32(JSString::FLAGS_LENGTH_SHIFT), pic.objReg); + masm.urshift32(Imm32(JSString::LENGTH_SHIFT), pic.objReg); masm.move(ImmType(JSVAL_TYPE_INT32), pic.shapeReg); Jump done = masm.jump(); @@ -2136,7 +2136,7 @@ GetElementIC::attachGetProp(JSContext *cx, JSObject *obj, const Value &v, jsid i CodeLocationLabel cs = buffer.finalize(); #if DEBUG - char *chars = js_DeflateString(cx, v.toString()->chars(), v.toString()->length()); + char *chars = js_DeflateString(cx, v.toString()->nonRopeChars(), v.toString()->length()); JaegerSpew(JSpew_PICs, "generated %s stub at %p for atom 0x%x (\"%s\") shape 0x%x (%s: %d)\n", js_CodeName[op], cs.executableAddress(), id, chars, holder->shape(), cx->fp()->script()->filename, js_FramePCToLineNumber(cx, cx->fp())); diff --git a/js/src/methodjit/PolyIC.h b/js/src/methodjit/PolyIC.h index c5611d709e7..4feccd4d392 100644 --- a/js/src/methodjit/PolyIC.h +++ b/js/src/methodjit/PolyIC.h @@ -219,29 +219,99 @@ struct BaseIC : public MacroAssemblerTypedefs { bool isCallOp(); }; -struct BasePolyIC : public BaseIC { - BasePolyIC() : execPools(SystemAllocPolicy()) { } - ~BasePolyIC() { releasePools(); } +class BasePolyIC : public BaseIC { + typedef Vector ExecPoolVector; + + // ExecutablePools that IC stubs were generated into. Very commonly (eg. + // 99.5% of BasePolyICs) there are 0 or 1, and there are lots of + // BasePolyICs, so we space-optimize for that case. If the bottom bit of + // the pointer is 0, execPool should be used, and it will be NULL (for 0 + // pools) or non-NULL (for 1 pool). If the bottom bit of the + // pointer is 1, taggedExecPools should be used, but only after de-tagging + // (for 2 or more pools). + union { + JSC::ExecutablePool *execPool; // valid when bottom bit is a 0 + ExecPoolVector *taggedExecPools; // valid when bottom bit is a 1 + } u; + + static bool isTagged(void *p) { + return !!(intptr_t(p) & 1); + } + + static ExecPoolVector *tag(ExecPoolVector *p) { + JS_ASSERT(!isTagged(p)); + return (ExecPoolVector *)(intptr_t(p) | 1); + } + + static ExecPoolVector *detag(ExecPoolVector *p) { + JS_ASSERT(isTagged(p)); + return (ExecPoolVector *)(intptr_t(p) & ~1); + } + + bool areZeroPools() { return !u.execPool; } + bool isOnePool() { return u.execPool && !isTagged(u.execPool); } + bool areMultiplePools() { return isTagged(u.taggedExecPools); } + + ExecPoolVector *multiplePools() { + JS_ASSERT(areMultiplePools()); + return detag(u.taggedExecPools); + } + + public: + BasePolyIC() { + u.execPool = NULL; + } + + ~BasePolyIC() { + releasePools(); + if (areMultiplePools()) + delete multiplePools(); + } void reset() { BaseIC::reset(); releasePools(); - execPools.clear(); + if (areZeroPools()) { + // Common case: do nothing. + } else if (isOnePool()) { + u.execPool = NULL; + } else { + multiplePools()->clear(); + } } - typedef Vector ExecPoolVector; - - // ExecutablePools that IC stubs were generated into. - ExecPoolVector execPools; - - // Release ExecutablePools referred to by this PIC. void releasePools() { - for (JSC::ExecutablePool **pExecPool = execPools.begin(); - pExecPool != execPools.end(); - ++pExecPool) { - (*pExecPool)->release(); + if (areZeroPools()) { + // Common case: do nothing. + } else if (isOnePool()) { + u.execPool->release(); + } else { + ExecPoolVector *execPools = multiplePools(); + for (size_t i = 0; i < execPools->length(); i++) + (*execPools)[i]->release(); } } + + bool addPool(JSContext *cx, JSC::ExecutablePool *pool) { + if (areZeroPools()) { + u.execPool = pool; + return true; + } + if (isOnePool()) { + JSC::ExecutablePool *oldPool = u.execPool; + JS_ASSERT(!isTagged(oldPool)); + ExecPoolVector *execPools = new ExecPoolVector(SystemAllocPolicy()); + if (!execPools) + return false; + if (!execPools->append(oldPool) || !execPools->append(pool)) { + delete execPools; + return false; + } + u.taggedExecPools = tag(execPools); + return true; + } + return multiplePools()->append(pool); + } }; struct GetElementIC : public BasePolyIC { diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index 3f422bdd853..3cb43e7481e 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -559,7 +559,7 @@ stubs::SetElem(VMFrame &f) Value &objval = regs.sp[-3]; Value &idval = regs.sp[-2]; - Value retval = regs.sp[-1]; + Value rval = regs.sp[-1]; JSObject *obj; jsid id; @@ -582,19 +582,19 @@ stubs::SetElem(VMFrame &f) if ((jsuint)i >= obj->getArrayLength()) obj->setArrayLength(i + 1); } - obj->setDenseArrayElement(i, regs.sp[-1]); + obj->setDenseArrayElement(i, rval); goto end_setelem; } } } while (0); - if (!obj->setProperty(cx, id, &retval, strict)) + if (!obj->setProperty(cx, id, &rval, strict)) THROW(); end_setelem: /* :FIXME: Moving the assigned object into the lowest stack slot * is a temporary hack. What we actually want is an implementation * of popAfterSet() that allows popping more than one value; * this logic can then be handled in Compiler.cpp. */ - regs.sp[-3] = retval; + regs.sp[-3] = regs.sp[-1]; } template void JS_FASTCALL stubs::SetElem(VMFrame &f); @@ -940,7 +940,10 @@ template void JS_FASTCALL stubs::DefFun(VMFrame &f, JSFunction *fun); DEFAULT_VALUE(cx, -1, JSTYPE_NUMBER, rval); \ if (lval.isString() && rval.isString()) { \ JSString *l = lval.toString(), *r = rval.toString(); \ - cond = js_CompareStrings(l, r) OP 0; \ + JSBool cmp; \ + if (!CompareStrings(cx, l, r, &cmp)) \ + THROWV(JS_FALSE); \ + cond = cmp OP 0; \ } else { \ double l, r; \ if (!ValueToNumber(cx, lval, &l) || \ @@ -1006,7 +1009,10 @@ StubEqualityOp(VMFrame &f) if (lval.isString() && rval.isString()) { JSString *l = lval.toString(); JSString *r = rval.toString(); - cond = js_EqualStrings(l, r) == EQ; + JSBool equal; + if (!EqualStrings(cx, l, r, &equal)) + return false; + cond = equal == EQ; } else #if JS_HAS_XML_SUPPORT if ((lval.isObject() && lval.toObject().isXML()) || @@ -1066,7 +1072,10 @@ StubEqualityOp(VMFrame &f) if (lval.isString() && rval.isString()) { JSString *l = lval.toString(); JSString *r = rval.toString(); - cond = js_EqualStrings(l, r) == EQ; + JSBool equal; + if (!EqualStrings(cx, l, r, &equal)) + return false; + cond = equal == EQ; } else { double l, r; if (!ValueToNumber(cx, lval, &l) || @@ -1316,11 +1325,32 @@ stubs::Interrupt(VMFrame &f, jsbytecode *pc) } void JS_FASTCALL -stubs::Trap(VMFrame &f, jsbytecode *pc) +stubs::Trap(VMFrame &f, uint32 trapTypes) { Value rval; + jsbytecode *pc = f.cx->regs->pc; - switch (JS_HandleTrap(f.cx, f.cx->fp()->script(), pc, Jsvalify(&rval))) { + /* + * Trap may be called for a single-step interrupt trap and/or a + * regular trap. Try the single-step first, and if it lets control + * flow through or does not exist, do the regular trap. + */ + JSTrapStatus result = JSTRAP_CONTINUE; + if (trapTypes & JSTRAP_SINGLESTEP) { + /* + * single step mode may be paused without recompiling by + * setting the interruptHook to NULL. + */ + JSInterruptHook hook = f.cx->debugHooks->interruptHook; + if (hook) + result = hook(f.cx, f.cx->fp()->script(), pc, Jsvalify(&rval), + f.cx->debugHooks->interruptHookData); + } + + if (result == JSTRAP_CONTINUE && (trapTypes & JSTRAP_TRAP)) + result = JS_HandleTrap(f.cx, f.cx->fp()->script(), pc, Jsvalify(&rval)); + + switch (result) { case JSTRAP_THROW: f.cx->throwing = JS_TRUE; f.cx->exception = rval; @@ -2281,9 +2311,11 @@ stubs::StrictEq(VMFrame &f) { const Value &rhs = f.regs.sp[-1]; const Value &lhs = f.regs.sp[-2]; - const bool b = StrictlyEqual(f.cx, lhs, rhs) == true; + JSBool equal; + if (!StrictlyEqual(f.cx, lhs, rhs, &equal)) + THROW(); f.regs.sp--; - f.regs.sp[-1].setBoolean(b); + f.regs.sp[-1].setBoolean(equal == JS_TRUE); } void JS_FASTCALL @@ -2291,9 +2323,11 @@ stubs::StrictNe(VMFrame &f) { const Value &rhs = f.regs.sp[-1]; const Value &lhs = f.regs.sp[-2]; - const bool b = StrictlyEqual(f.cx, lhs, rhs) != true; + JSBool equal; + if (!StrictlyEqual(f.cx, lhs, rhs, &equal)) + THROW(); f.regs.sp--; - f.regs.sp[-1].setBoolean(b); + f.regs.sp[-1].setBoolean(equal != JS_TRUE); } void JS_FASTCALL @@ -2466,13 +2500,15 @@ stubs::LookupSwitch(VMFrame &f, jsbytecode *pc) JS_ASSERT(npairs); if (lval.isString()) { - JSString *str = lval.toString(); + JSLinearString *str = lval.toString()->ensureLinear(f.cx); + if (!str) + THROWV(NULL); for (uint32 i = 1; i <= npairs; i++) { Value rval = script->getConst(GET_INDEX(pc)); pc += INDEX_LEN; if (rval.isString()) { - JSString *rhs = rval.toString(); - if (rhs == str || js_EqualStrings(str, rhs)) { + JSLinearString *rhs = rval.toString()->assertIsLinear(); + if (rhs == str || EqualStrings(str, rhs)) { void* native = script->nativeCodeForPC(ctor, jpc + GET_JUMP_OFFSET(pc)); JS_ASSERT(native); diff --git a/js/src/methodjit/StubCalls.h b/js/src/methodjit/StubCalls.h index dd3698a4411..577b680ed94 100644 --- a/js/src/methodjit/StubCalls.h +++ b/js/src/methodjit/StubCalls.h @@ -47,10 +47,16 @@ namespace js { namespace mjit { namespace stubs { +typedef enum JSTrapType { + JSTRAP_NONE = 0, + JSTRAP_TRAP = 1, + JSTRAP_SINGLESTEP = 2 +} JSTrapType; + void JS_FASTCALL This(VMFrame &f); JSObject * JS_FASTCALL NewInitArray(VMFrame &f, uint32 count); JSObject * JS_FASTCALL NewInitObject(VMFrame &f, JSObject *base); -void JS_FASTCALL Trap(VMFrame &f, jsbytecode *pc); +void JS_FASTCALL Trap(VMFrame &f, uint32 trapTypes); void JS_FASTCALL Debugger(VMFrame &f, jsbytecode *pc); void JS_FASTCALL Interrupt(VMFrame &f, jsbytecode *pc); void JS_FASTCALL InitElem(VMFrame &f, uint32 last); diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index f2ff74c2eec..cf6405e96ca 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -1247,7 +1247,10 @@ AssertEq(JSContext *cx, uintN argc, jsval *vp) } jsval *argv = JS_ARGV(cx, vp); - if (!JS_SameValue(cx, argv[0], argv[1])) { + JSBool same; + if (!JS_SameValue(cx, argv[0], argv[1], &same)) + return JS_FALSE; + if (!same) { JSAutoByteString bytes0, bytes1; const char *actual = ToSource(cx, &argv[0], &bytes0); const char *expected = ToSource(cx, &argv[1], &bytes1); @@ -1338,6 +1341,10 @@ GCParameter(JSContext *cx, uintN argc, jsval *vp) vp[2] = STRING_TO_JSVAL(str); } + JSFlatString *flatStr = JS_FlattenString(cx, str); + if (!flatStr) + return JS_FALSE; + size_t paramIndex = 0; for (;; paramIndex++) { if (paramIndex == JS_ARRAY_LENGTH(paramMap)) { @@ -1347,7 +1354,7 @@ GCParameter(JSContext *cx, uintN argc, jsval *vp) "gcNumber or gcTriggerFactor"); return JS_FALSE; } - if (JS_MatchStringAndAscii(str, paramMap[paramIndex].name)) + if (JS_FlatStringEqualsAscii(flatStr, paramMap[paramIndex].name)) break; } JSGCParamKey param = paramMap[paramIndex].param; @@ -1494,8 +1501,11 @@ CountHeap(JSContext *cx, uintN argc, jsval *vp) str = JS_ValueToString(cx, JS_ARGV(cx, vp)[1]); if (!str) return JS_FALSE; + JSFlatString *flatStr = JS_FlattenString(cx, str); + if (!flatStr) + return JS_FALSE; for (i = 0; ;) { - if (JS_MatchStringAndAscii(str, traceKindNames[i].name)) { + if (JS_FlatStringEqualsAscii(flatStr, traceKindNames[i].name)) { traceKind = traceKindNames[i].kind; break; } @@ -1666,8 +1676,13 @@ TrapHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, { JSString *str = JSVAL_TO_STRING(closure); JSStackFrame *caller = JS_GetScriptedCaller(cx, NULL); - if (!JS_EvaluateUCInStackFrame(cx, caller, - JS_GetStringChars(str), JS_GetStringLength(str), + + size_t length; + const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length); + if (!chars) + return JSTRAP_ERROR; + + if (!JS_EvaluateUCInStackFrame(cx, caller, chars, length, caller->script()->filename, caller->script()->lineno, rval)) { @@ -2007,7 +2022,6 @@ DisassembleValue(JSContext *cx, jsval v, bool lines, bool recursive) SHOW_FLAG(LAMBDA); SHOW_FLAG(HEAVYWEIGHT); - SHOW_FLAG(PRIMITIVE_THIS); SHOW_FLAG(EXPR_CLOSURE); SHOW_FLAG(TRCINFO); @@ -2078,8 +2092,11 @@ Disassemble(JSContext *cx, uintN argc, jsval *vp) bool lines = false, recursive = false; while (argc > 0 && JSVAL_IS_STRING(argv[0])) { JSString *str = JSVAL_TO_STRING(argv[0]); - lines |= !!JS_MatchStringAndAscii(str, "-l"); - recursive |= !!JS_MatchStringAndAscii(str, "-r"); + JSFlatString *flatStr = JS_FlattenString(cx, str); + if (!flatStr) + return JS_FALSE; + lines |= !!JS_FlatStringEqualsAscii(flatStr, "-l"); + recursive |= !!JS_FlatStringEqualsAscii(flatStr, "-r"); if (!lines && !recursive) break; argv++, argc--; @@ -2319,13 +2336,16 @@ DumpStats(JSContext *cx, uintN argc, jsval *vp) if (!str) return JS_FALSE; argv[i] = STRING_TO_JSVAL(str); - if (JS_MatchStringAndAscii(str, "arena")) { + JSFlatString *flatStr = JS_FlattenString(cx, str); + if (!flatStr) + return JS_FALSE; + if (JS_FlatStringEqualsAscii(flatStr, "arena")) { #ifdef JS_ARENAMETER JS_DumpArenaStats(stdout); #endif - } else if (JS_MatchStringAndAscii(str, "atom")) { + } else if (JS_FlatStringEqualsAscii(flatStr, "atom")) { js_DumpAtoms(cx, gOutFile); - } else if (JS_MatchStringAndAscii(str, "global")) { + } else if (JS_FlatStringEqualsAscii(flatStr, "global")) { DumpScope(cx, cx->globalObject, stdout); } else { if (!JS_ValueToId(cx, STRING_TO_JSVAL(str), &id)) @@ -2617,17 +2637,20 @@ Clear(JSContext *cx, uintN argc, jsval *vp) static JSBool Intern(JSContext *cx, uintN argc, jsval *vp) { - JSString *str; - - str = JS_ValueToString(cx, argc == 0 ? JSVAL_VOID : vp[2]); + JSString *str = JS_ValueToString(cx, argc == 0 ? JSVAL_VOID : vp[2]); if (!str) - return JS_FALSE; - if (!JS_InternUCStringN(cx, JS_GetStringChars(str), - JS_GetStringLength(str))) { - return JS_FALSE; - } - *vp = JSVAL_VOID; - return JS_TRUE; + return false; + + size_t length; + const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length); + if (!chars) + return false; + + if (!JS_InternUCStringN(cx, chars, length)) + return false; + + JS_SET_RVAL(cx, vp, JSVAL_VOID); + return true; } static JSBool @@ -2848,18 +2871,21 @@ split_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) if (!cpx) return JS_TRUE; - if (JSID_IS_ATOM(id) && JS_MatchStringAndAscii(JSID_TO_STRING(id), "isInner")) { + if (JSID_IS_ATOM(id) && JS_FlatStringEqualsAscii(JSID_TO_FLAT_STRING(id), "isInner")) { *vp = BOOLEAN_TO_JSVAL(cpx->isInner); return JS_TRUE; } if (!cpx->isInner && cpx->inner) { if (JSID_IS_ATOM(id)) { - JSString *str; + JSString *str = JSID_TO_STRING(id); - str = JSID_TO_STRING(id); - return JS_GetUCProperty(cx, cpx->inner, JS_GetStringChars(str), - JS_GetStringLength(str), vp); + size_t length; + const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length); + if (!chars) + return false; + + return JS_GetUCProperty(cx, cpx->inner, chars, length, vp); } if (JSID_IS_INT(id)) return JS_GetElement(cx, cpx->inner, JSID_TO_INT(id), vp); @@ -2876,21 +2902,24 @@ split_setProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) cpx = split_get_private(cx, obj); if (!cpx) - return JS_TRUE; + return true; if (!cpx->isInner && cpx->inner) { if (JSID_IS_ATOM(id)) { - JSString *str; + JSString *str = JSID_TO_STRING(id); - str = JSID_TO_STRING(id); - return JS_SetUCProperty(cx, cpx->inner, JS_GetStringChars(str), - JS_GetStringLength(str), vp); + size_t length; + const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length); + if (!chars) + return false; + + return JS_SetUCProperty(cx, cpx->inner, chars, length, vp); } if (JSID_IS_INT(id)) return JS_SetElement(cx, cpx->inner, JSID_TO_INT(id), vp); - return JS_TRUE; + return true; } - return JS_TRUE; + return true; } static JSBool @@ -2975,7 +3004,7 @@ split_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **obj { ComplexObject *cpx; - if (JSID_IS_ATOM(id) && JS_MatchStringAndAscii(JSID_TO_STRING(id), "isInner")) { + if (JSID_IS_ATOM(id) && JS_FlatStringEqualsAscii(JSID_TO_FLAT_STRING(id), "isInner")) { *objp = obj; return JS_DefinePropertyById(cx, obj, id, JSVAL_VOID, NULL, NULL, JSPROP_SHARED); } @@ -3282,8 +3311,11 @@ EvalInContext(JSContext *cx, uintN argc, jsval *vp) if (!JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "S / o", &str, &sobj)) return false; - const jschar *src = JS_GetStringChars(str); - size_t srclen = JS_GetStringLength(str); + size_t srclen; + const jschar *src = JS_GetStringCharsAndLength(cx, str, &srclen); + if (!src) + return false; + bool split = false, lazy = false; if (srclen == 4) { if (src[0] == 'l' && src[1] == 'a' && src[2] == 'z' && src[3] == 'y') { @@ -3369,7 +3401,12 @@ EvalInFrame(JSContext *cx, uintN argc, jsval *vp) if (saveCurrent) oldfp = JS_SaveFrameChain(cx); - JSBool ok = JS_EvaluateUCInStackFrame(cx, fp, str->chars(), str->length(), + size_t length; + const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length); + if (!chars) + return JS_FALSE; + + JSBool ok = JS_EvaluateUCInStackFrame(cx, fp, chars, length, fp->script()->filename, JS_PCToLineNumber(cx, fp->script(), fi.pc()), @@ -4208,6 +4245,59 @@ Deserialize(JSContext *cx, uintN argc, jsval *vp) return true; } +JSBool +SetGlobalPropIf(JSContext *cx, uintN argc, jsval *vp) +{ + if (argc != 3) { + JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "setGlobalPropIf"); + return false; + } + + jsval *argv = JS_ARGV(cx, vp); + + JSBool doSet; + if (!JS_ValueToBoolean(cx, argv[0], &doSet)) + return false; + + JS_SET_RVAL(cx, vp, JSVAL_VOID); + if (!doSet) + return true; + + jsid id; + if (!JS_ValueToId(cx, argv[1], &id)) + return false; + + JSObject *global = JS_GetGlobalForScopeChain(cx); + return global && JS_SetPropertyById(cx, global, id, &argv[2]); +} + +JSBool +DefGlobalPropIf(JSContext *cx, uintN argc, jsval *vp) +{ + if (argc != 3) { + JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "setGlobalPropIf"); + return false; + } + + jsval *argv = JS_ARGV(cx, vp); + + JSBool doSet; + if (!JS_ValueToBoolean(cx, argv[0], &doSet)) + return false; + + JS_SET_RVAL(cx, vp, JSVAL_VOID); + if (!doSet) + return true; + + jsid id; + if (!JS_ValueToId(cx, argv[1], &id)) + return false; + + JSObject *global = JS_GetGlobalForScopeChain(cx); + JSBool ignore; + return global && JS_DefineOwnProperty(cx, global, id, argv[2], &ignore); +} + /* We use a mix of JS_FS and JS_FN to test both kinds of natives. */ static JSFunctionSpec shell_functions[] = { JS_FN("version", Version, 0,0), @@ -4305,6 +4395,8 @@ static JSFunctionSpec shell_functions[] = { JS_FN("wrap", Wrap, 1,0), JS_FN("serialize", Serialize, 1,0), JS_FN("deserialize", Deserialize, 1,0), + JS_FN("setGlobalPropIf",SetGlobalPropIf,3,0), + JS_FN("defGlobalPropIf",DefGlobalPropIf,3,0), JS_FS_END }; @@ -4433,7 +4525,10 @@ static const char *const shell_help_messages[] = { "parent(obj) Returns the parent of obj.\n", "wrap(obj) Wrap an object into a noop wrapper.\n", "serialize(sd) Serialize sd using JS_WriteStructuredClone. Returns a TypedArray.\n", -"deserialize(a) Deserialize data generated by serialize.\n" +"deserialize(a) Deserialize data generated by serialize.\n", +"setGlobalPropIf(b,id,v) If b, get the global object o and perform o[id] = v.\n", +"defGlobalPropIf(b,id,dsc)If b, get the global object o and perform\n" +" Object.defineProperty(o, id, dsc).\n" }; /* Help messages must match shell functions. */ @@ -4488,8 +4583,11 @@ Help(JSContext *cx, uintN argc, jsval *vp) str = NULL; } if (str) { + JSFlatString *flatStr = JS_FlattenString(cx, str); + if (!flatStr) + return JS_FALSE; for (j = 0; shell_functions[j].name; j++) { - if (JS_MatchStringAndAscii(str, shell_functions[j].name)) { + if (JS_FlatStringEqualsAscii(flatStr, shell_functions[j].name)) { if (!did_header) { did_header = 1; fputs(shell_help_header, gOutFile); diff --git a/js/src/tests/ecma_5/Array/jstests.list b/js/src/tests/ecma_5/Array/jstests.list index 402074b74f9..78f1b627dde 100644 --- a/js/src/tests/ecma_5/Array/jstests.list +++ b/js/src/tests/ecma_5/Array/jstests.list @@ -1,6 +1,7 @@ url-prefix ../../jsreftest.html?test=ecma_5/Array/ -script sort-01.js -script toString-01.js -script toLocaleString-01.js +script length-01.js script regress-599159.js +script sort-01.js +script toLocaleString-01.js +script toString-01.js script unshift-01.js diff --git a/js/src/tests/ecma_5/Array/length-01.js b/js/src/tests/ecma_5/Array/length-01.js new file mode 100644 index 00000000000..4fe81e2b6ee --- /dev/null +++ b/js/src/tests/ecma_5/Array/length-01.js @@ -0,0 +1,71 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + * Contributor: + * Jeff Walden + */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 600392; +var summary = + 'Object.preventExtensions([]).length = 0 should do nothing, not throw'; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + + +function testEmpty() +{ + var a = []; + assertEq(a.length, 0); + assertEq(Object.preventExtensions(a), a); + assertEq(a.length, 0); + a.length = 0; + assertEq(a.length, 0); +} +testEmpty(); + +function testEmptyStrict() +{ + "use strict"; + var a = []; + assertEq(a.length, 0); + assertEq(Object.preventExtensions(a), a); + assertEq(a.length, 0); + a.length = 0; + assertEq(a.length, 0); +} +testEmptyStrict(); + +function testNonEmpty() +{ + var a = [1, 2, 3]; + assertEq(a.length, 3); + assertEq(Object.preventExtensions(a), a); + assertEq(a.length, 3); + a.length = 0; + assertEq(a.length, 0); +} +testNonEmpty(); + +function testNonEmptyStrict() +{ + "use strict"; + var a = [1, 2, 3]; + assertEq(a.length, 3); + assertEq(Object.preventExtensions(a), a); + assertEq(a.length, 3); + a.length = 0; + assertEq(a.length, 0); +} +testNonEmptyStrict(); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("All tests passed!"); diff --git a/js/src/tests/ecma_5/Function/15.3.4.3-01.js b/js/src/tests/ecma_5/Function/15.3.4.3-01.js index e256059356d..955d773766c 100644 --- a/js/src/tests/ecma_5/Function/15.3.4.3-01.js +++ b/js/src/tests/ecma_5/Function/15.3.4.3-01.js @@ -29,6 +29,8 @@ function expectTypeError(fun, msg) function fun() { } +var global = this; + /* Step 1. */ var nonfuns = [null, 1, -1, 2.5, "[[Call]]", undefined, true, false, {}]; @@ -47,22 +49,75 @@ for (var i = 0, sz = nonfuns.length; i < sz; i++) /* Step 2. */ var thisObj = {}; +var currentThis, currentThisBox; function funLength() { assertEq(arguments.length, 0, "should have been called with no arguments"); + assertEq(this, currentThis, "wrong this"); } - -funLength.apply(); - -function funThisLength() +function strictFunLength() { - assertEq(this, thisObj, "should have gotten thisObj as this"); + "use strict"; assertEq(arguments.length, 0, "should have been called with no arguments"); + assertEq(this, currentThis, "wrong this"); } -funThisLength.apply(thisObj); -funThisLength.apply(thisObj, null); -funThisLength.apply(thisObj, undefined); +currentThis = global; +funLength.apply(); +funLength.apply(undefined); +funLength.apply(undefined, undefined); +funLength.apply(undefined, null); + +currentThis = undefined; +strictFunLength.apply(); +strictFunLength.apply(undefined); +strictFunLength.apply(undefined, undefined); +strictFunLength.apply(undefined, null); + +currentThis = null; +strictFunLength.apply(null); +strictFunLength.apply(null, undefined); +strictFunLength.apply(null, null); + +currentThis = thisObj; +funLength.apply(thisObj); +funLength.apply(thisObj, null); +funLength.apply(thisObj, undefined); +strictFunLength.apply(thisObj); +strictFunLength.apply(thisObj, null); +strictFunLength.apply(thisObj, undefined); + +currentThis = 17; +strictFunLength.apply(17); +strictFunLength.apply(17, null); +strictFunLength.apply(17, undefined); + +function funThisPrimitive() +{ + assertEq(arguments.length, 0, "should have been called with no arguments"); + assertEq(this instanceof currentThisBox, true, + "this not instanceof " + currentThisBox); + assertEq(this.valueOf(), currentThis, + "wrong this valueOf()"); +} + +currentThis = 17; +currentThisBox = Number; +funThisPrimitive.apply(17); +funThisPrimitive.apply(17, undefined); +funThisPrimitive.apply(17, null); + +currentThis = "foopy"; +currentThisBox = String; +funThisPrimitive.apply("foopy"); +funThisPrimitive.apply("foopy", undefined); +funThisPrimitive.apply("foopy", null); + +currentThis = false; +currentThisBox = Boolean; +funThisPrimitive.apply(false); +funThisPrimitive.apply(false, undefined); +funThisPrimitive.apply(false, null); /* Step 3. */ @@ -87,6 +142,11 @@ catch (e) } +/* + * NB: There was an erratum removing the steps numbered 5 and 7 in the original + * version of ES5; see also the comments in js_fun_apply. + */ + /* Step 5. */ var called = false; var argsObjectLength = @@ -113,35 +173,69 @@ assertEq(upvar, "both", "didn't call all hooks properly"); /* Step 6-9. */ -var steps = []; +var seenThis, res, steps; var argsAccessors = { - length: 3, + length: 4, get 0() { steps.push("0"); return 1; }, get 1() { steps.push("1"); return 2; }, - get 2() { steps.push("2"); return 4; }, + // make sure values shine through holes + get 3() { steps.push("3"); return 8; }, }; -var seenThis = "not seen"; +Object.prototype[2] = 729; + +seenThis = "not seen"; function argsAsArray() { seenThis = this; - steps.push("3"); + steps.push(Math.PI); return Array.prototype.map.call(arguments, function(v) { return v; }); } -var res = argsAsArray.apply(thisObj, argsAccessors); +steps = []; +res = argsAsArray.apply(thisObj, argsAccessors); assertEq(seenThis, thisObj, "saw wrong this"); assertEq(steps.length, 4, "wrong steps: " + steps); -for (var i = 0; i < 3; i++) - assertEq(steps[i], "" + i, "bad step " + i); -assertEq(steps[3], "3", "bad call"); +assertEq(steps[0], "0", "bad step 0"); +assertEq(steps[1], "1", "bad step 1"); +assertEq(steps[2], "3", "bad step 3"); +assertEq(steps[3], Math.PI, "bad last step"); -assertEq(res.length, 3, "wrong return: " + res); -for (var i = 0; i < 3; i++) - assertEq(res[i], 1 << i, "wrong ret[" + i + "]: " + res[i]); +assertEq(res.length, 4, "wrong return: " + res); +assertEq(res[0], 1, "wrong ret[0]"); +assertEq(res[1], 2, "wrong ret[0]"); +assertEq(res[2], 729, "wrong ret[0]"); +assertEq(res[3], 8, "wrong ret[0]"); +seenThis = "not seen"; +function strictArgsAsArray() +{ + "use strict"; + seenThis = this; + steps.push(NaN); + return Array.prototype.map.call(arguments, function(v) { return v; }); +} + +steps = []; +res = strictArgsAsArray.apply(null, argsAccessors); +assertEq(seenThis, null, "saw wrong this"); + +assertEq(steps.length, 4, "wrong steps: " + steps); +assertEq(steps[0], "0", "bad step 0"); +assertEq(steps[1], "1", "bad step 1"); +assertEq(steps[2], "3", "bad step 3"); +assertEq(steps[3], 0 / 0, "bad last step"); + +assertEq(res.length, 4, "wrong return: " + res); +assertEq(res[0], 1, "wrong ret[0]"); +assertEq(res[1], 2, "wrong ret[0]"); +assertEq(res[2], 729, "wrong ret[0]"); +assertEq(res[3], 8, "wrong ret[0]"); + +strictArgsAsArray.apply(17, argsAccessors); +assertEq(seenThis, 17, "saw wrong this"); /******************************************************************************/ diff --git a/js/src/tests/ecma_5/Function/function-call.js b/js/src/tests/ecma_5/Function/function-call.js new file mode 100644 index 00000000000..756b583842d --- /dev/null +++ b/js/src/tests/ecma_5/Function/function-call.js @@ -0,0 +1,134 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + * Contributor: + * Jeff Walden + */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 575535; +var summary = 'Function.prototype.call'; +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +function expectTypeError(fun, msg) +{ + try + { + fun(); + assertEq(true, false, "should have thrown a TypeError"); + } + catch (e) + { + assertEq(e instanceof TypeError, true, msg + "; instead threw " + e); + } +} + +function fun() { } + +var global = this; + +assertEq(Function.prototype.call.length, 1); + + +/* Step 1. */ +var nonfuns = [null, 1, -1, 2.5, "[[Call]]", undefined, true, false, {}]; +for (var i = 0, sz = nonfuns.length; i < sz; i++) +{ + var f = function() + { + Function.prototype.call.apply(nonfuns[i]); + }; + var msg = + "expected TypeError calling Function.prototype.call with uncallable this"; + expectTypeError(f, msg); +} + + +/* Steps 2-4. */ +function none() +{ + assertEq(this, global, "bad this"); + assertEq(arguments.length, 0, "wrong arguments"); +} + +none.call(); +none.call(undefined); +none.call(null); + +var seenThis; +function strictNone() +{ + "use strict"; + assertEq(this, seenThis, "bad this"); + assertEq(arguments.length, 0, "wrong arguments"); +} + +seenThis = undefined; +strictNone.call(); +strictNone.call(undefined); + +seenThis = null; +strictNone.call(null); + +seenThis = 17; +strictNone.call(17); + +var seenThisBox, args; +function some() +{ + assertEq(this instanceof seenThisBox, true, + "this not instanceof " + seenThisBox); + assertEq(this.valueOf(), seenThis, + "wrong this valueOf()"); + assertEq(arguments.length, args.length, "wrong arguments count"); + for (var i = 0; i < args.length; i++) + assertEq(arguments[i], args[i], "wrong argument " + i); +} + +seenThis = false; +seenThisBox = Boolean; +args = [8, 6, 7, NaN, undefined, 0.3]; +some.call(false, 8, 6, 7, NaN, undefined, 0.3); + +var obj = {}; + +seenThis = "foo"; +seenThisBox = String; +args = [obj]; +some.call("foo", obj); + +seenThis = obj; +seenThisBox = Object; +some.call(obj, obj); + +function strictSome() +{ + "use strict"; + assertEq(this, seenThis, "wrong this"); + assertEq(arguments.length, args.length, "wrong arguments count"); + for (var i = 0; i < args.length; i++) + assertEq(arguments[i], args[i], "wrong argument " + i); +} + +seenThis = NaN; +args = [8, 6, 7, NaN, undefined, 0.3]; +strictSome.call(NaN, 8, 6, 7, NaN, undefined, 0.3); + +seenThis = "foo"; +args = [obj]; +strictSome.call("foo", obj); + +seenThis = obj; +strictSome.call(obj, obj); + + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("All tests passed!"); diff --git a/js/src/tests/ecma_5/Function/jstests.list b/js/src/tests/ecma_5/Function/jstests.list index eff3602c14d..12799035f4d 100644 --- a/js/src/tests/ecma_5/Function/jstests.list +++ b/js/src/tests/ecma_5/Function/jstests.list @@ -6,5 +6,6 @@ script function-caller.js script strict-arguments.js script arguments-property-attributes.js script function-bind.js +script function-call.js script redefine-arguments-length.js script builtin-no-prototype.js diff --git a/js/src/tests/ecma_5/Global/eval-in-strict-eval-in-normal-function.js b/js/src/tests/ecma_5/Global/eval-in-strict-eval-in-normal-function.js new file mode 100644 index 00000000000..00d76652ddb --- /dev/null +++ b/js/src/tests/ecma_5/Global/eval-in-strict-eval-in-normal-function.js @@ -0,0 +1,30 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 620130; +var summary = + "Calls to eval with same code + varying strict mode of script containing " + + "eval == fail"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +function t(code) { return eval(code); } + +assertEq(t("'use strict'; try { eval('with (5) 17'); } catch (e) { 'threw'; }"), + "threw"); +assertEq(t("try { eval('with (5) 17'); } catch (e) { 'threw'; }"), + 17); +assertEq(t("'use strict'; try { eval('with (5) 17'); } catch (e) { 'threw'; }"), + "threw"); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("All tests passed!"); diff --git a/js/src/tests/ecma_5/Global/jstests.list b/js/src/tests/ecma_5/Global/jstests.list index 845d9c58f14..1da36470ae4 100644 --- a/js/src/tests/ecma_5/Global/jstests.list +++ b/js/src/tests/ecma_5/Global/jstests.list @@ -7,3 +7,4 @@ script eval-inside-with-is-direct.js script parenthesized-eval-is-direct.js script eval-native-callback-is-indirect.js script direct-eval-but-not.js +script eval-in-strict-eval-in-normal-function.js diff --git a/js/src/tests/ecma_5/Object/15.2.3.5-01.js b/js/src/tests/ecma_5/Object/15.2.3.5-01.js index d4bd2edde27..70cfd64f59e 100644 --- a/js/src/tests/ecma_5/Object/15.2.3.5-01.js +++ b/js/src/tests/ecma_5/Object/15.2.3.5-01.js @@ -44,6 +44,15 @@ assertEq(Object.getOwnPropertyDescriptor(o, "baz"), undefined); assertEq(o.baz, 12); assertEq(o.hasOwnProperty("baz"), false); +try { + var actual = + Object.create(Object.create({}, + { boom: { get: function() { return "base"; }}}), + { boom: { get: function() { return "overridden"; }}}).boom +} catch (e) { +} +assertEq(actual, "overridden"); + /******************************************************************************/ reportCompare(true, true); diff --git a/js/src/tests/ecma_5/strict/assign-to-callee-name.js b/js/src/tests/ecma_5/strict/assign-to-callee-name.js new file mode 100644 index 00000000000..3c2efba2e2b --- /dev/null +++ b/js/src/tests/ecma_5/strict/assign-to-callee-name.js @@ -0,0 +1,42 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +var gTestfile = 'assign-to-callee-name.js'; +var BUGNUMBER = 610350; +var summary = + "Assigning to a function expression's name within that function should " + + "throw a TypeError in strict mode code"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +var f = function assignSelfStrict() { "use strict"; assignSelfStrict = 12; }; + +try +{ + var r = f(); + throw new Error("should have thrown a TypeError, returned " + r); +} +catch (e) +{ + assertEq(e instanceof TypeError, true, + "didn't throw a TypeError: " + e); +} + +var assignSelf = 42; +var f2 = function assignSelf() { assignSelf = 12; }; + +f2(); // shouldn't throw, does nothing +assertEq(assignSelf, 42); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("All tests passed!"); diff --git a/js/src/tests/ecma_5/strict/jstests.list b/js/src/tests/ecma_5/strict/jstests.list index 6de46ec717d..1594c0f9a9f 100644 --- a/js/src/tests/ecma_5/strict/jstests.list +++ b/js/src/tests/ecma_5/strict/jstests.list @@ -37,4 +37,5 @@ script regress-532254.js script regress-532041.js script unbrand-this.js script this-for-function-expression-recursion.js +script assign-to-callee-name.js script directive-prologue-01.js diff --git a/js/src/tests/js1_5/extensions/jstests.list b/js/src/tests/js1_5/extensions/jstests.list index cff1a698a16..599c1295dbe 100644 --- a/js/src/tests/js1_5/extensions/jstests.list +++ b/js/src/tests/js1_5/extensions/jstests.list @@ -55,7 +55,7 @@ script regress-339685.js script regress-341956-01.js script regress-341956-02.js script regress-341956-03.js -skip-if(!xulRuntime.shell&&(xulRuntime.OS=="WINNT"||xulRuntime.OS=="Linux")) script regress-342960.js # bug 528464 +skip-if(!xulRuntime.shell&&(xulRuntime.OS=="WINNT"||xulRuntime.OS=="Linux")) silentfail script regress-342960.js # bug 528464 skip script regress-345967.js # slow script regress-346494-01.js script regress-346494.js diff --git a/js/src/tests/js1_8_5/regress/jstests.list b/js/src/tests/js1_8_5/regress/jstests.list index faf4b0db91c..6124dd52e2e 100644 --- a/js/src/tests/js1_8_5/regress/jstests.list +++ b/js/src/tests/js1_8_5/regress/jstests.list @@ -11,6 +11,7 @@ script regress-546615.js script regress-551763-0.js script regress-551763-1.js script regress-551763-2.js +script regress-552432.js script regress-553778.js script regress-555246-0.js script regress-555246-1.js @@ -56,5 +57,9 @@ script regress-610026.js script regress-609617.js script regress-617405-1.js script regress-617405-2.js +script regress-618572.js skip-if(!xulRuntime.shell) script regress-618576.js # uses evalcx fails-if(!xulRuntime.shell) script regress-618652.js +script regress-619003.js +script regress-620376-1.js +script regress-620376-2.js diff --git a/js/src/tests/js1_8_5/regress/regress-552432.js b/js/src/tests/js1_8_5/regress/regress-552432.js new file mode 100644 index 00000000000..001782ffe03 --- /dev/null +++ b/js/src/tests/js1_8_5/regress/regress-552432.js @@ -0,0 +1,16 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +(function (y) { + arguments.y = 2; + var x = Object.create(arguments); + x[0] = 3; + assertEq(x[0], 3); + assertEq(x.y, 2); + assertEq(y, 1); +})(1); + +reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/js1_8_5/regress/regress-618572.js b/js/src/tests/js1_8_5/regress/regress-618572.js new file mode 100644 index 00000000000..4768f035d29 --- /dev/null +++ b/js/src/tests/js1_8_5/regress/regress-618572.js @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 618572; +var summary = 'Do not assert when ungetting a Unicode char sequence'; +var actual = ''; +var expect = ''; + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + enterFunc ('test'); + printBugNumber(BUGNUMBER); + printStatus (summary); + + expect = 'SyntaxError: illegal character'; + + try + { + eval("var a\\u0346 = 3;"); + } + catch(ex) + { + actual = ex + ''; + } + + reportCompare(expect, actual, summary); + + exitFunc ('test'); +} diff --git a/js/src/tests/js1_8_5/regress/regress-619003.js b/js/src/tests/js1_8_5/regress/regress-619003.js new file mode 100644 index 00000000000..a76ba1451bc --- /dev/null +++ b/js/src/tests/js1_8_5/regress/regress-619003.js @@ -0,0 +1,20 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ +var expect = 'SyntaxError: duplicate argument is mixed with destructuring pattern'; +var actual = 'No error'; + +var a = []; + +// Test up to 200 to cover tunables such as js::PropertyTree::MAX_HEIGHT. +for (var i = 0; i < 200; i++) { + a.push("b" + i); + try { + eval("(function ([" + a.join("],[") + "],a,a){})"); + } catch (e) { + actual = '' + e; + } + assertEq(actual, expect); +} +reportCompare(0, 0, "ok"); diff --git a/js/src/tests/js1_8_5/regress/regress-620376-1.js b/js/src/tests/js1_8_5/regress/regress-620376-1.js new file mode 100644 index 00000000000..af3458b51c2 --- /dev/null +++ b/js/src/tests/js1_8_5/regress/regress-620376-1.js @@ -0,0 +1,23 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + * Contributors: Igor Bukanov + */ + +// Test that we can terminate looping array enumeration +function test() { + if (typeof timeout != "function") + return; + + var p = Proxy.create({ enumerate: function() { return Array(1e9); }}); + + expectExitCode(6); + timeout(0.001); + + var n = 0; + for (i in p) { ++n;} + return n; +} + +test(); +reportCompare(0, 0, "ok"); diff --git a/js/src/tests/js1_8_5/regress/regress-620376-2.js b/js/src/tests/js1_8_5/regress/regress-620376-2.js new file mode 100644 index 00000000000..acae97e3644 --- /dev/null +++ b/js/src/tests/js1_8_5/regress/regress-620376-2.js @@ -0,0 +1,19 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + * Contributors: Igor Bukanov + */ + +function test() { + var p = Proxy.create({ enumerate: function() { return { get length() { throw 1; }}; }}); + + try { + for (i in p); + throw new Error("an exception should be thrown"); + } catch (e) { + assertEq(e, 1); + } +} + +test(); +reportCompare(0, 0, "ok"); diff --git a/js/src/tests/jstests.py b/js/src/tests/jstests.py old mode 100644 new mode 100755 index cc1452d6242..1e80bc1bfd3 --- a/js/src/tests/jstests.py +++ b/js/src/tests/jstests.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python + # Test harness for JSTests, controlled by manifest files. import datetime, os, sys, subprocess diff --git a/js/src/tracejit/Writer.cpp b/js/src/tracejit/Writer.cpp index de2105ea0be..96a8297e17d 100644 --- a/js/src/tracejit/Writer.cpp +++ b/js/src/tracejit/Writer.cpp @@ -484,17 +484,17 @@ void ValidateWriter::checkAccSet(LOpcode op, LIns *base, int32_t disp, AccSet ac break; case ACCSET_STRING_MCHARS: - // base = ldp.string ...[offsetof(JSString, mChars)] + // base = ldp.string ...[offsetof(JSString, chars)] // ins = ldus2ui.strchars/c base[0] // OR - // base_oprnd1 = ldp.string ...[offsetof(JSString, mChars)] + // base_oprnd1 = ldp.string ...[offsetof(JSString, chars)] // base = addp base_oprnd1, ... // ins = ldus2ui.strchars/c base[0] ok = op == LIR_ldus2ui && disp == 0 && - (match(base, LIR_ldp, ACCSET_STRING, offsetof(JSString, mChars)) || + (match(base, LIR_ldp, ACCSET_STRING, JSString::offsetOfChars()) || (base->isop(LIR_addp) && - match(base->oprnd1(), LIR_ldp, ACCSET_STRING, offsetof(JSString, mChars)))); + match(base->oprnd1(), LIR_ldp, ACCSET_STRING, JSString::offsetOfChars()))); break; case ACCSET_TYPEMAP: diff --git a/js/src/tracejit/Writer.h b/js/src/tracejit/Writer.h index bb269aab7fb..9985c0832ae 100644 --- a/js/src/tracejit/Writer.h +++ b/js/src/tracejit/Writer.h @@ -180,6 +180,9 @@ struct Address Address(Address addr, int32 offset) : base(addr.base), offset(addr.offset + offset), accSet(addr.accSet) {} + + public: + Address() {} }; @@ -241,10 +244,10 @@ struct FCSlotsAddress : Address : Address(base, slot * sizeof(Value), ACCSET_FCSLOTS) {} }; -struct ArgsSlotsAddress : Address +struct ArgsSlotOffsetAddress : Address { - ArgsSlotsAddress(nj::LIns *base, unsigned slot = 0) - : Address(base, slot * sizeof(Value), ACCSET_ARGS_DATA) {} + ArgsSlotOffsetAddress(nj::LIns *base, unsigned offset = 0) + : Address(base, offset, ACCSET_ARGS_DATA) {} }; struct AnyAddress : Address @@ -595,14 +598,14 @@ class Writer } nj::LIns *ldpStringLengthAndFlags(nj::LIns *str) const { - return name(lir->insLoad(nj::LIR_ldp, str, offsetof(JSString, mLengthAndFlags), + return name(lir->insLoad(nj::LIR_ldp, str, JSString::offsetOfLengthAndFlags(), ACCSET_STRING), - "mLengthAndFlags"); + "lengthAndFlags"); } nj::LIns *ldpStringChars(nj::LIns *str) const { - return name(lir->insLoad(nj::LIR_ldp, str, offsetof(JSString, mChars), ACCSET_STRING), - "mChars"); + return name(lir->insLoad(nj::LIR_ldp, str, JSString::offsetOfChars(), ACCSET_STRING), + "chars"); } nj::LIns *lduc2uiConstTypeMapEntry(nj::LIns *typemap, nj::LIns *index) const { @@ -1185,7 +1188,7 @@ class Writer } nj::LIns *getStringLength(nj::LIns *str) const { - return name(rshupN(ldpStringLengthAndFlags(str), JSString::FLAGS_LENGTH_SHIFT), + return name(rshupN(ldpStringLengthAndFlags(str), JSString::LENGTH_SHIFT), "strLength"); } diff --git a/js/src/xpconnect/loader/mozJSComponentLoader.cpp b/js/src/xpconnect/loader/mozJSComponentLoader.cpp index 409943f92d7..3a72bf44d7b 100644 --- a/js/src/xpconnect/loader/mozJSComponentLoader.cpp +++ b/js/src/xpconnect/loader/mozJSComponentLoader.cpp @@ -200,7 +200,11 @@ Dump(JSContext *cx, uintN argc, jsval *vp) if (!str) return JS_FALSE; - jschar *chars = JS_GetStringChars(str); + size_t length; + const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length); + if (!chars) + return JS_FALSE; + fputs(NS_ConvertUTF16toUTF8(reinterpret_cast(chars)).get(), stderr); return JS_TRUE; } diff --git a/js/src/xpconnect/src/nsXPConnect.cpp b/js/src/xpconnect/src/nsXPConnect.cpp index 07d22f7efc9..77bc9c13fde 100644 --- a/js/src/xpconnect/src/nsXPConnect.cpp +++ b/js/src/xpconnect/src/nsXPConnect.cpp @@ -721,8 +721,7 @@ nsXPConnect::Traverse(void *p, nsCycleCollectionTraversalCallback &cb) JSString* str = JS_GetFunctionId(fun); if(str) { - NS_ConvertUTF16toUTF8 - fname(reinterpret_cast(JS_GetStringChars(str))); + NS_ConvertUTF16toUTF8 fname(JS_GetInternedStringChars(str)); JS_snprintf(name, sizeof(name), "JS Object (Function - %s)", fname.get()); } diff --git a/js/src/xpconnect/src/qsgen.py b/js/src/xpconnect/src/qsgen.py index 12111f3ed19..86c0dd85585 100644 --- a/js/src/xpconnect/src/qsgen.py +++ b/js/src/xpconnect/src/qsgen.py @@ -1143,20 +1143,33 @@ traceableArgumentConversionTemplates = { 'double': " jsdouble ${name} = ${argVal};\n", '[astring]': - " XPCReadableJSStringWrapper ${name}(${argVal});\n", + " XPCReadableJSStringWrapper ${name};\n" + " if (!${name}.init(cx, ${argVal})) {\n" + "${error}", '[domstring]': - " XPCReadableJSStringWrapper ${name}(${argVal});\n", + " XPCReadableJSStringWrapper ${name};\n" + " if (!${name}.init(cx, ${argVal})) {\n" + "${error}", '[utf8string]': - " NS_ConvertUTF16toUTF8 ${name}(" - "(const PRUnichar *)JS_GetStringChars(${argVal}), " - "JS_GetStringLength(${argVal}));\n", + " size_t ${name}_length;\n" + " const jschar *${name}_chars = JS_GetStringCharsAndLength(cx, " + "${argVal}, &${name}_length);\n" + " if (!${name}_chars) {\n" + "${error}" + " NS_ConvertUTF16toUTF8 ${name}(${argVal}_chars, ${argVal}_length);\n", 'string': - " NS_ConvertUTF16toUTF8 ${name}_utf8(" - "(const PRUnichar *)JS_GetStringChars(${argVal}), " - "JS_GetStringLength(${argVal}));\n" + " size_t ${name}_length;\n" + " const jschar *${name}_chars = JS_GetStringCharsAndLength(cx, " + "${argVal}, &${name}_length);\n" + " if (!${name}_chars) {\n" + "${error}" + " NS_ConvertUTF16toUTF8 ${name}_utf8(${name}_chars, ${name}_length);\n" " const char *${name} = ${name}_utf8.get();\n", 'wstring': - " const PRUnichar *${name} = JS_GetStringChars({argVal});\n", + " const jschar *${name}_chars = JS_GetStringCharsZ(cx, {argVal});\n" + " if (!${name}_chars) {\n" + "${error}" + " const PRUnichar *${name} = ${name}_chars;\n", } def writeTraceableArgumentConversion(f, member, i, name, type, haveCcx, @@ -1165,7 +1178,8 @@ def writeTraceableArgumentConversion(f, member, i, name, type, haveCcx, params = { 'name': name, - 'argVal': argVal + 'argVal': argVal, + 'error': getFailureString(getTraceInfoDefaultReturn(member.realtype), 2) } typeName = getBuiltinOrNativeTypeName(type) diff --git a/js/src/xpconnect/src/xpccomponents.cpp b/js/src/xpconnect/src/xpccomponents.cpp index d7893715e35..bae33e5bd3c 100644 --- a/js/src/xpconnect/src/xpccomponents.cpp +++ b/js/src/xpconnect/src/xpccomponents.cpp @@ -674,11 +674,10 @@ nsXPCComponents_InterfacesByID::NewResolve(nsIXPConnectWrappedNative *wrapper, if(mManager && JSID_IS_STRING(id) && 38 == JS_GetStringLength(JSID_TO_STRING(id)) && - nsnull != (name = JS_GetStringChars(JSID_TO_STRING(id)))) + nsnull != (name = JS_GetInternedStringChars(JSID_TO_STRING(id)))) { nsID iid; - if (!iid.Parse(NS_ConvertUTF16toUTF8(reinterpret_cast - (name)).get())) + if (!iid.Parse(NS_ConvertUTF16toUTF8(name).get())) return NS_OK; nsCOMPtr info; @@ -2963,8 +2962,11 @@ nsXPCComponents_Utils::ReportError() frame->GetLineNumber(&lineNo); } - rv = scripterr->InitWithWindowID(reinterpret_cast - (JS_GetStringChars(msgstr)), + const jschar *msgchars = JS_GetStringCharsZ(cx, msgstr); + if (!msgchars) + return NS_OK; + + rv = scripterr->InitWithWindowID(reinterpret_cast(msgchars), NS_ConvertUTF8toUTF16(fileName).get(), nsnull, lineNo, 0, @@ -3004,12 +3006,12 @@ SandboxDump(JSContext *cx, uintN argc, jsval *vp) if (!str) return JS_FALSE; - jschar *chars = JS_GetStringChars(str); + size_t length; + const jschar *chars = JS_GetStringCharsZAndLength(cx, str, &length); if (!chars) return JS_FALSE; - nsDependentString wstr(reinterpret_cast(chars), - JS_GetStringLength(str)); + nsDependentString wstr(chars, length); char *cstr = ToNewUTF8String(wstr); if (!cstr) return JS_FALSE; @@ -3339,10 +3341,15 @@ nsXPCComponents_utils_Sandbox::CallOrConstruct(nsIXPConnectWrappedNative *wrappe nsCOMPtr principal; nsISupports *prinOrSop = nsnull; if (JSVAL_IS_STRING(argv[0])) { - JSString *codebasestr = JSVAL_TO_STRING(argv[0]); - nsAutoString codebase(reinterpret_cast - (JS_GetStringChars(codebasestr)), - JS_GetStringLength(codebasestr)); + JSString *codebaseStr = JSVAL_TO_STRING(argv[0]); + size_t codebaseLength; + const jschar *codebaseChars = JS_GetStringCharsAndLength(cx, codebaseStr, + &codebaseLength); + if (!codebaseChars) { + return ThrowAndFail(NS_ERROR_FAILURE, cx, _retval); + } + + nsAutoString codebase(codebaseChars, codebaseLength); nsCOMPtr uri; rv = NS_NewURI(getter_AddRefs(uri), codebase); if (NS_FAILED(rv)) { diff --git a/js/src/xpconnect/src/xpcconvert.cpp b/js/src/xpconnect/src/xpcconvert.cpp index 4a60bbeffd6..e2ac4093df4 100644 --- a/js/src/xpconnect/src/xpcconvert.cpp +++ b/js/src/xpconnect/src/xpcconvert.cpp @@ -176,7 +176,8 @@ FinalizeXPCOMUCString(JSContext *cx, JSString *str) NS_ASSERTION(sXPCOMUCStringFinalizerIndex != -1, "XPCConvert: XPCOM Unicode string finalizer called uninitialized!"); - jschar* buffer = JS_GetStringChars(str); + jschar* buffer = const_cast(JS_GetStringCharsZ(cx, str)); + NS_ASSERTION(buffer, "How could this OOM if we allocated the memory?"); nsMemory::Free(buffer); } @@ -615,25 +616,35 @@ XPCConvert::JSData2Native(XPCCallContext& ccx, void* d, jsval s, { return JS_FALSE; } - jschar ch = JS_GetStringLength(str) ? JS_GetStringChars(str)[0] : 0; + size_t length; + const jschar* chars = JS_GetStringCharsAndLength(cx, str, &length); + if (!chars) + { + return JS_FALSE; + } + jschar ch = length ? chars[0] : 0; NS_ASSERTION(!ILLEGAL_RANGE(ch), "U+0080/U+0100 - U+FFFF data lost"); *((char*)d) = char(ch); break; } case nsXPTType::T_WCHAR : { - const jschar* chars=nsnull; JSString* str; if(!(str = JS_ValueToString(cx, s))) { return JS_FALSE; } - if(JS_GetStringLength(str) == 0) + size_t length; + const jschar* chars = JS_GetStringCharsAndLength(cx, str, &length); + if (!chars) + { + return JS_FALSE; + } + if(length == 0) { *((uint16*)d) = 0; break; } - chars = JS_GetStringChars(str); *((uint16*)d) = (uint16) chars[0]; break; } @@ -731,7 +742,7 @@ XPCConvert::JSData2Native(XPCCallContext& ccx, void* d, jsval s, length = (PRUint32) JS_GetStringLength(str); if(length) { - chars = (const PRUnichar*) JS_GetStringChars(str); + chars = JS_GetStringCharsZ(cx, str); if(!chars) return JS_FALSE; if(STRING_TO_JSVAL(str) != s) @@ -749,8 +760,13 @@ XPCConvert::JSData2Native(XPCCallContext& ccx, void* d, jsval s, // XXX extra string copy when isNewString if(str && !isNewString) { + size_t strLength; + const jschar *strChars = JS_GetStringCharsZAndLength(cx, str, &strLength); + if (!strChars) + return JS_FALSE; + XPCReadableJSStringWrapper *wrapper = - XPCStringConvert::JSStringToReadable(ccx, str); + ccx.NewStringWrapper(strChars, strLength); if(!wrapper) return JS_FALSE; @@ -817,12 +833,12 @@ XPCConvert::JSData2Native(XPCCallContext& ccx, void* d, jsval s, return JS_FALSE; } #ifdef DEBUG - jschar* chars=nsnull; - if(nsnull != (chars = JS_GetStringChars(str))) + const jschar* chars=nsnull; + if(nsnull != (chars = JS_GetStringCharsZ(cx, str))) { PRBool legalRange = PR_TRUE; int len = JS_GetStringLength(str); - jschar* t; + const jschar* t; PRInt32 i=0; for(t=chars; (i< len) && legalRange ; i++,t++) { if(ILLEGAL_RANGE(*t)) @@ -871,7 +887,7 @@ XPCConvert::JSData2Native(XPCCallContext& ccx, void* d, jsval s, } if(useAllocator) { - if(!(chars = JS_GetStringChars(str))) + if(!(chars = JS_GetStringCharsZ(cx, str))) { return JS_FALSE; } @@ -900,7 +916,7 @@ XPCConvert::JSData2Native(XPCCallContext& ccx, void* d, jsval s, case nsXPTType::T_UTF8STRING: { - jschar* chars; + const jschar* chars; PRUint32 length; JSString* str; @@ -927,7 +943,7 @@ XPCConvert::JSData2Native(XPCCallContext& ccx, void* d, jsval s, // The JS val is neither null nor void... if(!(str = JS_ValueToString(cx, s))|| - !(chars = JS_GetStringChars(str))) + !(chars = JS_GetStringCharsZ(cx, str))) { return JS_FALSE; } @@ -1031,8 +1047,7 @@ XPCConvert::JSData2Native(XPCCallContext& ccx, void* d, jsval s, { // We're trying to pass a string as an nsIAtom. Let's atomize! JSString* str = JSVAL_TO_STRING(s); - PRUnichar* chars = - reinterpret_cast(JS_GetStringChars(str)); + const PRUnichar* chars = JS_GetStringCharsZ(cx, str); if (!chars) { if (pErr) *pErr = NS_ERROR_XPC_BAD_CONVERT_JS_NULL_REF; @@ -2411,7 +2426,7 @@ XPCConvert::JSStringWithSize2Native(XPCCallContext& ccx, void* d, jsval s, if(useAllocator) { - if(!(chars = JS_GetStringChars(str))) + if(!(chars = JS_GetStringCharsZ(cx, str))) { return JS_FALSE; } diff --git a/js/src/xpconnect/src/xpcprivate.h b/js/src/xpconnect/src/xpcprivate.h index e4902c732da..8cfbd7e90e7 100644 --- a/js/src/xpconnect/src/xpcprivate.h +++ b/js/src/xpconnect/src/xpcprivate.h @@ -1016,10 +1016,17 @@ public: nsDependentString(char_traits::sEmptyBuffer, char_traits::sEmptyBuffer) { SetIsVoid(PR_TRUE); } - explicit XPCReadableJSStringWrapper(JSString *str) : - nsDependentString(reinterpret_cast(::JS_GetStringChars(str)), - str->length()) - { } + JSBool init(JSContext* aContext, JSString* str) + { + size_t length; + const jschar* chars = JS_GetStringCharsZAndLength(aContext, str, &length); + if (!chars) + return JS_FALSE; + + NS_ASSERTION(IsEmpty(), "init() on initialized string"); + new(static_cast(this)) nsDependentString(chars, length); + return JS_TRUE; + } }; // No virtuals @@ -1133,8 +1140,7 @@ public: operator JSContext*() const {return GetJSContext();} - XPCReadableJSStringWrapper *NewStringWrapper(const PRUnichar *str, - PRUint32 len); + XPCReadableJSStringWrapper *NewStringWrapper(const PRUnichar *str, PRUint32 len); void DeleteString(nsAString *string); #ifdef XPC_IDISPATCH_SUPPORT @@ -3338,9 +3344,6 @@ public: static jsval ReadableToJSVal(JSContext *cx, const nsAString &readable, nsStringBuffer** sharedBuffer); - static XPCReadableJSStringWrapper *JSStringToReadable(XPCCallContext& ccx, - JSString *str); - static void ShutdownDOMStringFinalizer(); private: diff --git a/js/src/xpconnect/src/xpcquickstubs.cpp b/js/src/xpconnect/src/xpcquickstubs.cpp index 751883d11a7..29ad6d80869 100644 --- a/js/src/xpconnect/src/xpcquickstubs.cpp +++ b/js/src/xpconnect/src/xpcquickstubs.cpp @@ -701,10 +701,11 @@ xpc_qsDOMString::xpc_qsDOMString(JSContext *cx, jsval v, jsval *pval, if (!s) return; - size_t len = s->length(); - const PRUnichar* chars = - (len == 0 ? traits::sEmptyBuffer : - reinterpret_cast(JS_GetStringChars(s))); + size_t len; + const jschar *chars = JS_GetStringCharsZAndLength(cx, s, &len); + if (!chars) + return; + new(mBuf) implementation_type(chars, len); mValid = JS_TRUE; } @@ -746,9 +747,10 @@ xpc_qsAUTF8String::xpc_qsAUTF8String(JSContext *cx, jsval v, jsval *pval) if (!s) return; - size_t len = s->length(); - const PRUnichar* chars = - reinterpret_cast(JS_GetStringChars(s)); + size_t len; + const PRUnichar *chars = JS_GetStringCharsZAndLength(cx, s, &len); + if (!chars) + return; new(mBuf) implementation_type(chars, len); mValid = JS_TRUE; @@ -1033,8 +1035,12 @@ xpc_qsJsvalToWcharStr(JSContext *cx, jsval v, jsval *pval, PRUnichar **pstr) *pval = STRING_TO_JSVAL(str); // Root the new string. } + const jschar *chars = JS_GetStringCharsZ(cx, str); + if (!chars) + return JS_FALSE; + // XXXbz this is casting away constness too... That seems like a bad idea. - *pstr = (PRUnichar*)JS_GetStringChars(str); + *pstr = const_cast(chars); return JS_TRUE; } diff --git a/js/src/xpconnect/src/xpcstring.cpp b/js/src/xpconnect/src/xpcstring.cpp index 8457b326362..ddbefa11fa5 100644 --- a/js/src/xpconnect/src/xpcstring.cpp +++ b/js/src/xpconnect/src/xpcstring.cpp @@ -60,7 +60,9 @@ static int sDOMStringFinalizerIndex = -1; static void DOMStringFinalizer(JSContext *cx, JSString *str) { - nsStringBuffer::FromData(JS_GetStringChars(str))->Release(); + jschar *chars = const_cast(JS_GetStringCharsZ(cx, str)); + NS_ASSERTION(chars, "How could this OOM if we allocated the memory?"); + nsStringBuffer::FromData(chars)->Release(); } void @@ -139,15 +141,3 @@ XPCStringConvert::ReadableToJSVal(JSContext *cx, } return STRING_TO_JSVAL(str); } - -// static -XPCReadableJSStringWrapper * -XPCStringConvert::JSStringToReadable(XPCCallContext& ccx, JSString *str) -{ - const PRUnichar *chars = - reinterpret_cast(JS_GetStringCharsZ(ccx, str)); - if(!chars) - return nsnull; - - return ccx.NewStringWrapper(chars, JS_GetStringLength(str)); -} diff --git a/js/src/xpconnect/src/xpcvariant.cpp b/js/src/xpconnect/src/xpcvariant.cpp index 41214c22f2f..890ddf26d15 100644 --- a/js/src/xpconnect/src/xpcvariant.cpp +++ b/js/src/xpconnect/src/xpcvariant.cpp @@ -327,11 +327,17 @@ JSBool XPCVariant::InitializeData(XPCCallContext& ccx) NS_ASSERTION(mData.mType == nsIDataType::VTYPE_EMPTY, "Why do we already have data?"); - mData.u.wstr.mWStringValue = - reinterpret_cast(JS_GetStringChars(str)); + // Despite the fact that the variant holds the length, there are + // implicit assumptions that mWStringValue[mWStringLength] == 0 + size_t length; + const jschar *chars = JS_GetStringCharsZAndLength(ccx, str, &length); + if (!chars) + return JS_FALSE; + + mData.u.wstr.mWStringValue = const_cast(chars); // Use C-style cast, because reinterpret cast from size_t to // PRUint32 is not valid on some platforms. - mData.u.wstr.mWStringLength = (PRUint32)JS_GetStringLength(str); + mData.u.wstr.mWStringLength = (PRUint32)length; mData.mType = nsIDataType::VTYPE_WSTRING_SIZE_IS; return JS_TRUE; diff --git a/js/src/xpconnect/src/xpcwrappedjsclass.cpp b/js/src/xpconnect/src/xpcwrappedjsclass.cpp index 6d0849e18a2..fbe1eec9b6e 100644 --- a/js/src/xpconnect/src/xpcwrappedjsclass.cpp +++ b/js/src/xpconnect/src/xpcwrappedjsclass.cpp @@ -454,10 +454,13 @@ nsXPCWrappedJSClass::BuildPropertyEnumerator(XPCCallContext& ccx, if(!name) goto out; + size_t length; + const jschar *chars = JS_GetStringCharsAndLength(cx, name, &length); + if (!chars) + goto out; + nsCOMPtr property = - new xpcProperty((const PRUnichar*) JS_GetStringChars(name), - (PRUint32) JS_GetStringLength(name), - value); + new xpcProperty(chars, (PRUint32) length, value); if(!property) goto out; diff --git a/js/src/xpconnect/tests/TestXPC.cpp b/js/src/xpconnect/tests/TestXPC.cpp index aa65ebd5e0f..658c59267a4 100644 --- a/js/src/xpconnect/tests/TestXPC.cpp +++ b/js/src/xpconnect/tests/TestXPC.cpp @@ -552,11 +552,11 @@ TestArgFormatter(JSContext* jscontext, JSObject* glob, nsIXPConnect* xpc) const char* e_in = "another meaningless chunck of text"; - JSString* a_out; + JSBool a_match; nsCOMPtr b_out; nsCOMPtr c_out; nsAutoString d_out; - JSString* e_out; + JSBool e_match; nsCOMPtr specified; PRInt32 val; @@ -588,6 +588,7 @@ TestArgFormatter(JSContext* jscontext, JSObject* glob, nsIXPConnect* xpc) return; } + JSString *a_out, *e_out; ok = JS_ConvertArguments(jscontext, 5, argv, "S %ip %iv %is S", &a_out, static_cast(getter_AddRefs(b_out)), @@ -604,11 +605,13 @@ TestArgFormatter(JSContext* jscontext, JSObject* glob, nsIXPConnect* xpc) TAF_CHECK(c_out, " JS to native for %%iv returned NULL -- FAILED!\n"); TAF_CHECK(NS_SUCCEEDED(c_out->GetAsInt32(&val)) && val == 5, " JS to native for %%iv holds wrong value -- FAILED!\n"); TAF_CHECK(d_in.Equals(d_out), " JS to native for %%is returned the wrong value -- FAILED!\n"); + TAF_CHECK(JS_StringEqualsAscii(jscontext, a_out, a_in, &a_match), " oom -- FAILED!\n"); + TAF_CHECK(JS_StringEqualsAscii(jscontext, e_out, e_in, &e_match), " oom -- FAILED!\n"); } while (0); if (!ok) return; - if(JS_MatchStringAndAscii(a_out, a_in) && JS_MatchStringAndAscii(e_out, e_in)) + if(a_match && e_match) printf("passed\n"); else printf(" conversion OK, but surrounding was mangled -- FAILED!\n"); diff --git a/js/src/xpconnect/wrappers/AccessCheck.cpp b/js/src/xpconnect/wrappers/AccessCheck.cpp index 0994653be39..98dbd51148b 100644 --- a/js/src/xpconnect/wrappers/AccessCheck.cpp +++ b/js/src/xpconnect/wrappers/AccessCheck.cpp @@ -109,18 +109,18 @@ AccessCheck::getPrincipal(JSCompartment *compartment) #define NAME(ch, str, cases) \ case ch: if (!strcmp(name, str)) switch (propChars[0]) { cases }; break; #define PROP(ch, actions) case ch: { actions }; break; -#define RW(str) if (JS_MatchStringAndAscii(prop, str)) return true; -#define R(str) if (!set && JS_MatchStringAndAscii(prop, str)) return true; -#define W(str) if (set && JS_MatchStringAndAscii(prop, str)) return true; +#define RW(str) if (JS_FlatStringEqualsAscii(prop, str)) return true; +#define R(str) if (!set && JS_FlatStringEqualsAscii(prop, str)) return true; +#define W(str) if (set && JS_FlatStringEqualsAscii(prop, str)) return true; // Hardcoded policy for cross origin property access. This was culled from the // preferences file (all.js). We don't want users to overwrite highly sensitive // security policies. static bool -IsPermitted(const char *name, JSString *prop, bool set) +IsPermitted(const char *name, JSFlatString *prop, bool set) { size_t propLength; - const jschar *propChars = JS_GetStringCharsAndLength(prop, &propLength); + const jschar *propChars = JS_GetInternedStringCharsAndLength(prop, &propLength); if (!propLength) return false; switch(name[0]) { @@ -183,8 +183,7 @@ IsFrameId(JSContext *cx, JSObject *obj, jsid id) if (JSID_IS_INT(id)) { col->Item(JSID_TO_INT(id), getter_AddRefs(domwin)); } else if (JSID_IS_ATOM(id)) { - nsAutoString str(reinterpret_cast - (JS_GetStringChars(ATOM_TO_STRING(JSID_TO_ATOM(id))))); + nsAutoString str(JS_GetInternedStringChars(JSID_TO_STRING(id))); col->NamedItem(str, getter_AddRefs(domwin)); } else { return false; @@ -291,7 +290,7 @@ AccessCheck::isCrossOriginAccessPermitted(JSContext *cx, JSObject *wrapper, jsid name = clasp->name; if (JSID_IS_ATOM(id)) { - if (IsPermitted(name, JSID_TO_STRING(id), act == JSWrapper::SET)) + if (IsPermitted(name, JSID_TO_FLAT_STRING(id), act == JSWrapper::SET)) return true; } @@ -418,7 +417,9 @@ AccessCheck::deny(JSContext *cx, jsid id) JSString *str = JS_ValueToString(cx, idval); if (!str) return; - JS_ReportError(cx, "Permission denied to access property '%hs'", JS_GetStringChars(str)); + const jschar *chars = JS_GetStringCharsZ(cx, str); + if (chars) + JS_ReportError(cx, "Permission denied to access property '%hs'", chars); } } @@ -480,8 +481,11 @@ ExposedPropertiesOnly::check(JSContext *cx, JSObject *wrapper, jsid id, JSWrappe } JSString *str = JSVAL_TO_STRING(desc.value); - const jschar *chars = JS_GetStringChars(str); - size_t length = JS_GetStringLength(str); + size_t length; + const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length); + if (!chars) + return false; + for (size_t i = 0; i < length; ++i) { switch (chars[i]) { case 'r': diff --git a/js/src/xpconnect/wrappers/XrayWrapper.cpp b/js/src/xpconnect/wrappers/XrayWrapper.cpp index 51cd6d45a24..1709e26b9b8 100644 --- a/js/src/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/src/xpconnect/wrappers/XrayWrapper.cpp @@ -649,6 +649,11 @@ EnumerateNames(JSContext *cx, JSObject *wrapper, uintN flags, js::AutoIdVector & return js::GetPropertyNames(cx, wnObject, flags, &props); } + if (WrapperFactory::IsPartiallyTransparent(wrapper)) { + JS_ReportError(cx, "Not allowed to enumerate cross origin objects"); + return false; + } + // Enumerate expando properties first. JSObject *expando = GetExpandoObject(cx, holder); if (!expando) diff --git a/js/src/yarr/jswtfbridge.h b/js/src/yarr/jswtfbridge.h index 2ed79102bf4..ef0f72b1739 100644 --- a/js/src/yarr/jswtfbridge.h +++ b/js/src/yarr/jswtfbridge.h @@ -50,7 +50,7 @@ #include "jstl.h" typedef jschar UChar; -typedef JSString UString; +typedef JSLinearString UString; template class ValueDeleter diff --git a/modules/plugin/base/src/nsJSNPRuntime.cpp b/modules/plugin/base/src/nsJSNPRuntime.cpp index c9bc54d3581..0eff426cfc4 100644 --- a/modules/plugin/base/src/nsJSNPRuntime.cpp +++ b/modules/plugin/base/src/nsJSNPRuntime.cpp @@ -463,8 +463,13 @@ JSValToNPVariant(NPP npp, JSContext *cx, jsval val, NPVariant *variant) } } else if (JSVAL_IS_STRING(val)) { JSString *jsstr = JSVAL_TO_STRING(val); - nsDependentString str((PRUnichar *)::JS_GetStringChars(jsstr), - ::JS_GetStringLength(jsstr)); + size_t length; + const jschar *chars = ::JS_GetStringCharsZAndLength(cx, jsstr, &length); + if (!chars) { + return false; + } + + nsDependentString str(chars, length); PRUint32 len; char *p = ToNewUTF8String(str, &len); @@ -603,16 +608,9 @@ nsJSObjWrapper::NP_Invalidate(NPObject *npobj) static JSBool GetProperty(JSContext *cx, JSObject *obj, NPIdentifier id, jsval *rval) { - if (NPIdentifierIsString(id)) { - JSString *str = NPIdentifierToString(id); - - return ::JS_GetUCProperty(cx, obj, ::JS_GetStringChars(str), - ::JS_GetStringLength(str), rval); - } - - NS_ASSERTION(NPIdentifierIsInt(id), "id must be either string or int!\n"); - - return ::JS_GetElement(cx, obj, NPIdentifierToInt(id), rval); + NS_ASSERTION(NPIdentifierIsInt(id) || NPIdentifierIsString(id), + "id must be either string or int!\n"); + return ::JS_GetPropertyById(cx, obj, NPIdentifierToJSId(id), rval); } // static @@ -796,17 +794,9 @@ nsJSObjWrapper::NP_HasProperty(NPObject *npobj, NPIdentifier id) if (!ac.enter(cx, npjsobj->mJSObj)) return PR_FALSE; - if (NPIdentifierIsString(id)) { - JSString *str = NPIdentifierToString(id); - - ok = ::JS_HasUCProperty(cx, npjsobj->mJSObj, ::JS_GetStringChars(str), - ::JS_GetStringLength(str), &found); - } else { - NS_ASSERTION(NPIdentifierIsInt(id), "id must be either string or int!\n"); - - ok = ::JS_HasElement(cx, npjsobj->mJSObj, NPIdentifierToInt(id), &found); - } - + NS_ASSERTION(NPIdentifierIsInt(id) || NPIdentifierIsString(id), + "id must be either string or int!\n"); + ok = ::JS_HasPropertyById(cx, npjsobj->mJSObj, NPIdentifierToJSId(id), &found); return ok && found; } @@ -877,16 +867,9 @@ nsJSObjWrapper::NP_SetProperty(NPObject *npobj, NPIdentifier id, jsval v = NPVariantToJSVal(npp, cx, value); js::AutoValueRooter tvr(cx, v); - if (NPIdentifierIsString(id)) { - JSString *str = NPIdentifierToString(id); - - ok = ::JS_SetUCProperty(cx, npjsobj->mJSObj, ::JS_GetStringChars(str), - ::JS_GetStringLength(str), &v); - } else { - NS_ASSERTION(NPIdentifierIsInt(id), "id must be either string or int!\n"); - - ok = ::JS_SetElement(cx, npjsobj->mJSObj, NPIdentifierToInt(id), &v); - } + NS_ASSERTION(NPIdentifierIsInt(id) || NPIdentifierIsString(id), + "id must be either string or int!\n"); + ok = ::JS_SetPropertyById(cx, npjsobj->mJSObj, NPIdentifierToJSId(id), &v); // return ok == JS_TRUE to quiet down compiler warning, even if // return ok is what we really want. @@ -923,45 +906,21 @@ nsJSObjWrapper::NP_RemoveProperty(NPObject *npobj, NPIdentifier id) if (!ac.enter(cx, npjsobj->mJSObj)) return PR_FALSE; - if (NPIdentifierIsString(id)) { - JSString *str = NPIdentifierToString(id); + NS_ASSERTION(NPIdentifierIsInt(id) || NPIdentifierIsString(id), + "id must be either string or int!\n"); + ok = ::JS_DeletePropertyById2(cx, npjsobj->mJSObj, NPIdentifierToJSId(id), &deleted); + if (ok && deleted == JSVAL_TRUE) { + // FIXME: See bug 425823, we shouldn't need to do this, and once + // that bug is fixed we can remove this code. - ok = ::JS_DeleteUCProperty2(cx, npjsobj->mJSObj, ::JS_GetStringChars(str), - ::JS_GetStringLength(str), &deleted); + JSBool hasProp; + ok = ::JS_HasPropertyById(cx, npjsobj->mJSObj, NPIdentifierToJSId(id), &hasProp); - if (ok && deleted == JSVAL_TRUE) { - // FIXME: See bug 425823, we shouldn't need to do this, and once - // that bug is fixed we can remove this code. + if (ok && hasProp) { + // The property might have been deleted, but it got + // re-resolved, so no, it's not really deleted. - JSBool hasProp; - ok = ::JS_HasUCProperty(cx, npjsobj->mJSObj, ::JS_GetStringChars(str), - ::JS_GetStringLength(str), &hasProp); - - if (ok && hasProp) { - // The property might have been deleted, but it got - // re-resolved, so no, it's not really deleted. - - deleted = JSVAL_FALSE; - } - } - } else { - NS_ASSERTION(NPIdentifierIsInt(id), "id must be either string or int!\n"); - - ok = ::JS_DeleteElement2(cx, npjsobj->mJSObj, NPIdentifierToInt(id), &deleted); - - if (ok && deleted == JSVAL_TRUE) { - // FIXME: See bug 425823, we shouldn't need to do this, and once - // that bug is fixed we can remove this code. - - JSBool hasProp; - ok = ::JS_HasElement(cx, npjsobj->mJSObj, NPIdentifierToInt(id), &hasProp); - - if (ok && hasProp) { - // The property might have been deleted, but it got - // re-resolved, so no, it's not really deleted. - - deleted = JSVAL_FALSE; - } + deleted = JSVAL_FALSE; } } @@ -1027,14 +986,12 @@ nsJSObjWrapper::NP_Enumerate(NPObject *npobj, NPIdentifier **idarray, NPIdentifier id; if (JSVAL_IS_STRING(v)) { - JSString *str = JSVAL_TO_STRING(v); + JSString *str = JS_InternJSString(cx, JSVAL_TO_STRING(v)); + if (!str) { + ::JS_DestroyIdArray(cx, ida); + PR_Free(*idarray); - if (!JS_InternUCStringN(cx, ::JS_GetStringChars(str), - ::JS_GetStringLength(str))) { - ::JS_DestroyIdArray(cx, ida); - PR_Free(*idarray); - - return PR_FALSE; + return PR_FALSE; } id = StringToNPIdentifier(str); } else { @@ -1693,21 +1650,11 @@ NPObjWrapper_NewResolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, return JS_FALSE; if (hasProperty) { - JSBool ok; - - if (JSID_IS_STRING(id)) { - JSString *str = JSID_TO_STRING(id); - - ok = ::JS_DefineUCProperty(cx, obj, ::JS_GetStringChars(str), - ::JS_GetStringLength(str), JSVAL_VOID, nsnull, - nsnull, JSPROP_ENUMERATE); - } else { - ok = ::JS_DefineElement(cx, obj, JSID_TO_INT(id), JSVAL_VOID, nsnull, - nsnull, JSPROP_ENUMERATE); - } - - if (!ok) { - return JS_FALSE; + NS_ASSERTION(JSID_IS_STRING(id) || JSID_IS_INT(id), + "id must be either string or int!\n"); + if (!::JS_DefinePropertyById(cx, obj, id, JSVAL_VOID, nsnull, + nsnull, JSPROP_ENUMERATE)) { + return JS_FALSE; } *objp = obj; @@ -1720,29 +1667,11 @@ NPObjWrapper_NewResolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, return JS_FALSE; if (hasMethod) { - JSString *str = nsnull; + NS_ASSERTION(JSID_IS_STRING(id) || JSID_IS_INT(id), + "id must be either string or int!\n"); - if (JSID_IS_STRING(id)) { - str = JSID_TO_STRING(id); - } else { - NS_ASSERTION(JSID_IS_INT(id), "id must be either string or int!\n"); - - jsval idval; - if (!JS_IdToValue(cx, id, &idval)) - return JS_FALSE; - - str = ::JS_ValueToString(cx, idval); - if (!str) { - // OOM. The JS engine throws exceptions for us in this case. - - return JS_FALSE; - } - } - - JSFunction *fnc = - ::JS_DefineUCFunction(cx, obj, ::JS_GetStringChars(str), - ::JS_GetStringLength(str), CallNPMethod, 0, - JSPROP_ENUMERATE); + JSFunction *fnc = ::JS_DefineFunctionById(cx, obj, id, CallNPMethod, 0, + JSPROP_ENUMERATE); *objp = obj; diff --git a/modules/plugin/base/src/nsNPAPIPlugin.cpp b/modules/plugin/base/src/nsNPAPIPlugin.cpp index 4034bb0e48f..5c123104b06 100644 --- a/modules/plugin/base/src/nsNPAPIPlugin.cpp +++ b/modules/plugin/base/src/nsNPAPIPlugin.cpp @@ -1441,7 +1441,7 @@ _utf8fromidentifier(NPIdentifier id) JSString *str = NPIdentifierToString(id); return - ToNewUTF8String(nsDependentString((PRUnichar *)::JS_GetStringChars(str), + ToNewUTF8String(nsDependentString(::JS_GetInternedStringChars(str), ::JS_GetStringLength(str))); } diff --git a/storage/src/Makefile.in b/storage/src/Makefile.in index a8ff128d7f9..bcb6e196160 100644 --- a/storage/src/Makefile.in +++ b/storage/src/Makefile.in @@ -83,9 +83,11 @@ CPPSRCS = \ VacuumManager.cpp \ $(NULL) +# For nsDependentJSString LOCAL_INCLUDES = \ $(SQLITE_CFLAGS) \ -I$(topsrcdir)/db/sqlite3/src \ + -I$(topsrcdir)/dom/base \ $(NULL) # This is the default value. If we ever change it when compiling sqlite, we diff --git a/storage/src/mozStorageAsyncStatementJSHelper.cpp b/storage/src/mozStorageAsyncStatementJSHelper.cpp index 2b7f7c11623..f545face552 100644 --- a/storage/src/mozStorageAsyncStatementJSHelper.cpp +++ b/storage/src/mozStorageAsyncStatementJSHelper.cpp @@ -136,7 +136,7 @@ AsyncStatementJSHelper::GetProperty(nsIXPConnectWrappedNative *aWrapper, } #endif - if (::JS_MatchStringAndAscii(JSID_TO_STRING(aId), "params")) + if (::JS_FlatStringEqualsAscii(JSID_TO_FLAT_STRING(aId), "params")) return getParams(stmt, aCtx, aScopeObj, _result); return NS_OK; diff --git a/storage/src/mozStorageAsyncStatementParams.cpp b/storage/src/mozStorageAsyncStatementParams.cpp index f7fb708a4a6..7f44702b5d1 100644 --- a/storage/src/mozStorageAsyncStatementParams.cpp +++ b/storage/src/mozStorageAsyncStatementParams.cpp @@ -97,9 +97,9 @@ AsyncStatementParams::SetProperty( } else if (JSID_IS_STRING(aId)) { JSString *str = JSID_TO_STRING(aId); - NS_ConvertUTF16toUTF8 name(reinterpret_cast - (::JS_GetStringChars(str)), - ::JS_GetStringLength(str)); + size_t length; + const jschar *chars = JS_GetInternedStringCharsAndLength(str, &length); + NS_ConvertUTF16toUTF8 name(chars, length); nsCOMPtr variant(convertJSValToVariant(aCtx, *_vp)); NS_ENSURE_TRUE(variant, NS_ERROR_UNEXPECTED); @@ -141,8 +141,8 @@ AsyncStatementParams::NewResolve( } else if (JSID_IS_STRING(aId)) { JSString *str = JSID_TO_STRING(aId); - jschar *nameChars = ::JS_GetStringChars(str); - size_t nameLength = ::JS_GetStringLength(str); + size_t nameLength; + const jschar *nameChars = ::JS_GetInternedStringCharsAndLength(str, &nameLength); // We are unable to tell if there's a parameter with this name and so // we must assume that there is. This screws the rest of the prototype diff --git a/storage/src/mozStoragePrivateHelpers.cpp b/storage/src/mozStoragePrivateHelpers.cpp index 0640fcf71e3..033e4e7dfa7 100644 --- a/storage/src/mozStoragePrivateHelpers.cpp +++ b/storage/src/mozStoragePrivateHelpers.cpp @@ -49,6 +49,7 @@ #include "mozilla/Mutex.h" #include "mozilla/CondVar.h" #include "nsThreadUtils.h" +#include "nsJSUtils.h" #include "Variant.h" #include "mozStoragePrivateHelpers.h" @@ -151,10 +152,9 @@ convertJSValToVariant( if (JSVAL_IS_STRING(aValue)) { JSString *str = JSVAL_TO_STRING(aValue); - nsDependentString value( - reinterpret_cast(::JS_GetStringChars(str)), - ::JS_GetStringLength(str) - ); + nsDependentJSString value; + if (!value.init(aCtx, str)) + return nsnull; return new TextVariant(value); } diff --git a/storage/src/mozStorageStatementJSHelper.cpp b/storage/src/mozStorageStatementJSHelper.cpp index 749b0068066..6add7e4e568 100644 --- a/storage/src/mozStorageStatementJSHelper.cpp +++ b/storage/src/mozStorageStatementJSHelper.cpp @@ -224,11 +224,11 @@ StatementJSHelper::GetProperty(nsIXPConnectWrappedNative *aWrapper, static_cast(aWrapper->Native()) ); - JSString *str = JSID_TO_STRING(aId); - if (::JS_MatchStringAndAscii(str, "row")) + JSFlatString *str = JSID_TO_FLAT_STRING(aId); + if (::JS_FlatStringEqualsAscii(str, "row")) return getRow(stmt, aCtx, aScopeObj, _result); - if (::JS_MatchStringAndAscii(str, "params")) + if (::JS_FlatStringEqualsAscii(str, "params")) return getParams(stmt, aCtx, aScopeObj, _result); return NS_OK; @@ -247,7 +247,7 @@ StatementJSHelper::NewResolve(nsIXPConnectWrappedNative *aWrapper, if (!JSID_IS_STRING(aId)) return NS_OK; - if (::JS_MatchStringAndAscii(JSID_TO_STRING(aId), "step")) { + if (::JS_FlatStringEqualsAscii(JSID_TO_FLAT_STRING(aId), "step")) { *_retval = ::JS_DefineFunction(aCtx, aScopeObj, "step", stepFunc, 0, 0) != nsnull; *_objp = aScopeObj; diff --git a/storage/src/mozStorageStatementParams.cpp b/storage/src/mozStorageStatementParams.cpp index 48a0c3c94c5..5c9f6f35c0d 100644 --- a/storage/src/mozStorageStatementParams.cpp +++ b/storage/src/mozStorageStatementParams.cpp @@ -95,9 +95,10 @@ StatementParams::SetProperty(nsIXPConnectWrappedNative *aWrapper, } else if (JSID_IS_STRING(aId)) { JSString *str = JSID_TO_STRING(aId); - NS_ConvertUTF16toUTF8 name(reinterpret_cast - (::JS_GetStringChars(str)), - ::JS_GetStringLength(str)); + size_t length; + const jschar *chars = JS_GetStringCharsAndLength(aCtx, str, &length); + NS_ENSURE_TRUE(chars, NS_ERROR_UNEXPECTED); + NS_ConvertUTF16toUTF8 name(chars, length); // check to see if there's a parameter with this name nsCOMPtr variant(convertJSValToVariant(aCtx, *_vp)); @@ -211,8 +212,9 @@ StatementParams::NewResolve(nsIXPConnectWrappedNative *aWrapper, } else if (JSID_IS_STRING(aId)) { JSString *str = JSID_TO_STRING(aId); - jschar *nameChars = ::JS_GetStringChars(str); - size_t nameLength = ::JS_GetStringLength(str); + size_t nameLength; + const jschar *nameChars = JS_GetStringCharsAndLength(aCtx, str, &nameLength); + NS_ENSURE_TRUE(nameChars, NS_ERROR_UNEXPECTED); // Check to see if there's a parameter with this name, and if not, let // the rest of the prototype chain be checked. diff --git a/toolkit/xre/Makefile.in b/toolkit/xre/Makefile.in index a5e2bd4456d..19cb187fa19 100644 --- a/toolkit/xre/Makefile.in +++ b/toolkit/xre/Makefile.in @@ -195,6 +195,7 @@ include $(topsrcdir)/config/rules.mk LOCAL_INCLUDES += \ -I$(topsrcdir)/dom/ipc \ -I$(topsrcdir)/toolkit/crashreporter \ + -I$(topsrcdir)/dom/base \ $(NULL) ifdef BUILD_STATIC_LIBS diff --git a/toolkit/xre/nsEmbedFunctions.cpp b/toolkit/xre/nsEmbedFunctions.cpp index b2ea9ecd7ea..c692d021621 100644 --- a/toolkit/xre/nsEmbedFunctions.cpp +++ b/toolkit/xre/nsEmbedFunctions.cpp @@ -70,6 +70,7 @@ #include "nsExceptionHandler.h" #include "nsString.h" #include "nsThreadUtils.h" +#include "nsJSUtils.h" #include "nsWidgetsCID.h" #include "nsXREDirProvider.h" @@ -677,8 +678,9 @@ XRE_SendTestShellCommand(JSContext* aCx, TestShellParent* tsp = GetOrCreateTestShellParent(); NS_ENSURE_TRUE(tsp, false); - nsDependentString command((PRUnichar*)JS_GetStringChars(aCommand), - JS_GetStringLength(aCommand)); + nsDependentJSString command; + NS_ENSURE_TRUE(command.init(aCx, aCommand), NS_ERROR_FAILURE); + if (!aCallback) { return tsp->SendExecuteCommand(command); }