[JAEGER] Merge for manual backout.

This commit is contained in:
David Mandelin 2010-08-18 12:24:47 -07:00
commit 7b3b9d1f90
120 changed files with 3632 additions and 1194 deletions

View File

@ -4892,7 +4892,7 @@ nsDOMClassInfo::ShutDown()
// Window helper // Window helper
NS_IMETHODIMP NS_IMETHODIMP
nsCommonWindowSH::PreCreate(nsISupports *nativeObj, JSContext *cx, nsInnerWindowSH::PreCreate(nsISupports *nativeObj, JSContext *cx,
JSObject *globalObj, JSObject **parentObj) JSObject *globalObj, JSObject **parentObj)
{ {
// Normally ::PreCreate() is used to give XPConnect the parent // Normally ::PreCreate() is used to give XPConnect the parent
@ -4911,25 +4911,30 @@ nsCommonWindowSH::PreCreate(nsISupports *nativeObj, JSContext *cx,
NS_ASSERTION(sgo, "nativeObj not a global object!"); NS_ASSERTION(sgo, "nativeObj not a global object!");
nsGlobalWindow *win = nsGlobalWindow::FromSupports(nativeObj); nsGlobalWindow *win = nsGlobalWindow::FromSupports(nativeObj);
JSObject *winObj = win->FastGetGlobalJSObject();
if (win->IsOuterWindow()) { if (!winObj) {
win->EnsureInnerWindow(); NS_ASSERTION(win->GetOuterWindowInternal()->IsCreatingInnerWindow(),
"should have a JS object by this point");
return NS_OK;
} }
if (sgo) { *parentObj = winObj;
*parentObj = sgo->GetGlobalJSObject(); return NS_OK;
}
if (*parentObj) { NS_IMETHODIMP
return win->IsChromeWindow() ? NS_OK : NS_SUCCESS_NEEDS_XOW; 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;
} }
// We're most likely being called when the global object is *parentObj = win->GetCurrentInnerWindowInternal()->FastGetGlobalJSObject();
// 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;
return win->IsChromeWindow() ? NS_OK : NS_SUCCESS_NEEDS_XOW; return win->IsChromeWindow() ? NS_OK : NS_SUCCESS_NEEDS_XOW;
} }
@ -6662,9 +6667,9 @@ nsCommonWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
my_cx = (JSContext *)my_context->GetNativeContext(); my_cx = (JSContext *)my_context->GetNativeContext();
} }
JSBool ok; JSBool ok = JS_TRUE;
jsval exn; jsval exn = JSVAL_VOID;
{ if (win->IsInnerWindow()) {
JSAutoRequest transfer(my_cx); JSAutoRequest transfer(my_cx);
JSObject *realObj; JSObject *realObj;

View File

@ -514,8 +514,6 @@ protected:
PRBool *did_resolve); PRBool *did_resolve);
public: public:
NS_IMETHOD PreCreate(nsISupports *nativeObj, JSContext *cx,
JSObject *globalObj, JSObject **parentObj);
#ifdef DEBUG #ifdef DEBUG
NS_IMETHOD PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, NS_IMETHOD PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
JSObject *obj) JSObject *obj)
@ -578,6 +576,8 @@ protected:
static PRBool sResolving; static PRBool sResolving;
public: public:
NS_IMETHOD PreCreate(nsISupports *nativeObj, JSContext *cx,
JSObject *globalObj, JSObject **parentObj);
NS_IMETHOD AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, NS_IMETHOD AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
JSObject *obj, jsid id, jsval *vp, PRBool *_retval); JSObject *obj, jsid id, jsval *vp, PRBool *_retval);
NS_IMETHOD NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, NS_IMETHOD NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
@ -607,6 +607,8 @@ protected:
} }
public: public:
NS_IMETHOD PreCreate(nsISupports *nativeObj, JSContext *cx,
JSObject *globalObj, JSObject **parentObj);
// We WANT_ADDPROPERTY, but are content to inherit it from nsEventReceiverSH. // We WANT_ADDPROPERTY, but are content to inherit it from nsEventReceiverSH.
NS_IMETHOD OuterObject(nsIXPConnectWrappedNative *wrapper, JSContext * cx, NS_IMETHOD OuterObject(nsIXPConnectWrappedNative *wrapper, JSContext * cx,
JSObject * obj, JSObject * *_retval); JSObject * obj, JSObject * *_retval);

View File

@ -1512,7 +1512,8 @@ public:
WindowStateHolder(nsGlobalWindow *aWindow, WindowStateHolder(nsGlobalWindow *aWindow,
nsIXPConnectJSObjectHolder *aHolder, nsIXPConnectJSObjectHolder *aHolder,
nsNavigator *aNavigator, nsNavigator *aNavigator,
nsIXPConnectJSObjectHolder *aOuterProto); nsIXPConnectJSObjectHolder *aOuterProto,
nsIXPConnectJSObjectHolder *aOuterRealProto);
nsGlobalWindow* GetInnerWindow() { return mInnerWindow; } nsGlobalWindow* GetInnerWindow() { return mInnerWindow; }
nsIXPConnectJSObjectHolder *GetInnerWindowHolder() nsIXPConnectJSObjectHolder *GetInnerWindowHolder()
@ -1520,6 +1521,7 @@ public:
nsNavigator* GetNavigator() { return mNavigator; } nsNavigator* GetNavigator() { return mNavigator; }
nsIXPConnectJSObjectHolder* GetOuterProto() { return mOuterProto; } nsIXPConnectJSObjectHolder* GetOuterProto() { return mOuterProto; }
nsIXPConnectJSObjectHolder* GetOuterRealProto() { return mOuterRealProto; }
void DidRestoreWindow() void DidRestoreWindow()
{ {
@ -1528,6 +1530,7 @@ public:
mInnerWindowHolder = nsnull; mInnerWindowHolder = nsnull;
mNavigator = nsnull; mNavigator = nsnull;
mOuterProto = nsnull; mOuterProto = nsnull;
mOuterRealProto = nsnull;
} }
protected: protected:
@ -1539,6 +1542,7 @@ protected:
nsCOMPtr<nsIXPConnectJSObjectHolder> mInnerWindowHolder; nsCOMPtr<nsIXPConnectJSObjectHolder> mInnerWindowHolder;
nsRefPtr<nsNavigator> mNavigator; nsRefPtr<nsNavigator> mNavigator;
nsCOMPtr<nsIXPConnectJSObjectHolder> mOuterProto; nsCOMPtr<nsIXPConnectJSObjectHolder> mOuterProto;
nsCOMPtr<nsIXPConnectJSObjectHolder> mOuterRealProto;
}; };
NS_DEFINE_STATIC_IID_ACCESSOR(WindowStateHolder, WINDOWSTATEHOLDER_IID) NS_DEFINE_STATIC_IID_ACCESSOR(WindowStateHolder, WINDOWSTATEHOLDER_IID)
@ -1546,10 +1550,12 @@ NS_DEFINE_STATIC_IID_ACCESSOR(WindowStateHolder, WINDOWSTATEHOLDER_IID)
WindowStateHolder::WindowStateHolder(nsGlobalWindow *aWindow, WindowStateHolder::WindowStateHolder(nsGlobalWindow *aWindow,
nsIXPConnectJSObjectHolder *aHolder, nsIXPConnectJSObjectHolder *aHolder,
nsNavigator *aNavigator, nsNavigator *aNavigator,
nsIXPConnectJSObjectHolder *aOuterProto) nsIXPConnectJSObjectHolder *aOuterProto,
nsIXPConnectJSObjectHolder *aOuterRealProto)
: mInnerWindow(aWindow), : mInnerWindow(aWindow),
mNavigator(aNavigator), mNavigator(aNavigator),
mOuterProto(aOuterProto) mOuterProto(aOuterProto),
mOuterRealProto(aOuterRealProto)
{ {
NS_PRECONDITION(aWindow, "null window"); NS_PRECONDITION(aWindow, "null window");
NS_PRECONDITION(aWindow->IsInnerWindow(), "Saving an outer window"); NS_PRECONDITION(aWindow->IsInnerWindow(), "Saving an outer window");
@ -1609,9 +1615,6 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
if (IsFrozen()) { if (IsFrozen()) {
// This outer is now getting its first inner, thaw the outer now // This outer is now getting its first inner, thaw the outer now
// that it's ready and is getting an inner window. // that it's ready and is getting an inner window.
mContext->CreateOuterObject(this, aDocument->NodePrincipal());
mContext->DidInitializeContext();
mJSObject = (JSObject *)mContext->GetNativeGlobal();
Thaw(); Thaw();
} }
@ -1725,12 +1728,20 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
JSAutoRequest ar(cx); 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 // Make sure to clear scope on the outer window *before* we
// initialize the new inner window. If we don't, things // initialize the new inner window. If we don't, things
// (Object.prototype etc) could leak from the old outer to the new // (Object.prototype etc) could leak from the old outer to the new
// inner scope. // inner scope.
mContext->ClearScope(mJSObject, PR_FALSE); 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) { if (reUseInnerWindow) {
// We're reusing the current inner window. // We're reusing the current inner window.
NS_ASSERTION(!currentInner->IsFrozen(), NS_ASSERTION(!currentInner->IsFrozen(),
@ -1742,9 +1753,6 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
} }
} else { } else {
if (aState) { if (aState) {
nsCOMPtr<WindowStateHolder> wsh = do_QueryInterface(aState);
NS_ASSERTION(wsh, "What kind of weird state are you giving me here?");
newInnerWindow = wsh->GetInnerWindow(); newInnerWindow = wsh->GetInnerWindow();
mInnerWindowHolder = wsh->GetInnerWindowHolder(); mInnerWindowHolder = wsh->GetInnerWindowHolder();
@ -1818,7 +1826,6 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
getter_AddRefs(holder)); getter_AddRefs(holder));
NS_ASSERTION(NS_SUCCEEDED(rv) && newGlobal && holder, NS_ASSERTION(NS_SUCCEEDED(rv) && newGlobal && holder,
"Failed to get script global and holder"); "Failed to get script global and holder");
newInnerWindow->mJSObject = (JSObject *)newGlobal;
mCreatingInnerWindow = PR_FALSE; mCreatingInnerWindow = PR_FALSE;
Thaw(); Thaw();
@ -1872,6 +1879,45 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
} }
mInnerWindow = newInnerWindow; 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) { if (!aState && !reUseInnerWindow) {
@ -1924,42 +1970,6 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
html_doc); 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) { if (aDocument) {
aDocument->SetScriptGlobalObject(newInnerWindow); aDocument->SetScriptGlobalObject(newInnerWindow);
} }
@ -9073,21 +9083,39 @@ nsGlobalWindow::SaveWindowState(nsISupports **aState)
// to the page. // to the page.
inner->Freeze(); 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 = nsCOMPtr<nsIClassInfo> ci =
do_QueryInterface((nsIScriptGlobalObject *)this); do_QueryInterface((nsIScriptGlobalObject *)this);
nsCOMPtr<nsIXPConnectJSObjectHolder> proto; nsCOMPtr<nsIXPConnectJSObjectHolder> proto;
nsresult rv = nsContentUtils::XPConnect()-> nsresult rv = xpc->GetWrappedNativePrototype(cx, mJSObject, ci,
GetWrappedNativePrototype((JSContext *)mContext->GetNativeContext(), getter_AddRefs(proto));
mJSObject, ci, getter_AddRefs(proto));
NS_ENSURE_SUCCESS(rv, rv); 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, nsCOMPtr<nsISupports> state = new WindowStateHolder(inner,
mInnerWindowHolder, mInnerWindowHolder,
mNavigator, mNavigator,
proto); proto,
realProtoHolder);
NS_ENSURE_TRUE(state, NS_ERROR_OUT_OF_MEMORY); 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 #ifdef DEBUG_PAGE_CACHE
printf("saving window state, state = %p\n", (void*)state); printf("saving window state, state = %p\n", (void*)state);
#endif #endif

View File

@ -383,6 +383,11 @@ public:
// Make sure this matches the casts we do in QueryInterface(). // Make sure this matches the casts we do in QueryInterface().
return (nsGlobalWindow *)(nsIScriptGlobalObject *)supports; 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) static nsGlobalWindow *FromWrapper(nsIXPConnectWrappedNative *wrapper)
{ {
return FromSupports(wrapper->Native()); return FromSupports(wrapper->Native());

View File

@ -344,11 +344,11 @@ public:
* @param aGlobalObject The script global object to use as our global. * @param aGlobalObject The script global object to use as our global.
*/ */
virtual nsresult CreateOuterObject(nsIScriptGlobalObject *aGlobalObject, virtual nsresult CreateOuterObject(nsIScriptGlobalObject *aGlobalObject,
nsIPrincipal *aPrincipal) = 0; nsIScriptGlobalObject *aCurrentInner) = 0;
/** /**
* Prepares this context for use with the current inner window for the * 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; virtual nsresult InitOuterWindow() = 0;

View File

@ -2594,7 +2594,7 @@ nsJSContext::ConnectToInner(nsIScriptGlobalObject *aNewInner, void *aOuterGlobal
{ {
NS_ENSURE_ARG(aNewInner); NS_ENSURE_ARG(aNewInner);
JSObject *newInnerJSObject = (JSObject *)aNewInner->GetScriptGlobal(JAVASCRIPT); JSObject *newInnerJSObject = (JSObject *)aNewInner->GetScriptGlobal(JAVASCRIPT);
JSObject *myobject = (JSObject *)aOuterGlobal; JSObject *outerGlobal = (JSObject *)aOuterGlobal;
// Make the inner and outer window both share the same // Make the inner and outer window both share the same
// prototype. The prototype we share is the outer window's // prototype. The prototype we share is the outer window's
@ -2615,12 +2615,18 @@ nsJSContext::ConnectToInner(nsIScriptGlobalObject *aNewInner, void *aOuterGlobal
// Object.prototype. This way the outer also gets the benefits // Object.prototype. This way the outer also gets the benefits
// of the global scope polluter, and the inner window's // of the global scope polluter, and the inner window's
// Object.prototype. // Object.prototype.
JSObject *proto = ::JS_GetPrototype(mContext, myobject); JSObject *proto = JS_GetPrototype(mContext, outerGlobal);
JSObject *innerProto = ::JS_GetPrototype(mContext, newInnerJSObject); JSObject *innerProto = JS_GetPrototype(mContext, newInnerJSObject);
JSObject *innerProtoProto = ::JS_GetPrototype(mContext, innerProto); JSObject *innerProtoProto = JS_GetPrototype(mContext, innerProto);
::JS_SetPrototype(mContext, newInnerJSObject, proto); JS_SetPrototype(mContext, newInnerJSObject, proto);
::JS_SetPrototype(mContext, proto, innerProtoProto); 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; return NS_OK;
} }
@ -2660,11 +2666,8 @@ nsJSContext::InitContext()
nsresult nsresult
nsJSContext::CreateOuterObject(nsIScriptGlobalObject *aGlobalObject, nsJSContext::CreateOuterObject(nsIScriptGlobalObject *aGlobalObject,
nsIPrincipal *aPrincipal) nsIScriptGlobalObject *aCurrentInner)
{ {
NS_PRECONDITION(!JS_GetGlobalObject(mContext),
"Outer window already initialized");
nsCOMPtr<nsIDOMChromeWindow> chromeWindow(do_QueryInterface(aGlobalObject)); nsCOMPtr<nsIDOMChromeWindow> chromeWindow(do_QueryInterface(aGlobalObject));
PRUint32 flags = 0; PRUint32 flags = 0;
@ -2678,18 +2681,21 @@ nsJSContext::CreateOuterObject(nsIScriptGlobalObject *aGlobalObject,
// need to preserve the <!-- script hiding hack from JS-in-HTML daze // need to preserve the <!-- script hiding hack from JS-in-HTML daze
// (introduced in 1995 for graceful script degradation in Netscape 1, // (introduced in 1995 for graceful script degradation in Netscape 1,
// Mosaic, and other pre-JS browsers). // 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(); nsIXPConnect *xpc = nsContentUtils::XPConnect();
nsCOMPtr<nsIXPConnectJSObjectHolder> holder; nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
nsresult rv = nsresult rv = xpc->WrapNative(mContext, aCurrentInner->GetGlobalJSObject(),
xpc->InitClassesWithNewWrappedGlobal(mContext, aGlobalObject, aGlobalObject, NS_GET_IID(nsISupports),
NS_GET_IID(nsISupports), getter_AddRefs(holder));
aPrincipal, EmptyCString(),
flags, getter_AddRefs(holder));
NS_ENSURE_SUCCESS(rv, rv); 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 // Hold a strong reference to the wrapper for the global to avoid
// rooting and unrooting the global object every time its AddRef() // rooting and unrooting the global object every time its AddRef()
// or Release() methods are called // or Release() methods are called
@ -2708,14 +2714,9 @@ nsJSContext::InitOuterWindow()
// properties will be forwarded to the inner window. // properties will be forwarded to the inner window.
JS_ClearScope(mContext, global); JS_ClearScope(mContext, global);
// Now that the inner and outer windows are connected, tell XPConnect to nsresult rv = NS_OK;
// 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);
nsCOMPtr<nsIClassInfo> ci(do_QueryInterface(sgo)); nsCOMPtr<nsIClassInfo> ci(do_QueryInterface(sgo));
if (ci) { if (ci) {
jsval v; jsval v;

View File

@ -142,7 +142,7 @@ public:
void *aOuterGlobal); void *aOuterGlobal);
virtual nsresult InitContext(); virtual nsresult InitContext();
virtual nsresult CreateOuterObject(nsIScriptGlobalObject *aGlobalObject, virtual nsresult CreateOuterObject(nsIScriptGlobalObject *aGlobalObject,
nsIPrincipal *aPrincipal); nsIScriptGlobalObject *aCurrentInner);
virtual nsresult InitOuterWindow(); virtual nsresult InitOuterWindow();
virtual PRBool IsContextInitialized(); virtual PRBool IsContextInitialized();
virtual void FinalizeContext(); virtual void FinalizeContext();

View File

@ -75,11 +75,11 @@ function run_test() {
do_check_eq(x, '{"bar":{"bar":42,"schmoo":[]},"qux":42,"quux":42}'); do_check_eq(x, '{"bar":{"bar":42,"schmoo":[]},"qux":42,"quux":42}');
var x = JSON.stringify(foo, null, " "); var x = JSON.stringify(foo, null, " ");
do_check_eq(x, '{\n "bar":{\n "bar":42,\n "schmoo":[]\n },\n "qux":42,\n "quux":42\n}'); do_check_eq(x, '{\n "bar": {\n "bar": 42,\n "schmoo": []\n },\n "qux": 42,\n "quux": 42\n}');
foo = {bar:{bar:{}}} foo = {bar:{bar:{}}}
var x = JSON.stringify(foo, null, " "); var x = JSON.stringify(foo, null, " ");
do_check_eq(x, '{\n "bar":{\n "bar":{}\n }\n}'); do_check_eq(x, '{\n "bar": {\n "bar": {}\n }\n}');
var x = JSON.stringify({x:1,arr:[1]}, function (k,v) { return typeof v === 'number' ? 3 : v; }); var x = JSON.stringify({x:1,arr:[1]}, function (k,v) { return typeof v === 'number' ? 3 : v; });
do_check_eq(x, '{"x":3,"arr":[3]}'); do_check_eq(x, '{"x":3,"arr":[3]}');

View File

@ -59,6 +59,8 @@ _TEST_FILES = \
test_clipboard_events.html \ test_clipboard_events.html \
test_focusrings.xul \ test_focusrings.xul \
test_nodesFromRect.html \ test_nodesFromRect.html \
test_frameElementWrapping.html \
file_frameElementWrapping.html \
$(NULL) $(NULL)
libs:: $(_TEST_FILES) libs:: $(_TEST_FILES)

View File

@ -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>

View File

@ -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>

View File

@ -386,9 +386,6 @@ CPPSRCS += Assertions.cpp \
ifeq (86, $(findstring 86,$(TARGET_CPU))) ifeq (86, $(findstring 86,$(TARGET_CPU)))
ifeq (x86_64, $(TARGET_CPU)) ifeq (x86_64, $(TARGET_CPU))
ifeq ($(OS_ARCH),WINNT)
ASFILES += TrampolineMasmX64.asm
endif
#CPPSRCS += only_on_x86_64.cpp #CPPSRCS += only_on_x86_64.cpp
else else
#CPPSRCS += only_on_x86.cpp #CPPSRCS += only_on_x86.cpp
@ -875,17 +872,21 @@ endif
############################################### ###############################################
# BEGIN kludges for the Nitro assembler # BEGIN kludges for the Nitro assembler
# #
## FIXME figure out where INCLUDES is being reset, and move this
## backwards to that point. Ideally, merge with the rest of the
## Nitro assembler stuff, far above.
ifdef ENABLE_METHODJIT
INCLUDES += -I$(srcdir)/assembler -I$(srcdir)/yarr
#
# Needed to "configure" it correctly. Unfortunately these # Needed to "configure" it correctly. Unfortunately these
# flags wind up being applied to all code in js/src, not just # flags wind up being applied to all code in js/src, not just
# the code in js/src/assembler. # the code in js/src/assembler.
CXXFLAGS += -DENABLE_ASSEMBLER=1 -DUSE_SYSTEM_MALLOC=1 -DENABLE_JIT=1 CXXFLAGS += -DUSE_SYSTEM_MALLOC=1
ifeq (android,$(TARGET_VENDOR))
CXXFLAGS += -DWTF_PLATFORM_ANDROID=1
else
CXXFLAGS += -DENABLE_ASSEMBLER=1 -DENABLE_JIT=1
endif
INCLUDES += -I$(srcdir)/assembler -I$(srcdir)/yarr
ifdef ENABLE_METHODJIT
# Build a standalone test program that exercises the assembler # Build a standalone test program that exercises the assembler
# sources a bit. # sources a bit.
TESTMAIN_OBJS = \ TESTMAIN_OBJS = \
@ -901,6 +902,7 @@ TESTMAIN_OBJS = \
TestMain$(HOST_BIN_SUFFIX): $(TESTMAIN_OBJS) TestMain$(HOST_BIN_SUFFIX): $(TESTMAIN_OBJS)
$(CXX) -o TestMain$(HOST_BIN_SUFFIX) $(TESTMAIN_OBJS) $(CXX) -o TestMain$(HOST_BIN_SUFFIX) $(TESTMAIN_OBJS)
endif endif
# #
# END kludges for the Nitro assembler # END kludges for the Nitro assembler
############################################### ###############################################

View File

@ -275,8 +275,8 @@ void ARMAssembler::dataTransfer32(bool isLoad, RegisterID srcDst, RegisterID bas
add_r(ARMRegisters::S0, base, OP2_IMM | (offset >> 12) | (10 << 8)); add_r(ARMRegisters::S0, base, OP2_IMM | (offset >> 12) | (10 << 8));
dtr_u(isLoad, srcDst, ARMRegisters::S0, (offset & 0xfff)); dtr_u(isLoad, srcDst, ARMRegisters::S0, (offset & 0xfff));
} else { } else {
ARMWord reg = getImm(offset, ARMRegisters::S0); moveImm(offset, ARMRegisters::S0);
dtr_ur(isLoad, srcDst, base, reg); dtr_ur(isLoad, srcDst, base, ARMRegisters::S0);
} }
} else { } else {
offset = -offset; offset = -offset;
@ -286,8 +286,8 @@ void ARMAssembler::dataTransfer32(bool isLoad, RegisterID srcDst, RegisterID bas
sub_r(ARMRegisters::S0, base, OP2_IMM | (offset >> 12) | (10 << 8)); sub_r(ARMRegisters::S0, base, OP2_IMM | (offset >> 12) | (10 << 8));
dtr_d(isLoad, srcDst, ARMRegisters::S0, (offset & 0xfff)); dtr_d(isLoad, srcDst, ARMRegisters::S0, (offset & 0xfff));
} else { } else {
ARMWord reg = getImm(offset, ARMRegisters::S0); moveImm(offset, ARMRegisters::S0);
dtr_dr(isLoad, srcDst, base, reg); dtr_dr(isLoad, srcDst, base, ARMRegisters::S0);
} }
} }
} }
@ -301,8 +301,8 @@ void ARMAssembler::dataTransfer8(bool isLoad, RegisterID srcDst, RegisterID base
add_r(ARMRegisters::S0, base, OP2_IMM | (offset >> 12) | (10 << 8)); add_r(ARMRegisters::S0, base, OP2_IMM | (offset >> 12) | (10 << 8));
dtrb_u(isLoad, srcDst, ARMRegisters::S0, (offset & 0xfff)); dtrb_u(isLoad, srcDst, ARMRegisters::S0, (offset & 0xfff));
} else { } else {
ARMWord reg = getImm(offset, ARMRegisters::S0); moveImm(offset, ARMRegisters::S0);
dtrb_ur(isLoad, srcDst, base, reg); dtrb_ur(isLoad, srcDst, base, ARMRegisters::S0);
} }
} else { } else {
offset = -offset; offset = -offset;
@ -312,8 +312,8 @@ void ARMAssembler::dataTransfer8(bool isLoad, RegisterID srcDst, RegisterID base
sub_r(ARMRegisters::S0, base, OP2_IMM | (offset >> 12) | (10 << 8)); sub_r(ARMRegisters::S0, base, OP2_IMM | (offset >> 12) | (10 << 8));
dtrb_d(isLoad, srcDst, ARMRegisters::S0, (offset & 0xfff)); dtrb_d(isLoad, srcDst, ARMRegisters::S0, (offset & 0xfff));
} else { } else {
ARMWord reg = getImm(offset, ARMRegisters::S0); moveImm(offset, ARMRegisters::S0);
dtrb_dr(isLoad, srcDst, base, reg); dtrb_dr(isLoad, srcDst, base, ARMRegisters::S0);
} }
} }
} }

View File

@ -283,6 +283,16 @@ public:
return dataLabel; return dataLabel;
} }
DataLabel32 load64WithAddressOffsetPatch(Address address, RegisterID hi, RegisterID lo)
{
DataLabel32 dataLabel(this);
m_assembler.ldr_un_imm(ARMRegisters::S0, 0);
m_assembler.add_r(ARMRegisters::S0, ARMRegisters::S0, address.base);
m_assembler.dtr_u(true, lo, ARMRegisters::S0, 0);
m_assembler.dtr_u(true, hi, ARMRegisters::S0, 4);
return dataLabel;
}
Label loadPtrWithPatchToLEA(Address address, RegisterID dest) Label loadPtrWithPatchToLEA(Address address, RegisterID dest)
{ {
Label label(this); Label label(this);
@ -312,6 +322,27 @@ public:
return dataLabel; return dataLabel;
} }
DataLabel32 store64WithAddressOffsetPatch(RegisterID hi, RegisterID lo, Address address)
{
DataLabel32 dataLabel(this);
m_assembler.ldr_un_imm(ARMRegisters::S0, 0);
m_assembler.add_r(ARMRegisters::S0, ARMRegisters::S0, address.base);
m_assembler.dtr_u(false, lo, ARMRegisters::S0, 0);
m_assembler.dtr_u(false, hi, ARMRegisters::S0, 4);
return dataLabel;
}
DataLabel32 store64WithAddressOffsetPatch(Imm32 hi, RegisterID lo, Address address)
{
DataLabel32 dataLabel(this);
m_assembler.ldr_un_imm(ARMRegisters::S0, 0);
m_assembler.getImm(hi.m_value, ARMRegisters::S1);
m_assembler.add_r(ARMRegisters::S0, ARMRegisters::S0, address.base);
m_assembler.dtr_u(false, lo, ARMRegisters::S0, 0);
m_assembler.dtr_u(false, ARMRegisters::S1, ARMRegisters::S0, 4);
return dataLabel;
}
void store32(RegisterID src, ImplicitAddress address) void store32(RegisterID src, ImplicitAddress address)
{ {
m_assembler.dataTransfer32(false, src, address.base, address.offset); m_assembler.dataTransfer32(false, src, address.base, address.offset);
@ -437,6 +468,13 @@ public:
return Jump(m_assembler.jmp(ARMCondition(cond), useConstantPool)); return Jump(m_assembler.jmp(ARMCondition(cond), useConstantPool));
} }
// As branch32, but allow the value ('right') to be patched.
Jump branch32WithPatch(Condition cond, RegisterID left, Imm32 right, DataLabel32 &dataLabel)
{
dataLabel = moveWithPatch(right, ARMRegisters::S1);
return branch32(cond, left, ARMRegisters::S1, true);
}
Jump branch32(Condition cond, RegisterID left, Address right) Jump branch32(Condition cond, RegisterID left, Address right)
{ {
load32(right, ARMRegisters::S1); load32(right, ARMRegisters::S1);
@ -822,6 +860,13 @@ public:
return dataLabel; return dataLabel;
} }
DataLabel32 moveWithPatch(Imm32 initialValue, RegisterID dest)
{
DataLabel32 dataLabel(this);
m_assembler.ldr_un_imm(dest, initialValue.m_value);
return dataLabel;
}
Jump branchPtrWithPatch(Condition cond, RegisterID left, DataLabelPtr& dataLabel, ImmPtr initialRightValue = ImmPtr(0)) Jump branchPtrWithPatch(Condition cond, RegisterID left, DataLabelPtr& dataLabel, ImmPtr initialRightValue = ImmPtr(0))
{ {
dataLabel = moveWithPatch(initialRightValue, ARMRegisters::S1); dataLabel = moveWithPatch(initialRightValue, ARMRegisters::S1);
@ -1116,8 +1161,8 @@ protected:
m_assembler.add_r(ARMRegisters::S0, base, ARMAssembler::OP2_IMM | (offset >> 12) | (10 << 8)); m_assembler.add_r(ARMRegisters::S0, base, ARMAssembler::OP2_IMM | (offset >> 12) | (10 << 8));
m_assembler.dtr_u(true, ARMRegisters::S0, ARMRegisters::S0, offset & 0xfff); m_assembler.dtr_u(true, ARMRegisters::S0, ARMRegisters::S0, offset & 0xfff);
} else { } else {
ARMWord reg = m_assembler.getImm(offset, ARMRegisters::S0); m_assembler.moveImm(offset, ARMRegisters::S0);
m_assembler.dtr_ur(true, ARMRegisters::S0, base, reg); m_assembler.dtr_ur(true, ARMRegisters::S0, base, ARMRegisters::S0);
} }
} else { } else {
offset = -offset; offset = -offset;
@ -1127,8 +1172,8 @@ protected:
m_assembler.sub_r(ARMRegisters::S0, base, ARMAssembler::OP2_IMM | (offset >> 12) | (10 << 8)); m_assembler.sub_r(ARMRegisters::S0, base, ARMAssembler::OP2_IMM | (offset >> 12) | (10 << 8));
m_assembler.dtr_d(true, ARMRegisters::S0, ARMRegisters::S0, offset & 0xfff); m_assembler.dtr_d(true, ARMRegisters::S0, ARMRegisters::S0, offset & 0xfff);
} else { } else {
ARMWord reg = m_assembler.getImm(offset, ARMRegisters::S0); m_assembler.moveImm(offset, ARMRegisters::S0);
m_assembler.dtr_dr(true, ARMRegisters::S0, base, reg); m_assembler.dtr_dr(true, ARMRegisters::S0, base, ARMRegisters::S0);
} }
} }
m_assembler.blx(ARMRegisters::S0); m_assembler.blx(ARMRegisters::S0);

View File

@ -894,11 +894,11 @@ on MinGW. See https://bugs.webkit.org/show_bug.cgi?id=29268 */
#if !defined(ENABLE_YARR_JIT) #if !defined(ENABLE_YARR_JIT)
/* YARR supports ARM, x86 & x86-64, and has been tested on Mac and Windows. */ /* YARR supports ARM, x86 & x86-64, and has been tested on Mac and Windows. */
#if WTF_CPU_X86 \ #if (WTF_CPU_X86 \
|| WTF_CPU_X86_64 \ || WTF_CPU_X86_64 \
|| WTF_CPU_ARM_THUMB2 \ || WTF_CPU_ARM_THUMB2 \
|| WTF_CPU_ARM_TRADITIONAL \ || WTF_CPU_ARM_TRADITIONAL \
|| WTF_CPU_X86 || WTF_CPU_X86) && !WTF_PLATFORM_ANDROID
#define ENABLE_YARR_JIT 1 #define ENABLE_YARR_JIT 1
#else #else
#define ENABLE_YARR_JIT 0 #define ENABLE_YARR_JIT 0
@ -906,7 +906,7 @@ on MinGW. See https://bugs.webkit.org/show_bug.cgi?id=29268 */
#endif /* !defined(ENABLE_YARR_JIT) */ #endif /* !defined(ENABLE_YARR_JIT) */
#if ENABLE_JIT || ENABLE_YARR_JIT #if (ENABLE_JIT || ENABLE_YARR_JIT) && !WTF_PLATFORM_ANDROID
#define ENABLE_ASSEMBLER 1 #define ENABLE_ASSEMBLER 1
#endif #endif
/* Setting this flag prevents the assembler from using RWX memory; this may improve /* Setting this flag prevents the assembler from using RWX memory; this may improve

View File

@ -2594,6 +2594,7 @@ arm*-*)
ENABLE_TRACEJIT=1 ENABLE_TRACEJIT=1
NANOJIT_ARCH=ARM NANOJIT_ARCH=ARM
ENABLE_METHODJIT=1 ENABLE_METHODJIT=1
ENABLE_MONOIC=1
AC_DEFINE(JS_CPU_ARM) AC_DEFINE(JS_CPU_ARM)
AC_DEFINE(JS_NUNBOX32) AC_DEFINE(JS_NUNBOX32)
;; ;;

View File

@ -330,4 +330,7 @@ MSG_DEF(JSMSG_CSP_BLOCKED_FUNCTION, 247, 0, JSEXN_ERR, "call to Function() blo
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_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_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_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")

View File

@ -85,6 +85,8 @@ BEGIN_TEST(testContexts_bug563735)
JSBool ok; JSBool ok;
{ {
JSAutoRequest req(cx2); JSAutoRequest req(cx2);
JSAutoCrossCompartmentCall crossCall;
CHECK(crossCall.enter(cx2, global));
jsval v = JSVAL_NULL; jsval v = JSVAL_NULL;
ok = JS_SetProperty(cx2, global, "x", &v); ok = JS_SetProperty(cx2, global, "x", &v);
} }

View File

@ -1687,6 +1687,12 @@ struct JSClass {
#define JSCLASS_MARK_IS_TRACE (1<<(JSCLASS_HIGH_FLAGS_SHIFT+3)) #define JSCLASS_MARK_IS_TRACE (1<<(JSCLASS_HIGH_FLAGS_SHIFT+3))
#define JSCLASS_INTERNAL_FLAG2 (1<<(JSCLASS_HIGH_FLAGS_SHIFT+4)) #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 * ECMA-262 requires that most constructors used internally create objects
* with "the original Foo.prototype value" as their [[Prototype]] (__proto__) * with "the original Foo.prototype value" as their [[Prototype]] (__proto__)
@ -1699,10 +1705,8 @@ struct JSClass {
* prevously allowed, but is now an ES5 violation and thus unsupported. * prevously allowed, but is now an ES5 violation and thus unsupported.
*/ */
#define JSCLASS_GLOBAL_FLAGS \ #define JSCLASS_GLOBAL_FLAGS \
(JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSProto_LIMIT * 3 + 2)) (JSCLASS_IS_GLOBAL | \
JSCLASS_HAS_RESERVED_SLOTS(JSProto_LIMIT * 3 + JSRESERVED_GLOBAL_SLOTS_COUNT))
#define JSRESERVED_GLOBAL_COMPARTMENT (JSProto_LIMIT * 3)
#define JSRESERVED_GLOBAL_THIS (JSRESERVED_GLOBAL_COMPARTMENT + 1)
/* Fast access to the original value of each standard class's prototype. */ /* 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_SHIFT (JSCLASS_HIGH_FLAGS_SHIFT + 8)

View File

@ -231,7 +231,7 @@ js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp)
} }
if (obj->isArguments() && !obj->isArgsLengthOverridden()) { if (obj->isArguments() && !obj->isArgsLengthOverridden()) {
*lengthp = obj->getArgsLength(); *lengthp = obj->getArgsInitialLength();
return true; return true;
} }

View File

@ -148,6 +148,7 @@ const char *const js_common_atom_names[] = {
js_ignoreCase_str, /* ignoreCaseAtom */ js_ignoreCase_str, /* ignoreCaseAtom */
js_index_str, /* indexAtom */ js_index_str, /* indexAtom */
js_input_str, /* inputAtom */ js_input_str, /* inputAtom */
"toISOString", /* toISOStringAtom */
js_iterator_str, /* iteratorAtom */ js_iterator_str, /* iteratorAtom */
js_join_str, /* joinAtom */ js_join_str, /* joinAtom */
js_lastIndex_str, /* lastIndexAtom */ js_lastIndex_str, /* lastIndexAtom */

View File

@ -340,6 +340,7 @@ struct JSAtomState
JSAtom *ignoreCaseAtom; JSAtom *ignoreCaseAtom;
JSAtom *indexAtom; JSAtom *indexAtom;
JSAtom *inputAtom; JSAtom *inputAtom;
JSAtom *toISOStringAtom;
JSAtom *iteratorAtom; JSAtom *iteratorAtom;
JSAtom *joinAtom; JSAtom *joinAtom;
JSAtom *lastIndexAtom; JSAtom *lastIndexAtom;

View File

@ -1968,6 +1968,8 @@ DSTOffsetCache::purge()
*/ */
offsetMilliseconds = 0; offsetMilliseconds = 0;
rangeStartSeconds = rangeEndSeconds = INT64_MIN; rangeStartSeconds = rangeEndSeconds = INT64_MIN;
oldOffsetMilliseconds = 0;
oldRangeStartSeconds = oldRangeEndSeconds = INT64_MIN;
#ifdef JS_METER_DST_OFFSET_CACHING #ifdef JS_METER_DST_OFFSET_CACHING
totalCalculations = 0; totalCalculations = 0;

View File

@ -205,6 +205,11 @@ YearFromTime(jsdouble t)
jsint y = (jsint) floor(t /(msPerDay*365.2425)) + 1970; jsint y = (jsint) floor(t /(msPerDay*365.2425)) + 1970;
jsdouble t2 = (jsdouble) TimeFromYear(y); 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) { if (t2 > t) {
y--; y--;
} else { } else {
@ -483,7 +488,7 @@ msFromTime(jsdouble t)
Class js_DateClass = { Class js_DateClass = {
js_Date_str, 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_HAS_CACHED_PROTO(JSProto_Date) |
JSCLASS_FAST_CONSTRUCTOR, JSCLASS_FAST_CONSTRUCTOR,
PropertyStub, /* addProperty */ PropertyStub, /* addProperty */
@ -1204,54 +1209,189 @@ GetUTCTime(JSContext *cx, JSObject *obj, Value *vp, jsdouble *dp)
return JS_TRUE; 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. * Set UTC time to a given time and invalidate cached local time.
*/ */
static JSBool static JSBool
SetUTCTime(JSContext *cx, JSObject *obj, jsdouble t, Value *vp = NULL) SetUTCTime(JSContext *cx, JSObject *obj, jsdouble t, Value *vp = NULL)
{ {
JS_ASSERT(obj->getClass() == &js_DateClass); 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->setDateLocalTime(cx->runtime->NaNValue);
obj->setDateUTCTime(DoubleValue(t)); obj->setDateUTCTime(DoubleValue(t));
if (vp) if (vp)
vp->setDouble(t); vp->setDouble(t);
return true; 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 * Cache the local time, year, month, and so forth of the object.
* (e.g., NaN), the local time slot is set to the UTC time without conversion. * If UTC time is not finite (e.g., NaN), the local time
* slots will be set to the UTC time without conversion.
*/ */
static JSBool static bool
GetAndCacheLocalTime(JSContext *cx, JSObject *obj, Value *vp, jsdouble *dp) 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)) if (!obj || !InstanceOf(cx, obj, &js_DateClass, vp ? vp + 2 : NULL))
return false; return false;
jsdouble result = obj->getDateLocalTime().toNumber(); /* If the local time is undefined, we need to fill in the cached values. */
if (JSDOUBLE_IS_NaN(result)) { if (obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_TIME).isUndefined()) {
result = obj->getDateUTCTime().toDouble(); if (!FillLocalTimes(cx, obj))
return false;
/* if result is NaN, it couldn't be finite. */
if (JSDOUBLE_IS_FINITE(result))
result = LocalTime(result, cx);
obj->setDateLocalTime(DoubleValue(result));
} }
*dp = result; if (time)
*time = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_TIME).toDouble();
return true; return true;
} }
@ -1269,36 +1409,34 @@ date_getTime(JSContext *cx, uintN argc, Value *vp)
return JS_TRUE; 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 static JSBool
date_getYear(JSContext *cx, uintN argc, Value *vp) 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 static JSBool
date_getFullYear(JSContext *cx, uintN argc, Value *vp) 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 static JSBool
@ -1319,15 +1457,11 @@ date_getUTCFullYear(JSContext *cx, uintN argc, Value *vp)
static JSBool static JSBool
date_getMonth(JSContext *cx, uintN argc, Value *vp) date_getMonth(JSContext *cx, uintN argc, Value *vp)
{ {
jsdouble result; JSObject *obj = ComputeThisFromVp(cx, vp);
if (!GetAndCacheLocalTime(cx, obj, vp))
if (!GetAndCacheLocalTime(cx, ComputeThisFromVp(cx, vp), vp, &result))
return JS_FALSE; return JS_FALSE;
if (JSDOUBLE_IS_FINITE(result)) *vp = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_MONTH);
result = MonthFromTime(result);
vp->setNumber(result);
return JS_TRUE; return JS_TRUE;
} }
@ -1349,15 +1483,11 @@ date_getUTCMonth(JSContext *cx, uintN argc, Value *vp)
static JSBool static JSBool
date_getDate(JSContext *cx, uintN argc, Value *vp) date_getDate(JSContext *cx, uintN argc, Value *vp)
{ {
jsdouble result; JSObject *obj = ComputeThisFromVp(cx, vp);
if (!GetAndCacheLocalTime(cx, obj, vp))
if (!GetAndCacheLocalTime(cx, ComputeThisFromVp(cx, vp), vp, &result))
return JS_FALSE; return JS_FALSE;
if (JSDOUBLE_IS_FINITE(result)) *vp = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_DATE);
result = DateFromTime(result);
vp->setNumber(result);
return JS_TRUE; return JS_TRUE;
} }
@ -1379,15 +1509,11 @@ date_getUTCDate(JSContext *cx, uintN argc, Value *vp)
static JSBool static JSBool
date_getDay(JSContext *cx, uintN argc, Value *vp) date_getDay(JSContext *cx, uintN argc, Value *vp)
{ {
jsdouble result; JSObject *obj = ComputeThisFromVp(cx, vp);
if (!GetAndCacheLocalTime(cx, obj, vp))
if (!GetAndCacheLocalTime(cx, ComputeThisFromVp(cx, vp), vp, &result))
return JS_FALSE; return JS_FALSE;
if (JSDOUBLE_IS_FINITE(result)) *vp = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_DAY);
result = WeekDay(result);
vp->setNumber(result);
return JS_TRUE; return JS_TRUE;
} }
@ -1409,15 +1535,11 @@ date_getUTCDay(JSContext *cx, uintN argc, Value *vp)
static JSBool static JSBool
date_getHours(JSContext *cx, uintN argc, Value *vp) date_getHours(JSContext *cx, uintN argc, Value *vp)
{ {
jsdouble result; JSObject *obj = ComputeThisFromVp(cx, vp);
if (!GetAndCacheLocalTime(cx, obj, vp))
if (!GetAndCacheLocalTime(cx, ComputeThisFromVp(cx, vp), vp, &result))
return JS_FALSE; return JS_FALSE;
if (JSDOUBLE_IS_FINITE(result)) *vp = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_HOURS);
result = HourFromTime(result);
vp->setNumber(result);
return JS_TRUE; return JS_TRUE;
} }
@ -1439,15 +1561,11 @@ date_getUTCHours(JSContext *cx, uintN argc, Value *vp)
static JSBool static JSBool
date_getMinutes(JSContext *cx, uintN argc, Value *vp) date_getMinutes(JSContext *cx, uintN argc, Value *vp)
{ {
jsdouble result; JSObject *obj = ComputeThisFromVp(cx, vp);
if (!GetAndCacheLocalTime(cx, obj, vp))
if (!GetAndCacheLocalTime(cx, ComputeThisFromVp(cx, vp), vp, &result))
return JS_FALSE; return JS_FALSE;
if (JSDOUBLE_IS_FINITE(result)) *vp = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_MINUTES);
result = MinFromTime(result);
vp->setNumber(result);
return JS_TRUE; return JS_TRUE;
} }
@ -1471,15 +1589,11 @@ date_getUTCMinutes(JSContext *cx, uintN argc, Value *vp)
static JSBool static JSBool
date_getUTCSeconds(JSContext *cx, uintN argc, Value *vp) date_getUTCSeconds(JSContext *cx, uintN argc, Value *vp)
{ {
jsdouble result; JSObject *obj = ComputeThisFromVp(cx, vp);
if (!GetAndCacheLocalTime(cx, obj, vp))
if (!GetUTCTime(cx, ComputeThisFromVp(cx, vp), vp, &result))
return JS_FALSE; return JS_FALSE;
if (JSDOUBLE_IS_FINITE(result)) *vp = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_SECONDS);
result = SecFromTime(result);
vp->setNumber(result);
return JS_TRUE; return JS_TRUE;
} }
@ -1898,6 +2012,57 @@ date_toISOString(JSContext *cx, uintN argc, Value *vp)
return date_utc_format(cx, vp, print_iso_string); 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. /* for Date.toLocaleString; interface to PRMJTime date struct.
*/ */
static void static void
@ -2293,7 +2458,7 @@ static JSFunctionSpec date_methods[] = {
JS_FN("toDateString", date_toDateString, 0,0), JS_FN("toDateString", date_toDateString, 0,0),
JS_FN("toTimeString", date_toTimeString, 0,0), JS_FN("toTimeString", date_toTimeString, 0,0),
JS_FN("toISOString", date_toISOString, 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 #if JS_HAS_TOSOURCE
JS_FN(js_toSource_str, date_toSource, 0,0), JS_FN(js_toSource_str, date_toSource, 0,0),
#endif #endif

View File

@ -64,7 +64,9 @@ jsdtrace_fun_classname(const JSFunction *fun)
static char * static char *
jsdtrace_filename(JSStackFrame *fp) 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 static int

View File

@ -2899,7 +2899,9 @@ EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
return JS_FALSE; return JS_FALSE;
if (left->pn_op == JSOP_ARGUMENTS && if (left->pn_op == JSOP_ARGUMENTS &&
JSDOUBLE_IS_INT32(next->pn_dval, &slot) && 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". * arguments[i]() requires arguments object as "this".
* Check that we never generates list for that usage. * Check that we never generates list for that usage.
@ -2973,7 +2975,9 @@ EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
return JS_FALSE; return JS_FALSE;
if (left->pn_op == JSOP_ARGUMENTS && if (left->pn_op == JSOP_ARGUMENTS &&
JSDOUBLE_IS_INT32(right->pn_dval, &slot) && 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; left->pn_offset = right->pn_offset = top;
EMIT_UINT16_IMM_OP(JSOP_ARGSUB, (jsatomid)slot); EMIT_UINT16_IMM_OP(JSOP_ARGSUB, (jsatomid)slot);
return JS_TRUE; return JS_TRUE;
@ -3625,6 +3629,13 @@ js_EmitFunctionScript(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body)
return false; 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 (cg->flags & TCF_FUN_UNBRAND_THIS) {
if (js_Emit1(cx, cg, JSOP_UNBRANDTHIS) < 0) if (js_Emit1(cx, cg, JSOP_UNBRANDTHIS) < 0)
return false; return false;
@ -3701,7 +3712,7 @@ MaybeEmitVarDecl(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp,
} }
if (JOF_OPTYPE(pn->pn_op) == JOF_LOCAL && if (JOF_OPTYPE(pn->pn_op) == JOF_LOCAL &&
!(cg->flags & TCF_FUN_USES_EVAL) && !(cg->flags & TCF_FUN_CALLS_EVAL) &&
pn->pn_defn && pn->pn_defn &&
(((JSDefinition *)pn)->pn_dflags & PND_CLOSED)) (((JSDefinition *)pn)->pn_dflags & PND_CLOSED))
{ {
@ -4536,7 +4547,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
JS_ASSERT(index < JS_BIT(20)); JS_ASSERT(index < JS_BIT(20));
pn->pn_index = index; pn->pn_index = index;
op = FUN_FLAT_CLOSURE(fun) ? JSOP_DEFLOCALFUN_FC : JSOP_DEFLOCALFUN; 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); CG_SWITCH_TO_PROLOG(cg);
EMIT_UINT16_IMM_OP(JSOP_DEFUPVAR, pn->pn_cookie.asInteger()); EMIT_UINT16_IMM_OP(JSOP_DEFUPVAR, pn->pn_cookie.asInteger());
CG_SWITCH_TO_MAIN(cg); CG_SWITCH_TO_MAIN(cg);

View File

@ -240,15 +240,16 @@ struct JSStmtInfo {
*/ */
#define TCF_FUN_ENTRAINS_SCOPES 0x400000 #define TCF_FUN_ENTRAINS_SCOPES 0x400000
/* /* The function calls 'eval'. */
* Function uses eval. #define TCF_FUN_CALLS_EVAL 0x800000
*/
#define TCF_FUN_USES_EVAL 0x800000 /* The function mutates a positional (non-destructuring) parameter. */
#define TCF_FUN_MUTATES_PARAMETER 0x1000000
/* /*
* Compiling an eval() script. * 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. * Flags to check for return; vs. return expr; in a function.
@ -264,8 +265,9 @@ struct JSStmtInfo {
TCF_FUN_HEAVYWEIGHT | \ TCF_FUN_HEAVYWEIGHT | \
TCF_FUN_IS_GENERATOR | \ TCF_FUN_IS_GENERATOR | \
TCF_FUN_USES_OWN_NAME | \ TCF_FUN_USES_OWN_NAME | \
TCF_FUN_USES_EVAL | \
TCF_HAS_SHARPS | \ TCF_HAS_SHARPS | \
TCF_FUN_CALLS_EVAL | \
TCF_FUN_MUTATES_PARAMETER | \
TCF_STRICT_MODE_CODE) TCF_STRICT_MODE_CODE)
struct JSTreeContext { /* tree context for semantic checks */ struct JSTreeContext { /* tree context for semantic checks */
@ -337,6 +339,10 @@ struct JSTreeContext { /* tree context for semantic checks */
/* Test whether we're in a statement of given type. */ /* Test whether we're in a statement of given type. */
bool inStatement(JSStmtType type); bool inStatement(JSStmtType type);
bool inStrictMode() const {
return flags & TCF_STRICT_MODE_CODE;
}
inline bool needStrictChecks(); inline bool needStrictChecks();
/* /*
@ -353,9 +359,43 @@ struct JSTreeContext { /* tree context for semantic checks */
// this context is itself a generator. // this context is itself a generator.
bool skipSpansGenerator(unsigned skip); bool skipSpansGenerator(unsigned skip);
bool compileAndGo() { return !!(flags & TCF_COMPILE_N_GO); } bool compileAndGo() const { return flags & TCF_COMPILE_N_GO; }
bool inFunction() { return !!(flags & TCF_IN_FUNCTION); } bool inFunction() const { return flags & TCF_IN_FUNCTION; }
bool compiling() { return !!(flags & TCF_COMPILING); } 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());
}
}; };
/* /*
@ -363,8 +403,7 @@ struct JSTreeContext { /* tree context for semantic checks */
* JSOPTION_STRICT warnings or strict mode errors. * JSOPTION_STRICT warnings or strict mode errors.
*/ */
inline bool JSTreeContext::needStrictChecks() { inline bool JSTreeContext::needStrictChecks() {
return JS_HAS_STRICT_OPTION(parser->context) || return JS_HAS_STRICT_OPTION(parser->context) || inStrictMode();
(flags & TCF_STRICT_MODE_CODE);
} }
/* /*

View File

@ -82,11 +82,18 @@
#include "jsatominlines.h" #include "jsatominlines.h"
#include "jscntxtinlines.h" #include "jscntxtinlines.h"
#include "jsfuninlines.h"
#include "jsobjinlines.h" #include "jsobjinlines.h"
#include "jscntxtinlines.h" #include "jscntxtinlines.h"
using namespace js; using namespace js;
inline JSObject *
JSObject::getThrowTypeError() const
{
return &getGlobal()->getReservedSlot(JSRESERVED_GLOBAL_THROWTYPEERROR).toObject();
}
JSBool JSBool
js_GetArgsValue(JSContext *cx, JSStackFrame *fp, Value *vp) js_GetArgsValue(JSContext *cx, JSStackFrame *fp, Value *vp)
{ {
@ -173,22 +180,25 @@ NewArguments(JSContext *cx, JSObject *parent, uint32 argc, JSObject *callee)
return NULL; return NULL;
/* Init immediately to avoid GC seeing a half-init'ed object. */ /* Init immediately to avoid GC seeing a half-init'ed object. */
argsobj->init(&js_ArgumentsClass, proto, parent, PrivateValue(NULL)); bool strict = callee->getFunctionPrivate()->inStrictMode();
argsobj->setArgsCallee(ObjectOrNullValue(callee)); argsobj->init(strict ? &StrictArgumentsClass : &js_ArgumentsClass, proto, parent,
PrivateValue(NULL));
argsobj->setArgsLength(argc); argsobj->setArgsLength(argc);
argsobj->setArgsCallee(ObjectValue(*callee));
argsobj->map = cx->runtime->emptyArgumentsScope->hold(); argsobj->map = cx->runtime->emptyArgumentsScope->hold();
/* This must come after argsobj->map has been set. */ /* This must come after argsobj->map has been set. */
if (!js_EnsureReservedSlots(cx, argsobj, argc)) if (!js_EnsureReservedSlots(cx, argsobj, argc))
return NULL; return NULL;
return argsobj; return argsobj;
} }
static void static void
PutArguments(JSContext *cx, JSObject *argsobj, Value *args) 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) { for (uint32 i = 0; i != argc; ++i) {
if (!argsobj->getArgsElement(i).isMagic(JS_ARGS_HOLE)) if (!argsobj->getArgsElement(i).isMagic(JS_ARGS_HOLE))
argsobj->setArgsElement(i, args[i]); argsobj->setArgsElement(i, args[i]);
@ -220,8 +230,22 @@ js_GetArgsObject(JSContext *cx, JSStackFrame *fp)
if (!argsobj) if (!argsobj)
return argsobj; return argsobj;
/* Link the new object to fp so it can get actual argument values. */ /*
* 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); argsobj->setPrivate(fp);
}
fp->setArgsObj(argsobj); fp->setArgsObj(argsobj);
return argsobj; return argsobj;
} }
@ -230,9 +254,13 @@ void
js_PutArgsObject(JSContext *cx, JSStackFrame *fp) js_PutArgsObject(JSContext *cx, JSStackFrame *fp)
{ {
JSObject *argsobj = fp->getArgsObj(); JSObject *argsobj = fp->getArgsObj();
if (argsobj->isNormalArguments()) {
JS_ASSERT(argsobj->getPrivate() == fp); JS_ASSERT(argsobj->getPrivate() == fp);
PutArguments(cx, argsobj, fp->argv); PutArguments(cx, argsobj, fp->argv);
argsobj->setPrivate(NULL); argsobj->setPrivate(NULL);
} else {
JS_ASSERT(!argsobj->getPrivate());
}
fp->setArgsObj(NULL); fp->setArgsObj(NULL);
} }
@ -247,7 +275,17 @@ js_Arguments(JSContext *cx, JSObject *parent, uint32 argc, JSObject *callee)
JSObject *argsobj = NewArguments(cx, parent, argc, callee); JSObject *argsobj = NewArguments(cx, parent, argc, callee);
if (!argsobj) if (!argsobj)
return NULL; return NULL;
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); argsobj->setPrivate(JS_ARGUMENT_OBJECT_ON_TRACE);
}
return argsobj; return argsobj;
} }
#endif #endif
@ -259,6 +297,7 @@ JS_DEFINE_CALLINFO_4(extern, OBJECT, js_Arguments, CONTEXT, OBJECT, UINT32, OBJE
JSBool JS_FASTCALL JSBool JS_FASTCALL
js_PutArguments(JSContext *cx, JSObject *argsobj, Value *args) js_PutArguments(JSContext *cx, JSObject *argsobj, Value *args)
{ {
JS_ASSERT(argsobj->isNormalArguments());
JS_ASSERT(argsobj->getPrivate() == JS_ARGUMENT_OBJECT_ON_TRACE); JS_ASSERT(argsobj->getPrivate() == JS_ARGUMENT_OBJECT_ON_TRACE);
PutArguments(cx, argsobj, args); PutArguments(cx, argsobj, args);
argsobj->setPrivate(NULL); argsobj->setPrivate(NULL);
@ -275,7 +314,7 @@ args_delProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
if (JSID_IS_INT(id)) { if (JSID_IS_INT(id)) {
uintN arg = uintN(JSID_TO_INT(id)); uintN arg = uintN(JSID_TO_INT(id));
if (arg < obj->getArgsLength()) if (arg < obj->getArgsInitialLength())
obj->setArgsElement(arg, MagicValue(JS_ARGS_HOLE)); obj->setArgsElement(arg, MagicValue(JS_ARGS_HOLE));
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) { } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
obj->setArgsLengthOverridden(); obj->setArgsLengthOverridden();
@ -478,7 +517,7 @@ ArgGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
* prototype to point to another Arguments object with a bigger argc. * prototype to point to another Arguments object with a bigger argc.
*/ */
uintN arg = uintN(JSID_TO_INT(id)); uintN arg = uintN(JSID_TO_INT(id));
if (arg < obj->getArgsLength()) { if (arg < obj->getArgsInitialLength()) {
JSStackFrame *fp = (JSStackFrame *) obj->getPrivate(); JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
if (fp) { if (fp) {
*vp = fp->argv[arg]; *vp = fp->argv[arg];
@ -490,7 +529,7 @@ ArgGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
} }
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) { } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
if (!obj->isArgsLengthOverridden()) if (!obj->isArgsLengthOverridden())
vp->setInt32(obj->getArgsLength()); vp->setInt32(obj->getArgsInitialLength());
} else { } else {
JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom)); JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom));
const Value &v = obj->getArgsCallee(); const Value &v = obj->getArgsCallee();
@ -533,7 +572,7 @@ ArgSetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
if (JSID_IS_INT(id)) { if (JSID_IS_INT(id)) {
uintN arg = uintN(JSID_TO_INT(id)); uintN arg = uintN(JSID_TO_INT(id));
if (arg < obj->getArgsLength()) { if (arg < obj->getArgsInitialLength()) {
JSStackFrame *fp = (JSStackFrame *) obj->getPrivate(); JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
if (fp) { if (fp) {
fp->argv[arg] = *vp; fp->argv[arg] = *vp;
@ -547,8 +586,8 @@ ArgSetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
/* /*
* For simplicity we use delete/set to replace the property with one * 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 * backed by the default Object getter and setter. Note that we rely on
* args_delete to clear the corresponding reserved slot so the GC can * args_delProperty to clear the corresponding reserved slot so the GC can
* collect its value. * collect its value.
*/ */
AutoValueRooter tvr(cx); AutoValueRooter tvr(cx);
@ -560,13 +599,15 @@ static JSBool
args_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, args_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
JSObject **objp) JSObject **objp)
{ {
JS_ASSERT(obj->isArguments()); JS_ASSERT(obj->isNormalArguments());
*objp = NULL; *objp = NULL;
bool valid = false; bool valid = false;
uintN attrs = JSPROP_SHARED;
if (JSID_IS_INT(id)) { if (JSID_IS_INT(id)) {
uint32 arg = uint32(JSID_TO_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; valid = true;
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) { } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
if (!obj->isArgsLengthOverridden()) if (!obj->isArgsLengthOverridden())
@ -577,12 +618,8 @@ args_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
} }
if (valid) { if (valid) {
/*
* XXX ECMA specs DontEnum even for indexed properties, contrary to
* other array-like objects.
*/
Value tmp = UndefinedValue(); 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; return JS_FALSE;
*objp = obj; *objp = obj;
} }
@ -592,13 +629,13 @@ args_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
static JSBool static JSBool
args_enumerate(JSContext *cx, JSObject *obj) 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 * Trigger reflection in args_resolve using a series of js_LookupProperty
* calls. * calls.
*/ */
int argc = int(obj->getArgsLength()); int argc = int(obj->getArgsInitialLength());
for (int i = -2; i != argc; i++) { for (int i = -2; i != argc; i++) {
jsid id = (i == -2) jsid id = (i == -2)
? ATOM_TO_JSID(cx->runtime->atomState.lengthAtom) ? ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)
@ -618,6 +655,154 @@ args_enumerate(JSContext *cx, JSObject *obj)
return true; 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 JS_HAS_GENERATORS
/* /*
* If a generator's arguments or call object escapes, and the generator frame * If a generator's arguments or call object escapes, and the generator frame
@ -648,11 +833,14 @@ args_or_call_trace(JSTracer *trc, JSObject *obj)
#endif #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]]. * objects have the initial value of Object.prototype as their [[Prototype]].
* However, Object.prototype.toString.call(arguments) === "[object Arguments]" * 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". * "Object".
*/
/*
* *
* The JSClass functions below collaborate to lazily reflect and synchronize * The JSClass functions below collaborate to lazily reflect and synchronize
* actual argument values, argument count, and callee function object stored * actual argument values, argument count, and callee function object stored
@ -681,6 +869,37 @@ Class js_ArgumentsClass = {
JS_CLASS_TRACE(args_or_call_trace) 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_CALLEE = JSSLOT_PRIVATE + 1;
const uint32 JSSLOT_CALL_ARGUMENTS = JSSLOT_PRIVATE + 2; const uint32 JSSLOT_CALL_ARGUMENTS = JSSLOT_PRIVATE + 2;
const uint32 CALL_CLASS_FIXED_RESERVED_SLOTS = 2; const uint32 CALL_CLASS_FIXED_RESERVED_SLOTS = 2;
@ -1392,8 +1611,9 @@ fun_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
* Function.prototype object (we use JSPROP_PERMANENT with JSPROP_SHARED * Function.prototype object (we use JSPROP_PERMANENT with JSPROP_SHARED
* to make it appear so). * to make it appear so).
* *
* This code couples tightly to the attributes for lazy_function_props[] * This code couples tightly to the attributes for lazyFunctionDataProps[]
* initializers above, and to js_SetProperty and js_HasOwnProperty. * and poisonPillProps[] initializers below, and to js_SetProperty and
* js_HasOwnProperty.
* *
* It's important to allow delegating objects, even though they inherit * It's important to allow delegating objects, even though they inherit
* this getter (fun_getProperty), to override arguments, arity, caller, * this getter (fun_getProperty), to override arguments, arity, caller,
@ -1473,20 +1693,34 @@ fun_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
return true; return true;
} }
struct LazyFunctionProp { namespace {
struct LazyFunctionDataProp {
uint16 atomOffset; uint16 atomOffset;
int8 tinyid; int8 tinyid;
uint8 attrs; uint8 attrs;
}; };
/* NB: no sentinel at the end -- use JS_ARRAY_LENGTH to bound loops. */ struct PoisonPillProp {
static LazyFunctionProp lazy_function_props[] = { uint16 atomOffset;
{ATOM_OFFSET(arguments), FUN_ARGUMENTS, JSPROP_PERMANENT}, 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(arity), FUN_ARITY, JSPROP_PERMANENT},
{ATOM_OFFSET(caller), FUN_CALLER, JSPROP_PERMANENT},
{ATOM_OFFSET(name), FUN_NAME, 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 static JSBool
fun_enumerate(JSContext *cx, JSObject *obj) fun_enumerate(JSContext *cx, JSObject *obj)
{ {
@ -1500,13 +1734,20 @@ fun_enumerate(JSContext *cx, JSObject *obj)
if (!JS_LookupPropertyById(cx, obj, id, &v)) if (!JS_LookupPropertyById(cx, obj, id, &v))
return false; return false;
for (uintN i = 0; i < JS_ARRAY_LENGTH(lazy_function_props); i++) { for (uintN i = 0; i < JS_ARRAY_LENGTH(lazyFunctionDataProps); i++) {
LazyFunctionProp &lfp = lazy_function_props[i]; const LazyFunctionDataProp &lfp = lazyFunctionDataProps[i];
id = ATOM_TO_JSID(OFFSET_TO_ATOM(cx->runtime, lfp.atomOffset)); id = ATOM_TO_JSID(OFFSET_TO_ATOM(cx->runtime, lfp.atomOffset));
if (!JS_LookupPropertyById(cx, obj, id, &v)) if (!JS_LookupPropertyById(cx, obj, id, &v))
return false; 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; return true;
} }
@ -1582,8 +1823,8 @@ fun_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
return JS_TRUE; return JS_TRUE;
} }
for (uintN i = 0; i < JS_ARRAY_LENGTH(lazy_function_props); i++) { for (uintN i = 0; i < JS_ARRAY_LENGTH(lazyFunctionDataProps); i++) {
LazyFunctionProp *lfp = &lazy_function_props[i]; const LazyFunctionDataProp *lfp = &lazyFunctionDataProps[i];
atom = OFFSET_TO_ATOM(cx->runtime, lfp->atomOffset); atom = OFFSET_TO_ATOM(cx->runtime, lfp->atomOffset);
if (id == ATOM_TO_JSID(atom)) { if (id == ATOM_TO_JSID(atom)) {
@ -1601,6 +1842,37 @@ fun_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
} }
} }
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; return JS_TRUE;
} }
@ -2080,7 +2352,7 @@ js_fun_apply(JSContext *cx, uintN argc, Value *vp)
if (aobj->isArray()) { if (aobj->isArray()) {
length = aobj->getArrayLength(); length = aobj->getArrayLength();
} else if (aobj->isArguments() && !aobj->isArgsLengthOverridden()) { } else if (aobj->isArguments() && !aobj->isArgsLengthOverridden()) {
length = aobj->getArgsLength(); length = aobj->getArgsInitialLength();
} else { } else {
Value &lenval = vp[0]; Value &lenval = vp[0];
if (!aobj->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), &lenval)) if (!aobj->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), &lenval))
@ -2400,20 +2672,43 @@ Function(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval)
filename, lineno); 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 * JSObject *
js_InitFunctionClass(JSContext *cx, JSObject *obj) js_InitFunctionClass(JSContext *cx, JSObject *obj)
{ {
JSObject *proto; JSObject *proto = js_InitClass(cx, obj, NULL, &js_FunctionClass, Function, 1,
JSFunction *fun;
proto = js_InitClass(cx, obj, NULL, &js_FunctionClass, Function, 1,
NULL, function_methods, NULL, NULL); NULL, function_methods, NULL, NULL);
if (!proto) if (!proto)
return NULL; 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) if (!fun)
return NULL; return NULL;
fun->u.i.script = JSScript::emptyScript(); 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; return proto;
} }
@ -2610,34 +2905,13 @@ js_ValueToFunction(JSContext *cx, const Value *vp, uintN flags)
JSObject * JSObject *
js_ValueToFunctionObject(JSContext *cx, Value *vp, uintN flags) js_ValueToFunctionObject(JSContext *cx, Value *vp, uintN flags)
{ {
JSFunction *fun;
JSStackFrame *caller;
JSPrincipals *principals;
JSObject *funobj; JSObject *funobj;
if (IsFunctionObject(*vp, &funobj)) if (!IsFunctionObject(*vp, &funobj)) {
js_ReportIsNotFunction(cx, vp, flags);
return NULL;
}
return 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)) {
return NULL;
}
return FUN_OBJECT(fun);
} }
JSObject * JSObject *
@ -2648,7 +2922,9 @@ js_ValueToCallableObject(JSContext *cx, Value *vp, uintN flags)
if (callable->isCallable()) if (callable->isCallable())
return callable; return callable;
} }
return js_ValueToFunctionObject(cx, vp, flags);
js_ReportIsNotFunction(cx, vp, flags);
return NULL;
} }
void void
@ -3124,6 +3400,8 @@ js_FreezeLocalNames(JSContext *cx, JSFunction *fun)
JSAtom * JSAtom *
JSFunction::findDuplicateFormal() const JSFunction::findDuplicateFormal() const
{ {
JS_ASSERT(isInterpreted());
if (nargs <= 1) if (nargs <= 1)
return NULL; return NULL;

View File

@ -184,6 +184,8 @@ struct JSFunction : public JSObject
bool isHeavyweight() const { return JSFUN_HEAVYWEIGHT_TEST(flags); } bool isHeavyweight() const { return JSFUN_HEAVYWEIGHT_TEST(flags); }
unsigned minArgs() const { return FUN_MINARGS(this); } unsigned minArgs() const { return FUN_MINARGS(this); }
inline bool inStrictMode() const;
uintN countVars() const { uintN countVars() const {
JS_ASSERT(FUN_INTERPRETED(this)); JS_ASSERT(FUN_INTERPRETED(this));
return u.i.nvars; return u.i.nvars;
@ -278,8 +280,9 @@ JS_STATIC_ASSERT(sizeof(JSFunction) % JS_GCTHING_ALIGN == 0);
#endif #endif
/* /*
* NB: the Arguments class is an uninitialized internal class that masquerades * NB: the Arguments classes are uninitialized internal classes that masquerade
* (according to Object.prototype.toString.call(argsobj)) as "Object". * (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 * WARNING (to alert embedders reading this private .h file): arguments objects
* are *not* thread-safe and should not be used concurrently -- they should be * are *not* thread-safe and should not be used concurrently -- they should be
@ -291,11 +294,26 @@ JS_STATIC_ASSERT(sizeof(JSFunction) % JS_GCTHING_ALIGN == 0);
* single-threaded objects and GC heaps. * single-threaded objects and GC heaps.
*/ */
extern js::Class js_ArgumentsClass; 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 inline bool
JSObject::isArguments() const JSObject::isArguments() const
{ {
return getClass() == &js_ArgumentsClass; return isNormalArguments() || isStrictArguments();
} }
#define JS_ARGUMENT_OBJECT_ON_TRACE ((void *)0xa126) #define JS_ARGUMENT_OBJECT_ON_TRACE ((void *)0xa126)
@ -349,6 +367,10 @@ IsFunctionObject(const js::Value &v, JSObject **funobj)
(JS_ASSERT((funobj)->isFunction()), \ (JS_ASSERT((funobj)->isFunction()), \
(JSFunction *) (funobj)->getPrivate()) (JSFunction *) (funobj)->getPrivate())
extern JSFunction *
js_NewFunction(JSContext *cx, JSObject *funobj, js::Native native, uintN nargs,
uintN flags, JSObject *parent, JSAtom *atom);
namespace js { namespace js {
/* /*
@ -364,6 +386,9 @@ IsInternalFunctionObject(JSObject *funobj)
return funobj == fun && (fun->flags & JSFUN_LAMBDA) && !funobj->getParent(); return funobj == fun && (fun->flags & JSFUN_LAMBDA) && !funobj->getParent();
} }
extern JSString *
fun_toStringHelper(JSContext *cx, JSObject *obj, uintN indent);
} /* namespace js */ } /* namespace js */
extern JSObject * extern JSObject *
@ -372,10 +397,6 @@ js_InitFunctionClass(JSContext *cx, JSObject *obj);
extern JSObject * extern JSObject *
js_InitArgumentsClass(JSContext *cx, JSObject *obj); 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 extern void
js_TraceFunction(JSTracer *trc, JSFunction *fun); js_TraceFunction(JSTracer *trc, JSFunction *fun);
@ -467,6 +488,16 @@ js_GetArgsValue(JSContext *cx, JSStackFrame *fp, js::Value *vp);
extern JSBool extern JSBool
js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, js::Value *vp); 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 * extern JSObject *
js_GetArgsObject(JSContext *cx, JSStackFrame *fp); js_GetArgsObject(JSContext *cx, JSStackFrame *fp);
@ -489,9 +520,8 @@ js_IsNamedLambda(JSFunction *fun) { return (fun->flags & JSFUN_LAMBDA) && fun->a
const uint32 JS_ARGS_LENGTH_MAX = JS_BIT(19) - 1024; const uint32 JS_ARGS_LENGTH_MAX = JS_BIT(19) - 1024;
/* /*
* JSSLOT_ARGS_LENGTH stores ((argc << 1) | overwritten_flag) as int jsval. * JSSLOT_ARGS_LENGTH stores ((argc << 1) | overwritten_flag) as an Int32
* Thus (JS_ARGS_LENGTH_MAX << 1) | 1 must fit JSVAL_INT_MAX. To assert that * Value. Thus (JS_ARGS_LENGTH_MAX << 1) | 1 must be less than JSVAL_INT_MAX.
* we check first that the shift does not overflow uint32.
*/ */
JS_STATIC_ASSERT(JS_ARGS_LENGTH_MAX <= JS_BIT(30)); JS_STATIC_ASSERT(JS_ARGS_LENGTH_MAX <= JS_BIT(30));
JS_STATIC_ASSERT(((JS_ARGS_LENGTH_MAX << 1) | 1) <= JSVAL_INT_MAX); JS_STATIC_ASSERT(((JS_ARGS_LENGTH_MAX << 1) | 1) <= JSVAL_INT_MAX);
@ -555,11 +585,4 @@ js_fun_apply(JSContext *cx, uintN argc, js::Value *vp);
extern JSBool extern JSBool
js_fun_call(JSContext *cx, uintN argc, js::Value *vp); 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___ */ #endif /* jsfun_h___ */

52
js/src/jsfuninlines.h Normal file
View File

@ -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___ */

View File

@ -1352,6 +1352,8 @@ js_DumpGCStats(JSRuntime *rt, FILE *fp)
fprintf(fp, "\nTOTAL STATS:\n"); fprintf(fp, "\nTOTAL STATS:\n");
fprintf(fp, " bytes allocated: %lu\n", UL(rt->gcBytes)); fprintf(fp, " bytes allocated: %lu\n", UL(rt->gcBytes));
fprintf(fp, " total GC arenas: %lu\n", UL(sumArenas)); 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, " total GC things: %lu\n", UL(sumThings));
fprintf(fp, " max total GC things: %lu\n", UL(sumMaxThings)); fprintf(fp, " max total GC things: %lu\n", UL(sumMaxThings));
fprintf(fp, " GC cell utilization: %.1f%%\n", fprintf(fp, " GC cell utilization: %.1f%%\n",
@ -1363,25 +1365,14 @@ js_DumpGCStats(JSRuntime *rt, FILE *fp)
fprintf(fp, " alloc without locks: %lu (%.1f%%)\n", fprintf(fp, " alloc without locks: %lu (%.1f%%)\n",
UL(sumLocalAlloc), PERCENT(sumLocalAlloc, sumAlloc)); UL(sumLocalAlloc), PERCENT(sumLocalAlloc, sumAlloc));
fprintf(fp, " allocation failures: %lu\n", UL(sumFail)); 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 lock calls: %lu\n", ULSTAT(lock));
fprintf(fp, " valid unlock calls: %lu\n", ULSTAT(unlock)); 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)); fprintf(fp, " delayed tracing calls: %lu\n", ULSTAT(unmarked));
#ifdef DEBUG #ifdef DEBUG
fprintf(fp, " max trace later count: %lu\n", ULSTAT(maxunmarked)); fprintf(fp, " max trace later count: %lu\n", ULSTAT(maxunmarked));
#endif #endif
fprintf(fp, "potentially useful GC calls: %lu\n", ULSTAT(poke)); fprintf(fp, "potentially useful GC calls: %lu\n", ULSTAT(poke));
fprintf(fp, " thing arenas freed so far: %lu\n", ULSTAT(afree)); 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); rt->gcStats.conservative.dump(fp);
#undef UL #undef UL
@ -1647,10 +1638,8 @@ RefillFinalizableFreeList(JSContext *cx, unsigned thingKind)
{ {
AutoLockGC lock(rt); AutoLockGC lock(rt);
JS_ASSERT(!rt->gcRunning); JS_ASSERT(!rt->gcRunning);
if (rt->gcRunning) { if (rt->gcRunning)
METER(rt->gcStats.finalfail++);
return NULL; return NULL;
}
bool canGC = !JS_ON_TRACE(cx) && !JS_THREAD_DATA(cx)->waiveGCQuota; bool canGC = !JS_ON_TRACE(cx) && !JS_THREAD_DATA(cx)->waiveGCQuota;
bool doGC = canGC && IsGCThresholdReached(rt); bool doGC = canGC && IsGCThresholdReached(rt);

View File

@ -556,14 +556,8 @@ struct JSGCArenaStats {
}; };
struct JSGCStats { struct JSGCStats {
uint32 finalfail; /* finalizer calls allocator failures */
uint32 lockborn; /* things born locked */
uint32 lock; /* valid lock calls */ uint32 lock; /* valid lock calls */
uint32 unlock; /* valid unlock 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 uint32 unmarked; /* number of times marking of GC thing's children were
delayed due to a low C stack */ delayed due to a low C stack */
#ifdef DEBUG #ifdef DEBUG
@ -572,12 +566,6 @@ struct JSGCStats {
#endif #endif
uint32 poke; /* number of potentially useful GC calls */ uint32 poke; /* number of potentially useful GC calls */
uint32 afree; /* thing arenas freed so far */ 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 nallarenas; /* number of all allocated arenas */
uint32 maxnallarenas; /* maximum number of all allocated arenas */ uint32 maxnallarenas; /* maximum number of all allocated arenas */
uint32 nchunks; /* number of allocated chunks */ uint32 nchunks; /* number of allocated chunks */

View File

@ -4147,7 +4147,7 @@ BEGIN_CASE(JSOP_LENGTH)
jsuint length = obj->getArrayLength(); jsuint length = obj->getArrayLength();
regs.sp[-1].setNumber(length); regs.sp[-1].setNumber(length);
} else if (obj->isArguments() && !obj->isArgsLengthOverridden()) { } else if (obj->isArguments() && !obj->isArgsLengthOverridden()) {
uint32 length = obj->getArgsLength(); uint32 length = obj->getArgsInitialLength();
JS_ASSERT(length < INT32_MAX); JS_ASSERT(length < INT32_MAX);
regs.sp[-1].setInt32(int32_t(length)); regs.sp[-1].setInt32(int32_t(length));
} else { } else {
@ -4518,7 +4518,7 @@ BEGIN_CASE(JSOP_GETELEM)
} else if (obj->isArguments()) { } else if (obj->isArguments()) {
uint32 arg = uint32(i); uint32 arg = uint32(i);
if (arg < obj->getArgsLength()) { if (arg < obj->getArgsInitialLength()) {
JSStackFrame *afp = (JSStackFrame *) obj->getPrivate(); JSStackFrame *afp = (JSStackFrame *) obj->getPrivate();
if (afp) { if (afp) {
copyFrom = &afp->argv[arg]; copyFrom = &afp->argv[arg];

View File

@ -1024,10 +1024,13 @@ obj_eval(JSContext *cx, uintN argc, Value *vp)
return JS_FALSE; return JS_FALSE;
obj = obj->wrappedObject(cx); 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 * Ban indirect uses of eval (nonglobal.eval = eval; nonglobal.eval(....))
* calls that attempt to use a non-global object as the "with" object in * that attempt to use a non-global object as the scope object.
* the former indirect case.
*/ */
{ {
JSObject *parent = obj->getParent(); JSObject *parent = obj->getParent();
@ -1086,10 +1089,6 @@ obj_eval(JSContext *cx, uintN argc, Value *vp)
/* Pretend that we're top level. */ /* Pretend that we're top level. */
staticLevel = 0; staticLevel = 0;
OBJ_TO_INNER_OBJECT(cx, obj);
if (!obj)
return JS_FALSE;
if (!js_CheckPrincipalsAccess(cx, obj, if (!js_CheckPrincipalsAccess(cx, obj,
JS_StackFramePrincipals(cx, caller), JS_StackFramePrincipals(cx, caller),
cx->runtime->atomState.evalAtom)) { cx->runtime->atomState.evalAtom)) {
@ -6048,9 +6047,9 @@ JSObject::wrappedObject(JSContext *cx) const
} }
JSObject * JSObject *
JSObject::getGlobal() JSObject::getGlobal() const
{ {
JSObject *obj = this; JSObject *obj = const_cast<JSObject *>(this);
while (JSObject *parent = obj->getParent()) while (JSObject *parent = obj->getParent())
obj = parent; obj = parent;
return obj; return obj;

View File

@ -421,7 +421,7 @@ struct JSObject {
parent = newParent; parent = newParent;
} }
JSObject *getGlobal(); JSObject *getGlobal() const;
void *getPrivate() const { void *getPrivate() const {
JS_ASSERT(getClass()->flags & JSCLASS_HAS_PRIVATE); JS_ASSERT(getClass()->flags & JSCLASS_HAS_PRIVATE);
@ -500,7 +500,10 @@ struct JSObject {
* JSSLOT_PRIVATE - the corresponding frame until the frame exits. * JSSLOT_PRIVATE - the corresponding frame until the frame exits.
* JSSLOT_ARGS_LENGTH - the number of actual arguments and a flag * JSSLOT_ARGS_LENGTH - the number of actual arguments and a flag
* indicating whether arguments.length was * 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 * JSSLOT_ARGS_CALLEE - the arguments.callee value or JSVAL_HOLE if that
* was overwritten. * was overwritten.
* *
@ -514,8 +517,21 @@ struct JSObject {
static const uint32 JSSLOT_ARGS_LENGTH = JSSLOT_PRIVATE + 1; static const uint32 JSSLOT_ARGS_LENGTH = JSSLOT_PRIVATE + 1;
static const uint32 ARGS_FIXED_RESERVED_SLOTS = 2; 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); 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 void setArgsLengthOverridden();
inline bool isArgsLengthOverridden() const; inline bool isArgsLengthOverridden() const;
@ -530,16 +546,26 @@ struct JSObject {
* Date-specific getters and setters. * 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: public:
static const uint32 DATE_FIXED_RESERVED_SLOTS = 2; static const uint32 JSSLOT_DATE_UTC_TIME = JSSLOT_PRIVATE;
inline const js::Value &getDateLocalTime() const; /*
inline void setDateLocalTime(const js::Value &pthis); * 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;
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;
static const uint32 DATE_CLASS_RESERVED_SLOTS = 9;
inline const js::Value &getDateUTCTime() const; inline const js::Value &getDateUTCTime() const;
inline void setDateUTCTime(const js::Value &pthis); inline void setDateUTCTime(const js::Value &pthis);
@ -560,6 +586,8 @@ struct JSObject {
inline bool hasMethodObj(const JSObject& obj) const; inline bool hasMethodObj(const JSObject& obj) const;
inline void setMethodObj(JSObject& obj); inline void setMethodObj(JSObject& obj);
inline JSFunction *getFunctionPrivate() const;
/* /*
* RegExp-specific getters and setters. * RegExp-specific getters and setters.
*/ */
@ -731,11 +759,15 @@ struct JSObject {
JS_FRIEND_API(JSCompartment *) getCompartment(JSContext *cx); JS_FRIEND_API(JSCompartment *) getCompartment(JSContext *cx);
inline JSObject *getThrowTypeError() const;
void swap(JSObject *obj); void swap(JSObject *obj);
inline bool canHaveMethodBarrier() const; inline bool canHaveMethodBarrier() const;
inline bool isArguments() const; inline bool isArguments() const;
inline bool isNormalArguments() const;
inline bool isStrictArguments() const;
inline bool isArray() const; inline bool isArray() const;
inline bool isDenseArray() const; inline bool isDenseArray() const;
inline bool isSlowArray() const; inline bool isSlowArray() const;

View File

@ -220,15 +220,16 @@ JSObject::setArgsLength(uint32 argc)
{ {
JS_ASSERT(isArguments()); JS_ASSERT(isArguments());
JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX); 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()); JS_ASSERT(!isArgsLengthOverridden());
} }
inline uint32 inline uint32
JSObject::getArgsLength() const JSObject::getArgsInitialLength() const
{ {
JS_ASSERT(isArguments()); 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); JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
return argc; return argc;
} }
@ -237,7 +238,7 @@ inline void
JSObject::setArgsLengthOverridden() JSObject::setArgsLengthOverridden()
{ {
JS_ASSERT(isArguments()); JS_ASSERT(isArguments());
fslots[JSSLOT_ARGS_LENGTH].getInt32Ref() |= 1; fslots[JSSLOT_ARGS_LENGTH].getInt32Ref() |= ARGS_LENGTH_OVERRIDDEN_BIT;
} }
inline bool inline bool
@ -245,7 +246,7 @@ JSObject::isArgsLengthOverridden() const
{ {
JS_ASSERT(isArguments()); JS_ASSERT(isArguments());
const js::Value &v = fslots[JSSLOT_ARGS_LENGTH]; const js::Value &v = fslots[JSSLOT_ARGS_LENGTH];
return (v.toInt32() & 1) != 0; return v.toInt32() & ARGS_LENGTH_OVERRIDDEN_BIT;
} }
inline const js::Value & inline const js::Value &
@ -286,20 +287,6 @@ JSObject::setArgsElement(uint32 i, const js::Value &v)
dslots[i] = v; 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 & inline const js::Value &
JSObject::getDateUTCTime() const JSObject::getDateUTCTime() const
{ {
@ -327,6 +314,13 @@ JSObject::setMethodObj(JSObject& obj)
fslots[JSSLOT_FUN_METHOD_OBJ].setObject(obj); fslots[JSSLOT_FUN_METHOD_OBJ].setObject(obj);
} }
inline JSFunction *
JSObject::getFunctionPrivate() const
{
JS_ASSERT(isFunction());
return reinterpret_cast<JSFunction *>(getPrivate());
}
inline NativeIterator * inline NativeIterator *
JSObject::getNativeIterator() const JSObject::getNativeIterator() const
{ {

View File

@ -240,16 +240,14 @@ public:
if (gapValue.value().isString()) { if (gapValue.value().isString()) {
if (!js_ValueToCharBuffer(cx, gapValue.value(), gap)) if (!js_ValueToCharBuffer(cx, gapValue.value(), gap))
return false; return false;
if (cb.length() > 10) if (gap.length() > 10)
cb.resize(10); gap.resize(10);
} } else if (gapValue.value().isNumber()) {
if (gapValue.value().isNumber()) {
jsdouble d = gapValue.value().isInt32() jsdouble d = gapValue.value().isInt32()
? gapValue.value().toInt32() ? gapValue.value().toInt32()
: js_DoubleToInteger(gapValue.value().toDouble()); : js_DoubleToInteger(gapValue.value().toDouble());
d = JS_MIN(10, d); d = JS_MIN(10, d);
if (d >= 1 && !cb.appendN(' ', uint32(d))) if (d >= 1 && !gap.appendN(' ', uint32(d)))
return false; return false;
} }
@ -346,23 +344,23 @@ JO(JSContext *cx, Value *vp, StringifyContext *scx)
} }
JSBool memberWritten = JS_FALSE; JSBool memberWritten = JS_FALSE;
AutoIdArray ida(cx, JS_Enumerate(cx, &keySource->toObject())); AutoIdVector props(cx);
if (!ida) if (!GetPropertyNames(cx, &keySource->toObject(), JSITER_OWNONLY, props))
return JS_FALSE; 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(); outputValue.setUndefined();
if (!usingWhitelist) { if (!usingWhitelist) {
if (!js_ValueToStringId(cx, IdToValue(ida[i]), &id)) if (!js_ValueToStringId(cx, IdToValue(props[i]), &id))
return JS_FALSE; return JS_FALSE;
} else { } else {
// skip non-index properties // skip non-index properties
jsuint index = 0; jsuint index = 0;
if (!js_IdIsIndex(ida[i], &index)) if (!js_IdIsIndex(props[i], &index))
continue; continue;
if (!scx->replacer->getProperty(cx, ida[i], &whitelistElement)) if (!scx->replacer->getProperty(cx, props[i], &whitelistElement))
return JS_FALSE; return JS_FALSE;
if (!js_ValueToStringId(cx, whitelistElement, &id)) if (!js_ValueToStringId(cx, whitelistElement, &id))
@ -409,6 +407,7 @@ JO(JSContext *cx, Value *vp, StringifyContext *scx)
s->getCharsAndLength(chars, length); s->getCharsAndLength(chars, length);
if (!write_string(cx, scx->cb, chars, length) || if (!write_string(cx, scx->cb, chars, length) ||
!scx->cb.append(':') || !scx->cb.append(':') ||
!(scx->gap.empty() || scx->cb.append(' ')) ||
!Str(cx, id, obj, scx, &outputValue, true)) { !Str(cx, id, obj, scx, &outputValue, true)) {
return JS_FALSE; return JS_FALSE;
} }
@ -618,12 +617,12 @@ Walk(JSContext *cx, jsid id, JSObject *holder, const Value &reviver, Value *vp)
return false; return false;
} }
} else { } else {
AutoIdArray ida(cx, JS_Enumerate(cx, obj)); AutoIdVector props(cx);
if (!ida) if (!GetPropertyNames(cx, obj, JSITER_OWNONLY, props))
return false; return false;
for (jsint i = 0, len = ida.length(); i < len; i++) { for (size_t i = 0, len = props.length(); i < len; i++) {
jsid idName = ida[i]; jsid idName = props[i];
if (!Walk(cx, idName, obj, reviver, propValue.addr())) if (!Walk(cx, idName, obj, reviver, propValue.addr()))
return false; return false;
if (propValue.value().isUndefined()) { if (propValue.value().isUndefined()) {

View File

@ -5436,11 +5436,15 @@ SimulateImacroCFG(JSContext *cx, JSScript *script,
uintN pcdepth, jsbytecode *pc, jsbytecode *target, uintN pcdepth, jsbytecode *pc, jsbytecode *target,
jsbytecode **pcstack) jsbytecode **pcstack)
{ {
size_t nbytes = StackDepth(script) * sizeof *pcstack; size_t nbytes = 0;
jsbytecode** tmp_pcstack = (jsbytecode **) cx->malloc(nbytes); jsbytecode** tmp_pcstack = NULL;
if (pcstack) {
nbytes = StackDepth(script) * sizeof *pcstack;
tmp_pcstack = (jsbytecode **) cx->malloc(nbytes);
if (!tmp_pcstack) if (!tmp_pcstack)
return -1; return -1;
memcpy(tmp_pcstack, pcstack, nbytes); memcpy(tmp_pcstack, pcstack, nbytes);
}
ptrdiff_t oplen; ptrdiff_t oplen;
for (; pc < target; pc += oplen) { for (; pc < target; pc += oplen) {
@ -5476,11 +5480,14 @@ SimulateImacroCFG(JSContext *cx, JSScript *script,
LOCAL_ASSERT(pc == target); LOCAL_ASSERT(pc == target);
success: success:
if (tmp_pcstack) {
memcpy(pcstack, tmp_pcstack, nbytes); memcpy(pcstack, tmp_pcstack, nbytes);
cx->free(tmp_pcstack); cx->free(tmp_pcstack);
}
return pcdepth; return pcdepth;
failure: failure:
if (tmp_pcstack)
cx->free(tmp_pcstack); cx->free(tmp_pcstack);
return -1; return -1;
} }

View File

@ -1191,8 +1191,7 @@ CheckFinalReturn(JSContext *cx, JSTreeContext *tc, JSParseNode *pn)
bool bool
CheckStrictAssignment(JSContext *cx, JSTreeContext *tc, JSParseNode *lhs) CheckStrictAssignment(JSContext *cx, JSTreeContext *tc, JSParseNode *lhs)
{ {
if (tc->needStrictChecks() && if (tc->needStrictChecks() && lhs->pn_type == TOK_NAME) {
lhs->pn_type == TOK_NAME) {
JSAtom *atom = lhs->pn_atom; JSAtom *atom = lhs->pn_atom;
JSAtomState *atomState = &cx->runtime->atomState; JSAtomState *atomState = &cx->runtime->atomState;
if (atom == atomState->evalAtom || atom == atomState->argumentsAtom) { if (atom == atomState->evalAtom || atom == atomState->argumentsAtom) {
@ -1222,9 +1221,9 @@ CheckStrictBinding(JSContext *cx, JSTreeContext *tc, JSAtom *atom, JSParseNode *
JSAtomState *atomState = &cx->runtime->atomState; JSAtomState *atomState = &cx->runtime->atomState;
if (atom == atomState->evalAtom || atom == atomState->argumentsAtom) { if (atom == atomState->evalAtom || atom == atomState->argumentsAtom) {
const char *name = js_AtomToPrintableString(cx, atom); const char *name = js_AtomToPrintableString(cx, atom);
if (name) if (!name)
ReportStrictModeError(cx, TS(tc->parser), tc, pn, JSMSG_BAD_BINDING, name);
return false; return false;
return ReportStrictModeError(cx, TS(tc->parser), tc, pn, JSMSG_BAD_BINDING, name);
} }
return true; return true;
} }
@ -2529,7 +2528,7 @@ LeaveFunction(JSParseNode *fn, JSTreeContext *funtc, JSAtom *funAtom = NULL,
* Make sure to deoptimize lexical dependencies that are polluted * Make sure to deoptimize lexical dependencies that are polluted
* by eval or with, to safely statically bind globals (see bug 561923). * 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 && (outer_ale && tc->innermostWith &&
ALE_DEFN(outer_ale)->pn_pos < tc->innermostWith->pn_pos)) { ALE_DEFN(outer_ale)->pn_pos < tc->innermostWith->pn_pos)) {
DeoptimizeUsesWithin(dn, fn->pn_pos); DeoptimizeUsesWithin(dn, fn->pn_pos);
@ -2608,32 +2607,166 @@ LeaveFunction(JSParseNode *fn, JSTreeContext *funtc, JSAtom *funAtom = NULL,
funtc->lexdeps.clear(); 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; return true;
} }
static bool static bool
DefineGlobal(JSParseNode *pn, JSCodeGenerator *cg, JSAtom *atom); DefineGlobal(JSParseNode *pn, JSCodeGenerator *cg, JSAtom *atom);
JSParseNode * bool
Parser::functionDef(uintN lambda, bool namePermitted) Parser::functionArguments(JSTreeContext &funtc, JSFunctionBox *funbox, JSFunction *fun,
JSParseNode **listp)
{ {
JSParseNode *pn, *body, *result; if (tokenStream.getToken() != TOK_LP) {
TokenKind tt; reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_PAREN_BEFORE_FORMAL);
JSAtomListElement *ale; return false;
}
if (!tokenStream.matchToken(TOK_RP)) {
#if JS_HAS_DESTRUCTURING #if JS_HAS_DESTRUCTURING
JSParseNode *item, *list = NULL;
bool destructuringArg = false;
JSAtom *duplicatedArg = NULL; JSAtom *duplicatedArg = NULL;
bool destructuringArg = false;
JSParseNode *list = NULL;
#endif #endif
do {
switch (TokenKind tt = tokenStream.getToken()) {
#if JS_HAS_DESTRUCTURING
case TOK_LB:
case TOK_LC:
{
/* See comment below in the TOK_NAME case. */
if (duplicatedArg)
goto report_dup_and_destructuring;
destructuringArg = true;
/* /*
* Save the current op for later so we can tag the created function as a * A destructuring formal parameter turns into one or more
* getter/setter if necessary. * local variables initialized from properties of a single
* anonymous positional parameter, so here we must tweak our
* binder and its data.
*/ */
JSOp op = tokenStream.currentToken().t_op; 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
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. */ /* Make a TOK_FUNCTION node. */
pn = FunctionNode::create(tc); tokenStream.mungeCurrentToken(TOK_FUNCTION, JSOP_NOP);
JSParseNode *pn = FunctionNode::create(tc);
if (!pn) if (!pn)
return NULL; return NULL;
pn->pn_body = NULL; pn->pn_body = NULL;
@ -2650,28 +2783,12 @@ Parser::functionDef(uintN lambda, bool namePermitted)
bool topLevel = tc->atTopLevel(); bool topLevel = tc->atTopLevel();
pn->pn_dflags = (lambda || !topLevel) ? PND_FUNARG : 0; 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 * Record names for function statements in tc->decls so we know when to
* avoid optimizing variable references that might name a function. * avoid optimizing variable references that might name a function.
*/ */
if (lambda == 0 && funAtom) { if (lambda == 0 && funAtom) {
ale = tc->decls.lookup(funAtom); if (JSAtomListElement *ale = tc->decls.lookup(funAtom)) {
if (ale) {
JSDefinition *dn = ALE_DEFN(ale); JSDefinition *dn = ALE_DEFN(ale);
JSDefinition::Kind dn_kind = dn->kind(); JSDefinition::Kind dn_kind = dn->kind();
@ -2781,124 +2898,24 @@ Parser::functionDef(uintN lambda, bool namePermitted)
JSFunction *fun = (JSFunction *) funbox->object; 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. */ /* Now parse formal argument list and compute fun->nargs. */
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL); JSParseNode *prolog = NULL;
if (!tokenStream.matchToken(TOK_RP)) { if (!functionArguments(funtc, funbox, fun, &prolog))
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; return NULL;
/* if (type == GETTER && fun->nargs > 0) {
* Adjust fun->nargs to count the single anonymous positional reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_ACCESSOR_WRONG_ARGS,
* parameter that is to be destructured. "getter", "no", "s");
*/
slot = fun->nargs;
if (!js_AddLocal(context, fun, NULL, JSLOCAL_ARG))
return NULL; 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); if (type == SETTER && fun->nargs != 1) {
break; reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_ACCESSOR_WRONG_ARGS,
} "setter", "one", "");
#endif /* JS_HAS_DESTRUCTURING */
case TOK_NAME:
{
JSAtom *atom = tokenStream.currentToken().t_atom;
if (!DefineArg(pn, atom, fun->nargs, &funtc))
return NULL; 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);
} }
#if JS_HAS_EXPR_CLOSURES #if JS_HAS_EXPR_CLOSURES
tt = tokenStream.getToken(TSF_OPERAND); TokenKind tt = tokenStream.getToken(TSF_OPERAND);
if (tt != TOK_LC) { if (tt != TOK_LC) {
tokenStream.ungetToken(); tokenStream.ungetToken();
fun->flags |= JSFUN_EXPR_CLOSURE; fun->flags |= JSFUN_EXPR_CLOSURE;
@ -2907,7 +2924,7 @@ Parser::functionDef(uintN lambda, bool namePermitted)
MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY); MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY);
#endif #endif
body = functionBody(); JSParseNode *body = functionBody();
if (!body) if (!body)
return NULL; return NULL;
@ -2927,6 +2944,24 @@ Parser::functionDef(uintN lambda, bool namePermitted)
#endif #endif
pn->pn_pos.end = tokenStream.currentToken().pos.end; 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 JS_HAS_DESTRUCTURING
/* /*
* If there were destructuring formal parameters, prepend the initializing * If there were destructuring formal parameters, prepend the initializing
@ -2935,7 +2970,7 @@ Parser::functionDef(uintN lambda, bool namePermitted)
* parameter destructuring code without bracing the decompilation of the * parameter destructuring code without bracing the decompilation of the
* function body's lexical scope. * function body's lexical scope.
*/ */
if (list) { if (prolog) {
if (body->pn_arity != PN_LIST) { if (body->pn_arity != PN_LIST) {
JSParseNode *block; JSParseNode *block;
@ -2949,13 +2984,13 @@ Parser::functionDef(uintN lambda, bool namePermitted)
body = block; body = block;
} }
item = UnaryNode::create(outertc); JSParseNode *item = UnaryNode::create(outertc);
if (!item) if (!item)
return NULL; return NULL;
item->pn_type = TOK_SEMI; item->pn_type = TOK_SEMI;
item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin; 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; item->pn_next = body->pn_head;
body->pn_head = item; body->pn_head = item;
if (body->pn_tail == &body->pn_head) if (body->pn_tail == &body->pn_head)
@ -2984,7 +3019,8 @@ Parser::functionDef(uintN lambda, bool namePermitted)
outertc->flags |= TCF_FUN_HEAVYWEIGHT; outertc->flags |= TCF_FUN_HEAVYWEIGHT;
} }
result = pn; JSParseNode *result = pn;
JSOp op = JSOP_NOP;
if (lambda != 0) { if (lambda != 0) {
/* /*
* ECMA ed. 3 standard: function expression, possibly anonymous. * ECMA ed. 3 standard: function expression, possibly anonymous.
@ -3013,8 +3049,6 @@ Parser::functionDef(uintN lambda, bool namePermitted)
* sub-statement. * sub-statement.
*/ */
op = JSOP_DEFFUN; op = JSOP_DEFFUN;
} else {
op = JSOP_NOP;
} }
funbox->kids = funtc.functionList; funbox->kids = funtc.functionList;
@ -3041,7 +3075,7 @@ Parser::functionDef(uintN lambda, bool namePermitted)
return NULL; return NULL;
/* If the surrounding function is not strict code, reset the lexer. */ /* If the surrounding function is not strict code, reset the lexer. */
if (!(outertc->flags & TCF_STRICT_MODE_CODE)) if (!outertc->inStrictMode())
tokenStream.setStrictMode(false); tokenStream.setStrictMode(false);
return result; return result;
@ -3050,13 +3084,29 @@ Parser::functionDef(uintN lambda, bool namePermitted)
JSParseNode * JSParseNode *
Parser::functionStmt() 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 * JSParseNode *
Parser::functionExpr() 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);
} }
/* /*
@ -3673,7 +3723,8 @@ NoteLValue(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, uintN dflag = PND_
pn->pn_dflags |= dflag; 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; tc->flags |= TCF_FUN_HEAVYWEIGHT;
} }
@ -5806,15 +5857,6 @@ Parser::statement()
return MatchOrInsertSemicolon(context, &tokenStream) ? pn : NULL; 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 * JSParseNode *
Parser::variables(bool inLetHead) Parser::variables(bool inLetHead)
{ {
@ -5982,7 +6024,7 @@ Parser::variables(bool inLetHead)
if (tc->inFunction() && if (tc->inFunction() &&
atom == context->runtime->atomState.argumentsAtom) { atom == context->runtime->atomState.argumentsAtom) {
NoteArgumentsUse(tc); tc->noteArgumentsUse();
if (!let) if (!let)
tc->flags |= TCF_FUN_HEAVYWEIGHT; tc->flags |= TCF_FUN_HEAVYWEIGHT;
} }
@ -7184,7 +7226,8 @@ Parser::memberExpr(JSBool allowCallSyntax)
if (pn->pn_atom == context->runtime->atomState.evalAtom) { if (pn->pn_atom == context->runtime->atomState.evalAtom) {
/* Select JSOP_EVAL and flag tc as heavyweight. */ /* Select JSOP_EVAL and flag tc as heavyweight. */
pn2->pn_op = JSOP_EVAL; 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) { } else if (pn->pn_op == JSOP_GETPROP) {
if (pn->pn_atom == context->runtime->atomState.applyAtom || if (pn->pn_atom == context->runtime->atomState.applyAtom ||
@ -8115,9 +8158,8 @@ Parser::primaryExpr(TokenKind tt, JSBool afterDot)
goto property_name; goto property_name;
} }
/* We have to fake a 'function' token here. */ /* NB: Getter function in { get x(){} } is unnamed. */
tokenStream.mungeCurrentToken(TOK_FUNCTION, JSOP_NOP); pn2 = functionDef(NULL, op == JSOP_SETTER ? SETTER : GETTER, JSFUN_LAMBDA);
pn2 = functionDef(JSFUN_LAMBDA, false);
pn2 = JSParseNode::newBinaryOrAppend(TOK_COLON, op, pn3, pn2, tc); pn2 = JSParseNode::newBinaryOrAppend(TOK_COLON, op, pn3, pn2, tc);
goto skip; goto skip;
} }
@ -8333,7 +8375,7 @@ Parser::primaryExpr(TokenKind tt, JSBool afterDot)
* a reference of the form foo.arguments, which ancient code may * a reference of the form foo.arguments, which ancient code may
* still use instead of arguments (more hate). * still use instead of arguments (more hate).
*/ */
NoteArgumentsUse(tc); tc->noteArgumentsUse();
/* /*
* Bind early to JSOP_ARGUMENTS to relieve later code from having * Bind early to JSOP_ARGUMENTS to relieve later code from having

View File

@ -486,9 +486,9 @@ public:
#define PNX_DESTRUCT 0x200 /* destructuring special cases: #define PNX_DESTRUCT 0x200 /* destructuring special cases:
1. shorthand syntax used, at present 1. shorthand syntax used, at present
object destructuring ({x,y}) only; object destructuring ({x,y}) only;
2. the first child of function body 2. code evaluating destructuring
is code evaluating destructuring arguments occurs before function
arguments */ body */
#define PNX_HOLEY 0x400 /* array initialiser has holes */ #define PNX_HOLEY 0x400 /* array initialiser has holes */
uintN frameLevel() const { uintN frameLevel() const {
@ -1061,8 +1061,13 @@ private:
* Additional JS parsers. * Additional JS parsers.
*/ */
bool recognizeDirectivePrologue(JSParseNode *pn); bool recognizeDirectivePrologue(JSParseNode *pn);
enum FunctionType { GETTER, SETTER, GENERAL };
bool functionArguments(JSTreeContext &funtc, JSFunctionBox *funbox, JSFunction *fun,
JSParseNode **list);
JSParseNode *functionBody(); JSParseNode *functionBody();
JSParseNode *functionDef(uintN lambda, bool namePermitted); JSParseNode *functionDef(JSAtom *name, FunctionType type, uintN lambda);
JSParseNode *condition(); JSParseNode *condition();
JSParseNode *comprehensionTail(JSParseNode *kid, uintN blockid, JSParseNode *comprehensionTail(JSParseNode *kid, uintN blockid,
js::TokenKind type = js::TOK_SEMI, JSOp op = JSOP_NOP); js::TokenKind type = js::TOK_SEMI, JSOp op = JSOP_NOP);

View File

@ -176,7 +176,7 @@ js_IsIdentifier(JSString *str)
/* Initialize members that aren't initialized in |init|. */ /* Initialize members that aren't initialized in |init|. */
TokenStream::TokenStream(JSContext *cx) TokenStream::TokenStream(JSContext *cx)
: cx(cx), tokens(), cursor(), lookahead(), ungetpos(), ungetbuf(), flags(), : cx(cx), tokens(), cursor(), lookahead(), flags(),
linepos(), lineposNext(), file(), listenerTSData(), tokenbuf(cx) linepos(), lineposNext(), file(), listenerTSData(), tokenbuf(cx)
{} {}
@ -192,8 +192,8 @@ TokenStream::init(const jschar *base, size_t length, FILE *fp, const char *fn, u
JS_ASSERT_IF(fp, !base); JS_ASSERT_IF(fp, !base);
JS_ASSERT_IF(!base, length == 0); JS_ASSERT_IF(!base, length == 0);
size_t nb = fp size_t nb = fp
? 2 * LINE_LIMIT * sizeof(jschar) ? (UNGET_LIMIT + 2 * LINE_LIMIT) * sizeof(jschar) /* see below */
: LINE_LIMIT * sizeof(jschar); : (UNGET_LIMIT + 1 * LINE_LIMIT) * sizeof(jschar);
JS_ARENA_ALLOCATE_CAST(buf, jschar *, &cx->tempPool, nb); JS_ARENA_ALLOCATE_CAST(buf, jschar *, &cx->tempPool, nb);
if (!buf) { if (!buf) {
js_ReportOutOfScriptQuota(cx); js_ReportOutOfScriptQuota(cx);
@ -204,18 +204,38 @@ TokenStream::init(const jschar *base, size_t length, FILE *fp, const char *fn, u
/* Initialize members. */ /* Initialize members. */
filename = fn; filename = fn;
lineno = ln; lineno = ln;
linebuf.base = linebuf.limit = linebuf.ptr = buf; /*
* Split 'buf' into 3 (ungetbuf, linebuf, userbuf) or 2 (ungetbuf, linebuf).
* ungetbuf is empty and fills backwards. linebuf is empty and fills forwards.
*/
ungetbuf.base = buf;
ungetbuf.limit = ungetbuf.ptr = buf + UNGET_LIMIT;
linebuf.base = linebuf.limit = linebuf.ptr = buf + UNGET_LIMIT;
if (fp) { if (fp) {
file = fp; file = fp;
userbuf.base = buf + LINE_LIMIT; userbuf.base = buf + UNGET_LIMIT + LINE_LIMIT;
userbuf.ptr = userbuf.limit = userbuf.base + LINE_LIMIT; userbuf.ptr = userbuf.limit = userbuf.base + LINE_LIMIT;
} else { } else {
userbuf.base = (jschar *)base; userbuf.base = (jschar *)base;
userbuf.limit = (jschar *)base + length; userbuf.limit = (jschar *)base + length;
userbuf.ptr = (jschar *)base; userbuf.ptr = (jschar *)base;
} }
currbuf = &linebuf;
listener = cx->debugHooks->sourceHandler; listener = cx->debugHooks->sourceHandler;
listenerData = cx->debugHooks->sourceHandlerData; listenerData = cx->debugHooks->sourceHandlerData;
/* See getCharFillLinebuf() for an explanation of maybeEOL[]. */
memset(maybeEOL, 0, sizeof(maybeEOL));
maybeEOL['\n'] = true;
maybeEOL['\r'] = true;
maybeEOL[LINE_SEPARATOR & 0xff] = true;
maybeEOL[PARA_SEPARATOR & 0xff] = true;
/* See getTokenInternal() for an explanation of maybeStrSpecial[]. */
memset(maybeStrSpecial, 0, sizeof(maybeStrSpecial));
maybeStrSpecial['"'] = true;
maybeStrSpecial['\''] = true;
maybeStrSpecial['\n'] = true;
maybeStrSpecial['\\'] = true;
maybeStrSpecial[EOF & 0xff] = true;
return true; return true;
} }
@ -352,11 +372,21 @@ TokenStream::getCharFillLinebuf()
i++; i++;
/* /*
* Normalize the copied jschar if it was a newline. Try to * Normalize the copied jschar if it was a newline. We need to detect
* prevent multiple tests on most characters by first * any of these four characters: '\n' (0x000a), '\r' (0x000d),
* filtering out characters that aren't 000x or 202x. * LINE_SEPARATOR (0x2028), PARA_SEPARATOR (0x2029). Testing for each
* one in turn is slow, so we use a single probabilistic check, and if
* that succeeds, test for them individually.
*
* We use the bottom 8 bits to index into a lookup table, succeeding
* when d&0xff is 0xa, 0xd, 0x28 or 0x29. Among ASCII chars (which
* are by the far the most common) this gives false positives for '('
* (0x0028) and ')' (0x0029). We could avoid those by incorporating
* the 13th bit of d into the lookup, but that requires extra shifting
* and masking and isn't worthwhile. See TokenStream::init() for the
* initialization of the relevant entries in the table.
*/ */
if ((d & 0xDFD0) == 0) { if (maybeEOL[d & 0xff]) {
if (d == '\n') { if (d == '\n') {
break; break;
} }
@ -399,15 +429,20 @@ TokenStream::getCharFillLinebuf()
* This gets the next char, normalizing all EOL sequences to '\n' as it goes. * This gets the next char, normalizing all EOL sequences to '\n' as it goes.
*/ */
int32 int32
TokenStream::getChar() TokenStream::getCharSlowCase()
{ {
int32 c; int32 c;
if (ungetpos != 0) { if (currbuf->ptr == currbuf->limit - 1) {
c = ungetbuf[--ungetpos]; /* Last char of currbuf. Switch to linebuf if we're in ungetbuf. */
} else if (linebuf.ptr == linebuf.limit) { c = *currbuf->ptr++;
c = getCharFillLinebuf(); if (currbuf == &ungetbuf)
currbuf = &linebuf;
} else { } else {
c = *linebuf.ptr++; /* One past the last char of currbuf; can only happen for linebuf. */
JS_ASSERT(currbuf->ptr == currbuf->limit);
JS_ASSERT(currbuf == &linebuf);
c = getCharFillLinebuf();
} }
if (c == '\n') if (c == '\n')
lineno++; lineno++;
@ -419,10 +454,14 @@ TokenStream::ungetChar(int32 c)
{ {
if (c == EOF) if (c == EOF)
return; return;
JS_ASSERT(ungetpos < JS_ARRAY_LENGTH(ungetbuf)); JS_ASSERT(ungetbuf.ptr >= ungetbuf.base);
if (c == '\n') if (c == '\n') {
/* We can only unget one '\n', and it must be the first ungotten char. */
JS_ASSERT(ungetbuf.ptr == ungetbuf.limit);
lineno--; lineno--;
ungetbuf[ungetpos++] = (jschar)c; }
*(--ungetbuf.ptr) = (jschar)c;
currbuf = &ungetbuf;
} }
/* /*
@ -816,7 +855,7 @@ TokenStream::newToken(ptrdiff_t adjust)
cursor = (cursor + 1) & ntokensMask; cursor = (cursor + 1) & ntokensMask;
Token *tp = &tokens[cursor]; Token *tp = &tokens[cursor];
tp->ptr = linebuf.ptr + adjust; tp->ptr = linebuf.ptr + adjust;
tp->pos.begin.index = linepos + (tp->ptr - linebuf.base) - ungetpos; tp->pos.begin.index = linepos + (tp->ptr - linebuf.base) - (ungetbuf.limit - ungetbuf.ptr);
tp->pos.begin.lineno = tp->pos.end.lineno = lineno; tp->pos.begin.lineno = tp->pos.end.lineno = lineno;
return tp; return tp;
} }
@ -1184,14 +1223,17 @@ TokenStream::getTokenInternal()
if (c == '"' || c == '\'') { if (c == '"' || c == '\'') {
qc = c; qc = c;
tokenbuf.clear(); tokenbuf.clear();
while ((c = getChar()) != qc) { while (true) {
if (c == '\n' || c == EOF) { c = getChar();
ungetChar(c); /*
ReportCompileErrorNumber(cx, this, NULL, JSREPORT_ERROR, * We need to detect any of these four chars: " or ', \n, \\,
JSMSG_UNTERMINATED_STRING); * EOF. We use maybeStrSpecial[] in a manner similar to
goto error; * maybeEOL[], see above.
} */
if (c == '\\') { if (maybeStrSpecial[c & 0xff]) {
if (c == qc) {
break;
} else if (c == '\\') {
switch (c = getChar()) { switch (c = getChar()) {
case 'b': c = '\b'; break; case 'b': c = '\b'; break;
case 'f': c = '\f'; break; case 'f': c = '\f'; break;
@ -1251,6 +1293,12 @@ TokenStream::getTokenInternal()
} }
break; break;
} }
} else if (c == '\n' || c == EOF) {
ungetChar(c);
ReportCompileErrorNumber(cx, this, NULL, JSREPORT_ERROR,
JSMSG_UNTERMINATED_STRING);
goto error;
}
} }
if (!tokenbuf.append(c)) if (!tokenbuf.append(c))
goto error; goto error;
@ -1800,7 +1848,7 @@ TokenStream::getTokenInternal()
eol_out: eol_out:
JS_ASSERT(tt < TOK_LIMIT); JS_ASSERT(tt < TOK_LIMIT);
tp->pos.end.index = linepos + (linebuf.ptr - linebuf.base) - ungetpos; tp->pos.end.index = linepos + (linebuf.ptr - linebuf.base) - (ungetbuf.limit - ungetbuf.ptr);
tp->type = tt; tp->type = tt;
return tt; return tt;

View File

@ -292,8 +292,10 @@ enum TokenStreamFlags
#define t_atom2 u.p.atom2 #define t_atom2 u.p.atom2
#define t_dval u.dval #define t_dval u.dval
const size_t LINE_LIMIT = 1024; /* logical line buffer size limit static const size_t LINE_LIMIT = 1024; /* logical line buffer size limit
-- physical line length is unlimited */ -- physical line length is unlimited */
static const size_t UNGET_LIMIT = 6; /* maximum number of chars to unget at once
-- for \uXXXX lookahead */
class TokenStream class TokenStream
{ {
@ -449,7 +451,21 @@ class TokenStream
TokenKind getTokenInternal(); /* doesn't check for pushback or error flag. */ TokenKind getTokenInternal(); /* doesn't check for pushback or error flag. */
int fillUserbuf(); int fillUserbuf();
int32 getCharFillLinebuf(); int32 getCharFillLinebuf();
int32 getChar();
/* This gets the next char, normalizing all EOL sequences to '\n' as it goes. */
JS_ALWAYS_INLINE int32 getChar() {
int32 c;
if (currbuf->ptr < currbuf->limit - 1) {
/* Not yet the last char of currbuf, so it can't be a newline. Just get it. */
c = *currbuf->ptr++;
JS_ASSERT(c != '\n');
} else {
c = getCharSlowCase();
}
return c;
}
int32 getCharSlowCase();
void ungetChar(int32 c); void ungetChar(int32 c);
Token *newToken(ptrdiff_t adjust); Token *newToken(ptrdiff_t adjust);
int32 getUnicodeEscape(); int32 getUnicodeEscape();
@ -480,19 +496,21 @@ class TokenStream
uintN cursor; /* index of last parsed token */ uintN cursor; /* index of last parsed token */
uintN lookahead; /* count of lookahead tokens */ uintN lookahead; /* count of lookahead tokens */
uintN lineno; /* current line number */ uintN lineno; /* current line number */
uintN ungetpos; /* next free char slot in ungetbuf */
jschar ungetbuf[6]; /* at most 6, for \uXXXX lookahead */
uintN flags; /* flags -- see above */ uintN flags; /* flags -- see above */
uint32 linepos; /* linebuf offset in physical line */ uint32 linepos; /* linebuf offset in physical line */
uint32 lineposNext; /* the next value of linepos */ uint32 lineposNext; /* the next value of linepos */
TokenBuf linebuf; /* line buffer for diagnostics */ TokenBuf linebuf; /* line buffer for diagnostics */
TokenBuf userbuf; /* user input buffer if !file */ TokenBuf userbuf; /* user input buffer if !file */
TokenBuf ungetbuf; /* buffer for ungetChar */
TokenBuf *currbuf; /* the buffer getChar is currently using */
const char *filename; /* input filename or null */ const char *filename; /* input filename or null */
FILE *file; /* stdio stream if reading from file */ FILE *file; /* stdio stream if reading from file */
JSSourceHandler listener; /* callback for source; eg debugger */ JSSourceHandler listener; /* callback for source; eg debugger */
void *listenerData; /* listener 'this' data */ void *listenerData; /* listener 'this' data */
void *listenerTSData;/* listener data for this TokenStream */ void *listenerTSData;/* listener data for this TokenStream */
JSCharBuffer tokenbuf; /* current token string buffer */ JSCharBuffer tokenbuf; /* current token string buffer */
bool maybeEOL[256]; /* probabilistic EOL lookup table */
bool maybeStrSpecial[256];/* speeds up string scanning */
}; };
} /* namespace js */ } /* namespace js */

View File

@ -1030,13 +1030,25 @@ js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
{ {
/* /*
* We can probably use the immutable empty script singleton, just * 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(); JSScript *empty = JSScript::emptyScript();
if (cg->flags & TCF_IN_FUNCTION) { if (cg->flags & TCF_IN_FUNCTION) {
fun = cg->fun; 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) { if (fun->u.i.nupvars != 0) {
/* /*
* FIXME: upvar uses that were all optimized away may leave * FIXME: upvar uses that were all optimized away may leave
@ -1118,7 +1130,7 @@ js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
script->strictModeCode = true; script->strictModeCode = true;
if (cg->flags & TCF_COMPILE_N_GO) if (cg->flags & TCF_COMPILE_N_GO)
script->compileAndGo = true; script->compileAndGo = true;
if (cg->flags & TCF_FUN_USES_EVAL) if (cg->flags & TCF_FUN_CALLS_EVAL)
script->usesEval = true; script->usesEval = true;
if (cg->upvarList.count != 0) { if (cg->upvarList.count != 0) {

View File

@ -79,6 +79,7 @@
#include "jsatominlines.h" #include "jsatominlines.h"
#include "jscntxtinlines.h" #include "jscntxtinlines.h"
#include "jsfuninlines.h"
#include "jspropertycacheinlines.h" #include "jspropertycacheinlines.h"
#include "jsobjinlines.h" #include "jsobjinlines.h"
#include "jsscopeinlines.h" #include "jsscopeinlines.h"
@ -3453,8 +3454,10 @@ FlushNativeStackFrame(JSContext* cx, unsigned callDepth, const JSValueType* mp,
for (; n != 0; fp = fp->down) { for (; n != 0; fp = fp->down) {
--n; --n;
if (fp->argv) { 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); fp->getArgsObj()->setPrivate(fp);
}
JS_ASSERT(fp->argv[-1].isObjectOrNull()); JS_ASSERT(fp->argv[-1].isObjectOrNull());
JS_ASSERT(fp->callee()->isFunction()); JS_ASSERT(fp->callee()->isFunction());
@ -8748,13 +8751,22 @@ TraceRecorder::tableswitch()
high = GET_JUMPX_OFFSET(pc); 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. */ /* Cap maximum table-switch size for modesty. */
if ((high + 1 - low) > MAX_TABLE_SWITCH) if (count > MAX_TABLE_SWITCH)
return InjectStatus(switchop()); return InjectStatus(switchop());
/* Generate switch LIR. */ /* Generate switch LIR. */
SwitchInfo* si = new (traceAlloc()) SwitchInfo(); SwitchInfo* si = new (traceAlloc()) SwitchInfo();
si->count = high + 1 - low; si->count = count;
si->table = 0; si->table = 0;
si->index = (uint32) -1; si->index = (uint32) -1;
LIns* diff = lir->ins2(LIR_subi, v_ins, lir->insImmI(low)); LIns* diff = lir->ins2(LIR_subi, v_ins, lir->insImmI(low));
@ -10297,7 +10309,7 @@ TraceRecorder::clearCurrentFrameSlotsFromTracker(Tracker& which)
JS_REQUIRES_STACK void JS_REQUIRES_STACK void
TraceRecorder::putActivationObjects() 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() && bool have_call = cx->fp->hasFunction() &&
JSFUN_HEAVYWEIGHT_TEST(cx->fp->getFunction()->flags) && JSFUN_HEAVYWEIGHT_TEST(cx->fp->getFunction()->flags) &&
cx->fp->getFunction()->countArgsAndVars(); cx->fp->getFunction()->countArgsAndVars();
@ -10660,7 +10672,7 @@ TraceRecorder::record_JSOP_IFNE()
} }
LIns* LIns*
TraceRecorder::newArguments(LIns* callee_ins) TraceRecorder::newArguments(LIns* callee_ins, bool strict)
{ {
LIns* global_ins = INS_CONSTOBJ(globalObj); LIns* global_ins = INS_CONSTOBJ(globalObj);
LIns* argc_ins = INS_CONST(cx->fp->argc); LIns* argc_ins = INS_CONST(cx->fp->argc);
@ -10668,6 +10680,15 @@ TraceRecorder::newArguments(LIns* callee_ins)
LIns* args[] = { callee_ins, argc_ins, global_ins, cx_ins }; LIns* args[] = { callee_ins, argc_ins, global_ins, cx_ins };
LIns* call_ins = lir->insCall(&js_Arguments_ci, args); LIns* call_ins = lir->insCall(&js_Arguments_ci, args);
guard(false, lir->insEqP_0(call_ins), OOM_EXIT); 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; return call_ins;
} }
@ -10680,12 +10701,15 @@ TraceRecorder::record_JSOP_ARGUMENTS()
if (cx->fp->flags & JSFRAME_OVERRIDE_ARGS) if (cx->fp->flags & JSFRAME_OVERRIDE_ARGS)
RETURN_STOP_A("Can't trace |arguments| if |arguments| is assigned to"); 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* a_ins = getFrameObjPtr(cx->fp->addressArgsObj());
LIns* args_ins; 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()) { if (a_ins->isImmP()) {
// |arguments| is set to 0 by EnterFrame on this trace, so call to create it. // |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 { } else {
// Generate LIR to create arguments only if it has not already been created. // Generate LIR to create arguments only if it has not already been created.
@ -10698,7 +10722,7 @@ TraceRecorder::record_JSOP_ARGUMENTS()
LIns* label1 = lir->ins0(LIR_label); LIns* label1 = lir->ins0(LIR_label);
br1->setTarget(label1); 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); lir->insStore(call_ins, mem_ins, 0, ACCSET_OTHER);
LIns* label2 = lir->ins0(LIR_label); LIns* label2 = lir->ins0(LIR_label);
@ -10708,7 +10732,7 @@ TraceRecorder::record_JSOP_ARGUMENTS()
} }
stack(0, args_ins); stack(0, args_ins);
setFrameObjPtr(cx->fp->addressArgsObj(), args_ins); setFrameObjPtr(fp->addressArgsObj(), args_ins);
return ARECORD_CONTINUE; return ARECORD_CONTINUE;
} }
@ -13361,7 +13385,7 @@ TraceRecorder::guardArguments(JSObject *obj, LIns* obj_ins, unsigned *depthp)
return NULL; return NULL;
VMSideExit *exit = snapshot(MISMATCH_EXIT); 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* args_ins = getFrameObjPtr(afp->addressArgsObj());
LIns* cmp = lir->ins2(LIR_eqp, args_ins, obj_ins); LIns* cmp = lir->ins2(LIR_eqp, args_ins, obj_ins);
@ -15272,10 +15296,10 @@ TraceRecorder::record_JSOP_ARGSUB()
JS_REQUIRES_STACK LIns* JS_REQUIRES_STACK LIns*
TraceRecorder::guardArgsLengthNotAssigned(LIns* argsobj_ins) TraceRecorder::guardArgsLengthNotAssigned(LIns* argsobj_ins)
{ {
// The following implements IsOverriddenArgsLength on trace. // The following implements JSObject::isArgsLengthOverridden on trace.
// The '2' bit is set if length was overridden. // ARGS_LENGTH_OVERRIDDEN_BIT is set if length was overridden.
LIns *len_ins = stobj_get_fslot_uint32(argsobj_ins, JSObject::JSSLOT_ARGS_LENGTH); 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)); guard(true, lir->insEqI_0(ovr_ins), snapshot(BRANCH_EXIT));
return len_ins; return len_ins;
} }
@ -15973,9 +15997,11 @@ TraceRecorder::record_JSOP_LENGTH()
RETURN_STOP_A("can't trace JSOP_ARGCNT if arguments.length has been modified"); RETURN_STOP_A("can't trace JSOP_ARGCNT if arguments.length has been modified");
LIns* slot_ins = guardArgsLengthNotAssigned(obj_ins); LIns* slot_ins = guardArgsLengthNotAssigned(obj_ins);
// slot_ins is the value from the slot; right-shift by 2 bits to get // slot_ins is the value from the slot; right-shift to get the length
// the length (see GetArgsLength in jsfun.cpp). // (see JSObject::getArgsInitialLength in jsfun.cpp).
LIns* v_ins = lir->ins1(LIR_i2d, lir->ins2ImmI(LIR_rshi, slot_ins, 1)); LIns* v_ins =
lir->ins1(LIR_i2d, lir->ins2ImmI(LIR_rshi,
slot_ins, JSObject::ARGS_PACKED_BITS_COUNT));
set(&l, v_ins); set(&l, v_ins);
return ARECORD_CONTINUE; return ARECORD_CONTINUE;
} }

View File

@ -1167,7 +1167,7 @@ class TraceRecorder
JS_REQUIRES_STACK nanojit::LIns* makeNumberInt32(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* 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 bool canCallImacro() const;
JS_REQUIRES_STACK RecordingStatus callImacro(jsbytecode* imacro); JS_REQUIRES_STACK RecordingStatus callImacro(jsbytecode* imacro);

View File

@ -2115,8 +2115,7 @@ mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck)
pic.objRemat = frame.dataRematInfo(top); pic.objRemat = frame.dataRematInfo(top);
/* Guard on shape. */ /* Guard on shape. */
masm.loadPtr(Address(objReg, offsetof(JSObject, map)), shapeReg); masm.loadShape(objReg, shapeReg);
masm.load32(Address(shapeReg, offsetof(JSObjectMap, shape)), shapeReg);
pic.shapeGuard = masm.label(); pic.shapeGuard = masm.label();
DataLabel32 inlineShapeLabel; DataLabel32 inlineShapeLabel;
@ -2203,8 +2202,7 @@ mjit::Compiler::jsop_getelem_pic(FrameEntry *obj, FrameEntry *id, RegisterID obj
pic.fastPathStart = masm.label(); pic.fastPathStart = masm.label();
/* Guard on shape. */ /* Guard on shape. */
masm.loadPtr(Address(objReg, offsetof(JSObject, map)), shapeReg); masm.loadShape(objReg, shapeReg);
masm.load32(Address(shapeReg, offsetof(JSObjectMap, shape)), shapeReg);
pic.shapeGuard = masm.label(); pic.shapeGuard = masm.label();
DataLabel32 inlineShapeOffsetLabel; DataLabel32 inlineShapeOffsetLabel;
@ -2347,8 +2345,7 @@ mjit::Compiler::jsop_callprop_generic(JSAtom *atom)
frame.freeReg(pic.typeReg); frame.freeReg(pic.typeReg);
/* Guard on shape. */ /* Guard on shape. */
masm.loadPtr(Address(objReg, offsetof(JSObject, map)), shapeReg); masm.loadShape(objReg, shapeReg);
masm.load32(Address(shapeReg, offsetof(JSObjectMap, shape)), shapeReg);
pic.shapeGuard = masm.label(); pic.shapeGuard = masm.label();
DataLabel32 inlineShapeLabel; DataLabel32 inlineShapeLabel;
@ -2515,8 +2512,7 @@ mjit::Compiler::jsop_callprop_obj(JSAtom *atom)
pic.objRemat = frame.dataRematInfo(top); pic.objRemat = frame.dataRematInfo(top);
/* Guard on shape. */ /* Guard on shape. */
masm.loadPtr(Address(objReg, offsetof(JSObject, map)), shapeReg); masm.loadShape(objReg, shapeReg);
masm.load32(Address(shapeReg, offsetof(JSObjectMap, shape)), shapeReg);
pic.shapeGuard = masm.label(); pic.shapeGuard = masm.label();
DataLabel32 inlineShapeLabel; DataLabel32 inlineShapeLabel;
@ -2688,8 +2684,7 @@ mjit::Compiler::jsop_setprop(JSAtom *atom)
} }
/* Guard on shape. */ /* Guard on shape. */
masm.loadPtr(Address(objReg, offsetof(JSObject, map)), shapeReg); masm.loadShape(objReg, shapeReg);
masm.load32(Address(shapeReg, offsetof(JSObjectMap, shape)), shapeReg);
pic.shapeGuard = masm.label(); pic.shapeGuard = masm.label();
DataLabel32 inlineShapeOffsetLabel; DataLabel32 inlineShapeOffsetLabel;
Jump j = masm.branch32WithPatch(Assembler::NotEqual, shapeReg, Jump j = masm.branch32WithPatch(Assembler::NotEqual, shapeReg,
@ -3473,8 +3468,7 @@ mjit::Compiler::jsop_getgname(uint32 index)
frame.pop(); frame.pop();
RegisterID reg = frame.allocReg(); RegisterID reg = frame.allocReg();
masm.loadPtr(Address(objReg, offsetof(JSObject, map)), reg); masm.loadShape(objReg, reg);
masm.load32(Address(reg, offsetof(JSObjectMap, shape)), reg);
shapeGuard = masm.branch32WithPatch(Assembler::NotEqual, reg, shapeGuard = masm.branch32WithPatch(Assembler::NotEqual, reg,
Imm32(int32(JSObjectMap::INVALID_SHAPE)), mic.shape); Imm32(int32(JSObjectMap::INVALID_SHAPE)), mic.shape);
frame.freeReg(reg); frame.freeReg(reg);
@ -3510,8 +3504,13 @@ mjit::Compiler::jsop_getgname(uint32 index)
mic.load = masm.label(); mic.load = masm.label();
# if defined JS_NUNBOX32 # if defined JS_NUNBOX32
# if defined JS_CPU_ARM
DataLabel32 offsetAddress = masm.load64WithAddressOffsetPatch(address, treg, dreg);
JS_ASSERT(masm.differenceBetween(mic.load, offsetAddress) == 0);
# else
masm.loadPayload(address, dreg); masm.loadPayload(address, dreg);
masm.loadTypeTag(address, treg); masm.loadTypeTag(address, treg);
# endif
# elif defined JS_PUNBOX64 # elif defined JS_PUNBOX64
Label inlineValueLoadLabel = Label inlineValueLoadLabel =
masm.loadValueAsComponents(address, treg, dreg); masm.loadValueAsComponents(address, treg, dreg);
@ -3569,8 +3568,7 @@ mjit::Compiler::jsop_setgname(uint32 index)
frame.pinReg(objReg); frame.pinReg(objReg);
RegisterID reg = frame.allocReg(); RegisterID reg = frame.allocReg();
masm.loadPtr(Address(objReg, offsetof(JSObject, map)), reg); masm.loadShape(objReg, reg);
masm.load32(Address(reg, offsetof(JSObjectMap, shape)), reg);
shapeGuard = masm.branch32WithPatch(Assembler::NotEqual, reg, shapeGuard = masm.branch32WithPatch(Assembler::NotEqual, reg,
Imm32(int32(JSObjectMap::INVALID_SHAPE)), Imm32(int32(JSObjectMap::INVALID_SHAPE)),
mic.shape); mic.shape);
@ -3608,11 +3606,27 @@ mjit::Compiler::jsop_setgname(uint32 index)
v = fe->getValue(); v = fe->getValue();
} }
mic.load = masm.label();
masm.loadPtr(Address(objReg, offsetof(JSObject, dslots)), objReg); masm.loadPtr(Address(objReg, offsetof(JSObject, dslots)), objReg);
Address address(objReg, slot); Address address(objReg, slot);
mic.load = masm.label();
#if defined JS_NUNBOX32 #if defined JS_NUNBOX32
# if defined JS_CPU_ARM
DataLabel32 offsetAddress;
if (mic.u.name.dataConst) {
offsetAddress = masm.moveWithPatch(Imm32(address.offset), JSC::ARMRegisters::S0);
masm.add32(address.base, JSC::ARMRegisters::S0);
masm.storeValue(v, Address(JSC::ARMRegisters::S0, 0));
} else {
if (mic.u.name.typeConst) {
offsetAddress = masm.store64WithAddressOffsetPatch(ImmType(typeTag), dataReg, address);
} else {
offsetAddress = masm.store64WithAddressOffsetPatch(typeReg, dataReg, address);
}
}
JS_ASSERT(masm.differenceBetween(mic.load, offsetAddress) == 0);
# else
if (mic.u.name.dataConst) { if (mic.u.name.dataConst) {
masm.storeValue(v, address); masm.storeValue(v, address);
} else { } else {
@ -3622,6 +3636,7 @@ mjit::Compiler::jsop_setgname(uint32 index)
masm.storeTypeTag(typeReg, address); masm.storeTypeTag(typeReg, address);
masm.storePayload(dataReg, address); masm.storePayload(dataReg, address);
} }
# endif
#elif defined JS_PUNBOX64 #elif defined JS_PUNBOX64
if (mic.u.name.dataConst) { if (mic.u.name.dataConst) {
/* Emits a single move. No code length variation. */ /* Emits a single move. No code length variation. */
@ -3739,10 +3754,9 @@ mjit::Compiler::jsop_instanceof()
/* Quick test to avoid wrapped objects. */ /* Quick test to avoid wrapped objects. */
masm.loadPtr(Address(obj, offsetof(JSObject, clasp)), temp); masm.loadPtr(Address(obj, offsetof(JSObject, clasp)), temp);
masm.load32(Address(temp, offsetof(Class, ext) + masm.loadPtr(Address(temp, offsetof(Class, ext) +
offsetof(ClassExtension, wrappedObject)), offsetof(ClassExtension, wrappedObject)), temp);
temp); j = masm.branchTestPtr(Assembler::NonZero, temp, temp);
j = masm.branchTest32(Assembler::NonZero, temp, temp);
stubcc.linkExit(j, Uses(3)); stubcc.linkExit(j, Uses(3));
Address protoAddr(obj, offsetof(JSObject, proto)); Address protoAddr(obj, offsetof(JSObject, proto));

View File

@ -130,14 +130,21 @@ struct Registers {
(1 << JSC::ARMRegisters::r0) (1 << JSC::ARMRegisters::r0)
| (1 << JSC::ARMRegisters::r1) | (1 << JSC::ARMRegisters::r1)
| (1 << JSC::ARMRegisters::r2); | (1 << JSC::ARMRegisters::r2);
// r3 is reserved as a scratch register for the assembler.
static const uint32 SavedRegs = static const uint32 SavedRegs =
(1 << JSC::ARMRegisters::r4) (1 << JSC::ARMRegisters::r4)
| (1 << JSC::ARMRegisters::r5) | (1 << JSC::ARMRegisters::r5)
| (1 << JSC::ARMRegisters::r6) | (1 << JSC::ARMRegisters::r6)
| (1 << JSC::ARMRegisters::r7) | (1 << JSC::ARMRegisters::r7)
// r8 is reserved as a scratch register for the assembler.
| (1 << JSC::ARMRegisters::r9) | (1 << JSC::ARMRegisters::r9)
| (1 << JSC::ARMRegisters::r10); | (1 << JSC::ARMRegisters::r10);
// r11 is reserved for JSFrameReg.
// r12 is IP, and is used for stub calls.
// r13 is SP and must always point to VMFrame whilst in generated code.
// r14 is LR and is used for return sequences.
// r15 is PC (program counter).
static const uint32 SingleByteRegs = TempRegs | SavedRegs; static const uint32 SingleByteRegs = TempRegs | SavedRegs;
#else #else

View File

@ -101,9 +101,13 @@ ic::GetGlobalName(VMFrame &f, uint32 index)
slot -= JS_INITIAL_NSLOTS; slot -= JS_INITIAL_NSLOTS;
slot *= sizeof(Value); slot *= sizeof(Value);
JSC::RepatchBuffer loads(mic.load.executableAddress(), 32, false); JSC::RepatchBuffer loads(mic.load.executableAddress(), 32, false);
#if defined JS_NUNBOX32 #if defined JS_CPU_X86
loads.repatch(mic.load.dataLabel32AtOffset(MICInfo::GET_DATA_OFFSET), slot); loads.repatch(mic.load.dataLabel32AtOffset(MICInfo::GET_DATA_OFFSET), slot);
loads.repatch(mic.load.dataLabel32AtOffset(MICInfo::GET_TYPE_OFFSET), slot + 4); loads.repatch(mic.load.dataLabel32AtOffset(MICInfo::GET_TYPE_OFFSET), slot + 4);
#elif defined JS_CPU_ARM
// mic.load actually points to the LDR instruction which fetches the offset, but 'repatch'
// knows how to dereference it to find the integer value.
loads.repatch(mic.load.dataLabel32AtOffset(0), slot);
#elif defined JS_PUNBOX64 #elif defined JS_PUNBOX64
loads.repatch(mic.load.dataLabel32AtOffset(mic.patchValueOffset), slot); loads.repatch(mic.load.dataLabel32AtOffset(mic.patchValueOffset), slot);
#endif #endif
@ -167,7 +171,7 @@ ic::SetGlobalName(VMFrame &f, uint32 index)
slot *= sizeof(Value); slot *= sizeof(Value);
JSC::RepatchBuffer stores(mic.load.executableAddress(), 32, false); JSC::RepatchBuffer stores(mic.load.executableAddress(), 32, false);
#if defined JS_NUNBOX32 #if defined JS_CPU_X86
stores.repatch(mic.load.dataLabel32AtOffset(MICInfo::SET_TYPE_OFFSET), slot + 4); stores.repatch(mic.load.dataLabel32AtOffset(MICInfo::SET_TYPE_OFFSET), slot + 4);
uint32 dataOffset; uint32 dataOffset;
@ -177,6 +181,10 @@ ic::SetGlobalName(VMFrame &f, uint32 index)
dataOffset = MICInfo::SET_DATA_TYPE_OFFSET; dataOffset = MICInfo::SET_DATA_TYPE_OFFSET;
if (mic.u.name.dataWrite) if (mic.u.name.dataWrite)
stores.repatch(mic.load.dataLabel32AtOffset(dataOffset), slot); stores.repatch(mic.load.dataLabel32AtOffset(dataOffset), slot);
#elif defined JS_CPU_ARM
// mic.load actually points to the LDR instruction which fetches the offset, but 'repatch'
// knows how to dereference it to find the integer value.
stores.repatch(mic.load.dataLabel32AtOffset(0), slot);
#elif defined JS_PUNBOX64 #elif defined JS_PUNBOX64
stores.repatch(mic.load.dataLabel32AtOffset(mic.patchValueOffset), slot); stores.repatch(mic.load.dataLabel32AtOffset(mic.patchValueOffset), slot);
#endif #endif

View File

@ -55,11 +55,12 @@ struct MICInfo {
static const uint32 GET_DATA_OFFSET = 6; static const uint32 GET_DATA_OFFSET = 6;
static const uint32 GET_TYPE_OFFSET = 12; static const uint32 GET_TYPE_OFFSET = 12;
static const uint32 SET_TYPE_OFFSET = 9; static const uint32 SET_TYPE_OFFSET = 6;
static const uint32 SET_DATA_CONST_TYPE_OFFSET = 19; static const uint32 SET_DATA_CONST_TYPE_OFFSET = 16;
static const uint32 SET_DATA_TYPE_OFFSET = 15; static const uint32 SET_DATA_TYPE_OFFSET = 12;
#elif JS_CPU_X64 #elif JS_CPU_X64 || JS_CPU_ARM
/* No constants used, thanks to patchValueOffset. */ /* X64: No constants used, thanks to patchValueOffset. */
/* ARM: No constants used as mic.load always points to an LDR that loads the offset. */
#endif #endif
enum Kind enum Kind

View File

@ -153,7 +153,6 @@ class Assembler : public BaseAssembler
/* /*
* Stores type first, then payload. * Stores type first, then payload.
* Returns label after type store. Useful for offset verification.
*/ */
void storeValue(const Value &v, Address address) { void storeValue(const Value &v, Address address) {
jsval_layout jv; jsval_layout jv;

View File

@ -853,7 +853,7 @@ class GetPropCompiler : public PICStubCompiler
if (pic.objNeedsRemat()) { if (pic.objNeedsRemat()) {
if (pic.objRemat() >= sizeof(JSStackFrame)) if (pic.objRemat() >= sizeof(JSStackFrame))
masm.load32(Address(JSFrameReg, pic.objRemat()), pic.objReg); masm.loadPayload(Address(JSFrameReg, pic.objRemat()), pic.objReg);
else else
masm.move(RegisterID(pic.objRemat()), pic.objReg); masm.move(RegisterID(pic.objRemat()), pic.objReg);
pic.u.get.objNeedsRemat = false; pic.u.get.objNeedsRemat = false;
@ -1210,7 +1210,7 @@ class GetElemCompiler : public PICStubCompiler
if (pic.objNeedsRemat()) { if (pic.objNeedsRemat()) {
if (pic.objRemat() >= sizeof(JSStackFrame)) if (pic.objRemat() >= sizeof(JSStackFrame))
masm.load32(Address(JSFrameReg, pic.objRemat()), pic.objReg); masm.loadPayload(Address(JSFrameReg, pic.objRemat()), pic.objReg);
else else
masm.move(RegisterID(pic.objRemat()), pic.objReg); masm.move(RegisterID(pic.objRemat()), pic.objReg);
pic.u.get.objNeedsRemat = false; pic.u.get.objNeedsRemat = false;
@ -1218,7 +1218,7 @@ class GetElemCompiler : public PICStubCompiler
if (pic.idNeedsRemat()) { if (pic.idNeedsRemat()) {
if (pic.idRemat() >= sizeof(JSStackFrame)) if (pic.idRemat() >= sizeof(JSStackFrame))
masm.load32(Address(JSFrameReg, pic.idRemat()), pic.u.get.idReg); masm.loadPayload(Address(JSFrameReg, pic.idRemat()), pic.u.get.idReg);
else else
masm.move(RegisterID(pic.idRemat()), pic.u.get.idReg); masm.move(RegisterID(pic.idRemat()), pic.u.get.idReg);
pic.u.get.idNeedsRemat = false; pic.u.get.idNeedsRemat = false;
@ -1843,7 +1843,7 @@ ic::GetProp(VMFrame &f, uint32 index)
cc.disable("error"); cc.disable("error");
THROW(); THROW();
} }
f.regs.sp[-1].setInt32(int32_t(obj->getArgsLength())); f.regs.sp[-1].setInt32(int32_t(obj->getArgsInitialLength()));
} }
return; return;
} }

View File

@ -525,7 +525,7 @@ stubs::GetElem(VMFrame &f)
) { ) {
uint32 arg = uint32(rval.toInt32()); uint32 arg = uint32(rval.toInt32());
if (arg < obj->getArgsLength()) { if (arg < obj->getArgsInitialLength()) {
JSStackFrame *afp = (JSStackFrame *) obj->getPrivate(); JSStackFrame *afp = (JSStackFrame *) obj->getPrivate();
if (afp) { if (afp) {
copyFrom = &afp->argv[arg]; copyFrom = &afp->argv[arg];
@ -2159,7 +2159,7 @@ stubs::Length(VMFrame &f)
regs.sp[-1].setNumber(length); regs.sp[-1].setNumber(length);
return; return;
} else if (obj->isArguments() && !obj->isArgsLengthOverridden()) { } else if (obj->isArguments() && !obj->isArgsLengthOverridden()) {
uint32 length = obj->getArgsLength(); uint32 length = obj->getArgsInitialLength();
JS_ASSERT(length < INT32_MAX); JS_ASSERT(length < INT32_MAX);
regs.sp[-1].setInt32(int32_t(length)); regs.sp[-1].setInt32(int32_t(length));
return; return;

View File

@ -98,16 +98,12 @@ extern int gettimeofday(struct timeval *tv);
#define PRMJ_YEAR_SECONDS (PRMJ_DAY_SECONDS * PRMJ_YEAR_DAYS) #define PRMJ_YEAR_SECONDS (PRMJ_DAY_SECONDS * PRMJ_YEAR_DAYS)
#define PRMJ_MAX_UNIX_TIMET 2145859200L /*time_t value equiv. to 12/31/2037 */ #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) * get the difference in seconds between this time zone and UTC (GMT)
*/ */
JSInt32 JSInt32
PRMJ_LocalGMTDifference() PRMJ_LocalGMTDifference()
{ {
struct tm ltime;
#if defined(XP_WIN) && !defined(WINCE) #if defined(XP_WIN) && !defined(WINCE)
/* Windows does not follow POSIX. Updates to the /* Windows does not follow POSIX. Updates to the
* TZ environment variable are not reflected * TZ environment variable are not reflected
@ -116,11 +112,31 @@ PRMJ_LocalGMTDifference()
*/ */
_tzset(); _tzset();
#endif #endif
/* get the difference between this time zone and GMT */
memset((char *)&ltime,0,sizeof(ltime)); /*
ltime.tm_mday = 2; * Get the difference between this time zone and GMT, by checking the local
ltime.tm_year = 70; * time at the epoch.
return (JSInt32)mktime(&ltime) - (24L * 3600L); */
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 */ /* Constants for GMT offset from 1970 */
@ -130,34 +146,6 @@ PRMJ_LocalGMTDifference()
#define G2037GMTMICROHI 0x00e45fab /* micro secs to 2037 high */ #define G2037GMTMICROHI 0x00e45fab /* micro secs to 2037 high */
#define G2037GMTMICROLOW 0x7a238000 /* micro secs to 2037 low */ #define G2037GMTMICROLOW 0x7a238000 /* micro secs to 2037 low */
/* Convert from base time to extended time */
static JSInt64
PRMJ_ToExtendedTime(JSInt32 base_time)
{
JSInt64 exttime;
JSInt64 g1970GMTMicroSeconds;
JSInt64 low;
JSInt32 diff;
JSInt64 tmp;
JSInt64 tmp1;
diff = PRMJ_LocalGMTDifference();
JSLL_UI2L(tmp, PRMJ_USEC_PER_SEC);
JSLL_I2L(tmp1,diff);
JSLL_MUL(tmp,tmp,tmp1);
JSLL_UI2L(g1970GMTMicroSeconds,G1970GMTMICROHI);
JSLL_UI2L(low,G1970GMTMICROLOW);
JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,16);
JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,16);
JSLL_ADD(g1970GMTMicroSeconds,g1970GMTMicroSeconds,low);
JSLL_I2L(exttime,base_time);
JSLL_ADD(exttime,exttime,g1970GMTMicroSeconds);
JSLL_SUB(exttime,exttime,tmp);
return exttime;
}
#ifdef HAVE_SYSTEMTIMETOFILETIME #ifdef HAVE_SYSTEMTIMETOFILETIME
static const JSInt64 win2un = JSLL_INIT(0x19DB1DE, 0xD53E8000); static const JSInt64 win2un = JSLL_INIT(0x19DB1DE, 0xD53E8000);
@ -681,178 +669,6 @@ PRMJ_FormatTime(char *buf, int buflen, const char *fmt, PRMJTime *prtm)
return result; return result;
} }
/* 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 JSInt64
DSTOffsetCache::computeDSTOffsetMilliseconds(int64 localTimeSeconds) DSTOffsetCache::computeDSTOffsetMilliseconds(int64 localTimeSeconds)
{ {
@ -869,9 +685,7 @@ DSTOffsetCache::computeDSTOffsetMilliseconds(int64 localTimeSeconds)
#endif #endif
time_t local = static_cast<time_t>(localTimeSeconds); time_t local = static_cast<time_t>(localTimeSeconds);
PRMJTime prtm;
struct tm tm; struct tm tm;
PRMJ_basetime(localTimeSeconds, &prtm);
#ifndef HAVE_LOCALTIME_R #ifndef HAVE_LOCALTIME_R
struct tm *ptm = localtime(&local); struct tm *ptm = localtime(&local);
if (!ptm) if (!ptm)
@ -881,8 +695,13 @@ DSTOffsetCache::computeDSTOffsetMilliseconds(int64 localTimeSeconds)
localtime_r(&local, &tm); /* get dst information */ localtime_r(&local, &tm); /* get dst information */
#endif #endif
JSInt32 diff = ((tm.tm_hour - prtm.tm_hour) * SECONDS_PER_HOUR) + JSInt32 base = PRMJ_LocalGMTDifference();
((tm.tm_min - prtm.tm_min) * SECONDS_PER_MINUTE);
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) if (diff < 0)
diff += SECONDS_PER_DAY; diff += SECONDS_PER_DAY;
@ -911,12 +730,23 @@ DSTOffsetCache::getDSTOffsetMilliseconds(JSInt64 localTimeMilliseconds, JSContex
* values, must result in a cache miss. * values, must result in a cache miss.
*/ */
if (rangeStartSeconds <= localTimeSeconds) { if (rangeStartSeconds <= localTimeSeconds &&
if (localTimeSeconds <= rangeEndSeconds) { localTimeSeconds <= rangeEndSeconds) {
noteCacheHit(); noteCacheHit();
return offsetMilliseconds; return offsetMilliseconds;
} }
if (oldRangeStartSeconds <= localTimeSeconds &&
localTimeSeconds <= oldRangeEndSeconds) {
noteCacheHit();
return oldOffsetMilliseconds;
}
oldOffsetMilliseconds = offsetMilliseconds;
oldRangeStartSeconds = rangeStartSeconds;
oldRangeEndSeconds = rangeEndSeconds;
if (rangeStartSeconds <= localTimeSeconds) {
JSInt64 newEndSeconds = JS_MIN(rangeEndSeconds + RANGE_EXPANSION_AMOUNT, MAX_UNIX_TIMET); JSInt64 newEndSeconds = JS_MIN(rangeEndSeconds + RANGE_EXPANSION_AMOUNT, MAX_UNIX_TIMET);
if (newEndSeconds >= localTimeSeconds) { if (newEndSeconds >= localTimeSeconds) {
JSInt64 endOffsetMilliseconds = computeDSTOffsetMilliseconds(newEndSeconds); JSInt64 endOffsetMilliseconds = computeDSTOffsetMilliseconds(newEndSeconds);

View File

@ -112,6 +112,9 @@ class DSTOffsetCache {
JSInt64 offsetMilliseconds; JSInt64 offsetMilliseconds;
JSInt64 rangeStartSeconds, rangeEndSeconds; JSInt64 rangeStartSeconds, rangeEndSeconds;
JSInt64 oldOffsetMilliseconds;
JSInt64 oldRangeStartSeconds, oldRangeEndSeconds;
#ifdef JS_METER_DST_OFFSET_CACHING #ifdef JS_METER_DST_OFFSET_CACHING
size_t totalCalculations; size_t totalCalculations;
size_t hit; size_t hit;

View File

@ -1,2 +1,3 @@
url-prefix ../../jsreftest.html?test=ecma_5/Date/ url-prefix ../../jsreftest.html?test=ecma_5/Date/
script 15.9.4.2.js script 15.9.4.2.js
script toJSON-01.js

View File

@ -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!");

View File

@ -18,8 +18,8 @@ assertEq("get" in Object.getOwnPropertyDescriptor(o, "a b c"), true);
o = eval('({ get "a b c"() { return 17; } })'); o = eval('({ get "a b c"() { return 17; } })');
assertEq("get" in Object.getOwnPropertyDescriptor(o, "a b c"), true); assertEq("get" in Object.getOwnPropertyDescriptor(o, "a b c"), true);
var f = eval("(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'() { } }; }; f = function literalInside() { return { set 'c d e'(q) { } }; };
function checkO() function checkO()
{ {

View File

@ -1,3 +1,4 @@
url-prefix ../../jsreftest.html?test=ecma_5/Expressions/ url-prefix ../../jsreftest.html?test=ecma_5/Expressions/
script 11.1.5-01.js script 11.1.5-01.js
script named-accessor-function.js script named-accessor-function.js
script object-literal-accessor-arguments.js

View File

@ -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);

View File

@ -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!");

View File

@ -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!");

View File

@ -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!");

View File

@ -1,2 +1,6 @@
url-prefix ../../jsreftest.html?test=ecma_5/Function/ url-prefix ../../jsreftest.html?test=ecma_5/Function/
script 15.3.4.3-01.js 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

View File

@ -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!");

View File

@ -1,2 +1,3 @@
url-prefix ../../jsreftest.html?test=ecma_5/JSON/ url-prefix ../../jsreftest.html?test=ecma_5/JSON/
script cyclic-stringify.js script cyclic-stringify.js
script stringify-gap.js

View File

@ -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!");

View File

@ -156,7 +156,7 @@ expectDescriptor(pd, expected);
/******************************************************************************/ /******************************************************************************/
o = { get y() { return 17; }, set y() { } }; o = { get y() { return 17; }, set y(z) { } };
pd = Object.getOwnPropertyDescriptor(o, "y"); pd = Object.getOwnPropertyDescriptor(o, "y");
expected = expected =

View File

@ -103,32 +103,32 @@ assertEq(testLenientAndStrict('({x:1, get x() {}})',
parseRaisesException(SyntaxError)), parseRaisesException(SyntaxError)),
true); true);
assertEq(testLenientAndStrict('({set x() {}, x:1})', assertEq(testLenientAndStrict('({set x(q) {}, x:1})',
parsesSuccessfully, parsesSuccessfully,
parseRaisesException(SyntaxError)), parseRaisesException(SyntaxError)),
true); true);
assertEq(testLenientAndStrict('({x:1, set x() {}})', assertEq(testLenientAndStrict('({x:1, set x(q) {}})',
parsesSuccessfully, parsesSuccessfully,
parseRaisesException(SyntaxError)), parseRaisesException(SyntaxError)),
true); true);
assertEq(testLenientAndStrict('({get x() {}, set x() {}})', assertEq(testLenientAndStrict('({get x() {}, set x(q) {}})',
parsesSuccessfully, parsesSuccessfully,
parsesSuccessfully), parsesSuccessfully),
true); true);
assertEq(testLenientAndStrict('({set x() {}, get x() {}})', assertEq(testLenientAndStrict('({set x(q) {}, get x() {}})',
parsesSuccessfully, parsesSuccessfully,
parsesSuccessfully), parsesSuccessfully),
true); true);
assertEq(testLenientAndStrict('({get x() {}, set x() {}, x:1})', assertEq(testLenientAndStrict('({get x() {}, set x(q) {}, x:1})',
parsesSuccessfully, parsesSuccessfully,
parseRaisesException(SyntaxError)), parseRaisesException(SyntaxError)),
true); true);
assertEq(testLenientAndStrict('({set x() {}, get x() {}, x:1})', assertEq(testLenientAndStrict('({set x(q) {}, get x() {}, x:1})',
parsesSuccessfully, parsesSuccessfully,
parseRaisesException(SyntaxError)), parseRaisesException(SyntaxError)),
true); true);
@ -138,7 +138,7 @@ assertEq(testLenientAndStrict('({get x() {}, get x() {}})',
parseRaisesException(SyntaxError)), parseRaisesException(SyntaxError)),
true); true);
assertEq(testLenientAndStrict('({get x() {}, set x() {}, y:1})', assertEq(testLenientAndStrict('({get x() {}, set x(q) {}, y:1})',
parsesSuccessfully, parsesSuccessfully,
parsesSuccessfully), parsesSuccessfully),
true); true);

View File

@ -82,27 +82,6 @@ assertEq(testLenientAndStrict('Function("x","y","\'use strict\'")',
true); 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 * The parameter lists of function expressions should not contain
* duplicate identifiers. * duplicate identifiers.
@ -210,30 +189,6 @@ assertEq(testLenientAndStrict('(function eval() 2)',
parsesSuccessfully, parsesSuccessfully,
parseRaisesException(SyntaxError)), parseRaisesException(SyntaxError)),
true); 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){}})', assertEq(testLenientAndStrict('({set x(eval){}})',
parsesSuccessfully, parsesSuccessfully,
parseRaisesException(SyntaxError)), parseRaisesException(SyntaxError)),
@ -338,30 +293,6 @@ assertEq(testLenientAndStrict('(function arguments() 2)',
parsesSuccessfully, parsesSuccessfully,
parseRaisesException(SyntaxError)), parseRaisesException(SyntaxError)),
true); 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){}})', assertEq(testLenientAndStrict('({set x(arguments){}})',
parsesSuccessfully, parsesSuccessfully,
parseRaisesException(SyntaxError)), parseRaisesException(SyntaxError)),

View File

@ -52,14 +52,14 @@ function test()
printBugNumber(BUGNUMBER); printBugNumber(BUGNUMBER);
printStatus (summary); printStatus (summary);
var f = function() { return { set this() { } }; } ; var f = function() { return { set this(v) { } }; } ;
expect = 'function() { return { set this() { } }; }'; expect = 'function() { return { set this(v) { } }; }';
actual = f + ''; actual = f + '';
compareSource(expect, actual, summary); compareSource(expect, actual, summary);
expect = "({ set ''() {} })"; expect = "({ set ''(v) {} })";
actual = uneval({ set ''() {} }); actual = uneval({ set ''(v) {} });
compareSource(expect, actual, expect); compareSource(expect, actual, expect);
exitFunc ('test'); exitFunc ('test');
} }

View File

@ -55,7 +55,7 @@ function test()
try try
{ {
expect = 'undefined'; expect = 'undefined';
var a = { set x() {} }; var a = { set x(v) {} };
actual = a.x + ''; actual = a.x + '';
} }
catch(ex) catch(ex)

View File

@ -55,7 +55,7 @@ function test()
try try
{ {
expect = 'undefined'; expect = 'undefined';
var a = { set x() {} }; var a = { set x(v) {} };
for (var i = 0; i < 92169 - 3; ++i) a[i] = 1; for (var i = 0; i < 92169 - 3; ++i) a[i] = 1;
actual = a.x + ''; actual = a.x + '';
actual = a.x + ''; actual = a.x + '';

View File

@ -55,7 +55,7 @@ function test()
try try
{ {
expect = actual = 'No Crash'; expect = actual = 'No Crash';
var a = { set x() {} }; var a = { set x(v) {} };
for (var i = 0; i < 0x4bf20 - 3; ++i) a[i] = 1; for (var i = 0; i < 0x4bf20 - 3; ++i) a[i] = 1;
a.x; a.x;
a.x.x; a.x.x;

View File

@ -55,7 +55,7 @@ function test()
try try
{ {
expect = actual = 'No Crash'; expect = actual = 'No Crash';
var a = { set x() {} }; var a = { set x(v) {} };
for (var i = 0; i < 0x10050c - 3; ++i) a[i] = 1; for (var i = 0; i < 0x10050c - 3; ++i) a[i] = 1;
a.x; a.x;
typeof a.x; typeof a.x;

View File

@ -64,7 +64,7 @@ function test()
reportCompare(expect, actual, summary + ': 2'); reportCompare(expect, actual, summary + ': 2');
// Assertion failure: JOF_OPTYPE(op) == JOF_ATOM, at ../jsemit.cpp:5916 // 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'); reportCompare(expect, actual, summary + ': 3');
exitFunc ('test'); exitFunc ('test');

View File

@ -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);

View File

@ -12,3 +12,4 @@ script scripted-proxies.js
script array-length-protochange.js script array-length-protochange.js
script parseInt-octal.js script parseInt-octal.js
script proxy-enumerateOwn-duplicates.js script proxy-enumerateOwn-duplicates.js
script destructure-accessor.js

View File

@ -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;
}

View File

@ -1,5 +1,5 @@
actual = ''; actual = '';
expected = '5,'; expected = '6,';
// tracing length // tracing length
@ -14,7 +14,7 @@ function f() {
} }
for (var i = 0; i < 5; ++i) { for (var i = 0; i < 5; ++i) {
f(10, 20, 30, 40, 50); f(10, 20, 30, 40, 50, 60);
} }
appendToActual(g); appendToActual(g);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

Some files were not shown because too many files have changed in this diff Show More