[JAEGER] Merge from tracemonkey. Note that we will currently fail one trace test that was recently added. There is a bug in that test that will be fixed posthaste.
[JAEGER] Merge from tracemonkey. Note that we will currently fail one trace test that was recently added. There is a bug in that test that will be fixed posthaste.
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -4887,18 +4887,18 @@ nsDOMClassInfo::ShutDown()
NS_IF_RELEASE(sXPConnect);
NS_IF_RELEASE(sSecMan);
sIsInitialized = PR_FALSE;
}
// Window helper
NS_IMETHODIMP
-nsCommonWindowSH::PreCreate(nsISupports *nativeObj, JSContext *cx,
- JSObject *globalObj, JSObject **parentObj)
+nsInnerWindowSH::PreCreate(nsISupports *nativeObj, JSContext *cx,
+ JSObject *globalObj, JSObject **parentObj)
{
// Normally ::PreCreate() is used to give XPConnect the parent
// object for the object that's being wrapped, this parent object is
// set as the parent of the wrapper and it's also used to find the
// right scope for the object being wrapped. Now, in the case of the
// global object the wrapper shouldn't have a parent but we supply
// one here anyway (the global object itself) and this will be used
// by XPConnect only to find the right scope, once the scope is
@@ -4906,35 +4906,40 @@ nsCommonWindowSH::PreCreate(nsISupports
// exists since it's created on window construction), since an
// existing wrapper is found the parent we supply here is ignored
// after the wrapper is found.
nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryInterface(nativeObj));
NS_ASSERTION(sgo, "nativeObj not a global object!");
nsGlobalWindow *win = nsGlobalWindow::FromSupports(nativeObj);
-
- if (win->IsOuterWindow()) {
- win->EnsureInnerWindow();
- }
-
- if (sgo) {
- *parentObj = sgo->GetGlobalJSObject();
-
- if (*parentObj) {
- return win->IsChromeWindow() ? NS_OK : NS_SUCCESS_NEEDS_XOW;
- }
- }
-
- // We're most likely being called when the global object is
- // created, at that point we won't get a nsIScriptContext but we
- // know we're called on the correct context so we return globalObj
-
- *parentObj = globalObj;
-
+ JSObject *winObj = win->FastGetGlobalJSObject();
+ if (!winObj) {
+ NS_ASSERTION(win->GetOuterWindowInternal()->IsCreatingInnerWindow(),
+ "should have a JS object by this point");
+ return NS_OK;
+ }
+
+ *parentObj = winObj;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsOuterWindowSH::PreCreate(nsISupports *nativeObj, JSContext *cx,
+ JSObject *globalObj, JSObject **parentObj)
+{
+ nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryInterface(nativeObj));
+ NS_ASSERTION(sgo, "nativeObj not a global object!");
+
+ nsGlobalWindow *win = nsGlobalWindow::FromSupports(nativeObj);
+ if (!win->EnsureInnerWindow()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ *parentObj = win->GetCurrentInnerWindowInternal()->FastGetGlobalJSObject();
return win->IsChromeWindow() ? NS_OK : NS_SUCCESS_NEEDS_XOW;
}
// This JS class piggybacks on nsHTMLDocumentSH::ReleaseDocument()...
static JSClass sGlobalScopePolluterClass = {
"Global Scope Polluter",
@@ -6657,43 +6662,43 @@ nsCommonWindowSH::NewResolve(nsIXPConnec
JSContext *my_cx;
if (!my_context) {
my_cx = cx;
} else {
my_cx = (JSContext *)my_context->GetNativeContext();
}
- JSBool ok;
- jsval exn;
- {
+ JSBool ok = JS_TRUE;
+ jsval exn = JSVAL_VOID;
+ if (win->IsInnerWindow()) {
JSAutoRequest transfer(my_cx);
JSObject *realObj;
wrapper->GetJSObject(&realObj);
-
+
// Don't resolve standard classes on XPCNativeWrapper etc, only
// resolve them if we're resolving on the real global object.
ok = obj == realObj ?
::JS_ResolveStandardClass(my_cx, obj, id, &did_resolve) :
JS_TRUE;
-
+
if (!ok) {
// Trust the JS engine (or the script security manager) to set
// the exception in the JS engine.
-
+
if (!JS_GetPendingException(my_cx, &exn)) {
return NS_ERROR_UNEXPECTED;
}
-
+
// Return NS_OK to avoid stomping over the exception that was passed
// down from the ResolveStandardClass call.
// Note that the order of the JS_ClearPendingException and
// JS_SetPendingException is important in the case that my_cx == cx.
-
+
JS_ClearPendingException(my_cx);
}
}
if (!ok) {
JS_SetPendingException(cx, exn);
*_retval = JS_FALSE;
return NS_OK;
--- a/dom/base/nsDOMClassInfo.h
+++ b/dom/base/nsDOMClassInfo.h
@@ -509,18 +509,16 @@ protected:
{
}
static nsresult GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
JSObject *obj, JSString *str,
PRBool *did_resolve);
public:
- NS_IMETHOD PreCreate(nsISupports *nativeObj, JSContext *cx,
- JSObject *globalObj, JSObject **parentObj);
#ifdef DEBUG
NS_IMETHOD PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
JSObject *obj)
{
nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryWrappedNative(wrapper));
NS_ASSERTION(!sgo || sgo->GetGlobalJSObject() == nsnull,
"Multiple wrappers created for global object!");
@@ -573,16 +571,18 @@ protected:
virtual ~nsOuterWindowSH()
{
}
static PRBool sResolving;
public:
+ NS_IMETHOD PreCreate(nsISupports *nativeObj, JSContext *cx,
+ JSObject *globalObj, JSObject **parentObj);
NS_IMETHOD AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
JSObject *obj, jsid id, jsval *vp, PRBool *_retval);
NS_IMETHOD NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
JSObject *obj, jsid id, PRUint32 flags,
JSObject **objp, PRBool *_retval);
NS_IMETHOD NewEnumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
JSObject *obj, PRUint32 enum_op, jsval *statep,
jsid *idp, PRBool *_retval);
@@ -602,16 +602,18 @@ protected:
{
}
virtual ~nsInnerWindowSH()
{
}
public:
+ NS_IMETHOD PreCreate(nsISupports *nativeObj, JSContext *cx,
+ JSObject *globalObj, JSObject **parentObj);
// We WANT_ADDPROPERTY, but are content to inherit it from nsEventReceiverSH.
NS_IMETHOD OuterObject(nsIXPConnectWrappedNative *wrapper, JSContext * cx,
JSObject * obj, JSObject * *_retval);
static nsIClassInfo *doCreate(nsDOMClassInfoData* aData)
{
return new nsInnerWindowSH(aData);
}
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1507,54 +1507,60 @@ class WindowStateHolder : public nsISupp
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(WINDOWSTATEHOLDER_IID)
NS_DECL_ISUPPORTS
WindowStateHolder(nsGlobalWindow *aWindow,
nsIXPConnectJSObjectHolder *aHolder,
nsNavigator *aNavigator,
- nsIXPConnectJSObjectHolder *aOuterProto);
+ nsIXPConnectJSObjectHolder *aOuterProto,
+ nsIXPConnectJSObjectHolder *aOuterRealProto);
nsGlobalWindow* GetInnerWindow() { return mInnerWindow; }
nsIXPConnectJSObjectHolder *GetInnerWindowHolder()
{ return mInnerWindowHolder; }
nsNavigator* GetNavigator() { return mNavigator; }
nsIXPConnectJSObjectHolder* GetOuterProto() { return mOuterProto; }
+ nsIXPConnectJSObjectHolder* GetOuterRealProto() { return mOuterRealProto; }
void DidRestoreWindow()
{
mInnerWindow = nsnull;
mInnerWindowHolder = nsnull;
mNavigator = nsnull;
mOuterProto = nsnull;
+ mOuterRealProto = nsnull;
}
protected:
~WindowStateHolder();
nsGlobalWindow *mInnerWindow;
// We hold onto this to make sure the inner window doesn't go away. The outer
// window ends up recalculating it anyway.
nsCOMPtr<nsIXPConnectJSObjectHolder> mInnerWindowHolder;
nsRefPtr<nsNavigator> mNavigator;
nsCOMPtr<nsIXPConnectJSObjectHolder> mOuterProto;
+ nsCOMPtr<nsIXPConnectJSObjectHolder> mOuterRealProto;
};
NS_DEFINE_STATIC_IID_ACCESSOR(WindowStateHolder, WINDOWSTATEHOLDER_IID)
WindowStateHolder::WindowStateHolder(nsGlobalWindow *aWindow,
nsIXPConnectJSObjectHolder *aHolder,
nsNavigator *aNavigator,
- nsIXPConnectJSObjectHolder *aOuterProto)
+ nsIXPConnectJSObjectHolder *aOuterProto,
+ nsIXPConnectJSObjectHolder *aOuterRealProto)
: mInnerWindow(aWindow),
mNavigator(aNavigator),
- mOuterProto(aOuterProto)
+ mOuterProto(aOuterProto),
+ mOuterRealProto(aOuterRealProto)
{
NS_PRECONDITION(aWindow, "null window");
NS_PRECONDITION(aWindow->IsInnerWindow(), "Saving an outer window");
mInnerWindowHolder = aHolder;
aWindow->SuspendTimeouts();
}
@@ -1604,19 +1610,16 @@ nsGlobalWindow::SetNewDocument(nsIDocume
return GetOuterWindowInternal()->SetNewDocument(aDocument, aState);
}
NS_PRECONDITION(IsOuterWindow(), "Must only be called on outer windows");
if (IsFrozen()) {
// This outer is now getting its first inner, thaw the outer now
// that it's ready and is getting an inner window.
- mContext->CreateOuterObject(this, aDocument->NodePrincipal());
- mContext->DidInitializeContext();
- mJSObject = (JSObject *)mContext->GetNativeGlobal();
Thaw();
}
// XXX Brain transplant outer window JSObject and create new one!
NS_ASSERTION(!GetCurrentInnerWindow() ||
GetCurrentInnerWindow()->GetExtantDocument() == mDocument,
@@ -1720,36 +1723,41 @@ nsGlobalWindow::SetNewDocument(nsIDocume
nsCxPusher cxPusher;
if (!cxPusher.Push(cx)) {
return NS_ERROR_FAILURE;
}
JSAutoRequest ar(cx);
+ nsCOMPtr<WindowStateHolder> wsh = do_QueryInterface(aState);
+ NS_ASSERTION(!aState || wsh, "What kind of weird state are you giving me here?");
+
// Make sure to clear scope on the outer window *before* we
// initialize the new inner window. If we don't, things
// (Object.prototype etc) could leak from the old outer to the new
// inner scope.
mContext->ClearScope(mJSObject, PR_FALSE);
+ // This code should not be called during shutdown any more (now that
+ // we don't ever call SetNewDocument(nsnull), so no need to null
+ // check xpc here.
+ nsIXPConnect *xpc = nsContentUtils::XPConnect();
+ nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
if (reUseInnerWindow) {
// We're reusing the current inner window.
NS_ASSERTION(!currentInner->IsFrozen(),
"We should never be reusing a shared inner window");
newInnerWindow = currentInner;
if (aDocument != oldDoc) {
nsCommonWindowSH::InvalidateGlobalScopePolluter(cx, currentInner->mJSObject);
}
} else {
if (aState) {
- nsCOMPtr<WindowStateHolder> wsh = do_QueryInterface(aState);
- NS_ASSERTION(wsh, "What kind of weird state are you giving me here?");
-
newInnerWindow = wsh->GetInnerWindow();
mInnerWindowHolder = wsh->GetInnerWindowHolder();
// These assignments addref.
mNavigator = wsh->GetNavigator();
if (mNavigator) {
// Update mNavigator's docshell pointer now.
@@ -1813,17 +1821,16 @@ nsGlobalWindow::SetNewDocument(nsIDocume
void *&newGlobal = (void *&)newInnerWindow->mJSObject;
nsCOMPtr<nsIXPConnectJSObjectHolder> &holder = mInnerWindowHolder;
rv = mContext->CreateNativeGlobalForInner(sgo, isChrome,
aDocument->NodePrincipal(),
&newGlobal,
getter_AddRefs(holder));
NS_ASSERTION(NS_SUCCEEDED(rv) && newGlobal && holder,
"Failed to get script global and holder");
- newInnerWindow->mJSObject = (JSObject *)newGlobal;
mCreatingInnerWindow = PR_FALSE;
Thaw();
NS_ENSURE_SUCCESS(rv, rv);
}
if (currentInner && currentInner->mJSObject) {
@@ -1867,16 +1874,55 @@ nsGlobalWindow::SetNewDocument(nsIDocume
if (!currentInner->IsFrozen()) {
// Skip the ClearScope if we set a termination function to do
// it ourselves, later.
currentInner->FreeInnerObjects(!termFuncSet);
}
}
mInnerWindow = newInnerWindow;
+
+ if (!mJSObject) {
+ mContext->CreateOuterObject(this, newInnerWindow);
+ mContext->DidInitializeContext();
+ mJSObject = (JSObject *)mContext->GetNativeGlobal();
+ } else {
+ // XXX New global object and brain transplant!
+ rv = xpc->GetWrappedNativeOfJSObject(cx, mJSObject,
+ getter_AddRefs(wrapper));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Restore our object's prototype to its original value so we're sure to
+ // update it under ReparentWrappedNativeIfFound.
+ JSObject *proto;
+ wrapper->GetJSObjectPrototype(&proto);
+ if (!JS_SetPrototype(cx, mJSObject, proto)) {
+ NS_ERROR("Can't set prototype");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
+ xpc->ReparentWrappedNativeIfFound(cx, currentInner->mJSObject,
+ newInnerWindow->mJSObject,
+ ToSupports(this),
+ getter_AddRefs(holder));
+
+ if (aState) {
+ if (nsIXPConnectJSObjectHolder *holder = wsh->GetOuterRealProto()) {
+ holder->GetJSObject(&proto);
+ } else {
+ proto = nsnull;
+ }
+
+ if (!JS_SetPrototype(cx, mJSObject, proto)) {
+ NS_ERROR("can't set prototype");
+ return NS_ERROR_FAILURE;
+ }
+ }
+ }
}
if (!aState && !reUseInnerWindow) {
// Loading a new page and creating a new inner window, *not*
// restoring from session history.
// InitClassesWithNewWrappedGlobal() (via CreateNativeGlobalForInner)
// for the new inner window
@@ -1919,52 +1965,16 @@ nsGlobalWindow::SetNewDocument(nsIDocume
// alive etc.
if ((!reUseInnerWindow || aDocument != oldDoc) && !aState) {
nsCOMPtr<nsIHTMLDocument> html_doc(do_QueryInterface(mDocument));
nsCommonWindowSH::InstallGlobalScopePolluter(cx, newInnerWindow->mJSObject,
html_doc);
}
- // This code should not be called during shutdown any more (now that
- // we don't ever call SetNewDocument(nsnull), so no need to null
- // check xpc here.
- nsIXPConnect *xpc = nsContentUtils::XPConnect();
-
- nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
- if (aState) {
- // Restoring from session history.
-
- nsCOMPtr<WindowStateHolder> wsh = do_QueryInterface(aState);
- NS_ASSERTION(wsh, "What kind of weird state are you giving me here?");
-
- // Restore the prototype for the Window/ChromeWindow class in
- // the outer window scope.
- nsCOMPtr<nsIClassInfo> ci =
- do_QueryInterface((nsIScriptGlobalObject *)this);
-
- rv = xpc->RestoreWrappedNativePrototype(cx, mJSObject, ci,
- wsh->GetOuterProto());
- NS_ENSURE_SUCCESS(rv, rv);
-
- // Refresh the outer window's prototype to what it was when the
- // window state was saved. This will make the outer window
- // object (and wrapper) pick up the prototype it had when the
- // window state was saved. This means Object.prototype etc from
- // the old inner will again be on the outer window's prototype
- // chain.
-
- rv = xpc->GetWrappedNativeOfJSObject(cx, mJSObject,
- getter_AddRefs(wrapper));
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = wrapper->RefreshPrototype();
- NS_ENSURE_SUCCESS(rv, rv);
- }
-
if (aDocument) {
aDocument->SetScriptGlobalObject(newInnerWindow);
}
if (!aState) {
if (reUseInnerWindow) {
if (newInnerWindow->mDoc != aDocument) {
newInnerWindow->mDocument = do_QueryInterface(aDocument);
@@ -9068,31 +9078,49 @@ nsGlobalWindow::SaveWindowState(nsISuppo
// Don't do anything else to this inner window! After this point, all
// calls to SetTimeoutOrInterval will create entries in the timeout
// list that will only run after this window has come out of the bfcache.
// Also, while we're frozen, we won't dispatch online/offline events
// to the page.
inner->Freeze();
- // Remember the outer window's XPConnect prototype.
+ // Remember the outer window's prototype.
+ JSContext *cx = (JSContext *)mContext->GetNativeContext();
+ JSAutoRequest req(cx);
+
+ nsIXPConnect *xpc = nsContentUtils::XPConnect();
+
nsCOMPtr<nsIClassInfo> ci =
do_QueryInterface((nsIScriptGlobalObject *)this);
nsCOMPtr<nsIXPConnectJSObjectHolder> proto;
- nsresult rv = nsContentUtils::XPConnect()->
- GetWrappedNativePrototype((JSContext *)mContext->GetNativeContext(),
- mJSObject, ci, getter_AddRefs(proto));
+ nsresult rv = xpc->GetWrappedNativePrototype(cx, mJSObject, ci,
+ getter_AddRefs(proto));
NS_ENSURE_SUCCESS(rv, rv);
+ JSObject *realProto = JS_GetPrototype(cx, mJSObject);
+ nsCOMPtr<nsIXPConnectJSObjectHolder> realProtoHolder;
+ if (realProto) {
+ rv = xpc->HoldObject(cx, realProto, getter_AddRefs(realProtoHolder));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
nsCOMPtr<nsISupports> state = new WindowStateHolder(inner,
mInnerWindowHolder,
mNavigator,
- proto);
+ proto,
+ realProtoHolder);
NS_ENSURE_TRUE(state, NS_ERROR_OUT_OF_MEMORY);
+ JSObject *wnProto;
+ proto->GetJSObject(&wnProto);
+ if (!JS_SetPrototype(cx, mJSObject, wnProto)) {
+ return NS_ERROR_FAILURE;
+ }
+
#ifdef DEBUG_PAGE_CACHE
printf("saving window state, state = %p\n", (void*)state);
#endif
state.swap(*aState);
return NS_OK;
}
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -378,16 +378,21 @@ public:
// Object Management
nsGlobalWindow(nsGlobalWindow *aOuterWindow);
static nsGlobalWindow *FromSupports(nsISupports *supports)
{
// Make sure this matches the casts we do in QueryInterface().
return (nsGlobalWindow *)(nsIScriptGlobalObject *)supports;
}
+ static nsISupports *ToSupports(nsGlobalWindow *win)
+ {
+ // Make sure this matches the casts we do in QueryInterface().
+ return (nsISupports *)(nsIScriptGlobalObject *)win;
+ }
static nsGlobalWindow *FromWrapper(nsIXPConnectWrappedNative *wrapper)
{
return FromSupports(wrapper->Native());
}
nsIScriptContext *GetContextInternal()
{
if (mOuterWindow) {
--- a/dom/base/nsIScriptContext.h
+++ b/dom/base/nsIScriptContext.h
@@ -339,21 +339,21 @@ public:
virtual nsresult InitContext() = 0;
/**
* Creates the outer window for this context.
*
* @param aGlobalObject The script global object to use as our global.
*/
virtual nsresult CreateOuterObject(nsIScriptGlobalObject *aGlobalObject,
- nsIPrincipal *aPrincipal) = 0;
+ nsIScriptGlobalObject *aCurrentInner) = 0;
/**
* Prepares this context for use with the current inner window for the
- * context's global object. This must be called after InitOuterWindow.
+ * context's global object. This must be called after CreateOuterObject.
*/
virtual nsresult InitOuterWindow() = 0;
/**
* Check to see if context is as yet intialized. Used to prevent
* reentrancy issues during the initialization process.
*
* @return PR_TRUE if initialized, PR_FALSE if not
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -2589,17 +2589,17 @@ nsJSContext::CreateNativeGlobalForInner(
return NS_OK;
}
nsresult
nsJSContext::ConnectToInner(nsIScriptGlobalObject *aNewInner, void *aOuterGlobal)
{
NS_ENSURE_ARG(aNewInner);
JSObject *newInnerJSObject = (JSObject *)aNewInner->GetScriptGlobal(JAVASCRIPT);
- JSObject *myobject = (JSObject *)aOuterGlobal;
+ JSObject *outerGlobal = (JSObject *)aOuterGlobal;
// Make the inner and outer window both share the same
// prototype. The prototype we share is the outer window's
// prototype, this way XPConnect can still find the wrapper to
// use when making a call like alert() (w/o qualifying it with
// "window."). XPConnect looks up the wrapper based on the
// function object's parent, which is the object the function
// was called on, and when calling alert() we'll be calling the
@@ -2610,22 +2610,28 @@ nsJSContext::ConnectToInner(nsIScriptGlo
// We do *not* want to use anything else out of the outer
// object's prototype chain than the first prototype, which is
// the XPConnect prototype. The rest we want from the inner
// window's prototype, i.e. the global scope polluter and
// Object.prototype. This way the outer also gets the benefits
// of the global scope polluter, and the inner window's
// Object.prototype.
- JSObject *proto = ::JS_GetPrototype(mContext, myobject);
- JSObject *innerProto = ::JS_GetPrototype(mContext, newInnerJSObject);
- JSObject *innerProtoProto = ::JS_GetPrototype(mContext, innerProto);
-
- ::JS_SetPrototype(mContext, newInnerJSObject, proto);
- ::JS_SetPrototype(mContext, proto, innerProtoProto);
+ JSObject *proto = JS_GetPrototype(mContext, outerGlobal);
+ JSObject *innerProto = JS_GetPrototype(mContext, newInnerJSObject);
+ JSObject *innerProtoProto = JS_GetPrototype(mContext, innerProto);
+
+ JS_SetPrototype(mContext, newInnerJSObject, proto);
+ JS_SetPrototype(mContext, proto, innerProtoProto);
+
+ // Now that we're connecting the outer global to the inner one,
+ // we must have transplanted it. The JS engine tries to maintain
+ // the global object's compartment as its default compartment,
+ // so update that now since it might have changed.
+ JS_SetGlobalObject(mContext, outerGlobal);
return NS_OK;
}
void *
nsJSContext::GetNativeContext()
{
return mContext;
}
@@ -2655,46 +2661,46 @@ nsJSContext::InitContext()
nsDOMClassInfo::SetXrayWrapperPropertyHolderGetPropertyOp(getProperty);
}
return NS_OK;
}
nsresult
nsJSContext::CreateOuterObject(nsIScriptGlobalObject *aGlobalObject,
- nsIPrincipal *aPrincipal)
+ nsIScriptGlobalObject *aCurrentInner)
{
- NS_PRECONDITION(!JS_GetGlobalObject(mContext),
- "Outer window already initialized");
-
nsCOMPtr<nsIDOMChromeWindow> chromeWindow(do_QueryInterface(aGlobalObject));
PRUint32 flags = 0;
if (chromeWindow) {
// Flag this window's global object and objects under it as "system",
// for optional automated XPCNativeWrapper construction when chrome JS
// views a content DOM.
flags = nsIXPConnect::FLAG_SYSTEM_GLOBAL_OBJECT;
// Always enable E4X for XUL and other chrome content -- there is no
// need to preserve the <!-- script hiding hack from JS-in-HTML daze
// (introduced in 1995 for graceful script degradation in Netscape 1,
// Mosaic, and other pre-JS browsers).
- ::JS_SetOptions(mContext, ::JS_GetOptions(mContext) | JSOPTION_XML);
+ JS_SetOptions(mContext, JS_GetOptions(mContext) | JSOPTION_XML);
}
nsIXPConnect *xpc = nsContentUtils::XPConnect();
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
- nsresult rv =
- xpc->InitClassesWithNewWrappedGlobal(mContext, aGlobalObject,
- NS_GET_IID(nsISupports),
- aPrincipal, EmptyCString(),
- flags, getter_AddRefs(holder));
+ nsresult rv = xpc->WrapNative(mContext, aCurrentInner->GetGlobalJSObject(),
+ aGlobalObject, NS_GET_IID(nsISupports),
+ getter_AddRefs(holder));
NS_ENSURE_SUCCESS(rv, rv);
+ // Force our context's global object to be the outer.
+ JSObject *globalObj;
+ holder->GetJSObject(&globalObj);
+ JS_SetGlobalObject(mContext, globalObj);
+
// Hold a strong reference to the wrapper for the global to avoid
// rooting and unrooting the global object every time its AddRef()
// or Release() methods are called
mGlobalWrapperRef = holder;
return NS_OK;
}
nsresult
@@ -2703,24 +2709,19 @@ nsJSContext::InitOuterWindow()
JSObject *global = JS_GetGlobalObject(mContext);
nsIScriptGlobalObject *sgo = GetGlobalObject();
// Call ClearScope to nuke any properties (e.g. Function and Object) on the
// outer object. From now on, anybody asking the outer object for these
// properties will be forwarded to the inner window.
JS_ClearScope(mContext, global);
- // Now that the inner and outer windows are connected, tell XPConnect to
- // re-initialize the prototypes on the outer window's scope.
- nsIXPConnect *xpc = nsContentUtils::XPConnect();
- nsresult rv = xpc->InitClassesForOuterObject(mContext, global);
- NS_ENSURE_SUCCESS(rv, rv);
+ nsresult rv = NS_OK;
nsCOMPtr<nsIClassInfo> ci(do_QueryInterface(sgo));
-
if (ci) {
jsval v;
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
rv = nsContentUtils::WrapNative(mContext, global, sgo, &v,
getter_AddRefs(holder));
NS_ENSURE_SUCCESS(rv, rv);
--- a/dom/base/nsJSEnvironment.h
+++ b/dom/base/nsJSEnvironment.h
@@ -137,17 +137,17 @@ public:
PRBool aIsChrome,
nsIPrincipal *aPrincipal,
void **aNativeGlobal,
nsISupports **aHolder);
virtual nsresult ConnectToInner(nsIScriptGlobalObject *aNewInner,
void *aOuterGlobal);
virtual nsresult InitContext();
virtual nsresult CreateOuterObject(nsIScriptGlobalObject *aGlobalObject,
- nsIPrincipal *aPrincipal);
+ nsIScriptGlobalObject *aCurrentInner);
virtual nsresult InitOuterWindow();
virtual PRBool IsContextInitialized();
virtual void FinalizeContext();
virtual void GC();
virtual void ScriptEvaluated(PRBool aTerminated);
virtual nsresult SetTerminationFunction(nsScriptTerminationFunc aFunc,
--- a/dom/tests/mochitest/general/Makefile.in
+++ b/dom/tests/mochitest/general/Makefile.in
@@ -54,12 +54,14 @@ include $(topsrcdir)/config/rules.mk
test_innerScreen.xul \
test_offsets.html \
test_offsets.js \
test_offsets.xul \
test_windowProperties.html \
test_clipboard_events.html \
test_focusrings.xul \
test_nodesFromRect.html \
+ test_frameElementWrapping.html \
+ file_frameElementWrapping.html \
$(NULL)
libs:: $(_TEST_FILES)
$(INSTALL) $^ $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/general/file_frameElementWrapping.html
@@ -0,0 +1,26 @@
+<html>
+ <script>
+ function check(elt, expectXOW, message) {
+ netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+ var utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+ .getInterface(Components.interfaces.nsIDOMWindowUtils);
+ var result = ((utils.getClassName(elt) === 'XPCCrossOriginWrapper') === expectXOW)
+ ? "PASS"
+ : "FAIL";
+
+ parent.postMessage(result + ',' + message, '*');
+ }
+
+ try {
+ // true if same origin, throws otherwise
+ var sameOrigin = parent.location.href !== '';
+ } catch (e) {
+ sameOrigin = false;
+ }
+
+ check(frameElement, !sameOrigin,
+ sameOrigin
+ ? 'no wrapper needed if same origin'
+ : 'wrapper needed if not same origin');
+ </script>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/general/test_frameElementWrapping.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for location object behaviors</title>
+ <script type="text/javascript" src="/MochiKit/packed.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<iframe id="ifr" src="file_frameElementWrapping.html"></iframe>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+var count = 0;
+
+function runTest(result, message) {
+ ok(result === 'PASS', message);
+
+ if (++count === 2)
+ SimpleTest.finish();
+ else
+ $('ifr').contentWindow.location = 'http://example.org/tests/dom/tests/mochitest/general/file_frameElementWrapping.html';
+}
+
+window.addEventListener("message",
+ function(event) { runTest.apply(null, event.data.split(',')) },
+ false);
+
+</script>
+</pre>
+</body>
+</html>
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -325,9 +325,12 @@ MSG_DEF(JSMSG_DEFINE_ARRAY_LENGTH_UNSUPP
MSG_DEF(JSMSG_CANT_DEFINE_ARRAY_INDEX,243, 0, JSEXN_TYPEERR, "can't define array index property")
MSG_DEF(JSMSG_TYPED_ARRAY_BAD_INDEX, 244, 0, JSEXN_ERR, "invalid or out-of-range index")
MSG_DEF(JSMSG_TYPED_ARRAY_NEGATIVE_ARG, 245, 1, JSEXN_ERR, "argument {0} must be >= 0")
MSG_DEF(JSMSG_TYPED_ARRAY_BAD_ARGS, 246, 0, JSEXN_ERR, "invalid arguments")
MSG_DEF(JSMSG_CSP_BLOCKED_FUNCTION, 247, 0, JSEXN_ERR, "call to Function() blocked by CSP")
MSG_DEF(JSMSG_BAD_GET_SET_FIELD, 248, 1, JSEXN_TYPEERR, "property descriptor's {0} field is neither undefined nor a function")
MSG_DEF(JSMSG_BAD_PROXY_FIX, 249, 0, JSEXN_TYPEERR, "proxy was fixed while executing the handler")
MSG_DEF(JSMSG_INVALID_EVAL_SCOPE_ARG, 250, 0, JSEXN_EVALERR, "invalid eval scope argument")
-MSG_DEF(JSMSG_NEED_DEBUG_MODE, 251, 0, JSEXN_ERR, "function can be called only in debug mode")
+MSG_DEF(JSMSG_ACCESSOR_WRONG_ARGS, 251, 3, JSEXN_SYNTAXERR, "{0} functions must have {1} argument{2}")
+MSG_DEF(JSMSG_THROW_TYPE_ERROR, 252, 0, JSEXN_TYPEERR, "'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them")
+MSG_DEF(JSMSG_BAD_TOISOSTRING_PROP, 253, 0, JSEXN_TYPEERR, "toISOString property is not callable")
+MSG_DEF(JSMSG_NEED_DEBUG_MODE, 254, 0, JSEXN_ERR, "function can be called only in debug mode")
--- a/js/src/jsapi-tests/testContexts.cpp
+++ b/js/src/jsapi-tests/testContexts.cpp
@@ -80,16 +80,18 @@ END_TEST(testContexts_bug561444)
BEGIN_TEST(testContexts_bug563735)
{
JSContext *cx2 = JS_NewContext(rt, 8192);
CHECK(cx2);
JSBool ok;
{
JSAutoRequest req(cx2);
+ JSAutoCrossCompartmentCall crossCall;
+ CHECK(crossCall.enter(cx2, global));
jsval v = JSVAL_NULL;
ok = JS_SetProperty(cx2, global, "x", &v);
}
CHECK(ok);
EXEC("(function () { for (var i = 0; i < 9; i++) ; })();");
JS_DestroyContext(cx2);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1682,32 +1682,36 @@ struct JSClass {
#define JSCLASS_INTERNAL_FLAG1 (1<<(JSCLASS_HIGH_FLAGS_SHIFT+0))
#define JSCLASS_IS_ANONYMOUS (1<<(JSCLASS_HIGH_FLAGS_SHIFT+1))
#define JSCLASS_IS_GLOBAL (1<<(JSCLASS_HIGH_FLAGS_SHIFT+2))
/* Indicates that JSClass.mark is a tracer with JSTraceOp type. */
#define JSCLASS_MARK_IS_TRACE (1<<(JSCLASS_HIGH_FLAGS_SHIFT+3))
#define JSCLASS_INTERNAL_FLAG2 (1<<(JSCLASS_HIGH_FLAGS_SHIFT+4))
+/* Additional global reserved slots, beyond those for standard prototypes. */
+#define JSRESERVED_GLOBAL_SLOTS_COUNT 3
+#define JSRESERVED_GLOBAL_COMPARTMENT (JSProto_LIMIT * 3)
+#define JSRESERVED_GLOBAL_THIS (JSRESERVED_GLOBAL_COMPARTMENT + 1)
+#define JSRESERVED_GLOBAL_THROWTYPEERROR (JSRESERVED_GLOBAL_THIS + 1)
+
/*
* ECMA-262 requires that most constructors used internally create objects
* with "the original Foo.prototype value" as their [[Prototype]] (__proto__)
* member initial value. The "original ... value" verbiage is there because
* in ECMA-262, global properties naming class objects are read/write and
* deleteable, for the most part.
*
* Implementing this efficiently requires that global objects have classes
* with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS was
* prevously allowed, but is now an ES5 violation and thus unsupported.
*/
-#define JSCLASS_GLOBAL_FLAGS \
- (JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSProto_LIMIT * 3 + 2))
-
-#define JSRESERVED_GLOBAL_COMPARTMENT (JSProto_LIMIT * 3)
-#define JSRESERVED_GLOBAL_THIS (JSRESERVED_GLOBAL_COMPARTMENT + 1)
+#define JSCLASS_GLOBAL_FLAGS \
+ (JSCLASS_IS_GLOBAL | \
+ JSCLASS_HAS_RESERVED_SLOTS(JSProto_LIMIT * 3 + JSRESERVED_GLOBAL_SLOTS_COUNT))
/* Fast access to the original value of each standard class's prototype. */
#define JSCLASS_CACHED_PROTO_SHIFT (JSCLASS_HIGH_FLAGS_SHIFT + 8)
#define JSCLASS_CACHED_PROTO_WIDTH 8
#define JSCLASS_CACHED_PROTO_MASK JS_BITMASK(JSCLASS_CACHED_PROTO_WIDTH)
#define JSCLASS_HAS_CACHED_PROTO(key) ((key) << JSCLASS_CACHED_PROTO_SHIFT)
#define JSCLASS_CACHED_PROTO_KEY(clasp) ((JSProtoKey) \
(((clasp)->flags \
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -226,17 +226,17 @@ JSBool
js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp)
{
if (obj->isArray()) {
*lengthp = obj->getArrayLength();
return true;
}
if (obj->isArguments() && !obj->isArgsLengthOverridden()) {
- *lengthp = obj->getArgsLength();
+ *lengthp = obj->getArgsInitialLength();
return true;
}
AutoValueRooter tvr(cx);
if (!obj->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), tvr.addr()))
return false;
if (tvr.value().isInt32()) {
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -143,16 +143,17 @@ const char *const js_common_atom_names[]
js_each_str, /* eachAtom */
js_eval_str, /* evalAtom */
js_fileName_str, /* fileNameAtom */
js_get_str, /* getAtom */
js_global_str, /* globalAtom */
js_ignoreCase_str, /* ignoreCaseAtom */
js_index_str, /* indexAtom */
js_input_str, /* inputAtom */
+ "toISOString", /* toISOStringAtom */
js_iterator_str, /* iteratorAtom */
js_join_str, /* joinAtom */
js_lastIndex_str, /* lastIndexAtom */
js_length_str, /* lengthAtom */
js_lineNumber_str, /* lineNumberAtom */
js_message_str, /* messageAtom */
js_multiline_str, /* multilineAtom */
js_name_str, /* nameAtom */
--- a/js/src/jsatom.h
+++ b/js/src/jsatom.h
@@ -335,16 +335,17 @@ struct JSAtomState
JSAtom *eachAtom;
JSAtom *evalAtom;
JSAtom *fileNameAtom;
JSAtom *getAtom;
JSAtom *globalAtom;
JSAtom *ignoreCaseAtom;
JSAtom *indexAtom;
JSAtom *inputAtom;
+ JSAtom *toISOStringAtom;
JSAtom *iteratorAtom;
JSAtom *joinAtom;
JSAtom *lastIndexAtom;
JSAtom *lengthAtom;
JSAtom *lineNumberAtom;
JSAtom *messageAtom;
JSAtom *multilineAtom;
JSAtom *nameAtom;
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -1963,16 +1963,18 @@ DSTOffsetCache::purge()
{
/*
* NB: The initial range values are carefully chosen to result in a cache
* miss on first use given the range of possible values. Be careful
* to keep these values and the caching algorithm in sync!
*/
offsetMilliseconds = 0;
rangeStartSeconds = rangeEndSeconds = INT64_MIN;
+ oldOffsetMilliseconds = 0;
+ oldRangeStartSeconds = oldRangeEndSeconds = INT64_MIN;
#ifdef JS_METER_DST_OFFSET_CACHING
totalCalculations = 0;
hit = 0;
missIncreasing = missDecreasing = 0;
missIncreasingOffsetChangeExpand = missIncreasingOffsetChangeUpper = 0;
missDecreasingOffsetChangeExpand = missDecreasingOffsetChangeLower = 0;
missLargeIncrease = missLargeDecrease = 0;
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -200,16 +200,21 @@ TimeWithinDay(jsdouble t)
#define TimeFromYear(y) (DayFromYear(y) * msPerDay)
static jsint
YearFromTime(jsdouble t)
{
jsint y = (jsint) floor(t /(msPerDay*365.2425)) + 1970;
jsdouble t2 = (jsdouble) TimeFromYear(y);
+ /*
+ * Adjust the year if the approximation was wrong. Since the year was
+ * computed using the average number of ms per year, it will usually
+ * be wrong for dates within several hours of a year transition.
+ */
if (t2 > t) {
y--;
} else {
if (t2 + msPerDay * DaysInYear(y) <= t)
y++;
}
return y;
}
@@ -478,17 +483,17 @@ msFromTime(jsdouble t)
*/
/*
* Other Support routines and definitions
*/
Class js_DateClass = {
js_Date_str,
- JSCLASS_HAS_RESERVED_SLOTS(JSObject::DATE_FIXED_RESERVED_SLOTS) |
+ JSCLASS_HAS_RESERVED_SLOTS(JSObject::DATE_CLASS_RESERVED_SLOTS) |
JSCLASS_HAS_CACHED_PROTO(JSProto_Date) |
JSCLASS_FAST_CONSTRUCTOR,
PropertyStub, /* addProperty */
PropertyStub, /* delProperty */
PropertyStub, /* getProperty */
PropertyStub, /* setProperty */
EnumerateStub,
ResolveStub,
@@ -1199,64 +1204,199 @@ static JSBool
GetUTCTime(JSContext *cx, JSObject *obj, Value *vp, jsdouble *dp)
{
if (!InstanceOf(cx, obj, &js_DateClass, vp ? vp + 2 : NULL))
return JS_FALSE;
*dp = obj->getDateUTCTime().toNumber();
return JS_TRUE;
}
-static void
-SetDateToNaN(JSContext *cx, JSObject *obj, Value *vp = NULL)
-{
- JS_ASSERT(obj->getClass() == &js_DateClass);
-
- obj->setDateLocalTime(cx->runtime->NaNValue);
- obj->setDateUTCTime(cx->runtime->NaNValue);
- if (vp)
- vp->setDouble(js_NaN);
-}
-
/*
* Set UTC time to a given time and invalidate cached local time.
*/
static JSBool
SetUTCTime(JSContext *cx, JSObject *obj, jsdouble t, Value *vp = NULL)
{
- JS_ASSERT(obj->getClass() == &js_DateClass);
-
- obj->setDateLocalTime(cx->runtime->NaNValue);
+ JS_ASSERT(obj->isDate());
+
+ size_t slotCap = JS_MIN(obj->numSlots(), JSObject::DATE_CLASS_RESERVED_SLOTS);
+ for (size_t ind = JSObject::JSSLOT_DATE_COMPONENTS_START; ind < slotCap; ind++)
+ obj->getSlotRef(ind).setUndefined();
+
obj->setDateUTCTime(DoubleValue(t));
if (vp)
vp->setDouble(t);
return true;
}
+static void
+SetDateToNaN(JSContext *cx, JSObject *obj, Value *vp = NULL)
+{
+ jsdouble NaN = cx->runtime->NaNValue.getDoubleRef();
+ SetUTCTime(cx, obj, NaN, vp);
+}
+
/*
- * Get the local time, cache it if necessary. If UTC time is not finite
- * (e.g., NaN), the local time slot is set to the UTC time without conversion.
+ * Cache the local time, year, month, and so forth of the object.
+ * If UTC time is not finite (e.g., NaN), the local time
+ * slots will be set to the UTC time without conversion.
*/
-static JSBool
-GetAndCacheLocalTime(JSContext *cx, JSObject *obj, Value *vp, jsdouble *dp)
+static bool
+FillLocalTimes(JSContext *cx, JSObject *obj)
+{
+ JS_ASSERT(obj->isDate());
+
+ jsdouble utcTime = obj->getDateUTCTime().toNumber();
+
+ /* Make sure there are slots to store the cached information. */
+ if (obj->numSlots() < JSObject::DATE_CLASS_RESERVED_SLOTS) {
+ if (!obj->growSlots(cx, JSObject::DATE_CLASS_RESERVED_SLOTS))
+ return false;
+ }
+
+ if (!JSDOUBLE_IS_FINITE(utcTime)) {
+ for (size_t ind = JSObject::JSSLOT_DATE_COMPONENTS_START;
+ ind < JSObject::DATE_CLASS_RESERVED_SLOTS;
+ ind++) {
+ obj->setSlot(ind, DoubleValue(utcTime));
+ }
+ return true;
+ }
+
+ jsdouble localTime = LocalTime(utcTime, cx);
+
+ obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_TIME, DoubleValue(localTime));
+
+ jsint year = (jsint) floor(localTime /(msPerDay*365.2425)) + 1970;
+ jsdouble yearStartTime = (jsdouble) TimeFromYear(year);
+
+ /* Adjust the year in case the approximation was wrong, as in YearFromTime. */
+ jsint yearDays;
+ if (yearStartTime > localTime) {
+ year--;
+ yearStartTime -= (msPerDay * DaysInYear(year));
+ yearDays = DaysInYear(year);
+ } else {
+ yearDays = DaysInYear(year);
+ jsdouble nextStart = yearStartTime + (msPerDay * yearDays);
+ if (nextStart <= localTime) {
+ year++;
+ yearStartTime = nextStart;
+ yearDays = DaysInYear(year);
+ }
+ }
+
+ obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_YEAR, Int32Value(year));
+
+ uint64 yearTime = uint64(localTime - yearStartTime);
+ jsint yearSeconds = uint32(yearTime / 1000);
+
+ jsint day = yearSeconds / jsint(SecondsPerDay);
+
+ jsint step = -1, next = 30;
+ jsint month;
+
+ do {
+ if (day <= next) {
+ month = 0;
+ break;
+ }
+ step = next;
+ next += ((yearDays == 366) ? 29 : 28);
+ if (day <= next) {
+ month = 1;
+ break;
+ }
+ step = next;
+ if (day <= (next += 31)) {
+ month = 2;
+ break;
+ }
+ step = next;
+ if (day <= (next += 30)) {
+ month = 3;
+ break;
+ }
+ step = next;
+ if (day <= (next += 31)) {
+ month = 4;
+ break;
+ }
+ step = next;
+ if (day <= (next += 30)) {
+ month = 5;
+ break;
+ }
+ step = next;
+ if (day <= (next += 31)) {
+ month = 6;
+ break;
+ }
+ step = next;
+ if (day <= (next += 31)) {
+ month = 7;
+ break;
+ }
+ step = next;
+ if (day <= (next += 30)) {
+ month = 8;
+ break;
+ }
+ step = next;
+ if (day <= (next += 31)) {
+ month = 9;
+ break;
+ }
+ step = next;
+ if (day <= (next += 30)) {
+ month = 10;
+ break;
+ }
+ step = next;
+ month = 11;
+ } while (0);
+
+ obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_MONTH, Int32Value(month));
+ obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_DATE, Int32Value(day - step));
+
+ jsint weekday = WeekDay(localTime);
+
+ obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_DAY, Int32Value(weekday));
+
+ jsint seconds = yearSeconds % 60;
+
+ obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_SECONDS, Int32Value(seconds));
+
+ jsint minutes = (yearSeconds / 60) % 60;
+
+ obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_MINUTES, Int32Value(minutes));
+
+ jsint hours = (yearSeconds / (60 * 60)) % 24;
+
+ obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_HOURS, Int32Value(hours));
+
+ return true;
+}
+
+/* Cache the local times in obj, if necessary. */
+static inline JSBool
+GetAndCacheLocalTime(JSContext *cx, JSObject *obj, Value *vp, jsdouble *time = NULL)
{
if (!obj || !InstanceOf(cx, obj, &js_DateClass, vp ? vp + 2 : NULL))
return false;
- jsdouble result = obj->getDateLocalTime().toNumber();
- if (JSDOUBLE_IS_NaN(result)) {
- result = obj->getDateUTCTime().toDouble();
-
- /* if result is NaN, it couldn't be finite. */
- if (JSDOUBLE_IS_FINITE(result))
- result = LocalTime(result, cx);
-
- obj->setDateLocalTime(DoubleValue(result));
+ /* If the local time is undefined, we need to fill in the cached values. */
+ if (obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_TIME).isUndefined()) {
+ if (!FillLocalTimes(cx, obj))
+ return false;
}
- *dp = result;
+ if (time)
+ *time = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_TIME).toDouble();
+
return true;
}
/*
* See ECMA 15.9.5.4 thru 15.9.5.23
*/
static JSBool
date_getTime(JSContext *cx, uintN argc, Value *vp)
@@ -1265,45 +1405,43 @@ date_getTime(JSContext *cx, uintN argc,
if (!GetUTCTime(cx, ComputeThisFromVp(cx, vp), vp, &result))
return JS_FALSE;
vp->setNumber(result);
return JS_TRUE;
}
static JSBool
-GetYear(JSContext *cx, JSBool fullyear, Value *vp)
-{
- jsdouble result;
-
- if (!GetAndCacheLocalTime(cx, ComputeThisFromVp(cx, vp), vp, &result))
- return JS_FALSE;
-
- if (JSDOUBLE_IS_FINITE(result)) {
- result = YearFromTime(result);
-
- /* Follow ECMA-262 to the letter, contrary to IE JScript. */
- if (!fullyear)
- result -= 1900;
- }
-
- vp->setNumber(result);
- return JS_TRUE;
-}
-
-static JSBool
date_getYear(JSContext *cx, uintN argc, Value *vp)
{
- return GetYear(cx, JS_FALSE, vp);
+ JSObject *obj = ComputeThisFromVp(cx, vp);
+ if (!GetAndCacheLocalTime(cx, obj, vp))
+ return JS_FALSE;
+
+ Value yearVal = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_YEAR);
+ if (yearVal.isInt32()) {
+ /* Follow ECMA-262 to the letter, contrary to IE JScript. */
+ jsint year = yearVal.toInt32() - 1900;
+ vp->setInt32(year);
+ } else {
+ *vp = yearVal;
+ }
+
+ return JS_TRUE;
}
static JSBool
date_getFullYear(JSContext *cx, uintN argc, Value *vp)
{
- return GetYear(cx, JS_TRUE, vp);
+ JSObject *obj = ComputeThisFromVp(cx, vp);
+ if (!GetAndCacheLocalTime(cx, obj, vp))
+ return JS_FALSE;
+
+ *vp = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_YEAR);
+ return JS_TRUE;
}
static JSBool
date_getUTCFullYear(JSContext *cx, uintN argc, Value *vp)
{
jsdouble result;
if (!GetUTCTime(cx, ComputeThisFromVp(cx, vp), vp, &result))
@@ -1314,25 +1452,21 @@ date_getUTCFullYear(JSContext *cx, uintN
vp->setNumber(result);
return JS_TRUE;
}
static JSBool
date_getMonth(JSContext *cx, uintN argc, Value *vp)
{
- jsdouble result;
-
- if (!GetAndCacheLocalTime(cx, ComputeThisFromVp(cx, vp), vp, &result))
+ JSObject *obj = ComputeThisFromVp(cx, vp);
+ if (!GetAndCacheLocalTime(cx, obj, vp))
return JS_FALSE;
- if (JSDOUBLE_IS_FINITE(result))
- result = MonthFromTime(result);
-
- vp->setNumber(result);
+ *vp = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_MONTH);
return JS_TRUE;
}
static JSBool
date_getUTCMonth(JSContext *cx, uintN argc, Value *vp)
{
jsdouble result;
@@ -1344,25 +1478,21 @@ date_getUTCMonth(JSContext *cx, uintN ar
vp->setNumber(result);
return JS_TRUE;
}
static JSBool
date_getDate(JSContext *cx, uintN argc, Value *vp)
{
- jsdouble result;
-
- if (!GetAndCacheLocalTime(cx, ComputeThisFromVp(cx, vp), vp, &result))
+ JSObject *obj = ComputeThisFromVp(cx, vp);
+ if (!GetAndCacheLocalTime(cx, obj, vp))
return JS_FALSE;
- if (JSDOUBLE_IS_FINITE(result))
- result = DateFromTime(result);
-
- vp->setNumber(result);
+ *vp = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_DATE);
return JS_TRUE;
}
static JSBool
date_getUTCDate(JSContext *cx, uintN argc, Value *vp)
{
jsdouble result;
@@ -1374,25 +1504,21 @@ date_getUTCDate(JSContext *cx, uintN arg
vp->setNumber(result);
return JS_TRUE;
}
static JSBool
date_getDay(JSContext *cx, uintN argc, Value *vp)
{
- jsdouble result;
-
- if (!GetAndCacheLocalTime(cx, ComputeThisFromVp(cx, vp), vp, &result))
+ JSObject *obj = ComputeThisFromVp(cx, vp);
+ if (!GetAndCacheLocalTime(cx, obj, vp))
return JS_FALSE;
- if (JSDOUBLE_IS_FINITE(result))
- result = WeekDay(result);
-
- vp->setNumber(result);
+ *vp = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_DAY);
return JS_TRUE;
}
static JSBool
date_getUTCDay(JSContext *cx, uintN argc, Value *vp)
{
jsdouble result;
@@ -1404,25 +1530,21 @@ date_getUTCDay(JSContext *cx, uintN argc
vp->setNumber(result);
return JS_TRUE;
}
static JSBool
date_getHours(JSContext *cx, uintN argc, Value *vp)
{
- jsdouble result;
-
- if (!GetAndCacheLocalTime(cx, ComputeThisFromVp(cx, vp), vp, &result))
+ JSObject *obj = ComputeThisFromVp(cx, vp);
+ if (!GetAndCacheLocalTime(cx, obj, vp))
return JS_FALSE;
- if (JSDOUBLE_IS_FINITE(result))
- result = HourFromTime(result);
-
- vp->setNumber(result);
+ *vp = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_HOURS);
return JS_TRUE;
}
static JSBool
date_getUTCHours(JSContext *cx, uintN argc, Value *vp)
{
jsdouble result;
@@ -1434,25 +1556,21 @@ date_getUTCHours(JSContext *cx, uintN ar
vp->setNumber(result);
return JS_TRUE;
}
static JSBool
date_getMinutes(JSContext *cx, uintN argc, Value *vp)
{
- jsdouble result;
-
- if (!GetAndCacheLocalTime(cx, ComputeThisFromVp(cx, vp), vp, &result))
+ JSObject *obj = ComputeThisFromVp(cx, vp);
+ if (!GetAndCacheLocalTime(cx, obj, vp))
return JS_FALSE;
- if (JSDOUBLE_IS_FINITE(result))
- result = MinFromTime(result);
-
- vp->setNumber(result);
+ *vp = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_MINUTES);
return JS_TRUE;
}
static JSBool
date_getUTCMinutes(JSContext *cx, uintN argc, Value *vp)
{
jsdouble result;
@@ -1466,25 +1584,21 @@ date_getUTCMinutes(JSContext *cx, uintN
return JS_TRUE;
}
/* Date.getSeconds is mapped to getUTCSeconds */
static JSBool
date_getUTCSeconds(JSContext *cx, uintN argc, Value *vp)
{
- jsdouble result;
-
- if (!GetUTCTime(cx, ComputeThisFromVp(cx, vp), vp, &result))
+ JSObject *obj = ComputeThisFromVp(cx, vp);
+ if (!GetAndCacheLocalTime(cx, obj, vp))
return JS_FALSE;
- if (JSDOUBLE_IS_FINITE(result))
- result = SecFromTime(result);
-
- vp->setNumber(result);
+ *vp = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_SECONDS);
return JS_TRUE;
}
/* Date.getMilliseconds is mapped to getUTCMilliseconds */
static JSBool
date_getUTCMilliseconds(JSContext *cx, uintN argc, Value *vp)
{
@@ -1893,16 +2007,68 @@ date_toGMTString(JSContext *cx, uintN ar
}
static JSBool
date_toISOString(JSContext *cx, uintN argc, Value *vp)
{
return date_utc_format(cx, vp, print_iso_string);
}
+namespace {
+
+/* ES5 15.9.5.44. */
+JSBool
+date_toJSON(JSContext *cx, uintN argc, Value *vp)
+{
+ /* Step 1. */
+ JSObject *obj = ComputeThisFromVp(cx, vp);
+ if (!obj)
+ return false;
+
+ /* Step 2. */
+ Value &tv = vp[0];
+ if (!DefaultValue(cx, obj, JSTYPE_NUMBER, &tv))
+ return false;
+
+ /* Step 3. */
+ if (tv.isDouble() && !JSDOUBLE_IS_FINITE(tv.toDouble())) {
+ vp->setNull();
+ return true;
+ }
+
+ /* Step 4. */
+ Value &toISO = vp[0];
+ if (!obj->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.toISOStringAtom), &toISO))
+ return false;
+
+ /* Step 5. */
+ if (!js_IsCallable(toISO)) {
+ JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL,
+ JSMSG_BAD_TOISOSTRING_PROP);
+ return false;
+ }
+
+ /* Step 6. */
+ LeaveTrace(cx);
+ InvokeArgsGuard args;
+ if (!cx->stack().pushInvokeArgs(cx, 0, args))
+ return false;
+
+ args.callee() = toISO;
+ args.thisv().setObject(*obj);
+
+ if (!Invoke(cx, args, 0))
+ return false;
+
+ *vp = args.rval();
+ return true;
+}
+
+}
+
/* for Date.toLocaleString; interface to PRMJTime date struct.
*/
static void
new_explode(jsdouble timeval, PRMJTime *split, JSContext *cx)
{
jsint year = YearFromTime(timeval);
split->tm_usec = (int32) msFromTime(timeval) * 1000;
@@ -2288,17 +2454,17 @@ static JSFunctionSpec date_methods[] = {
JS_FN("toUTCString", date_toGMTString, 0,0),
JS_FN(js_toLocaleString_str, date_toLocaleString, 0,0),
JS_FN("toLocaleDateString", date_toLocaleDateString, 0,0),
JS_FN("toLocaleTimeString", date_toLocaleTimeString, 0,0),
JS_FN("toLocaleFormat", date_toLocaleFormat, 0,0),
JS_FN("toDateString", date_toDateString, 0,0),
JS_FN("toTimeString", date_toTimeString, 0,0),
JS_FN("toISOString", date_toISOString, 0,0),
- JS_FN(js_toJSON_str, date_toISOString, 0,0),
+ JS_FN(js_toJSON_str, date_toJSON, 1,0),
#if JS_HAS_TOSOURCE
JS_FN(js_toSource_str, date_toSource, 0,0),
#endif
JS_FN(js_toString_str, date_toString, 0,0),
JS_FN(js_valueOf_str, date_valueOf, 0,0),
JS_FS_END
};
--- a/js/src/jsdtracef.cpp
+++ b/js/src/jsdtracef.cpp
@@ -59,17 +59,19 @@ jsdtrace_fun_classname(const JSFunction
return (fun && !FUN_INTERPRETED(fun) && !(fun->flags & JSFUN_TRCINFO) && FUN_CLASP(fun))
? (char *)FUN_CLASP(fun)->name
: dempty;
}
static char *
jsdtrace_filename(JSStackFrame *fp)
{
- return (fp && fp->script && fp->script->filename) ? (char *)fp->script->filename : dempty;
+ return (fp && fp->hasScript() && fp->getScript()->filename)
+ ? (char *)fp->getScript()->filename
+ : dempty;
}
static int
jsdtrace_fun_linenumber(JSContext *cx, const JSFunction *fun)
{
if (fun && FUN_INTERPRETED(fun))
return (int) JS_GetScriptBaseLineNumber(cx, FUN_SCRIPT(fun));
--- a/js/src/jsemit.cpp
+++ b/js/src/jsemit.cpp
@@ -2894,17 +2894,19 @@ EmitElemOp(JSContext *cx, JSParseNode *p
* Try to optimize arguments[0][j]... into JSOP_ARGSUB<0> followed by
* one or more index expression and JSOP_GETELEM op pairs.
*/
if (left->pn_type == TOK_NAME && next->pn_type == TOK_NUMBER) {
if (!BindNameToSlot(cx, cg, left))
return JS_FALSE;
if (left->pn_op == JSOP_ARGUMENTS &&
JSDOUBLE_IS_INT32(next->pn_dval, &slot) &&
- (jsuint)slot < JS_BIT(16)) {
+ jsuint(slot) < JS_BIT(16) &&
+ (!cg->inStrictMode() ||
+ (!cg->mutatesParameter() && !cg->callsEval()))) {
/*
* arguments[i]() requires arguments object as "this".
* Check that we never generates list for that usage.
*/
JS_ASSERT(op != JSOP_CALLELEM || next->pn_next);
left->pn_offset = next->pn_offset = top;
EMIT_UINT16_IMM_OP(JSOP_ARGSUB, (jsatomid)slot);
left = next;
@@ -2968,17 +2970,19 @@ EmitElemOp(JSContext *cx, JSParseNode *p
/* Try to optimize arguments[0] (e.g.) into JSOP_ARGSUB<0>. */
if (op == JSOP_GETELEM &&
left->pn_type == TOK_NAME &&
right->pn_type == TOK_NUMBER) {
if (!BindNameToSlot(cx, cg, left))
return JS_FALSE;
if (left->pn_op == JSOP_ARGUMENTS &&
JSDOUBLE_IS_INT32(right->pn_dval, &slot) &&
- (jsuint)slot < JS_BIT(16)) {
+ jsuint(slot) < JS_BIT(16) &&
+ (!cg->inStrictMode() ||
+ (!cg->mutatesParameter() && !cg->callsEval()))) {
left->pn_offset = right->pn_offset = top;
EMIT_UINT16_IMM_OP(JSOP_ARGSUB, (jsatomid)slot);
return JS_TRUE;
}
}
if (!js_EmitTree(cx, cg, left))
return JS_FALSE;
@@ -3620,16 +3624,23 @@ js_EmitFunctionScript(JSContext *cx, JSC
/*
* Emit a trace hint opcode only if not in a generator, since generators
* are not yet traced and both want to be the first instruction.
*/
if (js_Emit1(cx, cg, JSOP_TRACE) < 0)
return false;
}
+ if (cg->needsEagerArguments()) {
+ CG_SWITCH_TO_PROLOG(cg);
+ if (js_Emit1(cx, cg, JSOP_ARGUMENTS) < 0 || js_Emit1(cx, cg, JSOP_POP) < 0)
+ return false;
+ CG_SWITCH_TO_MAIN(cg);
+ }
+
if (cg->flags & TCF_FUN_UNBRAND_THIS) {
if (js_Emit1(cx, cg, JSOP_UNBRANDTHIS) < 0)
return false;
}
return js_EmitTree(cx, cg, body) &&
js_Emit1(cx, cg, JSOP_STOP) >= 0 &&
js_NewScriptFromCG(cx, cg);
@@ -3696,17 +3707,17 @@ MaybeEmitVarDecl(JSContext *cx, JSCodeGe
CG_SWITCH_TO_PROLOG(cg);
if (!UpdateLineNumberNotes(cx, cg, pn->pn_pos.begin.lineno))
return JS_FALSE;
EMIT_INDEX_OP(prologOp, atomIndex);
CG_SWITCH_TO_MAIN(cg);
}
if (JOF_OPTYPE(pn->pn_op) == JOF_LOCAL &&
- !(cg->flags & TCF_FUN_USES_EVAL) &&
+ !(cg->flags & TCF_FUN_CALLS_EVAL) &&
pn->pn_defn &&
(((JSDefinition *)pn)->pn_dflags & PND_CLOSED))
{
CG_SWITCH_TO_PROLOG(cg);
EMIT_UINT16_IMM_OP(JSOP_DEFUPVAR, pn->pn_cookie.asInteger());
CG_SWITCH_TO_MAIN(cg);
}
@@ -4531,17 +4542,17 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
#ifdef DEBUG
JSLocalKind localKind =
#endif
js_LookupLocal(cx, cg->fun, fun->atom, &slot);
JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
JS_ASSERT(index < JS_BIT(20));
pn->pn_index = index;
op = FUN_FLAT_CLOSURE(fun) ? JSOP_DEFLOCALFUN_FC : JSOP_DEFLOCALFUN;
- if ((pn->pn_dflags & PND_CLOSED) && !(cg->flags & TCF_FUN_USES_EVAL)) {
+ if ((pn->pn_dflags & PND_CLOSED) && !(cg->flags & TCF_FUN_CALLS_EVAL)) {
CG_SWITCH_TO_PROLOG(cg);
EMIT_UINT16_IMM_OP(JSOP_DEFUPVAR, pn->pn_cookie.asInteger());
CG_SWITCH_TO_MAIN(cg);
}
if (!EmitSlotIndexOp(cx, op, slot, index, cg))
return JS_FALSE;
}
break;
--- a/js/src/jsemit.h
+++ b/js/src/jsemit.h
@@ -235,42 +235,44 @@ struct JSStmtInfo {
* Flag to prevent a non-escaping function from being optimized into a null
* closure (i.e., a closure that needs only its global object for free variable
* resolution, thanks to JSOP_{GET,CALL}UPVAR), because this function contains
* a closure that needs one or more scope objects surrounding it (i.e., Call
* object for a heavyweight outer function). See bug 560234.
*/
#define TCF_FUN_ENTRAINS_SCOPES 0x400000
-/*
- * Function uses eval.
- */
-#define TCF_FUN_USES_EVAL 0x800000
+/* The function calls 'eval'. */
+#define TCF_FUN_CALLS_EVAL 0x800000
+
+/* The function mutates a positional (non-destructuring) parameter. */
+#define TCF_FUN_MUTATES_PARAMETER 0x1000000
/*
* Compiling an eval() script.
*/
-#define TCF_COMPILE_FOR_EVAL 0x1000000
+#define TCF_COMPILE_FOR_EVAL 0x2000000
/*
* Flags to check for return; vs. return expr; in a function.
*/
#define TCF_RETURN_FLAGS (TCF_RETURN_EXPR | TCF_RETURN_VOID)
/*
* Sticky deoptimization flags to propagate from FunctionBody.
*/
#define TCF_FUN_FLAGS (TCF_FUN_SETS_OUTER_NAME | \
TCF_FUN_USES_ARGUMENTS | \
TCF_FUN_PARAM_ARGUMENTS | \
TCF_FUN_HEAVYWEIGHT | \
TCF_FUN_IS_GENERATOR | \
TCF_FUN_USES_OWN_NAME | \
- TCF_FUN_USES_EVAL | \
TCF_HAS_SHARPS | \
+ TCF_FUN_CALLS_EVAL | \
+ TCF_FUN_MUTATES_PARAMETER | \
TCF_STRICT_MODE_CODE)
struct JSTreeContext { /* tree context for semantic checks */
uint32 flags; /* statement state flags, see above */
uint16 ngvars; /* max. no. of global variables/regexps */
uint32 bodyid; /* block number of program/function body */
uint32 blockidGen; /* preincremented block number generator */
JSStmtInfo *topStmt; /* top of statement info stack */
@@ -332,44 +334,81 @@ struct JSTreeContext { /* t
uintN blockid() { return topStmt ? topStmt->blockid : bodyid; }
bool atTopLevel() { return !topStmt || (topStmt->flags & SIF_BODY_BLOCK); }
/* Test whether we're in a statement of given type. */
bool inStatement(JSStmtType type);
+ bool inStrictMode() const {
+ return flags & TCF_STRICT_MODE_CODE;
+ }
+
inline bool needStrictChecks();
/*
* sharpSlotBase is -1 or first slot of pair for [sharpArray, sharpDepth].
* The parser calls ensureSharpSlots to allocate these two stack locals.
*/
int sharpSlotBase;
bool ensureSharpSlots();
js::Compiler *compiler() { return (js::Compiler *)parser; }
// Return true there is a generator function within |skip| lexical scopes
// (going upward) from this context's lexical scope. Always return true if
// this context is itself a generator.
bool skipSpansGenerator(unsigned skip);
- bool compileAndGo() { return !!(flags & TCF_COMPILE_N_GO); }
- bool inFunction() { return !!(flags & TCF_IN_FUNCTION); }
- bool compiling() { return !!(flags & TCF_COMPILING); }
+ bool compileAndGo() const { return flags & TCF_COMPILE_N_GO; }
+ bool inFunction() const { return flags & TCF_IN_FUNCTION; }
+ bool compiling() const { return flags & TCF_COMPILING; }
+
+ bool usesArguments() const {
+ return flags & TCF_FUN_USES_ARGUMENTS;
+ }
+
+ void noteCallsEval() {
+ flags |= TCF_FUN_CALLS_EVAL;
+ }
+
+ bool callsEval() const {
+ JS_ASSERT(inFunction());
+ return flags & TCF_FUN_CALLS_EVAL;
+ }
+
+ void noteParameterMutation() {
+ JS_ASSERT(inFunction());
+ flags |= TCF_FUN_MUTATES_PARAMETER;
+ }
+
+ bool mutatesParameter() const {
+ JS_ASSERT(inFunction());
+ return flags & TCF_FUN_MUTATES_PARAMETER;
+ }
+
+ void noteArgumentsUse() {
+ JS_ASSERT(inFunction());
+ flags |= TCF_FUN_USES_ARGUMENTS;
+ if (funbox)
+ funbox->node->pn_dflags |= PND_FUNARG;
+ }
+
+ bool needsEagerArguments() const {
+ return inStrictMode() && ((usesArguments() && mutatesParameter()) || callsEval());
+ }
};
/*
* Return true if we need to check for conditions that elicit
* JSOPTION_STRICT warnings or strict mode errors.
*/
inline bool JSTreeContext::needStrictChecks() {
- return JS_HAS_STRICT_OPTION(parser->context) ||
- (flags & TCF_STRICT_MODE_CODE);
+ return JS_HAS_STRICT_OPTION(parser->context) || inStrictMode();
}
/*
* Span-dependent instructions are jumps whose span (from the jump bytecode to
* the jump target) may require 2 or 4 bytes of immediate operand.
*/
typedef struct JSSpanDep JSSpanDep;
typedef struct JSJumpTarget JSJumpTarget;
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -77,21 +77,28 @@
#endif
#if JS_HAS_XDR
# include "jsxdrapi.h"
#endif
#include "jsatominlines.h"
#include "jscntxtinlines.h"
+#include "jsfuninlines.h"
#include "jsobjinlines.h"
#include "jscntxtinlines.h"
using namespace js;
+inline JSObject *
+JSObject::getThrowTypeError() const
+{
+ return &getGlobal()->getReservedSlot(JSRESERVED_GLOBAL_THROWTYPEERROR).toObject();
+}
+
JSBool
js_GetArgsValue(JSContext *cx, JSStackFrame *fp, Value *vp)
{
JSObject *argsobj;
if (fp->flags & JSFRAME_OVERRIDE_ARGS) {
JS_ASSERT(fp->hasCallObj());
jsid id = ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom);
@@ -168,32 +175,35 @@ NewArguments(JSContext *cx, JSObject *pa
if (!js_GetClassPrototype(cx, parent, JSProto_Object, &proto))
return NULL;
JSObject *argsobj = js_NewGCObject(cx);
if (!argsobj)
return NULL;
/* Init immediately to avoid GC seeing a half-init'ed object. */
- argsobj->init(&js_ArgumentsClass, proto, parent, PrivateValue(NULL));
- argsobj->setArgsCallee(ObjectOrNullValue(callee));
+ bool strict = callee->getFunctionPrivate()->inStrictMode();
+ argsobj->init(strict ? &StrictArgumentsClass : &js_ArgumentsClass, proto, parent,
+ PrivateValue(NULL));
argsobj->setArgsLength(argc);
-
+ argsobj->setArgsCallee(ObjectValue(*callee));
argsobj->map = cx->runtime->emptyArgumentsScope->hold();
/* This must come after argsobj->map has been set. */
if (!js_EnsureReservedSlots(cx, argsobj, argc))
return NULL;
+
return argsobj;
}
static void
PutArguments(JSContext *cx, JSObject *argsobj, Value *args)
{
- uint32 argc = argsobj->getArgsLength();
+ JS_ASSERT(argsobj->isNormalArguments());
+ uint32 argc = argsobj->getArgsInitialLength();
for (uint32 i = 0; i != argc; ++i) {
if (!argsobj->getArgsElement(i).isMagic(JS_ARGS_HOLE))
argsobj->setArgsElement(i, args[i]);
}
}
JSObject *
js_GetArgsObject(JSContext *cx, JSStackFrame *fp)
@@ -215,72 +225,101 @@ js_GetArgsObject(JSContext *cx, JSStackF
return fp->getArgsObj();
/* Compute the arguments object's parent slot from fp's scope chain. */
JSObject *global = fp->getScopeChain()->getGlobal();
JSObject *argsobj = NewArguments(cx, global, fp->argc, &fp->argv[-2].toObject());
if (!argsobj)
return argsobj;
- /* Link the new object to fp so it can get actual argument values. */
- argsobj->setPrivate(fp);
+ /*
+ * Strict mode functions have arguments which copy the initial parameter
+ * values. It is the caller's responsibility to get the arguments object
+ * before any parameters are modified! (The emitter ensures this by
+ * synthesizing an arguments access at the start of any strict mode
+ * function which contains an assignment to a parameter or which calls
+ * eval.) Non-strict mode arguments use the frame pointer to retrieve
+ * up-to-date parameter values.
+ */
+ if (argsobj->isStrictArguments()) {
+ JS_ASSERT_IF(fp->argc > 0, argsobj->dslots[-1].toPrivateUint32() >= fp->argc);
+ memcpy(argsobj->dslots, fp->argv, fp->argc * sizeof(Value));
+ } else {
+ argsobj->setPrivate(fp);
+ }
+
fp->setArgsObj(argsobj);
return argsobj;
}
void
js_PutArgsObject(JSContext *cx, JSStackFrame *fp)
{
JSObject *argsobj = fp->getArgsObj();
- JS_ASSERT(argsobj->getPrivate() == fp);
- PutArguments(cx, argsobj, fp->argv);
- argsobj->setPrivate(NULL);
+ if (argsobj->isNormalArguments()) {
+ JS_ASSERT(argsobj->getPrivate() == fp);
+ PutArguments(cx, argsobj, fp->argv);
+ argsobj->setPrivate(NULL);
+ } else {
+ JS_ASSERT(!argsobj->getPrivate());
+ }
fp->setArgsObj(NULL);
}
/*
* Traced versions of js_GetArgsObject and js_PutArgsObject.
*/
#ifdef JS_TRACER
JSObject * JS_FASTCALL
js_Arguments(JSContext *cx, JSObject *parent, uint32 argc, JSObject *callee)
{
JSObject *argsobj = NewArguments(cx, parent, argc, callee);
if (!argsobj)
return NULL;
- argsobj->setPrivate(JS_ARGUMENT_OBJECT_ON_TRACE);
+
+ if (callee->getFunctionPrivate()->inStrictMode()) {
+ /*
+ * Strict mode callers must copy arguments into the created arguments
+ * object.
+ */
+ JS_ASSERT(!argsobj->getPrivate());
+ } else {
+ argsobj->setPrivate(JS_ARGUMENT_OBJECT_ON_TRACE);
+ }
+
return argsobj;
}
#endif
JS_DEFINE_CALLINFO_4(extern, OBJECT, js_Arguments, CONTEXT, OBJECT, UINT32, OBJECT,
0, nanojit::ACCSET_STORE_ANY)
/* FIXME change the return type to void. */
JSBool JS_FASTCALL
js_PutArguments(JSContext *cx, JSObject *argsobj, Value *args)
{
+ JS_ASSERT(argsobj->isNormalArguments());
JS_ASSERT(argsobj->getPrivate() == JS_ARGUMENT_OBJECT_ON_TRACE);
PutArguments(cx, argsobj, args);
argsobj->setPrivate(NULL);
return true;
}
JS_DEFINE_CALLINFO_3(extern, BOOL, js_PutArguments, CONTEXT, OBJECT, VALUEPTR, 0,
nanojit::ACCSET_STORE_ANY)
static JSBool
args_delProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{
JS_ASSERT(obj->isArguments());
if (JSID_IS_INT(id)) {
uintN arg = uintN(JSID_TO_INT(id));
- if (arg < obj->getArgsLength())
+ if (arg < obj->getArgsInitialLength())
obj->setArgsElement(arg, MagicValue(JS_ARGS_HOLE));
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
obj->setArgsLengthOverridden();
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom)) {
obj->setArgsCallee(MagicValue(JS_ARGS_HOLE));
}
return true;
}
@@ -473,29 +512,29 @@ ArgGetter(JSContext *cx, JSObject *obj,
return true;
if (JSID_IS_INT(id)) {
/*
* arg can exceed the number of arguments if a script changed the
* prototype to point to another Arguments object with a bigger argc.
*/
uintN arg = uintN(JSID_TO_INT(id));
- if (arg < obj->getArgsLength()) {
+ if (arg < obj->getArgsInitialLength()) {
JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
if (fp) {
*vp = fp->argv[arg];
} else {
const Value &v = obj->getArgsElement(arg);
if (!v.isMagic(JS_ARGS_HOLE))
*vp = v;
}
}
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
if (!obj->isArgsLengthOverridden())
- vp->setInt32(obj->getArgsLength());
+ vp->setInt32(obj->getArgsInitialLength());
} else {
JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom));
const Value &v = obj->getArgsCallee();
if (!v.isMagic(JS_ARGS_HOLE)) {
/*
* If this function or one in it needs upvars that reach above it
* in the scope chain, it must not be a null closure (it could be a
* flat closure, or an unoptimized closure -- the latter itself not
@@ -528,82 +567,80 @@ ArgSetter(JSContext *cx, JSObject *obj,
}
#endif
if (!InstanceOf(cx, obj, &js_ArgumentsClass, NULL))
return true;
if (JSID_IS_INT(id)) {
uintN arg = uintN(JSID_TO_INT(id));
- if (arg < obj->getArgsLength()) {
+ if (arg < obj->getArgsInitialLength()) {
JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
if (fp) {
fp->argv[arg] = *vp;
return true;
}
}
} else {
JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom) ||
JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom));
}
/*
* For simplicity we use delete/set to replace the property with one
- * backed by the default Object getter and setter. Note the we rely on
- * args_delete to clear the corresponding reserved slot so the GC can
+ * backed by the default Object getter and setter. Note that we rely on
+ * args_delProperty to clear the corresponding reserved slot so the GC can
* collect its value.
*/
AutoValueRooter tvr(cx);
return js_DeleteProperty(cx, obj, id, tvr.addr()) &&
js_SetProperty(cx, obj, id, vp);
}
static JSBool
args_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
JSObject **objp)
{
- JS_ASSERT(obj->isArguments());
+ JS_ASSERT(obj->isNormalArguments());
*objp = NULL;
bool valid = false;
+ uintN attrs = JSPROP_SHARED;
if (JSID_IS_INT(id)) {
uint32 arg = uint32(JSID_TO_INT(id));
- if (arg < obj->getArgsLength() && !obj->getArgsElement(arg).isMagic(JS_ARGS_HOLE))
+ attrs = JSPROP_ENUMERATE | JSPROP_SHARED;
+ 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)) {
if (!obj->getArgsCallee().isMagic(JS_ARGS_HOLE))
valid = true;
}
if (valid) {
- /*
- * XXX ECMA specs DontEnum even for indexed properties, contrary to
- * other array-like objects.
- */
Value tmp = UndefinedValue();
- if (!js_DefineProperty(cx, obj, id, &tmp, ArgGetter, ArgSetter, JSPROP_SHARED))
+ if (!js_DefineProperty(cx, obj, id, &tmp, ArgGetter, ArgSetter, attrs))
return JS_FALSE;
*objp = obj;
}
return true;
}
static JSBool
args_enumerate(JSContext *cx, JSObject *obj)
{
- JS_ASSERT(obj->isArguments());
+ JS_ASSERT(obj->isNormalArguments());
/*
* Trigger reflection in args_resolve using a series of js_LookupProperty
* calls.
*/
- int argc = int(obj->getArgsLength());
+ int argc = int(obj->getArgsInitialLength());
for (int i = -2; i != argc; i++) {
jsid id = (i == -2)
? ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)
: (i == -1)
? ATOM_TO_JSID(cx->runtime->atomState.calleeAtom)
: INT_TO_JSID(i);
JSObject *pobj;
@@ -613,16 +650,164 @@ args_enumerate(JSContext *cx, JSObject *
/* prop is null when the property was deleted. */
if (prop)
pobj->dropProperty(cx, prop);
}
return true;
}
+namespace {
+
+JSBool
+StrictArgGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
+{
+ LeaveTrace(cx);
+
+ if (!InstanceOf(cx, obj, &StrictArgumentsClass, NULL))
+ return true;
+
+ if (JSID_IS_INT(id)) {
+ /*
+ * arg can exceed the number of arguments if a script changed the
+ * prototype to point to another Arguments object with a bigger argc.
+ */
+ uintN arg = uintN(JSID_TO_INT(id));
+ if (arg < obj->getArgsInitialLength()) {
+ const Value &v = obj->getArgsElement(arg);
+ if (!v.isMagic(JS_ARGS_HOLE))
+ *vp = v;
+ }
+ } else {
+ JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom));
+ if (!obj->isArgsLengthOverridden())
+ vp->setInt32(obj->getArgsInitialLength());
+ }
+ return true;
+}
+
+JSBool
+StrictArgSetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
+{
+ if (!InstanceOf(cx, obj, &StrictArgumentsClass, NULL))
+ return true;
+
+ if (JSID_IS_INT(id)) {
+ uintN arg = uintN(JSID_TO_INT(id));
+ if (arg < obj->getArgsInitialLength()) {
+ obj->setArgsElement(arg, *vp);
+ return true;
+ }
+ } else {
+ JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom));
+ }
+
+ /*
+ * For simplicity we use delete/set to replace the property with one
+ * backed by the default Object getter and setter. Note that we rely on
+ * args_delProperty to clear the corresponding reserved slot so the GC can
+ * collect its value.
+ */
+ AutoValueRooter tvr(cx);
+ return js_DeleteProperty(cx, obj, id, tvr.addr()) &&
+ js_SetProperty(cx, obj, id, vp);
+}
+
+JSBool
+strictargs_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp)
+{
+ JS_ASSERT(obj->isStrictArguments());
+
+ *objp = NULL;
+ bool valid = false;
+ uintN attrs = JSPROP_SHARED;
+ 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;
+
+ *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;
+ }
+
+ *objp = obj;
+ return true;
+ }
+
+ if (valid) {
+ Value tmp = UndefinedValue();
+ if (!js_DefineProperty(cx, obj, id, &tmp, StrictArgGetter, StrictArgSetter, attrs))
+ return false;
+ *objp = obj;
+ }
+ return true;
+}
+
+JSBool
+strictargs_enumerate(JSContext *cx, JSObject *obj)
+{
+ JS_ASSERT(obj->isStrictArguments());
+
+ /*
+ * Trigger reflection in strictargs_resolve using a series of
+ * js_LookupProperty calls. Beware deleted properties!
+ */
+ JSObject *pobj;
+ JSProperty *prop;
+
+ // length
+ if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), &pobj, &prop))
+ return false;
+ if (prop)
+ pobj->dropProperty(cx, prop);
+
+ // callee
+ if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.calleeAtom), &pobj, &prop))
+ return false;
+ if (prop)
+ pobj->dropProperty(cx, prop);
+
+ // caller
+ if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.callerAtom), &pobj, &prop))
+ return false;
+ if (prop)
+ pobj->dropProperty(cx, prop);
+
+ for (uint32 i = 0, argc = obj->getArgsInitialLength(); i < argc; i++) {
+ if (!js_LookupProperty(cx, obj, INT_TO_JSID(i), &pobj, &prop))
+ return false;
+ if (prop)
+ pobj->dropProperty(cx, prop);
+ }
+
+ return true;
+}
+
+} // namespace
+
#if JS_HAS_GENERATORS
/*
* If a generator's arguments or call object escapes, and the generator frame
* is not executing, the generator object needs to be marked because it is not
* otherwise reachable. An executing generator is rooted by its invocation. To
* distinguish the two cases (which imply different access paths to the
* generator object), we use the JSFRAME_FLOATING_GENERATOR flag, which is only
* set on the JSStackFrame kept in the generator object's JSGenerator.
@@ -643,21 +828,24 @@ args_or_call_trace(JSTracer *trc, JSObje
JS_CALL_OBJECT_TRACER(trc, obj, "generator object");
}
}
#else
# define args_or_call_trace NULL
#endif
/*
- * The Arguments class is not initialized via JS_InitClass, because arguments
+ * The Arguments classes aren't initialized via JS_InitClass, because arguments
* objects have the initial value of Object.prototype as their [[Prototype]].
* However, Object.prototype.toString.call(arguments) === "[object Arguments]"
- * per ES5 (although not ES3), so its class name is "Arguments" rather than
+ * per ES5 (although not ES3), so the class name is "Arguments" rather than
* "Object".
+ */
+
+/*
*
* The JSClass functions below collaborate to lazily reflect and synchronize
* actual argument values, argument count, and callee function object stored
* in a JSStackFrame with their corresponding property values in the frame's
* arguments object.
*/
Class js_ArgumentsClass = {
"Arguments",
@@ -676,16 +864,47 @@ Class js_ArgumentsClass = {
NULL, /* checkAccess */
NULL, /* call */
NULL, /* construct */
NULL, /* xdrObject */
NULL, /* hasInstance */
JS_CLASS_TRACE(args_or_call_trace)
};
+namespace js {
+
+/*
+ * Strict mode arguments is significantly less magical than non-strict mode
+ * arguments, so it is represented by a different class while sharing some
+ * functionality.
+ */
+Class StrictArgumentsClass = {
+ "Arguments",
+ JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE |
+ JSCLASS_HAS_RESERVED_SLOTS(JSObject::ARGS_FIXED_RESERVED_SLOTS) |
+ JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
+ PropertyStub, /* addProperty */
+ args_delProperty,
+ PropertyStub, /* getProperty */
+ PropertyStub, /* setProperty */
+ strictargs_enumerate,
+ reinterpret_cast<JSResolveOp>(strictargs_resolve),
+ ConvertStub,
+ NULL, /* finalize */
+ NULL, /* reserved0 */
+ NULL, /* checkAccess */
+ NULL, /* call */
+ NULL, /* construct */
+ NULL, /* xdrObject */
+ NULL, /* hasInstance */
+ JS_CLASS_TRACE(args_or_call_trace)
+};
+
+}
+
const uint32 JSSLOT_CALLEE = JSSLOT_PRIVATE + 1;
const uint32 JSSLOT_CALL_ARGUMENTS = JSSLOT_PRIVATE + 2;
const uint32 CALL_CLASS_FIXED_RESERVED_SLOTS = 2;
/*
* A Declarative Environment object stores its active JSStackFrame pointer in
* its private slot, just as Call and Arguments objects do.
*/
@@ -1387,18 +1606,19 @@ fun_getProperty(JSContext *cx, JSObject
/*
* Loop because getter and setter can be delegated from another class,
* but loop only for FUN_LENGTH because we must pretend that f.length
* is in each function instance f, per ECMA-262, instead of only in the
* Function.prototype object (we use JSPROP_PERMANENT with JSPROP_SHARED
* to make it appear so).
*
- * This code couples tightly to the attributes for lazy_function_props[]
- * initializers above, and to js_SetProperty and js_HasOwnProperty.
+ * This code couples tightly to the attributes for lazyFunctionDataProps[]
+ * and poisonPillProps[] initializers below, and to js_SetProperty and
+ * js_HasOwnProperty.
*
* It's important to allow delegating objects, even though they inherit
* this getter (fun_getProperty), to override arguments, arity, caller,
* and name. If we didn't return early for slot != FUN_LENGTH, we would
* clobber *vp with the native property value, instead of letting script
* override that value in delegating objects.
*
* Note how that clobbering is what simulates JSPROP_READONLY for all of
@@ -1468,50 +1688,71 @@ fun_getProperty(JSContext *cx, JSObject
if (fp && fp->hasFunction() && (uintN)slot < fp->getFunction()->nargs)
*vp = fp->argv[slot];
break;
}
return true;
}
-struct LazyFunctionProp {
+namespace {
+
+struct LazyFunctionDataProp {
uint16 atomOffset;
int8 tinyid;
uint8 attrs;
};
-/* NB: no sentinel at the end -- use JS_ARRAY_LENGTH to bound loops. */
-static LazyFunctionProp lazy_function_props[] = {
- {ATOM_OFFSET(arguments), FUN_ARGUMENTS, JSPROP_PERMANENT},
+struct PoisonPillProp {
+ uint16 atomOffset;
+ int8 tinyid;
+};
+
+/* NB: no sentinels at ends -- use JS_ARRAY_LENGTH to bound loops. */
+
+const LazyFunctionDataProp lazyFunctionDataProps[] = {
{ATOM_OFFSET(arity), FUN_ARITY, JSPROP_PERMANENT},
- {ATOM_OFFSET(caller), FUN_CALLER, JSPROP_PERMANENT},
{ATOM_OFFSET(name), FUN_NAME, JSPROP_PERMANENT},
};
+/* Properties censored into [[ThrowTypeError]] in strict mode. */
+const PoisonPillProp poisonPillProps[] = {
+ {ATOM_OFFSET(arguments), FUN_ARGUMENTS },
+ {ATOM_OFFSET(caller), FUN_CALLER },
+};
+
+}
+
static JSBool
fun_enumerate(JSContext *cx, JSObject *obj)
{
JS_ASSERT(obj->isFunction());
jsval v;
jsid id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
if (!JS_LookupPropertyById(cx, obj, id, &v))
return false;
id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
if (!JS_LookupPropertyById(cx, obj, id, &v))
return false;
- for (uintN i = 0; i < JS_ARRAY_LENGTH(lazy_function_props); i++) {
- LazyFunctionProp &lfp = lazy_function_props[i];
+ for (uintN i = 0; i < JS_ARRAY_LENGTH(lazyFunctionDataProps); i++) {
+ const LazyFunctionDataProp &lfp = lazyFunctionDataProps[i];
id = ATOM_TO_JSID(OFFSET_TO_ATOM(cx->runtime, lfp.atomOffset));
if (!JS_LookupPropertyById(cx, obj, id, &v))
return false;
}
+ for (uintN i = 0; i < JS_ARRAY_LENGTH(poisonPillProps); i++) {
+ const PoisonPillProp &p = poisonPillProps[i];
+ id = ATOM_TO_JSID(OFFSET_TO_ATOM(cx->runtime, p.atomOffset));
+ if (!JS_LookupPropertyById(cx, obj, id, &v))
+ return false;
+ }
+
return true;
}
static JSBool
fun_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
JSObject **objp)
{
if (!JSID_IS_ATOM(id))
@@ -1577,18 +1818,18 @@ fun_resolve(JSContext *cx, JSObject *obj
PropertyStub, PropertyStub,
JSPROP_PERMANENT | JSPROP_READONLY, 0, 0, NULL)) {
return JS_FALSE;
}
*objp = obj;
return JS_TRUE;
}
- for (uintN i = 0; i < JS_ARRAY_LENGTH(lazy_function_props); i++) {
- LazyFunctionProp *lfp = &lazy_function_props[i];
+ for (uintN i = 0; i < JS_ARRAY_LENGTH(lazyFunctionDataProps); i++) {
+ const LazyFunctionDataProp *lfp = &lazyFunctionDataProps[i];
atom = OFFSET_TO_ATOM(cx->runtime, lfp->atomOffset);
if (id == ATOM_TO_JSID(atom)) {
JS_ASSERT(!IsInternalFunctionObject(obj));
if (!js_DefineNativeProperty(cx, obj,
ATOM_TO_JSID(atom), UndefinedValue(),
fun_getProperty, PropertyStub,
@@ -1596,16 +1837,47 @@ fun_resolve(JSContext *cx, JSObject *obj
lfp->tinyid, NULL)) {
return JS_FALSE;
}
*objp = obj;
return JS_TRUE;
}
}
+ for (uintN i = 0; i < JS_ARRAY_LENGTH(poisonPillProps); i++) {
+ const PoisonPillProp &p = poisonPillProps[i];
+
+ atom = OFFSET_TO_ATOM(cx->runtime, p.atomOffset);
+ if (id == ATOM_TO_JSID(atom)) {
+ JS_ASSERT(!IsInternalFunctionObject(obj));
+
+ PropertyOp getter, setter;
+ uintN attrs = JSPROP_PERMANENT;
+ if (fun->inStrictMode()) {
+ JSObject *throwTypeError = obj->getThrowTypeError();
+
+ getter = CastAsPropertyOp(throwTypeError);
+ setter = CastAsPropertyOp(throwTypeError);
+ attrs |= JSPROP_GETTER | JSPROP_SETTER;
+ } else {
+ getter = fun_getProperty;
+ setter = PropertyStub;
+ }
+
+ if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), UndefinedValue(),
+ getter, setter,
+ attrs, JSScopeProperty::HAS_SHORTID,
+ p.tinyid, NULL)) {
+ return JS_FALSE;
+ }
+ *objp = obj;
+ return JS_TRUE;
+ }
+ }
+
return JS_TRUE;
}
#if JS_HAS_XDR
/* XXX store parent and proto, if defined */
JSBool
js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp)
@@ -2075,17 +2347,17 @@ js_fun_apply(JSContext *cx, uintN argc,
* Steps 4-5 (note erratum removing steps originally numbered 5 and 7 in
* original version of ES5).
*/
JSObject *aobj = vp[3].toObject().wrappedObject(cx);
jsuint length;
if (aobj->isArray()) {
length = aobj->getArrayLength();
} else if (aobj->isArguments() && !aobj->isArgsLengthOverridden()) {
- length = aobj->getArgsLength();
+ length = aobj->getArgsInitialLength();
} else {
Value &lenval = vp[0];
if (!aobj->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), &lenval))
return false;
if (lenval.isInt32()) {
length = jsuint(lenval.toInt32()); /* jsuint cast does ToUint32 */
} else {
@@ -2395,30 +2667,53 @@ Function(JSContext *cx, JSObject *obj, u
str = cx->runtime->emptyString;
}
return Compiler::compileFunctionBody(cx, fun, principals,
str->chars(), str->length(),
filename, lineno);
}
+namespace {
+
+JSBool
+ThrowTypeError(JSContext *cx, uintN argc, Value *vp)
+{
+ JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL,
+ JSMSG_THROW_TYPE_ERROR);
+ return false;
+}
+
+}
+
JSObject *
js_InitFunctionClass(JSContext *cx, JSObject *obj)
{
- JSObject *proto;
- JSFunction *fun;
-
- proto = js_InitClass(cx, obj, NULL, &js_FunctionClass, Function, 1,
- NULL, function_methods, NULL, NULL);
+ JSObject *proto = js_InitClass(cx, obj, NULL, &js_FunctionClass, Function, 1,
+ NULL, function_methods, NULL, NULL);
if (!proto)
return NULL;
- fun = js_NewFunction(cx, proto, NULL, 0, JSFUN_INTERPRETED, obj, NULL);
+
+ JSFunction *fun = js_NewFunction(cx, proto, NULL, 0, JSFUN_INTERPRETED, obj, NULL);
if (!fun)
return NULL;
fun->u.i.script = JSScript::emptyScript();
+
+ if (obj->getClass()->flags & JSCLASS_IS_GLOBAL) {
+ /* ES5 13.2.3: Construct the unique [[ThrowTypeError]] function object. */
+ JSObject *throwTypeError =
+ js_NewFunction(cx, NULL, reinterpret_cast<Native>(ThrowTypeError), 0,
+ JSFUN_FAST_NATIVE, obj, NULL);
+ if (!throwTypeError)
+ return NULL;
+
+ JS_ALWAYS_TRUE(js_SetReservedSlot(cx, obj, JSRESERVED_GLOBAL_THROWTYPEERROR,
+ ObjectValue(*throwTypeError)));
+ }
+
return proto;
}
JSFunction *
js_NewFunction(JSContext *cx, JSObject *funobj, Native native, uintN nargs,
uintN flags, JSObject *parent, JSAtom *atom)
{
JSFunction *fun;
@@ -2605,55 +2900,36 @@ js_ValueToFunction(JSContext *cx, const
return NULL;
}
return GET_FUNCTION_PRIVATE(cx, funobj);
}
JSObject *
js_ValueToFunctionObject(JSContext *cx, Value *vp, uintN flags)
{
- JSFunction *fun;
- JSStackFrame *caller;
- JSPrincipals *principals;
-
JSObject *funobj;
- if (IsFunctionObject(*vp, &funobj))
- return funobj;
-
- fun = js_ValueToFunction(cx, vp, flags);
- if (!fun)
- return NULL;
- vp->setObject(*fun);
-
- caller = js_GetScriptedCaller(cx, NULL);
- if (caller) {
- principals = JS_StackFramePrincipals(cx, caller);
- } else {
- /* No scripted caller, don't allow access. */
- principals = NULL;
- }
-
- if (!js_CheckPrincipalsAccess(cx, FUN_OBJECT(fun), principals,
- fun->atom
- ? fun->atom
- : cx->runtime->atomState.anonymousAtom)) {
+ if (!IsFunctionObject(*vp, &funobj)) {
+ js_ReportIsNotFunction(cx, vp, flags);
return NULL;
}
- return FUN_OBJECT(fun);
+
+ return funobj;
}
JSObject *
js_ValueToCallableObject(JSContext *cx, Value *vp, uintN flags)
{
if (vp->isObject()) {
JSObject *callable = &vp->toObject();
if (callable->isCallable())
return callable;
}
- return js_ValueToFunctionObject(cx, vp, flags);
+
+ js_ReportIsNotFunction(cx, vp, flags);
+ return NULL;
}
void
js_ReportIsNotFunction(JSContext *cx, const Value *vp, uintN flags)
{
const char *name = NULL, *source = NULL;
AutoValueRooter tvr(cx);
uintN error = (flags & JSV2F_CONSTRUCT) ? JSMSG_NOT_CONSTRUCTOR : JSMSG_NOT_FUNCTION;
@@ -3119,16 +3395,18 @@ js_FreezeLocalNames(JSContext *cx, JSFun
if (n > MAX_ARRAY_LOCALS)
JS_DHashMarkTableImmutable(&fun->u.i.names.map->names);
#endif
}
JSAtom *
JSFunction::findDuplicateFormal() const
{
+ JS_ASSERT(isInterpreted());
+
if (nargs <= 1)
return NULL;
/* Function with two to MAX_ARRAY_LOCALS parameters use an aray. */
unsigned n = nargs + u.i.nvars + u.i.nupvars;
if (n <= MAX_ARRAY_LOCALS) {
jsuword *array = u.i.names.array;
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -179,16 +179,18 @@ struct JSFunction : public JSObject
bool optimizedClosure() const { return FUN_KIND(this) > JSFUN_INTERPRETED; }
bool needsWrapper() const { return FUN_NULL_CLOSURE(this) && u.i.skipmin != 0; }
bool isInterpreted() const { return FUN_INTERPRETED(this); }
bool isFastNative() const { return !!(flags & JSFUN_FAST_NATIVE); }
bool isFastConstructor() const { return !!(flags & JSFUN_FAST_NATIVE_CTOR); }
bool isHeavyweight() const { return JSFUN_HEAVYWEIGHT_TEST(flags); }
unsigned minArgs() const { return FUN_MINARGS(this); }
+ inline bool inStrictMode() const;
+
uintN countVars() const {
JS_ASSERT(FUN_INTERPRETED(this));
return u.i.nvars;
}
/* uint16 representation bounds number of call object dynamic slots. */
enum { MAX_ARGS_AND_VARS = 2 * ((1U << 16) - 1) };
@@ -273,34 +275,50 @@ JS_STATIC_ASSERT(sizeof(JSFunction) % JS
JS_FN(name, JS_DATA_TO_FUNC_PTR(JSNative, trcinfo), nargs, \
(flags) | JSFUN_FAST_NATIVE | JSFUN_STUB_GSOPS | JSFUN_TRCINFO)
#else
# define JS_TN(name,fastcall,nargs,flags,trcinfo) \
JS_FN(name, fastcall, nargs, flags)
#endif
/*
- * NB: the Arguments class is an uninitialized internal class that masquerades
- * (according to Object.prototype.toString.call(argsobj)) as "Object".
+ * NB: the Arguments classes are uninitialized internal classes that masquerade
+ * (according to Object.prototype.toString.call(arguments)) as "Arguments",
+ * while having Object.getPrototypeOf(arguments) === Object.prototype.
*
* WARNING (to alert embedders reading this private .h file): arguments objects
* are *not* thread-safe and should not be used concurrently -- they should be
* used by only one thread at a time, preferably by only one thread over their
* lifetime (a JS worker that migrates from one OS thread to another but shares
* nothing is ok).
*
* Yes, this is an incompatible change, which prefigures the impending move to
* single-threaded objects and GC heaps.
*/
extern js::Class js_ArgumentsClass;
+namespace js {
+extern Class StrictArgumentsClass;
+}
+
+inline bool
+JSObject::isNormalArguments() const
+{
+ return getClass() == &js_ArgumentsClass;
+}
+
+inline bool
+JSObject::isStrictArguments() const
+{
+ return getClass() == &js::StrictArgumentsClass;
+}
inline bool
JSObject::isArguments() const
{
- return getClass() == &js_ArgumentsClass;
+ return isNormalArguments() || isStrictArguments();
}
#define JS_ARGUMENT_OBJECT_ON_TRACE ((void *)0xa126)
extern JS_PUBLIC_DATA(js::Class) js_CallClass;
extern JS_PUBLIC_DATA(js::Class) js_FunctionClass;
extern js::Class js_DeclEnvClass;
extern const uint32 CALL_CLASS_FIXED_RESERVED_SLOTS;
@@ -344,43 +362,46 @@ IsFunctionObject(const js::Value &v, JSO
/*
* Macro to access the private slot of the function object after the slot is
* initialized.
*/
#define GET_FUNCTION_PRIVATE(cx, funobj) \
(JS_ASSERT((funobj)->isFunction()), \
(JSFunction *) (funobj)->getPrivate())
+extern JSFunction *
+js_NewFunction(JSContext *cx, JSObject *funobj, js::Native native, uintN nargs,
+ uintN flags, JSObject *parent, JSAtom *atom);
+
namespace js {
/*
* Return true if this is a compiler-created internal function accessed by
* its own object. Such a function object must not be accessible to script
* or embedding code.
*/
inline bool
IsInternalFunctionObject(JSObject *funobj)
{
JS_ASSERT(funobj->isFunction());
JSFunction *fun = (JSFunction *) funobj->getPrivate();
return funobj == fun && (fun->flags & JSFUN_LAMBDA) && !funobj->getParent();
}
+extern JSString *
+fun_toStringHelper(JSContext *cx, JSObject *obj, uintN indent);
+
} /* namespace js */
extern JSObject *
js_InitFunctionClass(JSContext *cx, JSObject *obj);
extern JSObject *
js_InitArgumentsClass(JSContext *cx, JSObject *obj);
-extern JSFunction *
-js_NewFunction(JSContext *cx, JSObject *funobj, js::Native native, uintN nargs,
- uintN flags, JSObject *parent, JSAtom *atom);
-
extern void
js_TraceFunction(JSTracer *trc, JSFunction *fun);
extern void
js_FinalizeFunction(JSContext *cx, JSFunction *fun);
extern JSObject * JS_FASTCALL
js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent,
@@ -462,16 +483,26 @@ extern JSBool
js_GetCallVarChecked(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
extern JSBool
js_GetArgsValue(JSContext *cx, JSStackFrame *fp, js::Value *vp);
extern JSBool
js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, js::Value *vp);
+/*
+ * Get the arguments object for the given frame. If the frame is strict mode
+ * code, its current arguments will be copied into the arguments object.
+ *
+ * NB: Callers *must* get the arguments object before any parameters are
+ * mutated when the frame is strict mode code! The emitter ensures this
+ * occurs for strict mode functions containing syntax which might mutate a
+ * named parameter by synthesizing an arguments access at the start of the
+ * function.
+ */
extern JSObject *
js_GetArgsObject(JSContext *cx, JSStackFrame *fp);
extern void
js_PutArgsObject(JSContext *cx, JSStackFrame *fp);
inline bool
js_IsNamedLambda(JSFunction *fun) { return (fun->flags & JSFUN_LAMBDA) && fun->atom; }
@@ -484,19 +515,18 @@ js_IsNamedLambda(JSFunction *fun) { retu
*
* The thread's stack is the limiting factor for this number. It is currently
* 2MB, which fits a little less than 2^19 arguments (once the stack frame,
* callstack, etc. are included). Pick a max args length that is a little less.
*/
const uint32 JS_ARGS_LENGTH_MAX = JS_BIT(19) - 1024;
/*
- * JSSLOT_ARGS_LENGTH stores ((argc << 1) | overwritten_flag) as int jsval.
- * Thus (JS_ARGS_LENGTH_MAX << 1) | 1 must fit JSVAL_INT_MAX. To assert that
- * we check first that the shift does not overflow uint32.
+ * JSSLOT_ARGS_LENGTH stores ((argc << 1) | overwritten_flag) as an Int32
+ * Value. Thus (JS_ARGS_LENGTH_MAX << 1) | 1 must be less than JSVAL_INT_MAX.
*/
JS_STATIC_ASSERT(JS_ARGS_LENGTH_MAX <= JS_BIT(30));
JS_STATIC_ASSERT(((JS_ARGS_LENGTH_MAX << 1) | 1) <= JSVAL_INT_MAX);
extern JSBool
js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp);
typedef enum JSLocalKind {
@@ -550,16 +580,9 @@ extern void
js_FreezeLocalNames(JSContext *cx, JSFunction *fun);
extern JSBool
js_fun_apply(JSContext *cx, uintN argc, js::Value *vp);
extern JSBool
js_fun_call(JSContext *cx, uintN argc, js::Value *vp);
-
-namespace js {
-
-extern JSString *
-fun_toStringHelper(JSContext *cx, JSObject *obj, uintN indent);
-
-}
#endif /* jsfun_h___ */
new file mode 100644
--- /dev/null
+++ b/js/src/jsfuninlines.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 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.
+ *
+ * The Initial Developer of the Original Code is
+ * the 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 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 jsfuninlines_h___
+#define jsfuninlines_h___
+
+#include "jsfun.h"
+#include "jsscript.h"
+
+inline bool
+JSFunction::inStrictMode() const
+{
+ return isInterpreted() && u.i.script->strictModeCode;
+}
+
+#endif /* jsfuninlines_h___ */
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1347,46 +1347,37 @@ js_DumpGCStats(JSRuntime *rt, FILE *fp)
continue;
fprintf(fp,
"%s (thing size %lu, %lu things per arena)\n",
GC_ARENA_NAMES[i], UL(thingSize), UL(thingsPerArena));
}
fprintf(fp, "\nTOTAL STATS:\n");
fprintf(fp, " bytes allocated: %lu\n", UL(rt->gcBytes));
fprintf(fp, " total GC arenas: %lu\n", UL(sumArenas));
+ fprintf(fp, " max allocated arenas: %lu\n", ULSTAT(maxnallarenas));
+ fprintf(fp, " max allocated chunks: %lu\n", ULSTAT(maxnchunks));
fprintf(fp, " total GC things: %lu\n", UL(sumThings));
fprintf(fp, " max total GC things: %lu\n", UL(sumMaxThings));
fprintf(fp, " GC cell utilization: %.1f%%\n",
PERCENT(sumThingSize, sumArenaCapacity));
fprintf(fp, " average cell utilization: %.1f%%\n",
PERCENT(sumTotalThingSize, sumTotalArenaCapacity));
fprintf(fp, "allocation retries after GC: %lu\n", UL(sumRetry));
fprintf(fp, " alloc attempts: %lu\n", UL(sumAlloc));
fprintf(fp, " alloc without locks: %lu (%.1f%%)\n",
UL(sumLocalAlloc), PERCENT(sumLocalAlloc, sumAlloc));
fprintf(fp, " allocation failures: %lu\n", UL(sumFail));
- fprintf(fp, " things born locked: %lu\n", ULSTAT(lockborn));
fprintf(fp, " valid lock calls: %lu\n", ULSTAT(lock));
fprintf(fp, " valid unlock calls: %lu\n", ULSTAT(unlock));
- fprintf(fp, " mark recursion depth: %lu\n", ULSTAT(depth));
- fprintf(fp, " maximum mark recursion: %lu\n", ULSTAT(maxdepth));
- fprintf(fp, " mark C recursion depth: %lu\n", ULSTAT(cdepth));
- fprintf(fp, " maximum mark C recursion: %lu\n", ULSTAT(maxcdepth));
fprintf(fp, " delayed tracing calls: %lu\n", ULSTAT(unmarked));
#ifdef DEBUG
fprintf(fp, " max trace later count: %lu\n", ULSTAT(maxunmarked));
#endif
fprintf(fp, "potentially useful GC calls: %lu\n", ULSTAT(poke));
fprintf(fp, " thing arenas freed so far: %lu\n", ULSTAT(afree));
- fprintf(fp, " stack segments scanned: %lu\n", ULSTAT(stackseg));
- fprintf(fp, "stack segment slots scanned: %lu\n", ULSTAT(segslots));
- fprintf(fp, "reachable closeable objects: %lu\n", ULSTAT(nclose));
- fprintf(fp, " max reachable closeable: %lu\n", ULSTAT(maxnclose));
- fprintf(fp, " scheduled close hooks: %lu\n", ULSTAT(closelater));
- fprintf(fp, " max scheduled close hooks: %lu\n", ULSTAT(maxcloselater));
rt->gcStats.conservative.dump(fp);
#undef UL
#undef ULSTAT
#undef PERCENT
}
#endif
@@ -1642,20 +1633,18 @@ RefillFinalizableFreeList(JSContext *cx,
JS_ASSERT(!JS_THREAD_DATA(cx)->gcFreeLists.finalizables[thingKind]);
JSRuntime *rt = cx->runtime;
JSGCArenaList *arenaList;
JSGCArena *a;
{
AutoLockGC lock(rt);
JS_ASSERT(!rt->gcRunning);
- if (rt->gcRunning) {
- METER(rt->gcStats.finalfail++);
+ if (rt->gcRunning)
return NULL;
- }
bool canGC = !JS_ON_TRACE(cx) && !JS_THREAD_DATA(cx)->waiveGCQuota;
bool doGC = canGC && IsGCThresholdReached(rt);
arenaList = &rt->gcArenaList[thingKind];
for (;;) {
if (doGC) {
LastDitchGC(cx);
METER(cx->runtime->gcStats.arenaStats[thingKind].retry++);
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -551,38 +551,26 @@ struct JSGCArenaStats {
uint32 newarenas; /* new arenas allocated before the last GC */
uint32 livearenas; /* number of live arenas after the last GC */
uint32 maxarenas; /* maximum of allocated arenas */
uint32 totalarenas; /* total number of arenas with live things that
GC scanned so far */
};
struct JSGCStats {
- uint32 finalfail; /* finalizer calls allocator failures */
- uint32 lockborn; /* things born locked */
uint32 lock; /* valid lock calls */
uint32 unlock; /* valid unlock calls */
- uint32 depth; /* mark tail recursion depth */
- uint32 maxdepth; /* maximum mark tail recursion depth */
- uint32 cdepth; /* mark recursion depth of C functions */
- uint32 maxcdepth; /* maximum mark recursion depth of C functions */
uint32 unmarked; /* number of times marking of GC thing's children were
delayed due to a low C stack */
#ifdef DEBUG
uint32 maxunmarked;/* maximum number of things with children to mark
later */
#endif
uint32 poke; /* number of potentially useful GC calls */
uint32 afree; /* thing arenas freed so far */
- uint32 stackseg; /* total extraordinary stack segments scanned */
- uint32 segslots; /* total stack segment value slots scanned */
- uint32 nclose; /* number of objects with close hooks */
- uint32 maxnclose; /* max number of objects with close hooks */
- uint32 closelater; /* number of close hooks scheduled to run */
- uint32 maxcloselater; /* max number of close hooks scheduled to run */
uint32 nallarenas; /* number of all allocated arenas */
uint32 maxnallarenas; /* maximum number of all allocated arenas */
uint32 nchunks; /* number of allocated chunks */
uint32 maxnchunks; /* maximum number of allocated chunks */
JSGCArenaStats arenaStats[FINALIZE_LIMIT];
js::ConservativeGCStats conservative;
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -4142,17 +4142,17 @@ BEGIN_CASE(JSOP_LENGTH)
if (vp->isString()) {
vp->setInt32(vp->toString()->length());
} else if (vp->isObject()) {
JSObject *obj = &vp->toObject();
if (obj->isArray()) {
jsuint length = obj->getArrayLength();
regs.sp[-1].setNumber(length);
} else if (obj->isArguments() && !obj->isArgsLengthOverridden()) {
- uint32 length = obj->getArgsLength();
+ uint32 length = obj->getArgsInitialLength();
JS_ASSERT(length < INT32_MAX);
regs.sp[-1].setInt32(int32_t(length));
} else {
i = -2;
goto do_getprop_with_lval;
}
} else {
i = -2;
@@ -4513,17 +4513,17 @@ BEGIN_CASE(JSOP_GETELEM)
goto end_getelem;
/* Reload retval from the stack in the rare hole case. */
copyFrom = ®s.sp[-1];
}
} else if (obj->isArguments()) {
uint32 arg = uint32(i);
- if (arg < obj->getArgsLength()) {
+ if (arg < obj->getArgsInitialLength()) {
JSStackFrame *afp = (JSStackFrame *) obj->getPrivate();
if (afp) {
copyFrom = &afp->argv[arg];
goto end_getelem;
}
copyFrom = obj->addressOfArgsElement(arg);
if (!copyFrom->isMagic())
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -1019,20 +1019,23 @@ obj_eval(JSContext *cx, uintN argc, Valu
* check.
*/
Value *argv = JS_ARGV(cx, vp);
JSObject *obj = ComputeThisFromVp(cx, vp);
if (!obj)
return JS_FALSE;
obj = obj->wrappedObject(cx);
+ OBJ_TO_INNER_OBJECT(cx, obj);
+ if (!obj)
+ return JS_FALSE;
+
/*
- * Ban all indirect uses of eval (global.foo = eval; global.foo(...)) and
- * calls that attempt to use a non-global object as the "with" object in
- * the former indirect case.
+ * Ban indirect uses of eval (nonglobal.eval = eval; nonglobal.eval(....))
+ * that attempt to use a non-global object as the scope object.
*/
{
JSObject *parent = obj->getParent();
if (indirectCall || parent) {
uintN flags = parent
? JSREPORT_ERROR
: JSREPORT_STRICT | JSREPORT_WARNING;
if (!JS_ReportErrorFlagsAndNumber(cx, flags, js_GetErrorMessage, NULL,
@@ -1081,20 +1084,16 @@ obj_eval(JSContext *cx, uintN argc, Valu
* If we see an indirect call, then run eval in the global scope. We do
* this so the compiler can make assumptions about what bindings may or
* may not exist in the current frame if it doesn't see 'eval'.
*/
if (indirectCall) {
/* Pretend that we're top level. */
staticLevel = 0;
- OBJ_TO_INNER_OBJECT(cx, obj);
- if (!obj)
- return JS_FALSE;
-
if (!js_CheckPrincipalsAccess(cx, obj,
JS_StackFramePrincipals(cx, caller),
cx->runtime->atomState.evalAtom)) {
return JS_FALSE;
}
/* NB: We know inner is a global object here. */
JS_ASSERT(!obj->getParent());
@@ -6043,19 +6042,19 @@ JSObject::wrappedObject(JSContext *cx) c
if (JSObjectOp op = getClass()->ext.wrappedObject) {
if (JSObject *obj = op(cx, const_cast<JSObject *>(this)))
return obj;
}
return const_cast<JSObject *>(this);
}
JSObject *
-JSObject::getGlobal()
-{
- JSObject *obj = this;
+JSObject::getGlobal() const
+{
+ JSObject *obj = const_cast<JSObject *>(this);
while (JSObject *parent = obj->getParent())
obj = parent;
return obj;
}
JSBool
js_ReportGetterOnlyAssignment(JSContext *cx)
{
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -416,17 +416,17 @@ struct JSObject {
#ifdef DEBUG
for (JSObject *obj = newParent; obj; obj = obj->getParent())
JS_ASSERT(obj != this);
#endif
setDelegateNullSafe(newParent);
parent = newParent;
}
- JSObject *getGlobal();
+ JSObject *getGlobal() const;
void *getPrivate() const {
JS_ASSERT(getClass()->flags & JSCLASS_HAS_PRIVATE);
void *priv = fslots[JSSLOT_PRIVATE].toPrivate();
return priv;
}
void setPrivate(void *data) {
@@ -495,56 +495,82 @@ struct JSObject {
private:
/*
* Reserved slot structure for Arguments objects:
*
* JSSLOT_PRIVATE - the corresponding frame until the frame exits.
* JSSLOT_ARGS_LENGTH - the number of actual arguments and a flag
* indicating whether arguments.length was
- * overwritten.
+ * overwritten. This slot is not used to represent
+ * arguments.length after that property has been
+ * assigned, even if the new value is integral: it's
+ * always the original length.
* JSSLOT_ARGS_CALLEE - the arguments.callee value or JSVAL_HOLE if that
* was overwritten.
*
* Argument index i is stored in dslots[i], accessible via
* {get,set}ArgsElement().
*/
static const uint32 JSSLOT_ARGS_CALLEE = JSSLOT_PRIVATE + 2;
public:
/* Number of extra fixed slots besides JSSLOT_PRIVATE. */
static const uint32 JSSLOT_ARGS_LENGTH = JSSLOT_PRIVATE + 1;
static const uint32 ARGS_FIXED_RESERVED_SLOTS = 2;
- inline uint32 getArgsLength() const;
+ /* Lower-order bit stolen from the length slot. */
+ static const uint32 ARGS_LENGTH_OVERRIDDEN_BIT = 0x1;
+ static const uint32 ARGS_PACKED_BITS_COUNT = 1;
+
+ /*
+ * Set the initial length of the arguments, and mark it as not overridden.
+ */
inline void setArgsLength(uint32 argc);
+
+ /*
+ * Return the initial length of the arguments. This may differ from the
+ * current value of arguments.length!
+ */
+ inline uint32 getArgsInitialLength() const;
+
inline void setArgsLengthOverridden();
inline bool isArgsLengthOverridden() const;
inline const js::Value &getArgsCallee() const;
inline void setArgsCallee(const js::Value &callee);
inline const js::Value &getArgsElement(uint32 i) const;
inline js::Value *addressOfArgsElement(uint32 i) const;
inline void setArgsElement(uint32 i, const js::Value &v);
/*
* Date-specific getters and setters.
*/
- private:
- // The second slot caches the local time; it's initialized to NaN.
- static const uint32 JSSLOT_DATE_UTC_TIME = JSSLOT_PRIVATE;
- static const uint32 JSSLOT_DATE_LOCAL_TIME = JSSLOT_PRIVATE + 1;
+ public:
+ static const uint32 JSSLOT_DATE_UTC_TIME = JSSLOT_PRIVATE;
+
+ /*
+ * Cached slots holding local properties of the date.
+ * These are undefined until the first actual lookup occurs
+ * and are reset to undefined whenever the date's time is modified.
+ */
+ static const uint32 JSSLOT_DATE_COMPONENTS_START = JSSLOT_PRIVATE + 1;
- public:
- static const uint32 DATE_FIXED_RESERVED_SLOTS = 2;
+ static const uint32 JSSLOT_DATE_LOCAL_TIME = JSSLOT_PRIVATE + 1;
+ static const uint32 JSSLOT_DATE_LOCAL_YEAR = JSSLOT_PRIVATE + 2;
+ static const uint32 JSSLOT_DATE_LOCAL_MONTH = JSSLOT_PRIVATE + 3;
+ static const uint32 JSSLOT_DATE_LOCAL_DATE = JSSLOT_PRIVATE + 4;
+ static const uint32 JSSLOT_DATE_LOCAL_DAY = JSSLOT_PRIVATE + 5;
+ static const uint32 JSSLOT_DATE_LOCAL_HOURS = JSSLOT_PRIVATE + 6;
+ static const uint32 JSSLOT_DATE_LOCAL_MINUTES = JSSLOT_PRIVATE + 7;
+ static const uint32 JSSLOT_DATE_LOCAL_SECONDS = JSSLOT_PRIVATE + 8;
- inline const js::Value &getDateLocalTime() const;
- inline void setDateLocalTime(const js::Value &pthis);
+ static const uint32 DATE_CLASS_RESERVED_SLOTS = 9;
inline const js::Value &getDateUTCTime() const;
inline void setDateUTCTime(const js::Value &pthis);
/*
* Function-specific getters and setters.
*/
@@ -555,16 +581,18 @@ struct JSObject {
static const uint32 JSSLOT_FUN_METHOD_OBJ = JSSLOT_PRIVATE + 2;
public:
static const uint32 FUN_FIXED_RESERVED_SLOTS = 2;
inline bool hasMethodObj(const JSObject& obj) const;
inline void setMethodObj(JSObject& obj);
+ inline JSFunction *getFunctionPrivate() const;
+
/*
* RegExp-specific getters and setters.
*/
private:
static const uint32 JSSLOT_REGEXP_LAST_INDEX = JSSLOT_PRIVATE + 1;
public:
@@ -726,21 +754,25 @@ struct JSObject {
}
static bool thisObject(JSContext *cx, const js::Value &v, js::Value *vp);
inline void dropProperty(JSContext *cx, JSProperty *prop);
JS_FRIEND_API(JSCompartment *) getCompartment(JSContext *cx);
+ inline JSObject *getThrowTypeError() const;
+
void swap(JSObject *obj);
inline bool canHaveMethodBarrier() const;
inline bool isArguments() const;
+ inline bool isNormalArguments() const;
+ inline bool isStrictArguments() const;
inline bool isArray() const;
inline bool isDenseArray() const;
inline bool isSlowArray() const;
inline bool isNumber() const;
inline bool isBoolean() const;
inline bool isString() const;
inline bool isPrimitive() const;
inline bool isDate() const;
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -215,42 +215,43 @@ JSObject::voidDenseOnlyArraySlots()
fslots[JSSLOT_DENSE_ARRAY_CAPACITY].setUndefined();
}
inline void
JSObject::setArgsLength(uint32 argc)
{
JS_ASSERT(isArguments());
JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
- fslots[JSSLOT_ARGS_LENGTH].setInt32(argc << 1);
+ JS_ASSERT(UINT32_MAX > (uint64(argc) << ARGS_PACKED_BITS_COUNT));
+ fslots[JSSLOT_ARGS_LENGTH].setInt32(argc << ARGS_PACKED_BITS_COUNT);
JS_ASSERT(!isArgsLengthOverridden());
}
inline uint32
-JSObject::getArgsLength() const
+JSObject::getArgsInitialLength() const
{
JS_ASSERT(isArguments());
- uint32 argc = uint32(fslots[JSSLOT_ARGS_LENGTH].toInt32()) >> 1;
+ uint32 argc = uint32(fslots[JSSLOT_ARGS_LENGTH].toInt32()) >> ARGS_PACKED_BITS_COUNT;
JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
return argc;
}
inline void
JSObject::setArgsLengthOverridden()
{
JS_ASSERT(isArguments());
- fslots[JSSLOT_ARGS_LENGTH].getInt32Ref() |= 1;
+ fslots[JSSLOT_ARGS_LENGTH].getInt32Ref() |= ARGS_LENGTH_OVERRIDDEN_BIT;
}
inline bool
JSObject::isArgsLengthOverridden() const
{
JS_ASSERT(isArguments());
const js::Value &v = fslots[JSSLOT_ARGS_LENGTH];
- return (v.toInt32() & 1) != 0;
+ return v.toInt32() & ARGS_LENGTH_OVERRIDDEN_BIT;
}
inline const js::Value &
JSObject::getArgsCallee() const
{
JS_ASSERT(isArguments());
return fslots[JSSLOT_ARGS_CALLEE];
}
@@ -282,30 +283,16 @@ inline void
JSObject::setArgsElement(uint32 i, const js::Value &v)
{
JS_ASSERT(isArguments());
JS_ASSERT(i < numSlots() - JS_INITIAL_NSLOTS);
dslots[i] = v;
}
inline const js::Value &
-JSObject::getDateLocalTime() const
-{
- JS_ASSERT(isDate());
- return fslots[JSSLOT_DATE_LOCAL_TIME];
-}
-
-inline void
-JSObject::setDateLocalTime(const js::Value &time)
-{
- JS_ASSERT(isDate());
- fslots[JSSLOT_DATE_LOCAL_TIME] = time;
-}
-
-inline const js::Value &
JSObject::getDateUTCTime() const
{
JS_ASSERT(isDate());
return fslots[JSSLOT_DATE_UTC_TIME];
}
inline void
JSObject::setDateUTCTime(const js::Value &time)
@@ -322,16 +309,23 @@ JSObject::hasMethodObj(const JSObject& o
}
inline void
JSObject::setMethodObj(JSObject& obj)
{
fslots[JSSLOT_FUN_METHOD_OBJ].setObject(obj);
}
+inline JSFunction *
+JSObject::getFunctionPrivate() const
+{
+ JS_ASSERT(isFunction());
+ return reinterpret_cast<JSFunction *>(getPrivate());
+}
+
inline NativeIterator *
JSObject::getNativeIterator() const
{
return (NativeIterator *) getPrivate();
}
inline void
JSObject::setNativeIterator(NativeIterator *ni)
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -235,26 +235,24 @@ public:
Class *clasp = obj.getClass();
if (clasp == &js_NumberClass || clasp == &js_StringClass)
*gapValue.addr() = obj.getPrimitiveThis();
}
if (gapValue.value().isString()) {
if (!js_ValueToCharBuffer(cx, gapValue.value(), gap))
return false;
- if (cb.length() > 10)
- cb.resize(10);
- }
-
- if (gapValue.value().isNumber()) {
+ if (gap.length() > 10)
+ gap.resize(10);
+ } else if (gapValue.value().isNumber()) {
jsdouble d = gapValue.value().isInt32()
? gapValue.value().toInt32()
: js_DoubleToInteger(gapValue.value().toDouble());
d = JS_MIN(10, d);
- if (d >= 1 && !cb.appendN(' ', uint32(d)))
+ if (d >= 1 && !gap.appendN(' ', uint32(d)))
return false;
}
return true;
}
bool initializeStack() {
return objectStack.init(16);
@@ -341,33 +339,33 @@ JO(JSContext *cx, Value *vp, StringifyCo
// if the replacer is an array, we use the keys from it
if (scx->replacer && JS_IsArrayObject(cx, scx->replacer)) {
usingWhitelist = true;
vec[2].setObject(*scx->replacer);
keySource = &vec[2];
}
JSBool memberWritten = JS_FALSE;
- AutoIdArray ida(cx, JS_Enumerate(cx, &keySource->toObject()));
- if (!ida)
+ AutoIdVector props(cx);
+ if (!GetPropertyNames(cx, &keySource->toObject(), JSITER_OWNONLY, props))
return JS_FALSE;
- for (jsint i = 0, len = ida.length(); i < len; i++) {
+ for (size_t i = 0, len = props.length(); i < len; i++) {
outputValue.setUndefined();
if (!usingWhitelist) {
- if (!js_ValueToStringId(cx, IdToValue(ida[i]), &id))
+ if (!js_ValueToStringId(cx, IdToValue(props[i]), &id))
return JS_FALSE;
} else {
// skip non-index properties
jsuint index = 0;
- if (!js_IdIsIndex(ida[i], &index))
+ if (!js_IdIsIndex(props[i], &index))
continue;
- if (!scx->replacer->getProperty(cx, ida[i], &whitelistElement))
+ if (!scx->replacer->getProperty(cx, props[i], &whitelistElement))
return JS_FALSE;
if (!js_ValueToStringId(cx, whitelistElement, &id))
return JS_FALSE;
}
// We should have a string id by this point. Either from
// JS_Enumerate's id array, or by converting an element
@@ -404,16 +402,17 @@ JO(JSContext *cx, Value *vp, StringifyCo
if (!s)
return JS_FALSE;
const jschar *chars;
size_t length;
s->getCharsAndLength(chars, length);
if (!write_string(cx, scx->cb, chars, length) ||
!scx->cb.append(':') ||
+ !(scx->gap.empty() || scx->cb.append(' ')) ||
!Str(cx, id, obj, scx, &outputValue, true)) {
return JS_FALSE;
}
}
if (memberWritten && !WriteIndent(cx, scx, scx->depth - 1))
return JS_FALSE;
@@ -613,22 +612,22 @@ Walk(JSContext *cx, jsid id, JSObject *h
if (!Walk(cx, index, obj, reviver, propValue.addr()))
return false;
if (!obj->defineProperty(cx, index, propValue.value(), NULL, NULL, JSPROP_ENUMERATE))
return false;
}
} else {
- AutoIdArray ida(cx, JS_Enumerate(cx, obj));
- if (!ida)
+ AutoIdVector props(cx);
+ if (!GetPropertyNames(cx, obj, JSITER_OWNONLY, props))
return false;
- for (jsint i = 0, len = ida.length(); i < len; i++) {
- jsid idName = ida[i];
+ for (size_t i = 0, len = props.length(); i < len; i++) {
+ jsid idName = props[i];
if (!Walk(cx, idName, obj, reviver, propValue.addr()))
return false;
if (propValue.value().isUndefined()) {
if (!js_DeleteProperty(cx, obj, idName, propValue.addr()))
return false;
} else {
if (!obj->defineProperty(cx, idName, propValue.value(), NULL, NULL,
JSPROP_ENUMERATE)) {
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -5431,21 +5431,25 @@ SimulateOp(JSContext *cx, JSScript *scri
#undef LOCAL_ASSERT
#define LOCAL_ASSERT(expr) LOCAL_ASSERT_CUSTOM(expr, goto failure);
static intN
SimulateImacroCFG(JSContext *cx, JSScript *script,
uintN pcdepth, jsbytecode *pc, jsbytecode *target,
jsbytecode **pcstack)
{
- size_t nbytes = StackDepth(script) * sizeof *pcstack;
- jsbytecode** tmp_pcstack = (jsbytecode **) cx->malloc(nbytes);
- if (!tmp_pcstack)
- return -1;
- memcpy(tmp_pcstack, pcstack, nbytes);
+ size_t nbytes;
+ jsbytecode** tmp_pcstack = NULL;
+ if (pcstack) {
+ nbytes = StackDepth(script) * sizeof *pcstack;
+ tmp_pcstack = (jsbytecode **) cx->malloc(nbytes);
+ if (!tmp_pcstack)
+ return -1;
+ memcpy(tmp_pcstack, pcstack, nbytes);
+ }
ptrdiff_t oplen;
for (; pc < target; pc += oplen) {
JSOp op = js_GetOpcode(cx, script, pc);
const JSCodeSpec *cs = &js_CodeSpec[op];
oplen = cs->length;
if (oplen < 0)
oplen = js_GetVariableBytecodeLength(pc);
@@ -5471,22 +5475,25 @@ SimulateImacroCFG(JSContext *cx, JSScrip
}
if (pc > target)
goto failure;
LOCAL_ASSERT(pc == target);
success:
- memcpy(pcstack, tmp_pcstack, nbytes);
- cx->free(tmp_pcstack);
+ if (tmp_pcstack) {
+ memcpy(pcstack, tmp_pcstack, nbytes);
+ cx->free(tmp_pcstack);
+ }
return pcdepth;
failure:
- cx->free(tmp_pcstack);
+ if (tmp_pcstack)
+ cx->free(tmp_pcstack);
return -1;
}
#undef LOCAL_ASSERT
#define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, -1);
static intN
ReconstructImacroPCStack(JSContext *cx, JSScript *script,
--- a/js/src/jsparse.cpp
+++ b/js/src/jsparse.cpp
@@ -1186,18 +1186,17 @@ CheckFinalReturn(JSContext *cx, JSTreeCo
/*
* Check that it is permitted to assign to lhs. Strict mode code may not
* assign to 'eval' or 'arguments'.
*/
bool
CheckStrictAssignment(JSContext *cx, JSTreeContext *tc, JSParseNode *lhs)
{
- if (tc->needStrictChecks() &&
- lhs->pn_type == TOK_NAME) {
+ if (tc->needStrictChecks() && lhs->pn_type == TOK_NAME) {
JSAtom *atom = lhs->pn_atom;
JSAtomState *atomState = &cx->runtime->atomState;
if (atom == atomState->evalAtom || atom == atomState->argumentsAtom) {
const char *name = js_AtomToPrintableString(cx, atom);
if (!name ||
!ReportStrictModeError(cx, TS(tc->parser), tc, lhs, JSMSG_DEPRECATED_ASSIGN,
name)) {
return false;
@@ -1217,19 +1216,19 @@ bool
CheckStrictBinding(JSContext *cx, JSTreeContext *tc, JSAtom *atom, JSParseNode *pn)
{
if (!tc->needStrictChecks())
return true;
JSAtomState *atomState = &cx->runtime->atomState;
if (atom == atomState->evalAtom || atom == atomState->argumentsAtom) {
const char *name = js_AtomToPrintableString(cx, atom);
- if (name)
- ReportStrictModeError(cx, TS(tc->parser), tc, pn, JSMSG_BAD_BINDING, name);
- return false;
+ if (!name)
+ return false;
+ return ReportStrictModeError(cx, TS(tc->parser), tc, pn, JSMSG_BAD_BINDING, name);
}
return true;
}
/*
* In strict mode code, all formal parameter names must be distinct. If fun's
* formals are legit given fun's strictness level, return true. Otherwise,
* report an error and return false. Use pn for error position reporting,
@@ -2524,17 +2523,17 @@ LeaveFunction(JSParseNode *fn, JSTreeCon
}
JSAtomListElement *outer_ale = tc->decls.lookup(atom);
/*
* Make sure to deoptimize lexical dependencies that are polluted
* by eval or with, to safely statically bind globals (see bug 561923).
*/
- if ((funtc->flags & TCF_FUN_USES_EVAL) ||
+ if ((funtc->flags & TCF_FUN_CALLS_EVAL) ||
(outer_ale && tc->innermostWith &&
ALE_DEFN(outer_ale)->pn_pos < tc->innermostWith->pn_pos)) {
DeoptimizeUsesWithin(dn, fn->pn_pos);
}
JSDefinition *outer_dn;
if (!outer_ale)
@@ -2603,80 +2602,198 @@ LeaveFunction(JSParseNode *fn, JSTreeCon
funtc->lexdeps.remove(tc->parser, funAtom);
fn->pn_body->pn_names = funtc->lexdeps;
fn->pn_body->pn_tree = body;
}
funtc->lexdeps.clear();
}
+ /*
+ * Check whether any parameters have been assigned within this function.
+ * In strict mode parameters do not alias arguments[i], and to make the
+ * arguments object reflect initial parameter values prior to any mutation
+ * we create it eagerly whenever parameters are (or might, in the case of
+ * calls to eval) be assigned.
+ */
+ if (funtc->inStrictMode() && funbox->object->getFunctionPrivate()->nargs > 0) {
+ JSAtomListIterator iter(&funtc->decls);
+ JSAtomListElement *ale;
+
+ while ((ale = iter()) != NULL) {
+ JSDefinition *dn = ALE_DEFN(ale);
+ if (dn->kind() == JSDefinition::ARG && dn->isAssigned()) {
+ funbox->tcflags |= TCF_FUN_MUTATES_PARAMETER;
+ break;
+ }
+ }
+ }
+
return true;
}
static bool
DefineGlobal(JSParseNode *pn, JSCodeGenerator *cg, JSAtom *atom);
-JSParseNode *
-Parser::functionDef(uintN lambda, bool namePermitted)
-{
- JSParseNode *pn, *body, *result;
- TokenKind tt;
- JSAtomListElement *ale;
+bool
+Parser::functionArguments(JSTreeContext &funtc, JSFunctionBox *funbox, JSFunction *fun,
+ JSParseNode **listp)
+{
+ if (tokenStream.getToken() != TOK_LP) {
+ reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_PAREN_BEFORE_FORMAL);
+ return false;
+ }
+
+ if (!tokenStream.matchToken(TOK_RP)) {
+#if JS_HAS_DESTRUCTURING
+ JSAtom *duplicatedArg = NULL;
+ bool destructuringArg = false;
+ JSParseNode *list = NULL;
+#endif
+ do {
+ switch (TokenKind tt = tokenStream.getToken()) {
#if JS_HAS_DESTRUCTURING
- JSParseNode *item, *list = NULL;
- bool destructuringArg = false;
- JSAtom *duplicatedArg = NULL;
+ case TOK_LB:
+ case TOK_LC:
+ {
+ /* See comment below in the TOK_NAME case. */
+ if (duplicatedArg)
+ goto report_dup_and_destructuring;
+ destructuringArg = true;
+
+ /*
+ * A destructuring formal parameter turns into one or more
+ * local variables initialized from properties of a single
+ * anonymous positional parameter, so here we must tweak our
+ * binder and its data.
+ */
+ BindData data;
+ data.pn = NULL;
+ data.op = JSOP_DEFVAR;
+ data.binder = BindDestructuringArg;
+ JSParseNode *lhs = destructuringExpr(&data, tt);
+ if (!lhs)
+ return false;
+
+ /*
+ * Adjust fun->nargs to count the single anonymous positional
+ * parameter that is to be destructured.
+ */
+ jsint slot = fun->nargs;
+ if (!js_AddLocal(context, fun, NULL, JSLOCAL_ARG))
+ return false;
+
+ /*
+ * Synthesize a destructuring assignment from the single
+ * anonymous positional parameter into the destructuring
+ * left-hand-side expression and accumulate it in list.
+ */
+ JSParseNode *rhs = NameNode::create(context->runtime->atomState.emptyAtom, &funtc);
+ if (!rhs)
+ return false;
+ rhs->pn_type = TOK_NAME;
+ rhs->pn_op = JSOP_GETARG;
+ rhs->pn_cookie.set(funtc.staticLevel, uint16(slot));
+ rhs->pn_dflags |= PND_BOUND;
+
+ JSParseNode *item = JSParseNode::newBinaryOrAppend(TOK_ASSIGN, JSOP_NOP, lhs, rhs, &funtc);
+ if (!item)
+ return false;
+ if (!list) {
+ list = ListNode::create(&funtc);
+ if (!list)
+ return false;
+ list->pn_type = TOK_COMMA;
+ list->makeEmpty();
+ *listp = list;
+ }
+ list->append(item);
+ break;
+ }
+#endif /* JS_HAS_DESTRUCTURING */
+
+ 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
+ * 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.
+ */
+ if (js_LookupLocal(context, fun, atom, NULL) != JSLOCAL_NONE) {
+ duplicatedArg = atom;
+ if (destructuringArg)
+ goto report_dup_and_destructuring;
+ }
#endif
-
- /*
- * Save the current op for later so we can tag the created function as a
- * getter/setter if necessary.
- */
- JSOp op = tokenStream.currentToken().t_op;
-
+ if (!js_AddLocal(context, fun, atom, JSLOCAL_ARG))
+ return false;
+ break;
+ }
+
+ default:
+ reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_MISSING_FORMAL);
+ /* FALL THROUGH */
+ case TOK_ERROR:
+ return false;
+
+#if JS_HAS_DESTRUCTURING
+ report_dup_and_destructuring:
+ JSDefinition *dn = ALE_DEFN(funtc.decls.lookup(duplicatedArg));
+ reportErrorNumber(dn, JSREPORT_ERROR, JSMSG_DESTRUCT_DUP_ARG);
+ return false;
+#endif
+ }
+ } while (tokenStream.matchToken(TOK_COMMA));
+
+ if (tokenStream.getToken() != TOK_RP) {
+ reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_PAREN_AFTER_FORMAL);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+JSParseNode *
+Parser::functionDef(JSAtom *funAtom, FunctionType type, uintN lambda)
+{
/* Make a TOK_FUNCTION node. */
- pn = FunctionNode::create(tc);
+ tokenStream.mungeCurrentToken(TOK_FUNCTION, JSOP_NOP);
+ JSParseNode *pn = FunctionNode::create(tc);
if (!pn)
return NULL;
pn->pn_body = NULL;
pn->pn_cookie.makeFree();
/*
* If a lambda, give up on JSOP_{GET,CALL}UPVAR usage unless this function
* is immediately applied (we clear PND_FUNARG if so -- see memberExpr).
*
* Also treat function sub-statements (non-lambda, non-top-level functions)
* as escaping funargs, since we can't statically analyze their definitions
* and uses.
*/
bool topLevel = tc->atTopLevel();
pn->pn_dflags = (lambda || !topLevel) ? PND_FUNARG : 0;
- /* Scan the optional function name into funAtom. */
- JSAtom *funAtom = NULL;
- if (namePermitted) {
- tt = tokenStream.getToken(TSF_KEYWORD_IS_NAME);
- if (tt == TOK_NAME) {
- funAtom = tokenStream.currentToken().t_atom;
- } else {
- if (lambda == 0 && (context->options & JSOPTION_ANONFUNFIX)) {
- reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
- return NULL;
- }
- tokenStream.ungetToken();
- }
- }
-
/*
* Record names for function statements in tc->decls so we know when to
* avoid optimizing variable references that might name a function.
*/
if (lambda == 0 && funAtom) {
- ale = tc->decls.lookup(funAtom);
- if (ale) {
+ if (JSAtomListElement *ale = tc->decls.lookup(funAtom)) {
JSDefinition *dn = ALE_DEFN(ale);
JSDefinition::Kind dn_kind = dn->kind();
JS_ASSERT(!dn->pn_used);
JS_ASSERT(dn->pn_defn);
if (JS_HAS_STRICT_OPTION(context) || dn_kind == JSDefinition::CONST) {
const char *name = js_AtomToPrintableString(context, funAtom);
@@ -2776,143 +2893,43 @@ Parser::functionDef(uintN lambda, bool n
JSTreeContext funtc(tc->parser);
JSFunctionBox *funbox = EnterFunction(pn, &funtc, funAtom, lambda);
if (!funbox)
return NULL;
JSFunction *fun = (JSFunction *) funbox->object;
- if (op != JSOP_NOP)
- fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER;
-
/* Now parse formal argument list and compute fun->nargs. */
- MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL);
- if (!tokenStream.matchToken(TOK_RP)) {
- do {
- tt = tokenStream.getToken();
- switch (tt) {
-#if JS_HAS_DESTRUCTURING
- case TOK_LB:
- case TOK_LC:
- {
- BindData data;
- JSParseNode *lhs, *rhs;
- jsint slot;
-
- /* See comment below in the TOK_NAME case. */
- if (duplicatedArg)
- goto report_dup_and_destructuring;
- destructuringArg = true;
-
- /*
- * A destructuring formal parameter turns into one or more
- * local variables initialized from properties of a single
- * anonymous positional parameter, so here we must tweak our
- * binder and its data.
- */
- data.pn = NULL;
- data.op = JSOP_DEFVAR;
- data.binder = BindDestructuringArg;
- lhs = destructuringExpr(&data, tt);
- if (!lhs)
- return NULL;
-
- /*
- * Adjust fun->nargs to count the single anonymous positional
- * parameter that is to be destructured.
- */
- slot = fun->nargs;
- if (!js_AddLocal(context, fun, NULL, JSLOCAL_ARG))
- return NULL;
-
- /*
- * Synthesize a destructuring assignment from the single
- * anonymous positional parameter into the destructuring
- * left-hand-side expression and accumulate it in list.
- */
- rhs = NameNode::create(context->runtime->atomState.emptyAtom, &funtc);
- if (!rhs)
- return NULL;
- rhs->pn_type = TOK_NAME;
- rhs->pn_op = JSOP_GETARG;
- rhs->pn_cookie.set(funtc.staticLevel, uint16(slot));
- rhs->pn_dflags |= PND_BOUND;
-
- item = JSParseNode::newBinaryOrAppend(TOK_ASSIGN, JSOP_NOP, lhs, rhs, &funtc);
- if (!item)
- return NULL;
- if (!list) {
- list = ListNode::create(&funtc);
- if (!list)
- return NULL;
- list->pn_type = TOK_COMMA;
- list->makeEmpty();
- }
- list->append(item);
- break;
- }
-#endif /* JS_HAS_DESTRUCTURING */
-
- case TOK_NAME:
- {
- JSAtom *atom = tokenStream.currentToken().t_atom;
- if (!DefineArg(pn, atom, fun->nargs, &funtc))
- return NULL;
-#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
- * 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.
- */
- if (js_LookupLocal(context, fun, atom, NULL) != JSLOCAL_NONE) {
- duplicatedArg = atom;
- if (destructuringArg)
- goto report_dup_and_destructuring;
- }
-#endif
- if (!js_AddLocal(context, fun, atom, JSLOCAL_ARG))
- return NULL;
- break;
- }
-
- default:
- reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_MISSING_FORMAL);
- /* FALL THROUGH */
- case TOK_ERROR:
- return NULL;
-
-#if JS_HAS_DESTRUCTURING
- report_dup_and_destructuring:
- JSDefinition *dn = ALE_DEFN(funtc.decls.lookup(duplicatedArg));
- reportErrorNumber(dn, JSREPORT_ERROR, JSMSG_DESTRUCT_DUP_ARG);
- return NULL;
-#endif
- }
- } while (tokenStream.matchToken(TOK_COMMA));
-
- MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FORMAL);
+ JSParseNode *prolog = NULL;
+ if (!functionArguments(funtc, funbox, fun, &prolog))
+ return NULL;
+
+ if (type == GETTER && fun->nargs > 0) {
+ reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_ACCESSOR_WRONG_ARGS,
+ "getter", "no", "s");
+ return NULL;
+ }
+ if (type == SETTER && fun->nargs != 1) {
+ reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_ACCESSOR_WRONG_ARGS,
+ "setter", "one", "");
+ return NULL;
}
#if JS_HAS_EXPR_CLOSURES
- tt = tokenStream.getToken(TSF_OPERAND);
+ TokenKind tt = tokenStream.getToken(TSF_OPERAND);
if (tt != TOK_LC) {
tokenStream.ungetToken();
fun->flags |= JSFUN_EXPR_CLOSURE;
}
#else
MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY);
#endif
- body = functionBody();
+ JSParseNode *body = functionBody();
if (!body)
return NULL;
if (!CheckStrictBinding(context, &funtc, funAtom, pn))
return NULL;
if (!CheckStrictFormals(context, &funtc, fun, pn))
return NULL;
@@ -2922,45 +2939,63 @@ Parser::functionDef(uintN lambda, bool n
MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
else if (lambda == 0 && !MatchOrInsertSemicolon(context, &tokenStream))
return NULL;
#else
MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
#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.
+ */
+ if (funtc.inStrictMode()) {
+ /*
+ * Fruit of the poisonous tree: eval forces eager arguments
+ * creation in (strict mode) parent functions.
+ */
+ if (outertc->inFunction() && outertc->inStrictMode()) {
+ if (funtc.callsEval())
+ outertc->noteCallsEval();
+ }
+ }
+
#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 lexical
* scope node, we must make a special TOK_SEQ node, to prepend the formal
* parameter destructuring code without bracing the decompilation of the
* function body's lexical scope.
*/
- if (list) {
+ if (prolog) {
if (body->pn_arity != PN_LIST) {
JSParseNode *block;
block = ListNode::create(outertc);
if (!block)
return NULL;
block->pn_type = TOK_SEQ;
block->pn_pos = body->pn_pos;
block->initList(body);
body = block;
}
- item = UnaryNode::create(outertc);
+ JSParseNode *item = UnaryNode::create(outertc);
if (!item)
return NULL;
item->pn_type = TOK_SEMI;
item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin;
- item->pn_kid = list;
+ item->pn_kid = prolog;
item->pn_next = body->pn_head;
body->pn_head = item;
if (body->pn_tail == &body->pn_head)
body->pn_tail = &item->pn_next;
++body->pn_count;
body->pn_xflags |= PNX_DESTRUCT;
}
#endif
@@ -2979,17 +3014,18 @@ Parser::functionDef(uintN lambda, bool n
* If this function is a named statement function not at top-level
* (i.e. not a top-level function definiton or expression), then our
* enclosing function, if any, must be heavyweight.
*/
if (!topLevel && lambda == 0 && funAtom)
outertc->flags |= TCF_FUN_HEAVYWEIGHT;
}
- result = pn;
+ JSParseNode *result = pn;
+ JSOp op = JSOP_NOP;
if (lambda != 0) {
/*
* ECMA ed. 3 standard: function expression, possibly anonymous.
*/
op = JSOP_LAMBDA;
} else if (!funAtom) {
/*
* If this anonymous function definition is *not* embedded within a
@@ -3008,18 +3044,16 @@ Parser::functionDef(uintN lambda, bool n
} else if (!topLevel) {
/*
* ECMA ed. 3 extension: a function expression statement not at the
* top level, e.g., in a compound statement such as the "then" part
* of an "if" statement, binds a closure only if control reaches that
* sub-statement.
*/
op = JSOP_DEFFUN;
- } else {
- op = JSOP_NOP;
}
funbox->kids = funtc.functionList;
pn->pn_funbox = funbox;
pn->pn_op = op;
if (pn->pn_body) {
pn->pn_body->append(body);
@@ -3036,32 +3070,48 @@ Parser::functionDef(uintN lambda, bool n
}
pn->pn_blockid = outertc->blockid();
if (!LeaveFunction(pn, &funtc, funAtom, lambda))
return NULL;
/* If the surrounding function is not strict code, reset the lexer. */
- if (!(outertc->flags & TCF_STRICT_MODE_CODE))
+ if (!outertc->inStrictMode())
tokenStream.setStrictMode(false);
return result;
}
JSParseNode *
Parser::functionStmt()
{
- return functionDef(0, true);
+ JSAtom *name = NULL;
+ if (tokenStream.getToken(TSF_KEYWORD_IS_NAME) == TOK_NAME) {
+ name = tokenStream.currentToken().t_atom;
+ } else {
+ if (context->options & JSOPTION_ANONFUNFIX) {
+ /* Extension: accept unnamed function expressions as statements. */
+ reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
+ return NULL;
+ }
+ tokenStream.ungetToken();
+ }
+ return functionDef(name, GENERAL, 0);
}
JSParseNode *
Parser::functionExpr()
{
- return functionDef(JSFUN_LAMBDA, true);
+ JSAtom *name = NULL;
+ if (tokenStream.getToken(TSF_KEYWORD_IS_NAME) == TOK_NAME)
+ name = tokenStream.currentToken().t_atom;
+ else
+ tokenStream.ungetToken();
+ return functionDef(name, GENERAL, JSFUN_LAMBDA);
}
/*
* Recognize Directive Prologue members and directives. Assuming pn
* is a candidate for membership in a directive prologue, return
* true if it is in fact a member. Recognize directives and set
* tc's flags accordingly.
*
@@ -3668,17 +3718,18 @@ NoteLValue(JSContext *cx, JSParseNode *p
dn->pn_dflags |= dflag;
if (dn->pn_cookie.isFree() || dn->frameLevel() < tc->staticLevel)
tc->flags |= TCF_FUN_SETS_OUTER_NAME;
}
pn->pn_dflags |= dflag;
- if (pn->pn_atom == cx->runtime->atomState.argumentsAtom)
+ JSAtom *lname = pn->pn_atom;
+ if (lname == cx->runtime->atomState.argumentsAtom)
tc->flags |= TCF_FUN_HEAVYWEIGHT;
}
#if JS_HAS_DESTRUCTURING
static JSBool
BindDestructuringVar(JSContext *cx, BindData *data, JSParseNode *pn,
JSTreeContext *tc)
@@ -5801,25 +5852,16 @@ Parser::statement()
}
break;
}
/* Check termination of this primitive statement. */
return MatchOrInsertSemicolon(context, &tokenStream) ? pn : NULL;
}
-static void
-NoteArgumentsUse(JSTreeContext *tc)
-{
- JS_ASSERT(tc->inFunction());
- tc->flags |= TCF_FUN_USES_ARGUMENTS;
- if (tc->funbox)
- tc->funbox->node->pn_dflags |= PND_FUNARG;
-}
-
JSParseNode *
Parser::variables(bool inLetHead)
{
TokenKind tt;
bool let;
JSStmtInfo *scopeStmt;
BindData data;
JSParseNode *pn, *pn2;
@@ -5977,17 +6019,17 @@ Parser::variables(bool inLetHead)
NoteLValue(context, pn2, tc, data.fresh ? PND_INITIALIZED : PND_ASSIGNED);
/* The declarator's position must include the initializer. */
pn2->pn_pos.end = init->pn_pos.end;
if (tc->inFunction() &&
atom == context->runtime->atomState.argumentsAtom) {
- NoteArgumentsUse(tc);
+ tc->noteArgumentsUse();
if (!let)
tc->flags |= TCF_FUN_HEAVYWEIGHT;
}
}
} while (tokenStream.matchToken(TOK_COMMA));
pn->pn_pos.end = pn->last()->pn_pos.end;
return pn;
@@ -7179,17 +7221,18 @@ Parser::memberExpr(JSBool allowCallSynta
return NULL;
pn2->pn_op = JSOP_CALL;
pn = CheckForImmediatelyAppliedLambda(pn);
if (pn->pn_op == JSOP_NAME) {
if (pn->pn_atom == context->runtime->atomState.evalAtom) {
/* Select JSOP_EVAL and flag tc as heavyweight. */
pn2->pn_op = JSOP_EVAL;
- tc->flags |= TCF_FUN_HEAVYWEIGHT | TCF_FUN_USES_EVAL;
+ tc->noteCallsEval();
+ tc->flags |= TCF_FUN_HEAVYWEIGHT;
}
} else if (pn->pn_op == JSOP_GETPROP) {
if (pn->pn_atom == context->runtime->atomState.applyAtom ||
pn->pn_atom == context->runtime->atomState.callAtom) {
/* Select JSOP_APPLY given foo.apply(...). */
pn2->pn_op = JSOP_APPLY;
}
}
@@ -8110,19 +8153,18 @@ Parser::primaryExpr(TokenKind tt, JSBool
} else {
atom = NULL; /* for the compiler */
}
} else {
tokenStream.ungetToken();
goto property_name;
}
- /* We have to fake a 'function' token here. */
- tokenStream.mungeCurrentToken(TOK_FUNCTION, JSOP_NOP);
- pn2 = functionDef(JSFUN_LAMBDA, false);
+ /* NB: Getter function in { get x(){} } is unnamed. */
+ pn2 = functionDef(NULL, op == JSOP_SETTER ? SETTER : GETTER, JSFUN_LAMBDA);
pn2 = JSParseNode::newBinaryOrAppend(TOK_COLON, op, pn3, pn2, tc);
goto skip;
}
property_name:
case TOK_STRING:
atom = tokenStream.currentToken().t_atom;
pn3 = NullaryNode::create(tc);
if (!pn3)
@@ -8328,17 +8370,17 @@ Parser::primaryExpr(TokenKind tt, JSBool
pn->pn_atom == context->runtime->atomState.argumentsAtom) {
/*
* Flag arguments usage so we can avoid unsafe optimizations such
* as formal parameter assignment analysis (because of the hated
* feature whereby arguments alias formals). We do this even for
* a reference of the form foo.arguments, which ancient code may
* still use instead of arguments (more hate).
*/
- NoteArgumentsUse(tc);
+ tc->noteArgumentsUse();
/*
* Bind early to JSOP_ARGUMENTS to relieve later code from having
* to do this work (new rule for the emitter to count on).
*/
if (!afterDot && !(tc->flags & TCF_DECL_DESTRUCTURING) && !tc->inStatement(STMT_WITH)) {
pn->pn_op = JSOP_ARGUMENTS;
pn->pn_dflags |= PND_BOUND;
--- a/js/src/jsparse.h
+++ b/js/src/jsparse.h
@@ -481,19 +481,19 @@ public:
#define PNX_XMLROOT 0x20 /* top-most node in XML literal tree */
#define PNX_GROUPINIT 0x40 /* var [a, b] = [c, d]; unit list */
#define PNX_NEEDBRACES 0x80 /* braces necessary due to closure */
#define PNX_FUNCDEFS 0x100 /* contains top-level function
statements */
#define PNX_DESTRUCT 0x200 /* destructuring special cases:
1. shorthand syntax used, at present
object destructuring ({x,y}) only;
- 2. the first child of function body
- is code evaluating destructuring
- arguments */
+ 2. code evaluating destructuring
+ arguments occurs before function
+ body */
#define PNX_HOLEY 0x400 /* array initialiser has holes */
uintN frameLevel() const {
JS_ASSERT(pn_arity == PN_FUNC || pn_arity == PN_NAME);
return pn_cookie.level();
}
uintN frameSlot() const {
@@ -1056,18 +1056,23 @@ private:
JSParseNode *memberExpr(JSBool allowCallSyntax);
JSParseNode *primaryExpr(js::TokenKind tt, JSBool afterDot);
JSParseNode *parenExpr(JSParseNode *pn1, JSBool *genexp);
/*
* Additional JS parsers.
*/
bool recognizeDirectivePrologue(JSParseNode *pn);
+
+ enum FunctionType { GETTER, SETTER, GENERAL };
+ bool functionArguments(JSTreeContext &funtc, JSFunctionBox *funbox, JSFunction *fun,
+ JSParseNode **list);
JSParseNode *functionBody();
- JSParseNode *functionDef(uintN lambda, bool namePermitted);
+ JSParseNode *functionDef(JSAtom *name, FunctionType type, uintN lambda);
+
JSParseNode *condition();
JSParseNode *comprehensionTail(JSParseNode *kid, uintN blockid,
js::TokenKind type = js::TOK_SEMI, JSOp op = JSOP_NOP);
JSParseNode *generatorExpr(JSParseNode *pn, JSParseNode *kid);
JSBool argumentList(JSParseNode *listNode);
JSParseNode *bracketedExpr();
JSParseNode *letBlock(JSBool statement);
JSParseNode *returnOrYield(bool useAssignExpr);
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1025,23 +1025,35 @@ js_NewScriptFromCG(JSContext *cx, JSCode
++pc;
if (JSOp(*pc) == JSOP_STOP &&
!cx->debugHooks->newScriptHook &&
!(cg->flags & TCF_NEED_MUTABLE_SCRIPT))
{
/*
* We can probably use the immutable empty script singleton, just
- * one hard case (nupvars != 0) may stand in our way.
+ * two hard cases (nupvars != 0, strict mode code) may stand in our
+ * way.
*/
JSScript *empty = JSScript::emptyScript();
if (cg->flags & TCF_IN_FUNCTION) {
fun = cg->fun;
- JS_ASSERT(FUN_INTERPRETED(fun) && !FUN_SCRIPT(fun));
+ JS_ASSERT(fun->isInterpreted() && !FUN_SCRIPT(fun));
+ if (cg->flags & TCF_STRICT_MODE_CODE) {
+ /*
+ * We can't use a script singleton for empty strict mode
+ * functions because they have poison-pill caller and
+ * arguments properties:
+ *
+ * function strict() { "use strict"; }
+ * strict.caller; // calls [[ThrowTypeError]] function
+ */
+ goto skip_empty;
+ }
if (fun->u.i.nupvars != 0) {
/*
* FIXME: upvar uses that were all optimized away may leave
* fun->u.i.nupvars non-zero, and since that count is added
* into fun->countLocalNames() in order to discriminate the
* fun->u.i.names union, we cannot force fun->u.i.nupvars
* to 0 to match JSScript::emptyScript()->upvars()->length.
* So we skip the empty script optimization.
@@ -1113,17 +1125,17 @@ js_NewScriptFromCG(JSContext *cx, JSCode
if (cg->flags & TCF_NO_SCRIPT_RVAL)
script->noScriptRval = true;
if (cg->hasSharps())
script->hasSharps = true;
if (cg->flags & TCF_STRICT_MODE_CODE)
script->strictModeCode = true;
if (cg->flags & TCF_COMPILE_N_GO)
script->compileAndGo = true;
- if (cg->flags & TCF_FUN_USES_EVAL)
+ if (cg->flags & TCF_FUN_CALLS_EVAL)
script->usesEval = true;
if (cg->upvarList.count != 0) {
JS_ASSERT(cg->upvarList.count <= cg->upvarMap.length);
memcpy(script->upvars()->vector, cg->upvarMap.vector,
cg->upvarList.count * sizeof(uint32));
cg->upvarList.clear();
cx->free(cg->upvarMap.vector);
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -74,16 +74,17 @@
#include "jsscript.h"
#include "jsstaticcheck.h"
#include "jstracer.h"
#include "jsxml.h"
#include "jstypedarray.h"
#include "jsatominlines.h"
#include "jscntxtinlines.h"
+#include "jsfuninlines.h"
#include "jspropertycacheinlines.h"
#include "jsobjinlines.h"
#include "jsscopeinlines.h"
#include "jsscriptinlines.h"
#include "jscntxtinlines.h"
#include "jsautooplen.h" // generated headers last
#include "imacros.c.out"
@@ -3448,18 +3449,20 @@ FlushNativeStackFrame(JSContext* cx, uns
// Skip over stopFrame itself.
JS_ASSERT(n != 0);
--n;
fp = fp->down;
}
for (; n != 0; fp = fp->down) {
--n;
if (fp->argv) {
- if (fp->hasArgsObj() && fp->getArgsObj()->getPrivate() == JS_ARGUMENT_OBJECT_ON_TRACE)
+ if (fp->hasArgsObj() && fp->getArgsObj()->getPrivate() == JS_ARGUMENT_OBJECT_ON_TRACE) {
+ JS_ASSERT(fp->getArgsObj()->isNormalArguments());
fp->getArgsObj()->setPrivate(fp);
+ }
JS_ASSERT(fp->argv[-1].isObjectOrNull());
JS_ASSERT(fp->callee()->isFunction());
JS_ASSERT(GET_FUNCTION_PRIVATE(cx, fp->callee()) == fp->getFunction());
if (FUN_INTERPRETED(fp->getFunction()) &&
(fp->getFunction()->flags & JSFUN_HEAVYWEIGHT)) {
// Iff these fields are NULL, then |fp| was synthesized on trace exit, so
@@ -8743,23 +8746,32 @@ TraceRecorder::tableswitch()
high = GET_JUMP_OFFSET(pc);
} else {
pc += JUMPX_OFFSET_LEN;
low = GET_JUMPX_OFFSET(pc);
pc += JUMPX_OFFSET_LEN;
high = GET_JUMPX_OFFSET(pc);
}
+ /*
+ * If there are no cases, this is a no-op. The default case immediately
+ * follows in the bytecode and is always taken, so we need no special
+ * action to handle it.
+ */
+ int count = high + 1 - low;
+ if (count == 0)
+ return ARECORD_CONTINUE;
+
/* Cap maximum table-switch size for modesty. */
- if ((high + 1 - low) > MAX_TABLE_SWITCH)
+ if (count > MAX_TABLE_SWITCH)
return InjectStatus(switchop());
/* Generate switch LIR. */
SwitchInfo* si = new (traceAlloc()) SwitchInfo();
- si->count = high + 1 - low;
+ si->count = count;
si->table = 0;
si->index = (uint32) -1;
LIns* diff = lir->ins2(LIR_subi, v_ins, lir->insImmI(low));
LIns* cmp = lir->ins2(LIR_ltui, diff, lir->insImmI(si->count));
lir->insGuard(LIR_xf, cmp, createGuardRecord(snapshot(DEFAULT_EXIT)));
lir->insStore(diff, lir->insImmP(&si->index), 0, ACCSET_OTHER);
VMSideExit* exit = snapshot(CASE_EXIT);
exit->switchInfo = si;
@@ -10292,17 +10304,17 @@ TraceRecorder::clearCurrentFrameSlotsFro
/*
* If we have created an |arguments| object for the frame, we must copy the
* argument values into the object as properties in case it is used after
* this frame returns.
*/
JS_REQUIRES_STACK void
TraceRecorder::putActivationObjects()
{
- bool have_args = cx->fp->hasArgsObj() && cx->fp->argc;
+ bool have_args = cx->fp->hasArgsObj() && !cx->fp->getArgsObj()->isStrictArguments() && cx->fp->argc > 0;
bool have_call = cx->fp->hasFunction() &&
JSFUN_HEAVYWEIGHT_TEST(cx->fp->getFunction()->flags) &&
cx->fp->getFunction()->countArgsAndVars();
if (!have_args && !have_call)
return;
int nargs = have_args ? argSlots(cx->fp) : cx->fp->getArgumentCount();
@@ -10655,65 +10667,77 @@ TraceRecorder::record_JSOP_IFEQ()
JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_IFNE()
{
return ifop();
}
LIns*
-TraceRecorder::newArguments(LIns* callee_ins)
+TraceRecorder::newArguments(LIns* callee_ins, bool strict)
{
LIns* global_ins = INS_CONSTOBJ(globalObj);
LIns* argc_ins = INS_CONST(cx->fp->argc);
LIns* args[] = { callee_ins, argc_ins, global_ins, cx_ins };
LIns* call_ins = lir->insCall(&js_Arguments_ci, args);
guard(false, lir->insEqP_0(call_ins), OOM_EXIT);
+
+ if (strict) {
+ JSStackFrame* fp = cx->fp;
+ uintN argc = fp->argc;
+ LIns* argsSlots_ins = NULL;
+ for (uintN i = 0; i < argc; i++)
+ stobj_set_dslot(call_ins, i, argsSlots_ins, fp->argv[i], get(&fp->argv[i]));
+ }
+
return call_ins;
}
JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_ARGUMENTS()
{
/* In an eval, 'arguments' will be a BINDNAME, which we don't trace. */
JS_ASSERT(!(cx->fp->flags & JSFRAME_EVAL));
if (cx->fp->flags & JSFRAME_OVERRIDE_ARGS)
RETURN_STOP_A("Can't trace |arguments| if |arguments| is assigned to");
+ JSStackFrame* const fp = cx->fp;
+
LIns* a_ins = getFrameObjPtr(cx->fp->addressArgsObj());
LIns* args_ins;
- LIns* callee_ins = get(&cx->fp->argv[-2]);
+ LIns* callee_ins = get(&fp->argv[-2]);
+ bool strict = fp->getFunction()->inStrictMode();
if (a_ins->isImmP()) {
// |arguments| is set to 0 by EnterFrame on this trace, so call to create it.
- args_ins = newArguments(callee_ins);
+ args_ins = newArguments(callee_ins, strict);
} else {
// Generate LIR to create arguments only if it has not already been created.
LIns* mem_ins = lir->insAlloc(sizeof(JSObject *));
LIns* br1 = lir->insBranch(LIR_jt, lir->insEqP_0(a_ins), NULL);
lir->insStore(a_ins, mem_ins, 0, ACCSET_OTHER);
LIns* br2 = lir->insBranch(LIR_j, NULL, NULL);
LIns* label1 = lir->ins0(LIR_label);
br1->setTarget(label1);
- LIns* call_ins = newArguments(callee_ins);
+ LIns* call_ins = newArguments(callee_ins, strict);
lir->insStore(call_ins, mem_ins, 0, ACCSET_OTHER);
LIns* label2 = lir->ins0(LIR_label);
br2->setTarget(label2);
args_ins = lir->insLoad(LIR_ldp, mem_ins, 0, ACCSET_OTHER);
}
stack(0, args_ins);
- setFrameObjPtr(cx->fp->addressArgsObj(), args_ins);
+ setFrameObjPtr(fp->addressArgsObj(), args_ins);
return ARECORD_CONTINUE;
}
JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_DUP()
{
stack(0, get(&stackval(-1)));
return ARECORD_CONTINUE;
@@ -13356,17 +13380,17 @@ TraceRecorder::guardArguments(JSObject *
{
JS_ASSERT(obj->isArguments());
JSStackFrame *afp = frameIfInRange(obj, depthp);
if (!afp)
return NULL;
VMSideExit *exit = snapshot(MISMATCH_EXIT);
- guardClass(obj_ins, &js_ArgumentsClass, exit, LOAD_CONST);
+ guardClass(obj_ins, obj->getClass(), exit, LOAD_CONST);
LIns* args_ins = getFrameObjPtr(afp->addressArgsObj());
LIns* cmp = lir->ins2(LIR_eqp, args_ins, obj_ins);
lir->insGuard(LIR_xf, cmp, createGuardRecord(exit));
return afp;
}
JS_REQUIRES_STACK RecordingStatus
@@ -15267,20 +15291,20 @@ TraceRecorder::record_JSOP_ARGSUB()
return ARECORD_CONTINUE;
}
RETURN_STOP_A("can't trace JSOP_ARGSUB hard case");
}
JS_REQUIRES_STACK LIns*
TraceRecorder::guardArgsLengthNotAssigned(LIns* argsobj_ins)
{
- // The following implements IsOverriddenArgsLength on trace.
- // The '2' bit is set if length was overridden.
+ // The following implements JSObject::isArgsLengthOverridden on trace.
+ // ARGS_LENGTH_OVERRIDDEN_BIT is set if length was overridden.
LIns *len_ins = stobj_get_fslot_uint32(argsobj_ins, JSObject::JSSLOT_ARGS_LENGTH);
- LIns *ovr_ins = lir->ins2(LIR_andi, len_ins, INS_CONST(2));
+ LIns *ovr_ins = lir->ins2(LIR_andi, len_ins, INS_CONST(JSObject::ARGS_LENGTH_OVERRIDDEN_BIT));
guard(true, lir->insEqI_0(ovr_ins), snapshot(BRANCH_EXIT));
return len_ins;
}
JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_ARGCNT()
{
if (cx->fp->getFunction()->flags & JSFUN_HEAVYWEIGHT)
@@ -15968,19 +15992,21 @@ TraceRecorder::record_JSOP_LENGTH()
RETURN_STOP_A("can't reach arguments object's frame");
// We must both check at record time and guard at run time that
// arguments.length has not been reassigned, redefined or deleted.
if (obj->isArgsLengthOverridden())
RETURN_STOP_A("can't trace JSOP_ARGCNT if arguments.length has been modified");
LIns* slot_ins = guardArgsLengthNotAssigned(obj_ins);
- // slot_ins is the value from the slot; right-shift by 2 bits to get
- // the length (see GetArgsLength in jsfun.cpp).
- LIns* v_ins = lir->ins1(LIR_i2d, lir->ins2ImmI(LIR_rshi, slot_ins, 1));
+ // slot_ins is the value from the slot; right-shift to get the length
+ // (see JSObject::getArgsInitialLength in jsfun.cpp).
+ LIns* v_ins =
+ lir->ins1(LIR_i2d, lir->ins2ImmI(LIR_rshi,
+ slot_ins, JSObject::ARGS_PACKED_BITS_COUNT));
set(&l, v_ins);
return ARECORD_CONTINUE;
}
LIns* v_ins;
if (obj->isArray()) {
if (obj->isDenseArray()) {
guardDenseArray(obj_ins, BRANCH_EXIT);
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -1162,17 +1162,17 @@ class TraceRecorder
nanojit::LIns* s0, nanojit::LIns* s1);
nanojit::LIns* i2d(nanojit::LIns* i);
nanojit::LIns* d2i(nanojit::LIns* f, bool resultCanBeImpreciseIfFractional = false);
nanojit::LIns* f2u(nanojit::LIns* f);
JS_REQUIRES_STACK nanojit::LIns* makeNumberInt32(nanojit::LIns* f);
JS_REQUIRES_STACK nanojit::LIns* stringify(const Value& v);
- JS_REQUIRES_STACK nanojit::LIns* newArguments(nanojit::LIns* callee_ins);
+ JS_REQUIRES_STACK nanojit::LIns* newArguments(nanojit::LIns* callee_ins, bool strict);
JS_REQUIRES_STACK bool canCallImacro() const;
JS_REQUIRES_STACK RecordingStatus callImacro(jsbytecode* imacro);
JS_REQUIRES_STACK RecordingStatus callImacroInfallibly(jsbytecode* imacro);
JS_REQUIRES_STACK AbortableRecordingStatus ifop();
JS_REQUIRES_STACK RecordingStatus switchop();
#ifdef NANOJIT_IA32
--- a/js/src/methodjit/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -1838,17 +1838,17 @@ ic::GetProp(VMFrame &f, uint32 index)
THROW();
}
f.regs.sp[-1].setNumber(obj->getArrayLength());
} else if (obj->isArguments()) {
if (!cc.generateArgsLengthStub()) {
cc.disable("error");
THROW();
}
- f.regs.sp[-1].setInt32(int32_t(obj->getArgsLength()));
+ f.regs.sp[-1].setInt32(int32_t(obj->getArgsInitialLength()));
}
return;
}
}
atom = f.cx->runtime->atomState.lengthAtom;
}
JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-1]);
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -520,17 +520,17 @@ stubs::GetElem(VMFrame &f)
}
} else if (obj->isArguments()
#if 0 /* def JS_TRACER */
&& !GetArgsPrivateNative(obj)
#endif
) {
uint32 arg = uint32(rval.toInt32());
- if (arg < obj->getArgsLength()) {
+ if (arg < obj->getArgsInitialLength()) {
JSStackFrame *afp = (JSStackFrame *) obj->getPrivate();
if (afp) {
copyFrom = &afp->argv[arg];
goto end_getelem;
}
copyFrom = obj->addressOfArgsElement(arg);
if (!copyFrom->isMagic())
@@ -2154,17 +2154,17 @@ stubs::Length(VMFrame &f)
return;
} else if (vp->isObject()) {
JSObject *obj = &vp->toObject();
if (obj->isArray()) {
jsuint length = obj->getArrayLength();
regs.sp[-1].setNumber(length);
return;
} else if (obj->isArguments() && !obj->isArgsLengthOverridden()) {
- uint32 length = obj->getArgsLength();
+ uint32 length = obj->getArgsInitialLength();
JS_ASSERT(length < INT32_MAX);
regs.sp[-1].setInt32(int32_t(length));
return;
}
}
if (!InlineGetProp(f))
THROW();
--- a/js/src/prmjtime.cpp
+++ b/js/src/prmjtime.cpp
@@ -93,39 +93,55 @@ extern int gettimeofday(struct timeval *
#define PRMJ_FOUR_YEARS_DAYS (4 * PRMJ_YEAR_DAYS + 1)
#define PRMJ_CENTURY_DAYS (25 * PRMJ_FOUR_YEARS_DAYS - 1)
#define PRMJ_FOUR_CENTURIES_DAYS (4 * PRMJ_CENTURY_DAYS + 1)
#define PRMJ_HOUR_SECONDS 3600L
#define PRMJ_DAY_SECONDS (24L * PRMJ_HOUR_SECONDS)
#define PRMJ_YEAR_SECONDS (PRMJ_DAY_SECONDS * PRMJ_YEAR_DAYS)
#define PRMJ_MAX_UNIX_TIMET 2145859200L /*time_t value equiv. to 12/31/2037 */
-/* function prototypes */
-static void PRMJ_basetime(JSInt64 tsecs, PRMJTime *prtm);
/*
* get the difference in seconds between this time zone and UTC (GMT)
*/
JSInt32
PRMJ_LocalGMTDifference()
{
- struct tm ltime;
-
#if defined(XP_WIN) && !defined(WINCE)
/* Windows does not follow POSIX. Updates to the
* TZ environment variable are not reflected
* immediately on that platform as they are
* on UNIX systems without this call.
*/
_tzset();
#endif
- /* get the difference between this time zone and GMT */
- memset((char *)<ime,0,sizeof(ltime));
- ltime.tm_mday = 2;
- ltime.tm_year = 70;
- return (JSInt32)mktime(<ime) - (24L * 3600L);
+
+ /*
+ * Get the difference between this time zone and GMT, by checking the local
+ * time at the epoch.
+ */
+ time_t local = 0;
+ struct tm tm;
+#ifndef HAVE_LOCALTIME_R
+ struct tm *ptm = localtime(&local);
+ if (!ptm)
+ return 0;
+ tm = *ptm;
+#else
+ localtime_r(&local, &tm);
+#endif
+
+ JSInt32 time = (tm.tm_hour * 3600)
+ + (tm.tm_min * 60)
+ + tm.tm_sec;
+ time = (24 * 3600) - time;
+
+ if (time >= (12 * 3600))
+ time -= (24 * 3600);
+
+ return time;
}
/* Constants for GMT offset from 1970 */
#define G1970GMTMICROHI 0x00dcdcad /* micro secs to 1970 hi */
#define G1970GMTMICROLOW 0x8b3fa000 /* micro secs to 1970 low */
#define G2037GMTMICROHI 0x00e45fab /* micro secs to 2037 high */
#define G2037GMTMICROLOW 0x7a238000 /* micro secs to 2037 low */
@@ -684,210 +700,49 @@ PRMJ_FormatTime(char *buf, int buflen, c
/* table for number of days in a month */
static int mtab[] = {
/* jan, feb,mar,apr,may,jun */
31,28,31,30,31,30,
/* july,aug,sep,oct,nov,dec */
31,31,30,31,30,31
};
-/*
- * basic time calculation functionality for localtime and gmtime
- * setups up prtm argument with correct values based upon input number
- * of seconds.
- */
-static void
-PRMJ_basetime(JSInt64 tsecs, PRMJTime *prtm)
-{
- /* convert tsecs back to year,month,day,hour,secs */
- JSInt32 year = 0;
- JSInt32 month = 0;
- JSInt32 yday = 0;
- JSInt32 mday = 0;
- JSInt32 wday = 6; /* start on a Sunday */
- JSInt32 days = 0;
- JSInt32 seconds = 0;
- JSInt32 minutes = 0;
- JSInt32 hours = 0;
- JSInt32 isleap = 0;
-
- /* Temporaries used for various computations */
- JSInt64 result;
- JSInt64 result1;
- JSInt64 result2;
-
- JSInt64 base;
-
- /* Some variables for intermediate result storage to make computing isleap
- easier/faster */
- JSInt32 fourCenturyBlocks;
- JSInt32 centuriesLeft;
- JSInt32 fourYearBlocksLeft;
- JSInt32 yearsLeft;
-
- /* Since leap years work by 400/100/4 year intervals, precompute the length
- of those in seconds if they start at the beginning of year 1. */
- JSInt64 fourYears;
- JSInt64 century;
- JSInt64 fourCenturies;
-
- JSLL_UI2L(result, PRMJ_DAY_SECONDS);
-
- JSLL_I2L(fourYears, PRMJ_FOUR_YEARS_DAYS);
- JSLL_MUL(fourYears, fourYears, result);
-
- JSLL_I2L(century, PRMJ_CENTURY_DAYS);
- JSLL_MUL(century, century, result);
-
- JSLL_I2L(fourCenturies, PRMJ_FOUR_CENTURIES_DAYS);
- JSLL_MUL(fourCenturies, fourCenturies, result);
-
- /* get the base time via UTC */
- base = PRMJ_ToExtendedTime(0);
- JSLL_UI2L(result, PRMJ_USEC_PER_SEC);
- JSLL_DIV(base,base,result);
- JSLL_ADD(tsecs,tsecs,base);
-
- /* Compute our |year|, |isleap|, and part of |days|. When this part is
- done, |year| should hold the year our date falls in (number of whole
- years elapsed before our date), isleap should hold 1 if the year the
- date falls in is a leap year and 0 otherwise. */
-
- /* First do year 0; it's special and nonleap. */
- JSLL_UI2L(result, PRMJ_YEAR_SECONDS);
- if (!JSLL_CMP(tsecs,<,result)) {
- days = PRMJ_YEAR_DAYS;
- year = 1;
- JSLL_SUB(tsecs, tsecs, result);
- }
-
- /* Now use those constants we computed above */
- JSLL_UDIVMOD(&result1, &result2, tsecs, fourCenturies);
- JSLL_L2I(fourCenturyBlocks, result1);
- year += fourCenturyBlocks * 400;
- days += fourCenturyBlocks * PRMJ_FOUR_CENTURIES_DAYS;
- tsecs = result2;
-
- JSLL_UDIVMOD(&result1, &result2, tsecs, century);
- JSLL_L2I(centuriesLeft, result1);
- year += centuriesLeft * 100;
- days += centuriesLeft * PRMJ_CENTURY_DAYS;
- tsecs = result2;
-
- JSLL_UDIVMOD(&result1, &result2, tsecs, fourYears);
- JSLL_L2I(fourYearBlocksLeft, result1);
- year += fourYearBlocksLeft * 4;
- days += fourYearBlocksLeft * PRMJ_FOUR_YEARS_DAYS;
- tsecs = result2;
-
- /* Recall that |result| holds PRMJ_YEAR_SECONDS */
- JSLL_UDIVMOD(&result1, &result2, tsecs, result);
- JSLL_L2I(yearsLeft, result1);
- year += yearsLeft;
- days += yearsLeft * PRMJ_YEAR_DAYS;
- tsecs = result2;
-
- /* now compute isleap. Note that we don't have to use %, since we've
- already computed those remainders. Also note that they're all offset by
- 1 because of the 1 for year 0. */
- isleap =
- (yearsLeft == 3) && (fourYearBlocksLeft != 24 || centuriesLeft == 3);
- JS_ASSERT(isleap ==
- ((year % 4 == 0) && (year % 100 != 0 || year % 400 == 0)));
-
- JSLL_UI2L(result1,PRMJ_DAY_SECONDS);
-
- JSLL_DIV(result,tsecs,result1);
- JSLL_L2I(mday,result);
-
- /* let's find the month */
- while(((month == 1 && isleap) ?
- (mday >= mtab[month] + 1) :
- (mday >= mtab[month]))){
- yday += mtab[month];
- days += mtab[month];
-
- mday -= mtab[month];
-
- /* it's a Feb, check if this is a leap year */
- if(month == 1 && isleap != 0){
- yday++;
- days++;
- mday--;
- }
- month++;
- }
-
- /* now adjust tsecs */
- JSLL_MUL(result,result,result1);
- JSLL_SUB(tsecs,tsecs,result);
-
- mday++; /* day of month always start with 1 */
- days += mday;
- wday = (days + wday) % 7;
-
- yday += mday;
-
- /* get the hours */
- JSLL_UI2L(result1,PRMJ_HOUR_SECONDS);
- JSLL_DIV(result,tsecs,result1);
- JSLL_L2I(hours,result);
- JSLL_MUL(result,result,result1);
- JSLL_SUB(tsecs,tsecs,result);
-
- /* get minutes */
- JSLL_UI2L(result1,60);
- JSLL_DIV(result,tsecs,result1);
- JSLL_L2I(minutes,result);
- JSLL_MUL(result,result,result1);
- JSLL_SUB(tsecs,tsecs,result);
-
- JSLL_L2I(seconds,tsecs);
-
- prtm->tm_usec = 0L;
- prtm->tm_sec = (JSInt8)seconds;
- prtm->tm_min = (JSInt8)minutes;
- prtm->tm_hour = (JSInt8)hours;
- prtm->tm_mday = (JSInt8)mday;
- prtm->tm_mon = (JSInt8)month;
- prtm->tm_wday = (JSInt8)wday;
- prtm->tm_year = (JSInt16)year;
- prtm->tm_yday = (JSInt16)yday;
-}
-
JSInt64
DSTOffsetCache::computeDSTOffsetMilliseconds(int64 localTimeSeconds)
{
JS_ASSERT(localTimeSeconds >= 0);
JS_ASSERT(localTimeSeconds <= MAX_UNIX_TIMET);
#if defined(XP_WIN) && !defined(WINCE)
/* Windows does not follow POSIX. Updates to the
* TZ environment variable are not reflected
* immediately on that platform as they are
* on UNIX systems without this call.
*/
_tzset();
#endif
time_t local = static_cast<time_t>(localTimeSeconds);
- PRMJTime prtm;
struct tm tm;
- PRMJ_basetime(localTimeSeconds, &prtm);
#ifndef HAVE_LOCALTIME_R
struct tm *ptm = localtime(&local);
if (!ptm)
return 0;
tm = *ptm;
#else
localtime_r(&local, &tm); /* get dst information */
#endif
- JSInt32 diff = ((tm.tm_hour - prtm.tm_hour) * SECONDS_PER_HOUR) +
- ((tm.tm_min - prtm.tm_min) * SECONDS_PER_MINUTE);
+ JSInt32 base = PRMJ_LocalGMTDifference();
+
+ int32 dayoff = int32((localTimeSeconds - base) % (SECONDS_PER_HOUR * 24));
+ int32 tmoff = tm.tm_sec + (tm.tm_min * SECONDS_PER_MINUTE) +
+ (tm.tm_hour * SECONDS_PER_HOUR);
+
+ JSInt32 diff = tmoff - dayoff;
if (diff < 0)
diff += SECONDS_PER_DAY;
return diff * MILLISECONDS_PER_SECOND;
}
JSInt64
@@ -906,22 +761,33 @@ DSTOffsetCache::getDSTOffsetMilliseconds
}
/*
* NB: Be aware of the initial range values when making changes to this
* code: the first call to this method, with those initial range
* values, must result in a cache miss.
*/
+ if (rangeStartSeconds <= localTimeSeconds &&
+ localTimeSeconds <= rangeEndSeconds) {
+ noteCacheHit();
+ return offsetMilliseconds;
+ }
+
+ if (oldRangeStartSeconds <= localTimeSeconds &&
+ localTimeSeconds <= oldRangeEndSeconds) {
+ noteCacheHit();
+ return oldOffsetMilliseconds;
+ }
+
+ oldOffsetMilliseconds = offsetMilliseconds;
+ oldRangeStartSeconds = rangeStartSeconds;
+ oldRangeEndSeconds = rangeEndSeconds;
+
if (rangeStartSeconds <= localTimeSeconds) {
- if (localTimeSeconds <= rangeEndSeconds) {
- noteCacheHit();
- return offsetMilliseconds;
- }
-
JSInt64 newEndSeconds = JS_MIN(rangeEndSeconds + RANGE_EXPANSION_AMOUNT, MAX_UNIX_TIMET);
if (newEndSeconds >= localTimeSeconds) {
JSInt64 endOffsetMilliseconds = computeDSTOffsetMilliseconds(newEndSeconds);
if (endOffsetMilliseconds == offsetMilliseconds) {
noteCacheMissIncrease();
rangeEndSeconds = newEndSeconds;
return offsetMilliseconds;
}
--- a/js/src/prmjtime.h
+++ b/js/src/prmjtime.h
@@ -107,16 +107,19 @@ class DSTOffsetCache {
#endif
private:
JSInt64 computeDSTOffsetMilliseconds(int64 localTimeSeconds);
JSInt64 offsetMilliseconds;
JSInt64 rangeStartSeconds, rangeEndSeconds;
+ JSInt64 oldOffsetMilliseconds;
+ JSInt64 oldRangeStartSeconds, oldRangeEndSeconds;
+
#ifdef JS_METER_DST_OFFSET_CACHING
size_t totalCalculations;
size_t hit;
size_t missIncreasing;
size_t missDecreasing;
size_t missIncreasingOffsetChangeUpper;
size_t missIncreasingOffsetChangeExpand;
size_t missLargeIncrease;
--- a/js/src/tests/ecma_5/Date/jstests.list
+++ b/js/src/tests/ecma_5/Date/jstests.list
@@ -1,2 +1,3 @@
url-prefix ../../jsreftest.html?test=ecma_5/Date/
script 15.9.4.2.js
+script toJSON-01.js
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/Date/toJSON-01.js
@@ -0,0 +1,242 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+
+var gTestfile = 'toJSON-01.js';
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 584811;
+var summary = "Date.prototype.toJSON isn't to spec";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+var called;
+
+var dateToJSON = Date.prototype.toJSON;
+assertEq(Date.prototype.hasOwnProperty("toJSON"), true);
+assertEq(typeof dateToJSON, "function");
+
+// brief test to exercise this outside of isolation, just for sanity
+var invalidDate = new Date();
+invalidDate.setTime(NaN);
+assertEq(JSON.stringify({ p: invalidDate }), '{"p":null}');
+
+
+/* 15.9.5.44 Date.prototype.toJSON ( key ) */
+assertEq(dateToJSON.length, 1);
+
+/*
+ * 1. Let O be the result of calling ToObject, giving it the this value as its
+ * argument.
+ */
+function strictThis() { "use strict"; return this; }
+if (strictThis.call(null) === null)
+{
+ try
+ {
+ dateToJSON.call(null);
+ throw new Error("should have thrown a TypeError");
+ }
+ catch (e)
+ {
+ assertEq(e instanceof TypeError, true,
+ "ToObject throws TypeError for null/undefined");
+ }
+
+ try
+ {
+ dateToJSON.call(undefined);
+ throw new Error("should have thrown a TypeError");
+ }
+ catch (e)
+ {
+ assertEq(e instanceof TypeError, true,
+ "ToObject throws TypeError for null/undefined");
+ }
+}
+
+
+/*
+ * 2. Let tv be ToPrimitive(O, hint Number).
+ * ...expands to:
+ * 1. Let valueOf be the result of calling the [[Get]] internal method of object O with argument "valueOf".
+ * 2. If IsCallable(valueOf) is true then,
+ * a. Let val be the result of calling the [[Call]] internal method of valueOf, with O as the this value and
+ * an empty argument list.
+ * b. If val is a primitive value, return val.
+ * 3. Let toString be the result of calling the [[Get]] internal method of object O with argument "toString".
+ * 4. If IsCallable(toString) is true then,
+ * a. Let str be the result of calling the [[Call]] internal method of toString, with O as the this value and
+ * an empty argument list.
+ * b. If str is a primitive value, return str.
+ * 5. Throw a TypeError exception.
+ */
+try
+{
+ var r = dateToJSON.call({ get valueOf() { throw 17; } });
+ throw new Error("didn't throw, returned: " + r);
+}
+catch (e)
+{
+ assertEq(e, 17, "bad exception: " + e);
+}
+
+called = false;
+assertEq(dateToJSON.call({ valueOf: null,
+ toString: function() { called = true; return 12; },
+ toISOString: function() { return "ohai"; } }),
+ "ohai");
+assertEq(called, true);
+
+called = false;
+assertEq(dateToJSON.call({ valueOf: function() { called = true; return 42; },
+ toISOString: function() { return null; } }),
+ null);
+assertEq(called, true);
+
+try
+{
+ called = false;
+ dateToJSON.call({ valueOf: function() { called = true; return {}; },
+ get toString() { throw 42; } });
+}
+catch (e)
+{
+ assertEq(called, true);
+ assertEq(e, 42, "bad exception: " + e);
+}
+
+called = false;
+assertEq(dateToJSON.call({ valueOf: function() { called = true; return {}; },
+ get toString() { return function() { return 8675309; }; },
+ toISOString: function() { return true; } }),
+ true);
+assertEq(called, true);
+
+var asserted = false;
+called = false;
+assertEq(dateToJSON.call({ valueOf: function() { called = true; return {}; },
+ get toString()
+ {
+ assertEq(called, true);
+ asserted = true;
+ return function() { return 8675309; };
+ },
+ toISOString: function() { return NaN; } }),
+ NaN);
+assertEq(asserted, true);
+
+try
+{
+ var r = dateToJSON.call({ valueOf: null, toString: null,
+ get toISOString()
+ {
+ throw new Error("shouldn't have been gotten");
+ } });
+ throw new Error("didn't throw, returned: " + r);
+}
+catch (e)
+{
+ assertEq(e instanceof TypeError, true, "bad exception: " + e);
+}
+
+
+/* 3. If tv is a Number and is not finite, return null. */
+assertEq(dateToJSON.call({ valueOf: function() { return Infinity; } }), null);
+assertEq(dateToJSON.call({ valueOf: function() { return -Infinity; } }), null);
+assertEq(dateToJSON.call({ valueOf: function() { return NaN; } }), null);
+
+assertEq(dateToJSON.call({ valueOf: function() { return Infinity; },
+ toISOString: function() { return {}; } }), null);
+assertEq(dateToJSON.call({ valueOf: function() { return -Infinity; },
+ toISOString: function() { return []; } }), null);
+assertEq(dateToJSON.call({ valueOf: function() { return NaN; },
+ toISOString: function() { return undefined; } }), null);
+
+
+/*
+ * 4. Let toISO be the result of calling the [[Get]] internal method of O with
+ * argument "toISOString".
+ */
+try
+{
+ var r = dateToJSON.call({ get toISOString() { throw 42; } });
+ throw new Error("didn't throw, returned: " + r);
+}
+catch (e)
+{
+ assertEq(e, 42, "bad exception: " + e);
+}
+
+
+/* 5. If IsCallable(toISO) is false, throw a TypeError exception. */
+try
+{
+ var r = dateToJSON.call({ toISOString: null });
+ throw new Error("didn't throw, returned: " + r);
+}
+catch (e)
+{
+ assertEq(e instanceof TypeError, true, "bad exception: " + e);
+}
+
+try
+{
+ var r = dateToJSON.call({ toISOString: undefined });
+ throw new Error("didn't throw, returned: " + r);
+}
+catch (e)
+{
+ assertEq(e instanceof TypeError, true, "bad exception: " + e);
+}
+
+try
+{
+ var r = dateToJSON.call({ toISOString: "oogabooga" });
+ throw new Error("didn't throw, returned: " + r);
+}
+catch (e)
+{
+ assertEq(e instanceof TypeError, true, "bad exception: " + e);
+}
+
+try
+{
+ var r = dateToJSON.call({ toISOString: Math.PI });
+ throw new Error("didn't throw, returned: " + r);
+}
+catch (e)
+{
+ assertEq(e instanceof TypeError, true, "bad exception: " + e);
+}
+
+
+/*
+ * 6. Return the result of calling the [[Call]] internal method of toISO with O
+ * as the this value and an empty argument list.
+ */
+var o =
+ {
+ toISOString: function(a)
+ {
+ called = true;
+ assertEq(this, o);
+ assertEq(a, undefined);
+ assertEq(arguments.length, 0);
+ return obj;
+ }
+ };
+var obj = {};
+called = false;
+assertEq(dateToJSON.call(o), obj, "should have gotten obj back");
+assertEq(called, true);
+
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
+
+print("All tests passed!");
--- a/js/src/tests/ecma_5/Expressions/11.1.5-01.js
+++ b/js/src/tests/ecma_5/Expressions/11.1.5-01.js
@@ -13,18 +13,18 @@ print(BUGNUMBER + ": " + summary);
var o;
o = { get "a b c"() { return 17; } };
assertEq("get" in Object.getOwnPropertyDescriptor(o, "a b c"), true);
o = eval('({ get "a b c"() { return 17; } })');
assertEq("get" in Object.getOwnPropertyDescriptor(o, "a b c"), true);
-var f = eval("(function literalInside() { return { set 'c d e'() { } }; })");
-f = function literalInside() { return { set 'c d e'() { } }; };
+var f = eval("(function literalInside() { return { set 'c d e'(q) { } }; })");
+f = function literalInside() { return { set 'c d e'(q) { } }; };
function checkO()
{
assertEq(3.141592654 in o, true, "fractional-named property isn't in object");
assertEq(10000 in o, true, "exponential-named property is in object");
assertEq(0xdeadbeef in o, true, "hex-named property is in object");
assertEq("Infinity" in o, true, "numeric index stringified correctly");
}
--- a/js/src/tests/ecma_5/Expressions/jstests.list
+++ b/js/src/tests/ecma_5/Expressions/jstests.list
@@ -1,3 +1,4 @@
url-prefix ../../jsreftest.html?test=ecma_5/Expressions/
script 11.1.5-01.js
script named-accessor-function.js
+script object-literal-accessor-arguments.js
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/Expressions/object-literal-accessor-arguments.js
@@ -0,0 +1,42 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+
+var gTestfile = 'object-literal-accessor-arguments.js';
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 536472;
+var summary =
+ 'ES5: { get x(v) { } } and { set x(v, v2) { } } should be syntax errors';
+
+print(BUGNUMBER + ": " + summary);
+
+//-----------------------------------------------------------------------------
+
+function expectSyntaxError(s)
+{
+ try
+ {
+ eval(s);
+ throw new Error("no error thrown");
+ }
+ catch (e)
+ {
+ assertEq(e instanceof SyntaxError, true,
+ "expected syntax error parsing '" + s + "', got: " + e);
+ }
+}
+
+expectSyntaxError("({ get x(a) { } })");
+expectSyntaxError("({ get x(a, a) { } })");
+expectSyntaxError("({ get x(a, b) { } })");
+expectSyntaxError("({ get x(a, a, b) { } })");
+expectSyntaxError("({ get x(a, b, c) { } })");
+
+expectSyntaxError("({ set x() { } })");
+expectSyntaxError("({ set x(a, a) { } })");
+expectSyntaxError("({ set x(a, b) { } })");
+expectSyntaxError("({ set x(a, a, b) { } })");
+expectSyntaxError("({ set x(a, b, c) { } })");
+
+//-----------------------------------------------------------------------------
+
+reportCompare(true, true);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/Function/arguments-caller-callee.js
@@ -0,0 +1,66 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+var gTestfile = 'arguments-caller-callee.js';
+var BUGNUMBER = 514563;
+var summary = "arguments.caller and arguments.callee are poison pills in ES5";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+// behavior
+
+function expectTypeError(fun)
+{
+ try
+ {
+ fun();
+ throw new Error("didn't throw");
+ }
+ catch (e)
+ {
+ assertEq(e instanceof TypeError, true,
+ "expected TypeError calling function" +
+ ("name" in fun ? " " + fun.name : "") + ", instead got: " + e);
+ }
+}
+
+function bar() { "use strict"; return arguments; }
+expectTypeError(function barCaller() { bar().caller; });
+expectTypeError(function barCallee() { bar().callee; });
+
+function baz() { return arguments; }
+assertEq(baz().callee, baz);
+
+
+// accessor identity
+
+function strictMode() { "use strict"; return arguments; }
+var canonicalTTE = Object.getOwnPropertyDescriptor(strictMode(), "caller").get;
+
+var args = strictMode();
+
+var argsCaller = Object.getOwnPropertyDescriptor(args, "caller");
+assertEq("get" in argsCaller, true);
+assertEq("set" in argsCaller, true);
+assertEq(argsCaller.get, canonicalTTE);
+assertEq(argsCaller.set, canonicalTTE);
+
+var argsCallee = Object.getOwnPropertyDescriptor(args, "callee");
+assertEq("get" in argsCallee, true);
+assertEq("set" in argsCallee, true);
+assertEq(argsCallee.get, canonicalTTE);
+assertEq(argsCallee.set, canonicalTTE);
+
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
+
+print("All tests passed!");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/Function/arguments-property-attributes.js
@@ -0,0 +1,102 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+var gTestfile = 'arguments-property-attributes.js';
+var BUGNUMBER = 516255;
+var summary = "Attributes for properties of arguments objects";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+// normal
+
+function args() { return arguments; }
+var a = args(0, 1);
+
+var argProps = Object.getOwnPropertyNames(a).sort();
+assertEq(argProps.indexOf("callee") >= 0, true);
+assertEq(argProps.indexOf("0") >= 0, true);
+assertEq(argProps.indexOf("1") >= 0, true);
+assertEq(argProps.indexOf("length") >= 0, true);
+
+var calleeDesc = Object.getOwnPropertyDescriptor(a, "callee");
+assertEq(calleeDesc.value, args);
+assertEq(calleeDesc.writable, true);
+assertEq(calleeDesc.enumerable, false);
+assertEq(calleeDesc.configurable, true);
+
+var zeroDesc = Object.getOwnPropertyDescriptor(a, "0");
+assertEq(zeroDesc.value, 0);
+assertEq(zeroDesc.writable, true);
+assertEq(zeroDesc.enumerable, true);
+assertEq(zeroDesc.configurable, true);
+
+var oneDesc = Object.getOwnPropertyDescriptor(a, "1");
+assertEq(oneDesc.value, 1);
+assertEq(oneDesc.writable, true);
+assertEq(oneDesc.enumerable, true);
+assertEq(oneDesc.configurable, true);
+
+var lengthDesc = Object.getOwnPropertyDescriptor(a, "length");
+assertEq(lengthDesc.value, 2);
+assertEq(lengthDesc.writable, true);
+assertEq(lengthDesc.enumerable, false);
+assertEq(lengthDesc.configurable, true);
+
+
+// strict
+
+function strictArgs() { "use strict"; return arguments; }
+var sa = strictArgs(0, 1);
+
+var strictArgProps = Object.getOwnPropertyNames(sa).sort();
+assertEq(strictArgProps.indexOf("callee") >= 0, true);
+assertEq(strictArgProps.indexOf("caller") >= 0, true);
+assertEq(strictArgProps.indexOf("0") >= 0, true);
+assertEq(strictArgProps.indexOf("1") >= 0, true);
+assertEq(strictArgProps.indexOf("length") >= 0, true);
+
+var strictCalleeDesc = Object.getOwnPropertyDescriptor(sa, "callee");
+assertEq(typeof strictCalleeDesc.get, "function");
+assertEq(typeof strictCalleeDesc.set, "function");
+assertEq(strictCalleeDesc.get, strictCalleeDesc.set);
+assertEq(strictCalleeDesc.enumerable, false);
+assertEq(strictCalleeDesc.configurable, false);
+
+var strictCallerDesc = Object.getOwnPropertyDescriptor(sa, "caller");
+assertEq(typeof strictCallerDesc.get, "function");
+assertEq(typeof strictCallerDesc.set, "function");
+assertEq(strictCallerDesc.get, strictCallerDesc.set);
+assertEq(strictCallerDesc.enumerable, false);
+assertEq(strictCallerDesc.configurable, false);
+
+var strictZeroDesc = Object.getOwnPropertyDescriptor(sa, "0");
+assertEq(strictZeroDesc.value, 0);
+assertEq(strictZeroDesc.writable, true);
+assertEq(strictZeroDesc.enumerable, true);
+assertEq(strictZeroDesc.configurable, true);
+
+var strictOneDesc = Object.getOwnPropertyDescriptor(sa, "1");
+assertEq(strictOneDesc.value, 1);
+assertEq(strictOneDesc.writable, true);
+assertEq(strictOneDesc.enumerable, true);
+assertEq(strictOneDesc.configurable, true);
+
+var strictLengthDesc = Object.getOwnPropertyDescriptor(sa, "length");
+assertEq(strictLengthDesc.value, 2);
+assertEq(strictLengthDesc.writable, true);
+assertEq(strictLengthDesc.enumerable, false);
+assertEq(strictLengthDesc.configurable, true);
+
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
+
+print("All tests passed!");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/Function/function-caller.js
@@ -0,0 +1,76 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+var gTestfile = 'function-caller.js';
+var BUGNUMBER = 514581;
+var summary = "Function.prototype.caller should throw a TypeError for " +
+ "strict-mode functions";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+// behavior
+
+function expectTypeError(fun)
+{
+ try
+ {
+ fun();
+ throw new Error("didn't throw");
+ }
+ catch (e)
+ {
+ assertEq(e instanceof TypeError, true,
+ "expected TypeError calling function" +
+ ("name" in fun ? " " + fun.name : "") + ", instead got: " + e);
+ }
+}
+
+function bar() { "use strict"; }
+expectTypeError(function barCaller() { bar.caller; });
+
+function baz() { "use strict"; return 17; }
+expectTypeError(function bazCaller() { baz.caller; });
+
+
+// accessor identity
+
+function strictMode() { "use strict"; return 42; }
+var canonicalTTE = Object.getOwnPropertyDescriptor(strictMode, "caller").get;
+
+var barCaller = Object.getOwnPropertyDescriptor(bar, "caller");
+assertEq("get" in barCaller, true);
+assertEq("set" in barCaller, true);
+assertEq(barCaller.get, canonicalTTE);
+assertEq(barCaller.set, canonicalTTE);
+
+var barArguments = Object.getOwnPropertyDescriptor(bar, "arguments");
+assertEq("get" in barArguments, true);
+assertEq("set" in barArguments, true);
+assertEq(barArguments.get, canonicalTTE);
+assertEq(barArguments.set, canonicalTTE);
+
+var bazCaller = Object.getOwnPropertyDescriptor(baz, "caller");
+assertEq("get" in bazCaller, true);
+assertEq("set" in bazCaller, true);
+assertEq(bazCaller.get, canonicalTTE);
+assertEq(bazCaller.set, canonicalTTE);
+
+var bazArguments = Object.getOwnPropertyDescriptor(baz, "arguments");
+assertEq("get" in bazArguments, true);
+assertEq("set" in bazArguments, true);
+assertEq(bazArguments.get, canonicalTTE);
+assertEq(bazArguments.set, canonicalTTE);
+
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
+
+print("All tests passed!");
--- a/js/src/tests/ecma_5/Function/jstests.list
+++ b/js/src/tests/ecma_5/Function/jstests.list
@@ -1,2 +1,6 @@
url-prefix ../../jsreftest.html?test=ecma_5/Function/
script 15.3.4.3-01.js
+script arguments-caller-callee.js
+script function-caller.js
+script strict-arguments.js
+script arguments-property-attributes.js
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/Function/strict-arguments.js
@@ -0,0 +1,382 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+var gTestfile = 'strict-arguments.js';
+var BUGNUMBER = 516255;
+var summary =
+ "ES5 strict mode: arguments objects of strict mode functions must copy " +
+ "argument values";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+function arrayEvery(arr, fun)
+{
+ return Array.prototype.every.call(arr, fun);
+}
+
+function arraysEqual(a1, a2)
+{
+ return a1.length === a2.length &&
+ arrayEvery(a1, function(v, i) { return v === a2[i]; });
+}
+
+
+/************************
+ * NON-STRICT ARGUMENTS *
+ ************************/
+
+var obj = {};
+
+function noargs() { return arguments; }
+
+assertEq(arraysEqual(noargs(), []), true);
+assertEq(arraysEqual(noargs(1), [1]), true);
+assertEq(arraysEqual(noargs(2, obj, 8), [2, obj, 8]), true);
+
+function args(a) { return arguments; }
+
+assertEq(arraysEqual(args(), []), true);
+assertEq(arraysEqual(args(1), [1]), true);
+assertEq(arraysEqual(args(1, obj), [1, obj]), true);
+assertEq(arraysEqual(args("foopy"), ["foopy"]), true);
+
+function assign(a)
+{
+ a = 17;
+ return arguments;
+}
+
+assertEq(arraysEqual(assign(1), [17]), true);
+
+function getLaterAssign(a)
+{
+ var o = arguments;
+ a = 17;
+ return o;
+}
+
+assertEq(arraysEqual(getLaterAssign(1), [17]), true);
+
+function assignElementGetParameter(a)
+{
+ arguments[0] = 17;
+ return a;
+}
+
+assertEq(assignElementGetParameter(42), 17);
+
+function assignParameterGetElement(a)
+{
+ a = 17;
+ return arguments[0];
+}
+
+assertEq(assignParameterGetElement(42), 17);
+
+
+/********************
+ * STRICT ARGUMENTS *
+ ********************/
+
+function strictNoargs()
+{
+ "use strict";
+ return arguments;
+}
+
+assertEq(arraysEqual(strictNoargs(), []), true);
+assertEq(arraysEqual(strictNoargs(1), [1]), true);
+assertEq(arraysEqual(strictNoargs(1, obj), [1, obj]), true);
+
+function strictArgs(a)
+{
+ "use strict";
+ return arguments;
+}
+
+assertEq(arraysEqual(strictArgs(), []), true);
+assertEq(arraysEqual(strictArgs(1), [1]), true);
+assertEq(arraysEqual(strictArgs(1, obj), [1, obj]), true);
+
+function strictAssign(a)
+{
+ "use strict";
+ a = 17;
+ return arguments;
+}
+
+assertEq(arraysEqual(strictAssign(), []), true);
+assertEq(arraysEqual(strictAssign(1), [1]), true);
+assertEq(arraysEqual(strictAssign(1, obj), [1, obj]), true);
+
+var upper;
+function strictAssignAfter(a)
+{
+ "use strict";
+ upper = arguments;
+ a = 42;
+ return upper;
+}
+
+assertEq(arraysEqual(strictAssignAfter(), []), true);
+assertEq(arraysEqual(strictAssignAfter(17), [17]), true);
+assertEq(arraysEqual(strictAssignAfter(obj), [obj]), true);
+
+function strictMaybeAssignOuterParam(p)
+{
+ "use strict";
+ function inner() { p = 17; }
+ return arguments;
+}
+
+assertEq(arraysEqual(strictMaybeAssignOuterParam(), []), true);
+assertEq(arraysEqual(strictMaybeAssignOuterParam(42), [42]), true);
+assertEq(arraysEqual(strictMaybeAssignOuterParam(obj), [obj]), true);
+
+function strictAssignOuterParam(p)
+{
+ "use strict";
+ function inner() { p = 17; }
+ inner();
+ return arguments;
+}
+
+assertEq(arraysEqual(strictAssignOuterParam(), []), true);
+assertEq(arraysEqual(strictAssignOuterParam(17), [17]), true);
+assertEq(arraysEqual(strictAssignOuterParam(obj), [obj]), true);
+
+function strictAssignOuterParamPSYCH(p)
+{
+ "use strict";
+ function inner(p) { p = 17; }
+ inner();
+ return arguments;
+}
+
+assertEq(arraysEqual(strictAssignOuterParamPSYCH(), []), true);
+assertEq(arraysEqual(strictAssignOuterParamPSYCH(17), [17]), true);
+assertEq(arraysEqual(strictAssignOuterParamPSYCH(obj), [obj]), true);
+
+function strictEval(code, p)
+{
+ "use strict";
+ eval(code);
+ return arguments;
+}
+
+assertEq(arraysEqual(strictEval("1", 2), ["1", 2]), true);
+assertEq(arraysEqual(strictEval("arguments"), ["arguments"]), true);
+assertEq(arraysEqual(strictEval("p = 2"), ["p = 2"]), true);
+assertEq(arraysEqual(strictEval("p = 2", 17), ["p = 2", 17]), true);
+assertEq(arraysEqual(strictEval("arguments[0] = 17"), [17]), true);
+assertEq(arraysEqual(strictEval("arguments[0] = 17", 42), [17, 42]), true);
+
+function strictMaybeNestedEval(code, p)
+{
+ "use strict";
+ function inner() { eval(code); }
+ return arguments;
+}
+
+assertEq(arraysEqual(strictMaybeNestedEval("1", 2), ["1", 2]), true);
+assertEq(arraysEqual(strictMaybeNestedEval("arguments"), ["arguments"]), true);
+assertEq(arraysEqual(strictMaybeNestedEval("p = 2"), ["p = 2"]), true);
+assertEq(arraysEqual(strictMaybeNestedEval("p = 2", 17), ["p = 2", 17]), true);
+
+function strictNestedEval(code, p)
+{
+ "use strict";
+ function inner() { eval(code); }
+ inner();
+ return arguments;
+}
+
+assertEq(arraysEqual(strictNestedEval("1", 2), ["1", 2]), true);
+assertEq(arraysEqual(strictNestedEval("arguments"), ["arguments"]), true);
+assertEq(arraysEqual(strictNestedEval("p = 2"), ["p = 2"]), true);
+assertEq(arraysEqual(strictNestedEval("p = 2", 17), ["p = 2", 17]), true);
+assertEq(arraysEqual(strictNestedEval("arguments[0] = 17"), ["arguments[0] = 17"]), true);
+assertEq(arraysEqual(strictNestedEval("arguments[0] = 17", 42), ["arguments[0] = 17", 42]), true);
+
+function strictAssignArguments(a)
+{
+ "use strict";
+ arguments[0] = 42;
+ return a;
+}
+
+assertEq(strictAssignArguments(), undefined);
+assertEq(strictAssignArguments(obj), obj);
+assertEq(strictAssignArguments(17), 17);
+
+function strictAssignParameterGetElement(a)
+{
+ "use strict";
+ a = 17;
+ return arguments[0];
+}
+
+assertEq(strictAssignParameterGetElement(42), 42);
+
+function strictNestedAssignShadowVar(p)
+{
+ "use strict";
+ function inner()
+ {
+ var p = 12;
+ function innermost() { p = 1776; return 12; }
+ return innermost();
+ }
+ return arguments;
+}
+
+assertEq(arraysEqual(strictNestedAssignShadowVar(), []), true);
+assertEq(arraysEqual(strictNestedAssignShadowVar(99), [99]), true);
+assertEq(arraysEqual(strictNestedAssignShadowVar(""), [""]), true);
+assertEq(arraysEqual(strictNestedAssignShadowVar(obj), [obj]), true);
+
+function strictNestedAssignShadowCatch(p)
+{
+ "use strict";
+ function inner()
+ {
+ try
+ {
+ }
+ catch (p)
+ {
+ var f = function innermost() { p = 1776; return 12; };
+ f();
+ }
+ }
+ return arguments;
+}
+
+assertEq(arraysEqual(strictNestedAssignShadowCatch(), []), true);
+assertEq(arraysEqual(strictNestedAssignShadowCatch(99), [99]), true);
+assertEq(arraysEqual(strictNestedAssignShadowCatch(""), [""]), true);
+assertEq(arraysEqual(strictNestedAssignShadowCatch(obj), [obj]), true);
+
+function strictNestedAssignShadowCatchCall(p)
+{
+ "use strict";
+ function inner()
+ {
+ try
+ {
+ }
+ catch (p)
+ {
+ var f = function innermost() { p = 1776; return 12; };
+ f();
+ }
+ }
+ inner();
+ return arguments;
+}
+
+assertEq(arraysEqual(strictNestedAssignShadowCatchCall(), []), true);
+assertEq(arraysEqual(strictNestedAssignShadowCatchCall(99), [99]), true);
+assertEq(arraysEqual(strictNestedAssignShadowCatchCall(""), [""]), true);
+assertEq(arraysEqual(strictNestedAssignShadowCatchCall(obj), [obj]), true);
+
+function strictNestedAssignShadowFunction(p)
+{
+ "use strict";
+ function inner()
+ {
+ function p() { }
+ p = 1776;
+ }
+ return arguments;
+}
+
+assertEq(arraysEqual(strictNestedAssignShadowFunction(), []), true);
+assertEq(arraysEqual(strictNestedAssignShadowFunction(99), [99]), true);
+assertEq(arraysEqual(strictNestedAssignShadowFunction(""), [""]), true);
+assertEq(arraysEqual(strictNestedAssignShadowFunction(obj), [obj]), true);
+
+function strictNestedAssignShadowFunctionCall(p)
+{
+ "use strict";
+ function inner()
+ {
+ function p() { }
+ p = 1776;
+ }
+ return arguments;
+}
+
+assertEq(arraysEqual(strictNestedAssignShadowFunctionCall(), []), true);
+assertEq(arraysEqual(strictNestedAssignShadowFunctionCall(99), [99]), true);
+assertEq(arraysEqual(strictNestedAssignShadowFunctionCall(""), [""]), true);
+assertEq(arraysEqual(strictNestedAssignShadowFunctionCall(obj), [obj]), true);
+
+function strictNestedShadowAndMaybeEval(code, p)
+{
+ "use strict";
+ function inner(p) { eval(code); }
+ return arguments;
+}
+
+assertEq(arraysEqual(strictNestedShadowAndMaybeEval("1", 2), ["1", 2]), true);
+assertEq(arraysEqual(strictNestedShadowAndMaybeEval("arguments"), ["arguments"]), true);
+assertEq(arraysEqual(strictNestedShadowAndMaybeEval("p = 2"), ["p = 2"]), true);
+assertEq(arraysEqual(strictNestedShadowAndMaybeEval("p = 2", 17), ["p = 2", 17]), true);
+assertEq(arraysEqual(strictNestedShadowAndMaybeEval("arguments[0] = 17"), ["arguments[0] = 17"]), true);
+assertEq(arraysEqual(strictNestedShadowAndMaybeEval("arguments[0] = 17", 42), ["arguments[0] = 17", 42]), true);
+
+function strictNestedShadowAndEval(code, p)
+{
+ "use strict";
+ function inner(p) { eval(code); }
+ return arguments;
+}
+
+assertEq(arraysEqual(strictNestedShadowAndEval("1", 2), ["1", 2]), true);
+assertEq(arraysEqual(strictNestedShadowAndEval("arguments"), ["arguments"]), true);
+assertEq(arraysEqual(strictNestedShadowAndEval("p = 2"), ["p = 2"]), true);
+assertEq(arraysEqual(strictNestedShadowAndEval("p = 2", 17), ["p = 2", 17]), true);
+assertEq(arraysEqual(strictNestedShadowAndEval("arguments[0] = 17"), ["arguments[0] = 17"]), true);
+assertEq(arraysEqual(strictNestedShadowAndEval("arguments[0] = 17", 42), ["arguments[0] = 17", 42]), true);
+
+function strictEvalContainsMutation(code)
+{
+ "use strict";
+ return eval(code);
+}
+
+assertEq(arraysEqual(strictEvalContainsMutation("code = 17; arguments"), ["code = 17; arguments"]), true);
+assertEq(arraysEqual(strictEvalContainsMutation("arguments[0] = 17; arguments"), [17]), true);
+assertEq(strictEvalContainsMutation("arguments[0] = 17; code"), "arguments[0] = 17; code");
+
+function strictNestedAssignShadowFunctionName(p)
+{
+ "use strict";
+ function inner()
+ {
+ function p() { p = 1776; }
+ p();
+ }
+ inner();
+ return arguments;
+}
+
+assertEq(arraysEqual(strictNestedAssignShadowFunctionName(), []), true);
+assertEq(arraysEqual(strictNestedAssignShadowFunctionName(99), [99]), true);
+assertEq(arraysEqual(strictNestedAssignShadowFunctionName(""), [""]), true);
+assertEq(arraysEqual(strictNestedAssignShadowFunctionName(obj), [obj]), true);
+
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
+
+print("All tests passed!");
--- a/js/src/tests/ecma_5/JSON/jstests.list
+++ b/js/src/tests/ecma_5/JSON/jstests.list
@@ -1,2 +1,3 @@
url-prefix ../../jsreftest.html?test=ecma_5/JSON/
script cyclic-stringify.js
+script stringify-gap.js
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/JSON/stringify-gap.js
@@ -0,0 +1,52 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+
+var gTestfile = 'stringify-gap.js';
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 584909;
+var summary =
+ "JSON.stringify(_1, _2, numberGreaterThanOne) produces wrong output";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+var LF = "\n";
+var GAP = " ";
+
+var obj = { a: { b: [1, 2], c: { d: 3, e: 4 }, f: [], g: {}, h: [5], i: { j: 6 } } };
+
+var expected =
+ '{\n' +
+ ' "a": {\n' +
+ ' "b": [\n' +
+ ' 1,\n' +
+ ' 2\n' +
+ ' ],\n' +
+ ' "c": {\n' +
+ ' "d": 3,\n' +
+ ' "e": 4\n' +
+ ' },\n' +
+ ' "f": [],\n' +
+ ' "g": {},\n' +
+ ' "h": [\n' +
+ ' 5\n' +
+ ' ],\n' +
+ ' "i": {\n' +
+ ' "j": 6\n' +
+ ' }\n' +
+ ' }\n' +
+ '}';
+
+assertEq(JSON.stringify(obj, null, 3), expected);
+assertEq(JSON.stringify(obj, null, " "), expected);
+
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
+
+print("All tests passed!");
--- a/js/src/tests/ecma_5/Object/15.2.3.3-01.js
+++ b/js/src/tests/ecma_5/Object/15.2.3.3-01.js
@@ -151,17 +151,17 @@ expected =
writable: true,
enumerable: true,
configurable: true
};
expectDescriptor(pd, expected);
/******************************************************************************/
-o = { get y() { return 17; }, set y() { } };
+o = { get y() { return 17; }, set y(z) { } };
pd = Object.getOwnPropertyDescriptor(o, "y");
expected =
{
enumerable: true,
configurable: true
};
adjustDescriptorField(o, pd, expected, "get");
--- a/js/src/tests/ecma_5/strict/11.1.5.js
+++ b/js/src/tests/ecma_5/strict/11.1.5.js
@@ -98,49 +98,49 @@ assertEq(testLenientAndStrict('({get x()
parseRaisesException(SyntaxError)),
true);
assertEq(testLenientAndStrict('({x:1, get x() {}})',
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
-assertEq(testLenientAndStrict('({set x() {}, x:1})',
+assertEq(testLenientAndStrict('({set x(q) {}, x:1})',
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
-assertEq(testLenientAndStrict('({x:1, set x() {}})',
+assertEq(testLenientAndStrict('({x:1, set x(q) {}})',
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
-assertEq(testLenientAndStrict('({get x() {}, set x() {}})',
+assertEq(testLenientAndStrict('({get x() {}, set x(q) {}})',
parsesSuccessfully,
parsesSuccessfully),
true);
-assertEq(testLenientAndStrict('({set x() {}, get x() {}})',
+assertEq(testLenientAndStrict('({set x(q) {}, get x() {}})',
parsesSuccessfully,
parsesSuccessfully),
true);
-assertEq(testLenientAndStrict('({get x() {}, set x() {}, x:1})',
+assertEq(testLenientAndStrict('({get x() {}, set x(q) {}, x:1})',
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
-assertEq(testLenientAndStrict('({set x() {}, get x() {}, x:1})',
+assertEq(testLenientAndStrict('({set x(q) {}, get x() {}, x:1})',
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
assertEq(testLenientAndStrict('({get x() {}, get x() {}})',
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
-assertEq(testLenientAndStrict('({get x() {}, set x() {}, y:1})',
+assertEq(testLenientAndStrict('({get x() {}, set x(q) {}, y:1})',
parsesSuccessfully,
parsesSuccessfully),
true);
reportCompare(true, true);
--- a/js/src/tests/ecma_5/strict/13.1.js
+++ b/js/src/tests/ecma_5/strict/13.1.js
@@ -78,37 +78,16 @@ assertEq(testLenientAndStrict('Function(
true);
assertEq(testLenientAndStrict('Function("x","y","\'use strict\'")',
completesNormally,
completesNormally),
true);
/*
- * The parameter lists of getters and setters in object literals
- * should not contain duplicate identifiers.
- */
-assertEq(testLenientAndStrict('({get x(y,y) {}})',
- parsesSuccessfully,
- parseRaisesException(SyntaxError)),
- true);
-assertEq(testLenientAndStrict('({get x(y,y) { "use strict"; }})',
- parseRaisesException(SyntaxError),
- parseRaisesException(SyntaxError)),
- true);
-assertEq(testLenientAndStrict('({set x(y,y) {}})',
- parsesSuccessfully,
- parseRaisesException(SyntaxError)),
- true);
-assertEq(testLenientAndStrict('({set x(y,y) { "use strict"; }})',
- parseRaisesException(SyntaxError),
- parseRaisesException(SyntaxError)),
- true);
-
-/*
* The parameter lists of function expressions should not contain
* duplicate identifiers.
*/
assertEq(testLenientAndStrict('(function (x,x) 2)',
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
assertEq(testLenientAndStrict('(function (x,y) 2)',
@@ -205,40 +184,16 @@ assertEq(testLenientAndStrict('(function
assertEq(testLenientAndStrict('(function f({x:eval}) 2)',
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
assertEq(testLenientAndStrict('(function eval() 2)',
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
-assertEq(testLenientAndStrict('({get x(eval){}})',
- parsesSuccessfully,
- parseRaisesException(SyntaxError)),
- true);
-assertEq(testLenientAndStrict('({get x([eval]){}})',
- parsesSuccessfully,
- parseRaisesException(SyntaxError)),
- true);
-assertEq(testLenientAndStrict('({get x({x:eval}){}})',
- parsesSuccessfully,
- parseRaisesException(SyntaxError)),
- true);
-assertEq(testLenientAndStrict('({get x(eval){"use strict";}})',
- parseRaisesException(SyntaxError),
- parseRaisesException(SyntaxError)),
- true);
-assertEq(testLenientAndStrict('({get x([eval]){"use strict";}})',
- parseRaisesException(SyntaxError),
- parseRaisesException(SyntaxError)),
- true);
-assertEq(testLenientAndStrict('({get x({x:eval}){"use strict";}})',
- parseRaisesException(SyntaxError),
- parseRaisesException(SyntaxError)),
- true);
assertEq(testLenientAndStrict('({set x(eval){}})',
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
assertEq(testLenientAndStrict('({set x([eval]){}})',
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
@@ -333,40 +288,16 @@ assertEq(testLenientAndStrict('(function
assertEq(testLenientAndStrict('(function f({x:arguments}) 2)',
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
assertEq(testLenientAndStrict('(function arguments() 2)',
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
-assertEq(testLenientAndStrict('({get x(arguments){}})',
- parsesSuccessfully,
- parseRaisesException(SyntaxError)),
- true);
-assertEq(testLenientAndStrict('({get x([arguments]){}})',
- parsesSuccessfully,
- parseRaisesException(SyntaxError)),
- true);
-assertEq(testLenientAndStrict('({get x({x:arguments}){}})',
- parsesSuccessfully,
- parseRaisesException(SyntaxError)),
- true);
-assertEq(testLenientAndStrict('({get x(arguments){"use strict";}})',
- parseRaisesException(SyntaxError),
- parseRaisesException(SyntaxError)),
- true);
-assertEq(testLenientAndStrict('({get x([arguments]){"use strict";}})',
- parseRaisesException(SyntaxError),
- parseRaisesException(SyntaxError)),
- true);
-assertEq(testLenientAndStrict('({get x({x:arguments}){"use strict";}})',
- parseRaisesException(SyntaxError),
- parseRaisesException(SyntaxError)),
- true);
assertEq(testLenientAndStrict('({set x(arguments){}})',
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
assertEq(testLenientAndStrict('({set x([arguments]){}})',
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
--- a/js/src/tests/js1_5/decompilation/regress-356083.js
+++ b/js/src/tests/js1_5/decompilation/regress-356083.js
@@ -47,19 +47,19 @@ test();
//-----------------------------------------------------------------------------
function test()
{
enterFunc ('test');
printBugNumber(BUGNUMBER);
printStatus (summary);
- var f = function() { return { set this() { } }; } ;
- expect = 'function() { return { set this() { } }; }';
+ var f = function() { return { set this(v) { } }; } ;
+ expect = 'function() { return { set this(v) { } }; }';
actual = f + '';
compareSource(expect, actual, summary);
- expect = "({ set ''() {} })";
- actual = uneval({ set ''() {} });
+ expect = "({ set ''(v) {} })";
+ actual = uneval({ set ''(v) {} });
compareSource(expect, actual, expect);
exitFunc ('test');
}
--- a/js/src/tests/js1_5/extensions/regress-367501-01.js
+++ b/js/src/tests/js1_5/extensions/regress-367501-01.js
@@ -50,17 +50,17 @@ function test()
{
enterFunc ('test');
printBugNumber(BUGNUMBER);
printStatus (summary);
try
{
expect = 'undefined';
- var a = { set x() {} };
+ var a = { set x(v) {} };
actual = a.x + '';
}
catch(ex)
{
}
reportCompare(expect, actual, summary);
exitFunc ('test');
--- a/js/src/tests/js1_5/extensions/regress-367501-02.js
+++ b/js/src/tests/js1_5/extensions/regress-367501-02.js
@@ -50,17 +50,17 @@ function test()
{
enterFunc ('test');
printBugNumber(BUGNUMBER);
printStatus (summary);
try
{
expect = 'undefined';
- var a = { set x() {} };
+ var a = { set x(v) {} };
for (var i = 0; i < 92169 - 3; ++i) a[i] = 1;
actual = a.x + '';
actual = a.x + '';
}
catch(ex)
{
}
reportCompare(expect, actual, summary);
--- a/js/src/tests/js1_5/extensions/regress-367501-03.js
+++ b/js/src/tests/js1_5/extensions/regress-367501-03.js
@@ -50,17 +50,17 @@ function test()
{
enterFunc ('test');
printBugNumber(BUGNUMBER);
printStatus (summary);
try
{
expect = actual = 'No Crash';
- var a = { set x() {} };
+ var a = { set x(v) {} };
for (var i = 0; i < 0x4bf20 - 3; ++i) a[i] = 1;
a.x;
a.x.x;
}
catch(ex)
{
}
--- a/js/src/tests/js1_5/extensions/regress-367501-04.js
+++ b/js/src/tests/js1_5/extensions/regress-367501-04.js
@@ -50,17 +50,17 @@ function test()
{
enterFunc ('test');
printBugNumber(BUGNUMBER);
printStatus (summary);
try
{
expect = actual = 'No Crash';
- var a = { set x() {} };
+ var a = { set x(v) {} };
for (var i = 0; i < 0x10050c - 3; ++i) a[i] = 1;
a.x;
typeof a.x;
}
catch(ex)
{
}
--- a/js/src/tests/js1_8_1/regress/regress-452498-160.js
+++ b/js/src/tests/js1_8_1/regress/regress-452498-160.js
@@ -59,13 +59,13 @@ function test()
reportCompare(expect, actual, summary + ': 1');
// crash [@ js_Interpret]
(eval("(function(){ watch(\"x\", function () { new function ()y } ); const y });"))();
x = NaN;
reportCompare(expect, actual, summary + ': 2');
// Assertion failure: JOF_OPTYPE(op) == JOF_ATOM, at ../jsemit.cpp:5916
- ({ set z(){}, set y()--x, set w()--w });
+ ({ set z(v){}, set y(v)--x, set w(v)--w });
reportCompare(expect, actual, summary + ': 3');
exitFunc ('test');
}
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_8_5/extensions/destructure-accessor.js
@@ -0,0 +1,75 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+
+var gTestfile = 'destructure-accessor.js';
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 536472;
+var summary =
+ 'ES5: { get x(v) { } } and { set x(v, v2) { } } should be syntax errors';
+
+print(BUGNUMBER + ": " + summary);
+
+//-----------------------------------------------------------------------------
+
+function expectOk(s)
+{
+ try
+ {
+ eval(s);
+ return;
+ }
+ catch (e)
+ {
+ assertEq(true, false,
+ "expected no error parsing '" + "', got : " + e);
+ }
+}
+
+function expectSyntaxError(s)
+{
+ try
+ {
+ eval(s);
+ throw new Error("no error thrown");
+ }
+ catch (e)
+ {
+ assertEq(e instanceof SyntaxError, true,
+ "expected syntax error parsing '" + s + "', got: " + e);
+ }
+}
+
+expectSyntaxError("({ get x([]) { } })");
+expectSyntaxError("({ get x({}) { } })");
+expectSyntaxError("({ get x(a, []) { } })");
+expectSyntaxError("({ get x(a, {}) { } })");
+expectSyntaxError("({ get x([], a) { } })");
+expectSyntaxError("({ get x({}, a) { } })");
+expectSyntaxError("({ get x([], a, []) { } })");
+expectSyntaxError("({ get x([], a, {}) { } })");
+expectSyntaxError("({ get x({}, a, []) { } })");
+expectSyntaxError("({ get x({}, a, {}) { } })");
+
+expectOk("({ get x() { } })");
+
+
+expectSyntaxError("({ set x() { } })");
+expectSyntaxError("({ set x(a, []) { } })");
+expectSyntaxError("({ set x(a, b, c) { } })");
+
+expectOk("({ set x([]) { } })");
+expectOk("({ set x({}) { } })");
+expectOk("({ set x([a]) { } })");
+expectOk("({ set x([a, b]) { } })");
+expectOk("({ set x([a,]) { } })");
+expectOk("({ set x([a, b,]) { } })");
+expectOk("({ set x([, b]) { } })");
+expectOk("({ set x([, b,]) { } })");
+expectOk("({ set x([, b, c]) { } })");
+expectOk("({ set x([, b, c,]) { } })");
+expectOk("({ set x({ a: a }) { } })");
+expectOk("({ set x({ a: a, b: b }) { } })");
+
+//-----------------------------------------------------------------------------
+
+reportCompare(true, true);
--- a/js/src/tests/js1_8_5/extensions/jstests.list
+++ b/js/src/tests/js1_8_5/extensions/jstests.list
@@ -7,8 +7,9 @@ skip-if(!xulRuntime.shell) script worker
skip-if(!xulRuntime.shell) script worker-init.js
skip-if(!xulRuntime.shell) script worker-simple.js
skip-if(!xulRuntime.shell) script worker-terminate.js
skip-if(!xulRuntime.shell) script worker-timeout.js
script scripted-proxies.js
script array-length-protochange.js
script parseInt-octal.js
script proxy-enumerateOwn-duplicates.js
+script destructure-accessor.js
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/lib/array-compare.js
@@ -0,0 +1,24 @@
+// Library file for tests to load.
+
+function SameValue(v1, v2)
+{
+ if (v1 === 0 && v2 === 0)
+ return 1 / v1 === 1 / v2;
+ if (v1 !== v1 && v2 !== v2)
+ return true;
+ return v1 === v2;
+}
+
+function arraysEqual(a1, a2)
+{
+ var len1 = a1.length, len2 = a2.length;
+ if (len1 !== len2)
+ return false;
+ for (var i = 0; i < len1; i++)
+ {
+ if (!SameValue(a1[i], a2[i]))
+ return false;
+ }
+ return true;
+}
+
--- a/js/src/trace-test/tests/arguments/args6.js
+++ b/js/src/trace-test/tests/arguments/args6.js
@@ -1,22 +1,22 @@
actual = '';
-expected = '5,';
+expected = '6,';
// tracing length
var g = 0;
function h(args) {
g = args.length;
}
function f() {
h(arguments);
}
for (var i = 0; i < 5; ++i) {
- f(10, 20, 30, 40, 50);
+ f(10, 20, 30, 40, 50, 60);
}
appendToActual(g);
assertEq(actual, expected)
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/arguments/nonstrict-args.js
@@ -0,0 +1,24 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+load(libdir + 'array-compare.js');
+
+var obj = {};
+
+function args(a) { return arguments; }
+
+var a1, a2, a3, a4;
+
+for (var i = 0; i < 5; i++)
+{
+ a1 = args();
+ a2 = args(1);
+ a3 = args(1, obj);
+ a4 = args("foopy");
+}
+
+assertEq(arraysEqual(a1, []), true);
+assertEq(arraysEqual(a2, [1]), true);
+assertEq(arraysEqual(a3, [1, obj]), true);
+assertEq(arraysEqual(a4, ["foopy"]), true);
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/arguments/nonstrict-assign-element-get-parameter.js
@@ -0,0 +1,13 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+function assignElementGetParameter(a)
+{
+ arguments[0] = 17;
+ return a;
+}
+
+for (var i = 0; i < 5; i++)
+ assertEq(assignElementGetParameter(42), 17);
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/arguments/nonstrict-assign-parameter-get-element.js
@@ -0,0 +1,13 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+function assignParameterGetElement(a)
+{
+ a = 17;
+ return arguments[0];
+}
+
+for (var i = 0; i < 5; i++)
+ assertEq(assignParameterGetElement(42), 17);
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/arguments/nonstrict-assign.js
@@ -0,0 +1,17 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+load(libdir + 'array-compare.js');
+
+function assign(a)
+{
+ a = 17;
+ return arguments;
+}
+
+var a1;
+for (var i = 0; i < 5; i++)
+ a1 = assign(1);
+
+assertEq(arraysEqual(a1, [17]), true);
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/arguments/nonstrict-later-assign.js
@@ -0,0 +1,18 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+load(libdir + 'array-compare.js');
+
+function getLaterAssign(a)
+{
+ var o = arguments;
+ a = 17;
+ return o;
+}
+
+var a1, a2;
+for (var i = 0; i < 5; i++)
+ a1 = getLaterAssign(1);
+
+assertEq(arraysEqual(a1, [17]), true);
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/arguments/nonstrict-noargs.js
@@ -0,0 +1,21 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+load(libdir + 'array-compare.js');
+
+var obj = {};
+
+function noargs() { return arguments; }
+
+var a1, a2, a3;
+for (var i = 0; i < 5; i++)
+{
+ a1 = noargs();
+ a2 = noargs(1);
+ a3 = noargs(2, obj, 8);
+}
+
+assertEq(arraysEqual(a1, []), true);
+assertEq(arraysEqual(a2, [1]), true);
+assertEq(arraysEqual(a3, [2, obj, 8]), true);
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/arguments/strict-args.js
@@ -0,0 +1,25 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+load(libdir + 'array-compare.js');
+
+var obj = {};
+
+function strictArgs(a)
+{
+ "use strict";
+ return arguments;
+}
+
+var a1, a2, a3;
+for (var i = 0; i < 5; i++)
+{
+ a1 = strictArgs();
+ a2 = strictArgs(1);
+ a3 = strictArgs(1, obj);
+}
+
+assertEq(arraysEqual(a1, []), true);
+assertEq(arraysEqual(a2, [1]), true);
+assertEq(arraysEqual(a3, [1, obj]), true);
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/arguments/strict-assign-after.js
@@ -0,0 +1,28 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+load(libdir + 'array-compare.js');
+
+var obj = {};
+
+var upper;
+function strictAssignAfter(a)
+{
+ "use strict";
+ upper = arguments;
+ a = 42;
+ return upper;
+}
+
+var a1, a2, a3;
+for (var i = 0; i < 5; i++)
+{
+ a1 = strictAssignAfter();
+ a2 = strictAssignAfter(17);
+ a3 = strictAssignAfter(obj);
+}
+
+assertEq(arraysEqual(a1, []), true);
+assertEq(arraysEqual(a2, [17]), true);
+assertEq(arraysEqual(a3, [obj]), true);
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/arguments/strict-assign-arguments-element.js
@@ -0,0 +1,21 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+load(libdir + 'array-compare.js');
+
+var obj = {};
+
+function strictAssignArgumentsElement(a)
+{
+ "use strict";
+ arguments[0] = 42;
+ return a;
+}
+
+for (var i = 0; i < 5; i++)
+{
+ assertEq(strictAssignArgumentsElement(), undefined);
+ assertEq(strictAssignArgumentsElement(obj), obj);
+ assertEq(strictAssignArgumentsElement(17), 17);
+}
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/arguments/strict-assign-outer-param-psych.js
@@ -0,0 +1,27 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+load(libdir + 'array-compare.js');
+
+var obj = {};
+
+function strictAssignOuterParamPSYCH(p)
+{
+ "use strict";
+ function inner(p) { p = 17; }
+ inner();
+ return arguments;
+}
+
+var a1, a2, a3;
+for (var i = 0; i < 5; i++)
+{
+ a1 = strictAssignOuterParamPSYCH();
+ a2 = strictAssignOuterParamPSYCH(17);
+ a3 = strictAssignOuterParamPSYCH(obj);
+}
+
+assertEq(arraysEqual(a1, []), true);
+assertEq(arraysEqual(a2, [17]), true);
+assertEq(arraysEqual(a3, [obj]), true);
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/arguments/strict-assign-outer-param.js
@@ -0,0 +1,27 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+load(libdir + 'array-compare.js');
+
+var obj = {};
+
+function strictAssignOuterParam(p)
+{
+ "use strict";
+ function inner() { p = 17; }
+ inner();
+ return arguments;
+}
+
+var a1, a2, a3;
+for (var i = 0; i < 5; i++)
+{
+ a1 = strictAssignOuterParam();
+ a2 = strictAssignOuterParam(42);
+ a3 = strictAssignOuterParam(obj);
+}
+
+assertEq(arraysEqual(a1, []), true);
+assertEq(arraysEqual(a2, [42]), true);
+assertEq(arraysEqual(a3, [obj]), true);
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/arguments/strict-assign-parameter-get-element.js
@@ -0,0 +1,14 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+function strictAssignParameterGetElement(a)
+{
+ "use strict";
+ a = 17;
+ return arguments[0];
+}
+
+for (var i = 0; i < 5; i++)
+ assertEq(strictAssignParameterGetElement(42), 42);
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/arguments/strict-assign.js
@@ -0,0 +1,26 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+load(libdir + 'array-compare.js');
+
+var obj = {};
+
+function strictAssign(a)
+{
+ "use strict";
+ a = 17;
+ return arguments;
+}
+
+var a1, a2, a3;
+for (var i = 0; i < 5; i++)
+{
+ a1 = strictAssign();
+ a2 = strictAssign(1);
+ a3 = strictAssign(1, obj);
+}
+
+assertEq(arraysEqual(a1, []), true);
+assertEq(arraysEqual(a2, [1]), true);
+assertEq(arraysEqual(a3, [1, obj]), true);
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/arguments/strict-eval-mutation.js
@@ -0,0 +1,24 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+load(libdir + 'array-compare.js');
+
+var obj = {};
+
+function strictEvalMutation(code)
+{
+ "use strict";
+ return eval(code);
+}
+
+var a1, a2;
+for (var i = 0; i < 5; i++)
+{
+ a1 = strictEvalMutation("code = 17; arguments");
+ a2 = strictEvalMutation("arguments[0] = 17; arguments");
+ assertEq(strictEvalMutation("arguments[0] = 17; code"), "arguments[0] = 17; code");
+}
+
+assertEq(arraysEqual(a1, ["code = 17; arguments"]), true);
+assertEq(arraysEqual(a2, [17]), true);
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/arguments/strict-eval.js
@@ -0,0 +1,30 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+load(libdir + 'array-compare.js');
+
+function strictEval(code, p)
+{
+ "use strict";
+ eval(code);
+ return arguments;
+}
+
+var a1, a2, a3, a4, a5, a6;
+for (var i = 0; i < 5; i++)
+{
+ a1 = strictEval("1", 2);
+ a2 = strictEval("arguments");
+ a3 = strictEval("p = 2");
+ a4 = strictEval("p = 2", 17);
+ a5 = strictEval("arguments[0] = 17");
+ a6 = strictEval("arguments[0] = 17", 42);
+}
+
+assertEq(arraysEqual(a1, ["1", 2]), true);
+assertEq(arraysEqual(a2, ["arguments"]), true);
+assertEq(arraysEqual(a3, ["p = 2"]), true);
+assertEq(arraysEqual(a4, ["p = 2", 17]), true);
+assertEq(arraysEqual(a5, [17]), true);
+assertEq(arraysEqual(a6, [17, 42]), true);
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/arguments/strict-maybe-assign-outer.js
@@ -0,0 +1,26 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+load(libdir + 'array-compare.js');
+
+var obj = {};
+
+function strictMaybeAssignOuterParam(p)
+{
+ "use strict";
+ function inner() { p = 17; }
+ return arguments;
+}
+
+var a1, a2, a3;
+for (var i = 0; i < 5; i++)
+{
+ a1 = strictMaybeAssignOuterParam();
+ a2 = strictMaybeAssignOuterParam(17);
+ a3 = strictMaybeAssignOuterParam(obj);
+}
+
+assertEq(arraysEqual(a1, []), true);
+assertEq(arraysEqual(a2, [17]), true);
+assertEq(arraysEqual(a3, [obj]), true);
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/arguments/strict-maybe-nested-eval.js
@@ -0,0 +1,26 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+load(libdir + 'array-compare.js');
+
+function strictMaybeNestedEval(code, p)
+{
+ "use strict";
+ function inner() { eval(code); }
+ return arguments;
+}
+
+var a1, a2, a3, a4;
+for (var i = 0; i < 5; i++)
+{
+ a1 = strictMaybeNestedEval("1", 2);
+ a2 = strictMaybeNestedEval("arguments");
+ a3 = strictMaybeNestedEval("p = 2");
+ a4 = strictMaybeNestedEval("p = 2", 17);
+}
+
+assertEq(arraysEqual(a1, ["1", 2]), true);
+assertEq(arraysEqual(a2, ["arguments"]), true);
+assertEq(arraysEqual(a3, ["p = 2"]), true);
+assertEq(arraysEqual(a4, ["p = 2", 17]), true);
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/arguments/strict-nested-assign-shadow-function-call.js
@@ -0,0 +1,33 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+load(libdir + 'array-compare.js');
+
+var obj = {};
+
+function strictNestedAssignShadowFunctionCall(p)
+{
+ "use strict";
+ function inner()
+ {
+ function p() { }
+ p = 1776;
+ }
+ inner();
+ return arguments;
+}
+
+var a1, a2, a3, a4;
+for (var i = 0; i < 5; i++)
+{
+ a1 = strictNestedAssignShadowFunctionCall();
+ a2 = strictNestedAssignShadowFunctionCall(99);
+ a3 = strictNestedAssignShadowFunctionCall("");
+ a4 = strictNestedAssignShadowFunctionCall(obj);
+}
+
+assertEq(arraysEqual(a1, []), true);
+assertEq(arraysEqual(a2, [99]), true);
+assertEq(arraysEqual(a3, [""]), true);
+assertEq(arraysEqual(a4, [obj]), true);
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/arguments/strict-nested-assign-shadow-function-name.js
@@ -0,0 +1,33 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+load(libdir + 'array-compare.js');
+
+var obj = {};
+
+function strictNestedAssignShadowFunctionName(p)
+{
+ "use strict";
+ function inner()
+ {
+ function p() { p = 1776; }
+ p();
+ }
+ inner();
+ return arguments;
+}
+
+var a1, a2, a3, a4, a5;
+for (var i = 0; i < 5; i++)
+{
+ a1 = strictNestedAssignShadowFunctionName();
+ a2 = strictNestedAssignShadowFunctionName(99);
+ a3 = strictNestedAssignShadowFunctionName("");
+ a4 = strictNestedAssignShadowFunctionName(obj);
+}
+
+assertEq(arraysEqual(a1, []), true);
+assertEq(arraysEqual(a2, [99]), true);
+assertEq(arraysEqual(a3, [""]), true);
+assertEq(arraysEqual(a4, [obj]), true);
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/arguments/strict-nested-assign-shadow-function.js
@@ -0,0 +1,32 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+load(libdir + 'array-compare.js');
+
+var obj = {};
+
+function strictNestedAssignShadowFunction(p)
+{
+ "use strict";
+ function inner()
+ {
+ function p() { }
+ p = 1776;
+ }
+ return arguments;
+}
+
+var a1, a2, a3, a4;
+for (var i = 0; i < 5; i++)
+{
+ a1 = strictNestedAssignShadowFunction();
+ a2 = strictNestedAssignShadowFunction(99);
+ a3 = strictNestedAssignShadowFunction("");
+ a4 = strictNestedAssignShadowFunction(obj);
+}
+
+assertEq(arraysEqual(a1, []), true);
+assertEq(arraysEqual(a2, [99]), true);
+assertEq(arraysEqual(a3, [""]), true);
+assertEq(arraysEqual(a4, [obj]), true);
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/arguments/strict-nested-assign-shadowed-catch-call.js
@@ -0,0 +1,39 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+load(libdir + 'array-compare.js');
+
+var obj = {};
+
+function strictNestedAssignShadowCatchCall(p)
+{
+ "use strict";
+ function inner()
+ {
+ try
+ {
+ }
+ catch (p)
+ {
+ var f = function innermost() { p = 1776; return 12; };
+ f();
+ }
+ }
+ inner();
+ return arguments;
+}
+
+var a1, a2, a3, a4;
+for (var i = 0; i < 5; i++)
+{
+ a1 = strictNestedAssignShadowCatchCall();
+ a2 = strictNestedAssignShadowCatchCall(99);
+ a3 = strictNestedAssignShadowCatchCall("");
+ a4 = strictNestedAssignShadowCatchCall(obj);
+}
+
+assertEq(arraysEqual(a1, []), true);
+assertEq(arraysEqual(a2, [99]), true);
+assertEq(arraysEqual(a3, [""]), true);
+assertEq(arraysEqual(a4, [obj]), true);
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/arguments/strict-nested-assign-shadowed-catch.js
@@ -0,0 +1,39 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+load(libdir + 'array-compare.js');
+
+var obj = {};
+
+function strictNestedAssignShadowCatch(p)
+{
+ "use strict";
+ function inner()
+ {
+ try
+ {
+ }
+ catch (p)
+ {
+ var f = function innermost() { p = 1776; return 12; };
+ f();
+ }
+ }
+ return arguments;
+}
+
+var a1, a2, a3, a4;
+for (var i = 0; i < 5; i++)
+{
+ a1 = strictNestedAssignShadowCatch();
+ a2 = strictNestedAssignShadowCatch(99);
+ a3 = strictNestedAssignShadowCatch("");
+ a4 = strictNestedAssignShadowCatch(obj);
+}
+
+assertEq(arraysEqual(a1, []), true);
+assertEq(arraysEqual(a2, [99]), true);
+assertEq(arraysEqual(a3, [""]), true);
+assertEq(arraysEqual(a4, [obj]), true);
+
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/arguments/strict-nested-assign-shadowed-var.js
@@ -0,0 +1,29 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+load(libdir + 'array-compare.js');
+
+var obj = {};
+
+/********************
+ * STRICT ARGUMENTS *
+ ********************/
+
+function strictNestedAssignShadowVar(p)
+{
+ "use strict";
+ function inner()
+ {
+ var p = 12;
+ function innermost() { p = 1776; return 12; }
+ return innermost();
+ }
+ return arguments;
+}
+
+assertEq(arraysEqual(strictNestedAssignShadowVar(), []), true);
+assertEq(arraysEqual(strictNestedAssignShadowVar(99), [99]), true);
+assertEq(arraysEqual(strictNestedAssignShadowVar(""), [""]), true);
+assertEq(arraysEqual(strictNestedAssignShadowVar(obj), [obj]), true);
+
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/arguments/strict-nested-eval.js
@@ -0,0 +1,31 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+load(libdir + 'array-compare.js');
+
+function strictNestedEval(code, p)
+{
+ "use strict";
+ function inner() { eval(code); }
+ inner();
+ return arguments;
+}
+
+var a1, a2, a3, a4, a5, a6;
+for (var i = 0; i < 5; i++)
+{
+ a1 = strictNestedEval("1", 2);
+ a2 = strictNestedEval("arguments");
+ a3 = strictNestedEval("p = 2");
+ a4 = strictNestedEval("p = 2", 17);
+ a5 = strictNestedEval("arguments[0] = 17");
+ a6 = strictNestedEval("arguments[0] = 17", 42);
+}
+
+assertEq(arraysEqual(a1, ["1", 2]), true);
+assertEq(arraysEqual(a2, ["arguments"]), true);
+assertEq(arraysEqual(a3, ["p = 2"]), true);
+assertEq(arraysEqual(a4, ["p = 2", 17]), true);
+assertEq(arraysEqual(a5, ["arguments[0] = 17"]), true);
+assertEq(arraysEqual(a6, ["arguments[0] = 17", 42]), true);
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/arguments/strict-nested-shadow-eval.js
@@ -0,0 +1,30 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+load(libdir + 'array-compare.js');
+
+function strictNestedShadowEval(code, p)
+{
+ "use strict";
+ function inner(p) { eval(code); }
+ return arguments;
+}
+
+var a1, a2, a3, a4, a5, a6;
+for (var i = 0; i < 5; i++)
+{
+ a1 = strictNestedShadowEval("1", 2);
+ a2 = strictNestedShadowEval("arguments");
+ a3 = strictNestedShadowEval("p = 2");
+ a4 = strictNestedShadowEval("p = 2", 17);
+ a5 = strictNestedShadowEval("arguments[0] = 17");
+ a6 = strictNestedShadowEval("arguments[0] = 17", 42);
+}
+
+assertEq(arraysEqual(a1, ["1", 2]), true);
+assertEq(arraysEqual(a2, ["arguments"]), true);
+assertEq(arraysEqual(a3, ["p = 2"]), true);
+assertEq(arraysEqual(a4, ["p = 2", 17]), true);
+assertEq(arraysEqual(a5, ["arguments[0] = 17"]), true);
+assertEq(arraysEqual(a6, ["arguments[0] = 17", 42]), true);
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/arguments/strict-nested-shadow-maybe-eval.js
@@ -0,0 +1,30 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+load(libdir + 'array-compare.js');
+
+function strictNestedShadowMaybeEval(code, p)
+{
+ "use strict";
+ function inner(p) { eval(code); }
+ return arguments;
+}
+
+var a1, a2, a3, a4, a5, a6;
+for (var i = 0; i < 5; i++)
+{
+ a1 = strictNestedShadowMaybeEval("1", 2);
+ a2 = strictNestedShadowMaybeEval("arguments");
+ a3 = strictNestedShadowMaybeEval("p = 2");
+ a4 = strictNestedShadowMaybeEval("p = 2", 17);
+ a5 = strictNestedShadowMaybeEval("arguments[0] = 17");
+ a6 = strictNestedShadowMaybeEval("arguments[0] = 17", 42);
+}
+
+assertEq(arraysEqual(a1, ["1", 2]), true);
+assertEq(arraysEqual(a2, ["arguments"]), true);
+assertEq(arraysEqual(a3, ["p = 2"]), true);
+assertEq(arraysEqual(a4, ["p = 2", 17]), true);
+assertEq(arraysEqual(a5, ["arguments[0] = 17"]), true);
+assertEq(arraysEqual(a6, ["arguments[0] = 17", 42]), true);
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/arguments/strict-noargs.js
@@ -0,0 +1,25 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+load(libdir + 'array-compare.js');
+
+var obj = {};
+
+function strictNoargs()
+{
+ "use strict";
+ return arguments;
+}
+
+var a1, a2, a3;
+for (var i = 0; i < 5; i++)
+{
+ a1 = strictNoargs();
+ a2 = strictNoargs(1);
+ a3 = strictNoargs(1, obj);
+}
+
+assertEq(arraysEqual(a1, []), true);
+assertEq(arraysEqual(a2, [1]), true);
+assertEq(arraysEqual(a3, [1, obj]), true);
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/basic/bug570663-1.js
@@ -0,0 +1,4 @@
+// don't crash
+for (var a = 0; a < 4; a++) {
+ switch (NaN) {}
+}
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/basic/bug570663-2.js
@@ -0,0 +1,12 @@
+function f() {
+ var x;
+ for (var a = 0; a < 4; a++) {
+ switch (NaN) {
+ default:
+ x = a;
+ }
+ }
+ assertEq(x, 3);
+}
+
+f();
--- a/js/src/trace-test/tests/basic/bug584565.js
+++ b/js/src/trace-test/tests/basic/bug584565.js
@@ -1,10 +1,10 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
-// Contributor: Luke Wagner <lw@mozilla.com>
-
-var x, f;
-for (var i = 0; i < 100; i++) {
- f = function() {};
- f.foo;
- x = f.length;
-}
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+// Contributor: Luke Wagner <lw@mozilla.com>
+
+var x, f;
+for (var i = 0; i < 100; i++) {
+ f = function() {};
+ f.foo;
+ x = f.length;
+}
--- a/js/src/trace-test/tests/basic/testAssignmentThatIgnoresSetterRetval.js
+++ b/js/src/trace-test/tests/basic/testAssignmentThatIgnoresSetterRetval.js
@@ -1,10 +1,10 @@
var o = {
- set x() {
+ set x(v) {
return 42;
}
};
for (var i = 0; i < 10; ++i) {
var z = o.x = "choose me";
assertEq(z, "choose me");
}
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/basic/testReconstructImacroPCStack.js
@@ -0,0 +1,23 @@
+var actual = "";
+var expect = "TypeError: x is not a function";
+
+x = Proxy.create((function () {
+ return {
+ get: function () {}
+ }
+}()), Object.e)
+
+try {
+ Function("\
+ for(var a = 0; a < 2; ++a) {\
+ if (a == 0) {}\
+ else {\
+ x > x\
+ }\
+ }\
+ ")()
+} catch (e) {
+ actual = "" + e;
+}
+
+assertEq(actual, expect);
--- a/js/src/xpconnect/idl/nsIXPConnect.idl
+++ b/js/src/xpconnect/idl/nsIXPConnect.idl
@@ -394,41 +394,31 @@ interface nsIXPCFunctionThisTranslator :
%{ C++
// For use with the service manager
// {CB6593E0-F9B2-11d2-BDD6-000064657374}
#define NS_XPCONNECT_CID \
{ 0xcb6593e0, 0xf9b2, 0x11d2, \
{ 0xbd, 0xd6, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74 } }
%}
-[uuid(4abf8614-2b0c-495f-8c67-97115470b53c)]
+[uuid(7a3c8687-6f52-47d5-9b8e-2ed8bf86c415)]
interface nsIXPConnect : nsISupports
{
%{ C++
NS_DEFINE_STATIC_CID_ACCESSOR(NS_XPCONNECT_CID)
%}
/**
* Initializes classes on a global object that has already been created.
*/
void
initClasses(in JSContextPtr aJSContext,
in JSObjectPtr aGlobalJSObj);
/**
- * Like initClasses, but only does some of the initialization on the
- * existing global. In particular this function assumes that the outer
- * window has already been connected to an inner window, so
- * re-initializing things like XPCNativeWrapper is useless.
- */
- void
- initClassesForOuterObject(in JSContextPtr aJSContext,
- in JSObjectPtr aGlobalJSObj);
-
- /**
* Creates a new global object using the given aCOMObj as the global
* object. The object will be set up according to the flags (defined
* below). If you do not pass INIT_JS_STANDARD_CLASSES, then aCOMObj
* must implement nsIXPCScriptable so it can resolve the standard
* classes when asked by the JS engine.
*
* @param aJSContext the context to use while creating the global object.
* @param aCOMObj the native object that represents the global object.
@@ -890,9 +880,16 @@ interface nsIXPConnect : nsISupports
*/
virtual nsIPrincipal* GetPrincipal(JSObject* obj,
PRBool allowShortCircuit) const = 0;
#endif
%}
[notxpcom] void getNativeWrapperGetPropertyOp(out JSPropertyOp getProperty);
[notxpcom] void getXrayWrapperPropertyHolderGetPropertyOp(out JSPropertyOp getProperty);
+
+ /**
+ * Creates a JS object holder around aObject that will hold the object
+ * alive for as long as the holder stays alive.
+ */
+ nsIXPConnectJSObjectHolder holdObject(in JSContextPtr aJSContext,
+ in JSObjectPtr aObject);
};
--- a/js/src/xpconnect/src/XPCNativeWrapper.cpp
+++ b/js/src/xpconnect/src/XPCNativeWrapper.cpp
@@ -742,42 +742,16 @@ XPC_NW_Construct(JSContext *cx, JSObject
static JSBool
XPC_NW_HasInstance(JSContext *cx, JSObject *obj, const jsval *valp, JSBool *bp)
{
return JS_TRUE;
}
static JSBool
-MirrorWrappedNativeParent(JSContext *cx, XPCWrappedNative *wrapper,
- JSObject **result NS_OUTPARAM)
-{
- JSObject *wn_parent = wrapper->GetFlatJSObject()->getParent();
- if (!wn_parent) {
- *result = nsnull;
- } else {
- XPCWrappedNative *parent_wrapper =
- XPCWrappedNative::GetAndMorphWrappedNativeOfJSObject(cx, wn_parent);
-
- // parent_wrapper can be null if we're in a Components.utils.evalInSandbox
- // scope. In that case, the best we can do is just use the
- // non-native-wrapped sandbox global object for our parent.
- if (parent_wrapper) {
- *result = XPCNativeWrapper::GetNewOrUsed(cx, parent_wrapper, nsnull,
- nsnull);
- if (!*result)
- return JS_FALSE;
- } else {
- *result = nsnull;
- }
- }
- return JS_TRUE;
-}
-
-static JSBool
XPCNativeWrapperCtor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
jsval *rval)
{
JSStackFrame *fp = nsnull;
JSBool constructing = JS_FALSE;
if (JS_FrameIterator(cx, &fp) && JS_IsConstructorFrame(cx, fp)) {
constructing = JS_TRUE;
@@ -1104,48 +1078,25 @@ XPCNativeWrapper::GetNewOrUsed(JSContext
return nsnull;
}
JSObject *obj = wrapper->GetWrapper();
if (obj) {
return obj;
}
- JSObject *nw_parent;
- if (!MirrorWrappedNativeParent(cx, wrapper, &nw_parent)) {
- return nsnull;
- }
-
- PRBool lock;
-
- if (!nw_parent) {
- nw_parent = wrapper->GetScope()->GetGlobalJSObject();
-
- lock = PR_FALSE;
- } else {
- lock = PR_TRUE;
- }
-
- if (lock) {
- // Make sure nw_parent doesn't get collected while we're creating
- // the new wrapper.
- ::JS_LockGCThing(cx, nw_parent);
- }
+ JSObject *nw_parent = wrapper->GetScope()->GetGlobalJSObject();
bool call = NATIVE_HAS_FLAG(wrapper, WantCall) ||
NATIVE_HAS_FLAG(wrapper, WantConstruct);
- obj = ::JS_NewObjectWithGivenProto(cx, GetJSClass(call), nsnull, nw_parent);
-
- if (lock) {
- ::JS_UnlockGCThing(cx, nw_parent);
- }
+ obj = JS_NewObjectWithGivenProto(cx, GetJSClass(call), nsnull, nw_parent);
if (!obj ||
- !::JS_SetPrivate(cx, obj, wrapper) ||
- !::JS_SetReservedSlot(cx, obj, 0, JSVAL_ZERO)) {
+ !JS_SetPrivate(cx, obj, wrapper) ||
+ !JS_SetReservedSlot(cx, obj, 0, JSVAL_ZERO)) {
return nsnull;
}
wrapper->SetWrapper(obj);
#if defined(DEBUG_XPCNativeWrapper) || defined(DEBUG_xpc_leaks)
{
XPCCallContext ccx(NATIVE_CALLER, cx);
@@ -1180,40 +1131,20 @@ XPCNativeWrapper::CreateExplicitWrapper(
JS_NewObjectWithGivenProto(cx, XPCNativeWrapper::GetJSClass(call), nsnull,
wrappedNative->GetScope()->GetGlobalJSObject());
if (!wrapperObj) {
// JS_NewObject already threw (or reported OOM).
return JS_FALSE;
}
- if (!::JS_SetReservedSlot(cx, wrapperObj, 0, INT_TO_JSVAL(FLAG_EXPLICIT))) {
+ if (!JS_SetReservedSlot(cx, wrapperObj, 0, INT_TO_JSVAL(FLAG_EXPLICIT))) {
return JS_FALSE;
}
- JSObject *parent = nsnull;
-
- // Make sure wrapperObj doesn't get collected while we're wrapping
- // parents for it.
- JS_LockGCThing(cx, wrapperObj);
-
- // A deep XPCNativeWrapper has a parent chain that mirrors its
- // XPCWrappedNative's chain.
- if (!MirrorWrappedNativeParent(cx, wrappedNative, &parent))
- return JS_FALSE;
-
- JS_UnlockGCThing(cx, wrapperObj);
-
- if (!parent) {
- parent = wrappedNative->GetScope()->GetGlobalJSObject();
- }
-
- if (!JS_SetParent(cx, wrapperObj, parent))
- return JS_FALSE;
-
// Set the XPCWrappedNative as private data in the native wrapper.
if (!JS_SetPrivate(cx, wrapperObj, wrappedNative)) {
return JS_FALSE;
}
#if defined(DEBUG_XPCNativeWrapper) || defined(DEBUG_xpc_leaks)
{
XPCCallContext ccx(JS_CALLER, cx);
--- a/js/src/xpconnect/src/XPCWrapper.h
+++ b/js/src/xpconnect/src/XPCWrapper.h
@@ -104,23 +104,16 @@ CanAccessWrapper(JSContext *cx, JSObject
inline JSBool
ClassNeedsXOW(const char *name)
{
switch (*name) {
case 'W':
return strcmp(++name, "indow") == 0;
case 'L':
return strcmp(++name, "ocation") == 0;
- case 'H':
- if (strncmp(++name, "TML", 3))
- break;
- name += 3;
- if (*name == 'I')
- ++name;
- return strcmp(name, "FrameElement") == 0;
default:
break;
}
return JS_FALSE;
}
} // namespace XPCCrossOriginWrapper
--- a/js/src/xpconnect/src/nsXPConnect.cpp
+++ b/js/src/xpconnect/src/nsXPConnect.cpp
@@ -1037,35 +1037,16 @@ nsXPConnect::InitClasses(JSContext * aJS
}
if (!InitWebGLTypes(ccx, aGlobalJSObj))
return UnexpectedFailure(NS_ERROR_FAILURE);
return NS_OK;
}
-/* void initClassesForOuterObject (in JSContextPtr aJSContext, in JSObjectPtr aGlobalJSObj); */
-NS_IMETHODIMP nsXPConnect::InitClassesForOuterObject(JSContext * aJSContext, JSObject * aGlobalJSObj)
-{
- // Nest frame chain save/restore in request created by XPCCallContext.
- XPCCallContext ccx(NATIVE_CALLER, aJSContext);
- if(!ccx.IsValid())
- return UnexpectedFailure(NS_ERROR_FAILURE);
- SaveFrame sf(aJSContext);
-
- XPCWrappedNativeScope* scope =
- XPCWrappedNativeScope::GetNewOrUsed(ccx, aGlobalJSObj);
-
- if(!scope)
- return UnexpectedFailure(NS_ERROR_FAILURE);
-
- scope->RemoveWrappedNativeProtos();
- return NS_OK;
-}
-
static JSBool
TempGlobalResolve(JSContext *aJSContext, JSObject *obj, jsid id)
{
JSBool resolved;
return JS_ResolveStandardClass(aJSContext, obj, id, &resolved);
}
static JSClass xpcTempGlobalClass = {
@@ -2781,16 +2762,29 @@ nsXPConnect::GetNativeWrapperGetProperty
{
NS_ASSERTION(XPCNativeWrapper::GetJSClass(true)->getProperty ==
XPCNativeWrapper::GetJSClass(false)->getProperty,
"Call and NoCall XPCNativeWrapper Class must use the same "
"getProperty hook.");
*getPropertyPtr = XPCNativeWrapper::GetJSClass(true)->getProperty;
}
+NS_IMETHODIMP
+nsXPConnect::HoldObject(JSContext *aJSContext, JSObject *aObject,
+ nsIXPConnectJSObjectHolder **aHolder)
+{
+ XPCCallContext ccx(NATIVE_CALLER, aJSContext);
+ XPCJSObjectHolder* objHolder = XPCJSObjectHolder::newHolder(ccx, aObject);
+ if(!objHolder)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(*aHolder = objHolder);
+ return NS_OK;
+}
+
/* These are here to be callable from a debugger */
JS_BEGIN_EXTERN_C
JS_EXPORT_API(void) DumpJSStack()
{
nsresult rv;
nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(), &rv));
if(NS_SUCCEEDED(rv) && xpc)
xpc->DebugDumpJSStack(PR_TRUE, PR_TRUE, PR_FALSE);
--- a/js/src/xpconnect/src/xpcconvert.cpp
+++ b/js/src/xpconnect/src/xpcconvert.cpp
@@ -623,23 +623,25 @@ XPCConvert::JSData2Native(XPCCallContext
break;
}
chars = JS_GetStringChars(str);
*((uint16*)d) = (uint16) chars[0];
break;
}
case nsXPTType::T_JSVAL :
{
- NS_ASSERTION(useAllocator, "trying to convert a jsval to const jsval & without allocator : this would leak");
-
- // The C++ type is (const jsval &), which here means (jsval *).
- jsval *buf = new jsval(s);
- if(!buf)
- return JS_FALSE;
- *((jsval**)d) = buf;
+ if (useAllocator) {
+ // The C++ type is (const jsval &), which here means (jsval *).
+ jsval *buf = new jsval(s);
+ if(!buf)
+ return JS_FALSE;
+ *((jsval**)d) = buf;
+ } else {
+ **((jsval**)d) = s;
+ }
break;
}
default:
if(!type.IsPointer())
{
NS_ERROR("unsupported type");
return JS_FALSE;
}
--- a/js/src/xpconnect/src/xpcwrappednative.cpp
+++ b/js/src/xpconnect/src/xpcwrappednative.cpp
@@ -1645,18 +1645,29 @@ XPCWrappedNative::ReparentWrapperIfFound
NS_ERROR("JS_SetPrototype failed");
return NS_ERROR_FAILURE;
}
}
}
// Now we can just fix up the parent and return the wrapper
- if(aNewParent && !JS_SetParent(ccx, flat, aNewParent))
- return NS_ERROR_FAILURE;
+ if(aNewParent)
+ {
+ if(!JS_SetParent(ccx, flat, aNewParent))
+ return NS_ERROR_FAILURE;
+
+ JSObject *nw;
+ if(wrapper &&
+ (nw = wrapper->GetWrapper()) &&
+ !JS_SetParent(ccx, nw, JS_GetGlobalForObject(ccx, aNewParent)))
+ {
+ return NS_ERROR_FAILURE;
+ }
+ }
*aWrapper = nsnull;
wrapper.swap(*aWrapper);
return NS_OK;
}
#define IS_TEAROFF_CLASS(clazz) \
@@ -1723,22 +1734,18 @@ XPCWrappedNative::GetWrappedNativeOfJSOb
return_wrapper:
JSBool isWN = IS_WN_WRAPPER_OBJECT(cur);
XPCWrappedNative* wrapper =
isWN ? (XPCWrappedNative*) xpc_GetJSPrivate(cur) : nsnull;
if(proto)
{
XPCWrappedNativeProto* wrapper_proto =
isWN ? wrapper->GetProto() : GetSlimWrapperProto(cur);
- XPCWrappedNativeScope* wrapper_scope =
- wrapper_proto ? wrapper_proto->GetScope() :
- wrapper->GetScope();
if(proto != wrapper_proto &&
- (proto->GetScope() != wrapper_scope ||
- !protoClassInfo || !wrapper_proto ||
+ (!protoClassInfo || !wrapper_proto ||
protoClassInfo != wrapper_proto->GetClassInfo()))
continue;
}
if(pobj2)
*pobj2 = isWN ? nsnull : cur;
return wrapper;
}