зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to mozilla-inbound
This commit is contained in:
Коммит
5483df1d20
|
@ -6023,7 +6023,7 @@ function MultiplexHandler(event)
|
|||
} else if (name == 'charsetCustomize') {
|
||||
//do nothing - please remove this else statement, once the charset prefs moves to the pref window
|
||||
} else {
|
||||
SetForcedCharset(node.getAttribute('id'));
|
||||
BrowserSetForcedCharacterSet(node.getAttribute('id'));
|
||||
}
|
||||
} catch(ex) { alert(ex); }
|
||||
}
|
||||
|
|
|
@ -64,6 +64,7 @@ _BROWSER_TEST_FILES = \
|
|||
browser_webconsole_consoleonpage.js \
|
||||
browser_webconsole_chrome.js \
|
||||
browser_webconsole_execution_scope.js \
|
||||
browser_webconsole_for_of.js \
|
||||
browser_webconsole_history.js \
|
||||
browser_webconsole_hud_getters.js \
|
||||
browser_webconsole_js_input_and_output_styling.js \
|
||||
|
@ -226,6 +227,7 @@ _BROWSER_TEST_PAGES = \
|
|||
browser_gcli_inspect.html \
|
||||
test-bug-658368-time-methods.html \
|
||||
test-webconsole-error-observer.html \
|
||||
test-for-of.html \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(_BROWSER_TEST_FILES)
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// A for-of loop in Web Console code can loop over a content NodeList.
|
||||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-for-of.html";
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
Services.prefs.clearUserPref("devtools.gcli.enable");
|
||||
});
|
||||
|
||||
function test() {
|
||||
Services.prefs.setBoolPref("devtools.gcli.enable", false);
|
||||
addTab(TEST_URI);
|
||||
browser.addEventListener("DOMContentLoaded", testForOf, false);
|
||||
}
|
||||
|
||||
function testForOf() {
|
||||
browser.removeEventListener("DOMContentLoaded", testForOf, false);
|
||||
|
||||
openConsole();
|
||||
var hud = HUDService.getHudByWindow(content);
|
||||
var jsterm = hud.jsterm;
|
||||
jsterm.execute("{ [x.tagName for (x of document.body.childNodes) if (x.nodeType === 1)].join(' '); }");
|
||||
|
||||
let node = hud.outputNode.querySelector(".webconsole-msg-output");
|
||||
ok(/H1 DIV H2 P/.test(node.textContent),
|
||||
"for-of loop should find all top-level nodes");
|
||||
|
||||
jsterm.clearOutput();
|
||||
jsterm.history.splice(0, jsterm.history.length); // workaround for bug 592552
|
||||
finishTest();
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<body>
|
||||
<h1>a</h1>
|
||||
<div><p>b</p></div>
|
||||
<h2>c</h2>
|
||||
<p>d</p>
|
|
@ -42,7 +42,7 @@ import java.util.List;
|
|||
|
||||
public interface Actions {
|
||||
public enum SpecialKey {
|
||||
DOWN, UP, LEFT, RIGHT, ENTER
|
||||
DOWN, UP, LEFT, RIGHT, ENTER, MENU, BACK
|
||||
}
|
||||
|
||||
public interface EventExpecter {
|
||||
|
|
|
@ -46,4 +46,6 @@ public interface Element {
|
|||
boolean isDisplayed();
|
||||
//Returns the text currently displayed on the element.
|
||||
String getText();
|
||||
//Returns view ID.
|
||||
Integer getId();
|
||||
}
|
||||
|
|
|
@ -338,6 +338,12 @@ public class FennecNativeActions implements Actions {
|
|||
case ENTER:
|
||||
instr.sendCharacterSync(KeyEvent.KEYCODE_ENTER);
|
||||
break;
|
||||
case MENU:
|
||||
instr.sendCharacterSync(KeyEvent.KEYCODE_MENU);
|
||||
break;
|
||||
case BACK:
|
||||
instr.sendCharacterSync(KeyEvent.KEYCODE_BACK);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -4874,10 +4874,7 @@ nsContentUtils::SetDataTransferInEvent(nsDragEvent* aDragEvent)
|
|||
}
|
||||
|
||||
// each event should use a clone of the original dataTransfer.
|
||||
nsCOMPtr<nsIDOMNSDataTransfer> initialDataTransferNS =
|
||||
do_QueryInterface(initialDataTransfer);
|
||||
NS_ENSURE_TRUE(initialDataTransferNS, NS_ERROR_FAILURE);
|
||||
initialDataTransferNS->Clone(aDragEvent->message, aDragEvent->userCancelled,
|
||||
initialDataTransfer->Clone(aDragEvent->message, aDragEvent->userCancelled,
|
||||
getter_AddRefs(aDragEvent->dataTransfer));
|
||||
NS_ENSURE_TRUE(aDragEvent->dataTransfer, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
|
@ -4886,14 +4883,10 @@ nsContentUtils::SetDataTransferInEvent(nsDragEvent* aDragEvent)
|
|||
// the event is fired based on the keyboard state.
|
||||
if (aDragEvent->message == NS_DRAGDROP_ENTER ||
|
||||
aDragEvent->message == NS_DRAGDROP_OVER) {
|
||||
nsCOMPtr<nsIDOMNSDataTransfer> newDataTransfer =
|
||||
do_QueryInterface(aDragEvent->dataTransfer);
|
||||
NS_ENSURE_TRUE(newDataTransfer, NS_ERROR_FAILURE);
|
||||
|
||||
PRUint32 action, effectAllowed;
|
||||
dragSession->GetDragAction(&action);
|
||||
newDataTransfer->GetEffectAllowedInt(&effectAllowed);
|
||||
newDataTransfer->SetDropEffectInt(FilterDropEffect(action, effectAllowed));
|
||||
aDragEvent->dataTransfer->GetEffectAllowedInt(&effectAllowed);
|
||||
aDragEvent->dataTransfer->SetDropEffectInt(FilterDropEffect(action, effectAllowed));
|
||||
}
|
||||
else if (aDragEvent->message == NS_DRAGDROP_DROP ||
|
||||
aDragEvent->message == NS_DRAGDROP_DRAGDROP ||
|
||||
|
@ -4902,13 +4895,9 @@ nsContentUtils::SetDataTransferInEvent(nsDragEvent* aDragEvent)
|
|||
// last value that the dropEffect had. This will have been set in
|
||||
// nsEventStateManager::PostHandleEvent for the last dragenter or
|
||||
// dragover event.
|
||||
nsCOMPtr<nsIDOMNSDataTransfer> newDataTransfer =
|
||||
do_QueryInterface(aDragEvent->dataTransfer);
|
||||
NS_ENSURE_TRUE(newDataTransfer, NS_ERROR_FAILURE);
|
||||
|
||||
PRUint32 dropEffect;
|
||||
initialDataTransferNS->GetDropEffectInt(&dropEffect);
|
||||
newDataTransfer->SetDropEffectInt(dropEffect);
|
||||
initialDataTransfer->GetDropEffectInt(&dropEffect);
|
||||
aDragEvent->dataTransfer->SetDropEffectInt(dropEffect);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
|
|
@ -67,7 +67,6 @@ DOMCI_DATA(DataTransfer, nsDOMDataTransfer)
|
|||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMDataTransfer)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMDataTransfer)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMNSDataTransfer)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMDataTransfer)
|
||||
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(DataTransfer)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
|
|
@ -67,13 +67,11 @@ struct TransferItem {
|
|||
nsCOMPtr<nsIVariant> mData;
|
||||
};
|
||||
|
||||
class nsDOMDataTransfer : public nsIDOMDataTransfer,
|
||||
public nsIDOMNSDataTransfer
|
||||
class nsDOMDataTransfer : public nsIDOMDataTransfer
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_NSIDOMDATATRANSFER
|
||||
NS_DECL_NSIDOMNSDATATRANSFER
|
||||
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsDOMDataTransfer, nsIDOMDataTransfer)
|
||||
|
||||
|
|
|
@ -3302,13 +3302,10 @@ nsEventStateManager::PostHandleEvent(nsPresContext* aPresContext,
|
|||
|
||||
// the initial dataTransfer is the one from the dragstart event that
|
||||
// was set on the dragSession when the drag began.
|
||||
nsCOMPtr<nsIDOMNSDataTransfer> dataTransfer;
|
||||
nsCOMPtr<nsIDOMDataTransfer> dataTransfer;
|
||||
nsCOMPtr<nsIDOMDataTransfer> initialDataTransfer;
|
||||
dragSession->GetDataTransfer(getter_AddRefs(initialDataTransfer));
|
||||
|
||||
nsCOMPtr<nsIDOMNSDataTransfer> initialDataTransferNS =
|
||||
do_QueryInterface(initialDataTransfer);
|
||||
|
||||
nsDragEvent *dragEvent = (nsDragEvent*)aEvent;
|
||||
|
||||
// collect any changes to moz cursor settings stored in the event's
|
||||
|
@ -3340,7 +3337,7 @@ nsEventStateManager::PostHandleEvent(nsPresContext* aPresContext,
|
|||
// initialized (which is done in nsDOMDragEvent::GetDataTransfer),
|
||||
// so set it from the drag action. We'll still want to filter it
|
||||
// based on the effectAllowed below.
|
||||
dataTransfer = initialDataTransferNS;
|
||||
dataTransfer = initialDataTransfer;
|
||||
|
||||
PRUint32 action;
|
||||
dragSession->GetDragAction(&action);
|
||||
|
@ -3390,8 +3387,8 @@ nsEventStateManager::PostHandleEvent(nsPresContext* aPresContext,
|
|||
|
||||
// now set the drop effect in the initial dataTransfer. This ensures
|
||||
// that we can get the desired drop effect in the drop event.
|
||||
if (initialDataTransferNS)
|
||||
initialDataTransferNS->SetDropEffectInt(dropEffect);
|
||||
if (initialDataTransfer)
|
||||
initialDataTransfer->SetDropEffectInt(dropEffect);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -4184,18 +4181,11 @@ nsEventStateManager::UpdateDragDataTransfer(nsDragEvent* dragEvent)
|
|||
// was set on the dragSession when the drag began.
|
||||
nsCOMPtr<nsIDOMDataTransfer> initialDataTransfer;
|
||||
dragSession->GetDataTransfer(getter_AddRefs(initialDataTransfer));
|
||||
|
||||
// grab the interface that has GetMozCursor.
|
||||
nsCOMPtr<nsIDOMNSDataTransfer> initialDataTransferNS =
|
||||
do_QueryInterface(initialDataTransfer);
|
||||
nsCOMPtr<nsIDOMNSDataTransfer> eventTransferNS =
|
||||
do_QueryInterface(dragEvent->dataTransfer);
|
||||
|
||||
if (initialDataTransferNS && eventTransferNS) {
|
||||
if (initialDataTransfer) {
|
||||
// retrieve the current moz cursor setting and save it.
|
||||
nsAutoString mozCursor;
|
||||
eventTransferNS->GetMozCursor(mozCursor);
|
||||
initialDataTransferNS->SetMozCursor(mozCursor);
|
||||
dragEvent->dataTransfer->GetMozCursor(mozCursor);
|
||||
initialDataTransfer->SetMozCursor(mozCursor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4118,7 +4118,6 @@ nsDOMClassInfo::Init()
|
|||
|
||||
DOM_CLASSINFO_MAP_BEGIN(DataTransfer, nsIDOMDataTransfer)
|
||||
DOM_CLASSINFO_MAP_ENTRY(nsIDOMDataTransfer)
|
||||
DOM_CLASSINFO_MAP_ENTRY(nsIDOMNSDataTransfer)
|
||||
DOM_CLASSINFO_MAP_END
|
||||
|
||||
DOM_CLASSINFO_MAP_BEGIN(NotifyPaintEvent, nsIDOMNotifyPaintEvent)
|
||||
|
|
|
@ -9360,12 +9360,6 @@ nsGlobalWindow::RunTimeout(nsTimeout *aTimeout)
|
|||
handler->GetScriptVersion(), nsnull,
|
||||
&is_undefined);
|
||||
} else {
|
||||
// Let the script handler know about the "secret" final argument that
|
||||
// indicates timeout lateness in milliseconds
|
||||
TimeDuration lateness = now - timeout->mWhen;
|
||||
|
||||
handler->SetLateness(lateness.ToMilliseconds());
|
||||
|
||||
nsCOMPtr<nsIVariant> dummy;
|
||||
nsCOMPtr<nsISupports> me(static_cast<nsIDOMWindow *>(this));
|
||||
scx->CallEventHandler(me, FastGetGlobalJSObject(),
|
||||
|
|
|
@ -42,9 +42,9 @@
|
|||
class nsIArray;
|
||||
|
||||
#define NS_ISCRIPTTIMEOUTHANDLER_IID \
|
||||
{ /* {21ba4f96-30b8-4215-a75d-d438eb16a50c} */ \
|
||||
0x21ba4f96, 0x30b8, 0x4215, \
|
||||
{ 0xa7, 0x5d, 0xd4, 0x38, 0xeb, 0x16, 0xa5, 0x0c } }
|
||||
{ /* {17a9ce1a-d73b-45d1-8145-a0ae57bcc76e} */ \
|
||||
0x17a9ce1a, 0xd73b, 0x45d1, \
|
||||
{ 0x81, 0x45, 0xa0, 0xae, 0x57, 0xbc, 0xc7, 0x6e } }
|
||||
|
||||
/**
|
||||
* Abstraction of the script objects etc required to do timeouts in a
|
||||
|
@ -78,10 +78,6 @@ public:
|
|||
|
||||
// Get the language version for this timeout.
|
||||
virtual PRUint32 GetScriptVersion() = 0;
|
||||
|
||||
// Set the "secret" final lateness arg. This will be called before
|
||||
// GetArgv(), which should reflect this lateness value.
|
||||
virtual void SetLateness(PRIntervalTime aHowLate) = 0;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsIScriptTimeoutHandler,
|
||||
|
|
|
@ -4117,10 +4117,6 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSArgArray)
|
|||
nsresult
|
||||
nsJSArgArray::GetArgs(PRUint32 *argc, void **argv)
|
||||
{
|
||||
if (!mArgv) {
|
||||
NS_WARNING("nsJSArgArray has no argv!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
*argv = (void *)mArgv;
|
||||
*argc = mArgc;
|
||||
return NS_OK;
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
#include "nsDOMError.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "nsIContentSecurityPolicy.h"
|
||||
#include "nsAlgorithm.h"
|
||||
|
||||
static const char kSetIntervalStr[] = "setInterval";
|
||||
static const char kSetTimeoutStr[] = "setTimeout";
|
||||
|
@ -85,9 +86,6 @@ public:
|
|||
virtual nsIArray *GetArgv() {
|
||||
return mArgv;
|
||||
}
|
||||
// Called by the timeout mechanism so the secret 'lateness' arg can be
|
||||
// added.
|
||||
virtual void SetLateness(PRIntervalTime aHowLate);
|
||||
|
||||
nsresult Init(nsGlobalWindow *aWindow, bool *aIsInterval,
|
||||
PRInt32 *aInterval);
|
||||
|
@ -324,11 +322,13 @@ nsJSScriptTimeoutHandler::Init(nsGlobalWindow *aWindow, bool *aIsInterval,
|
|||
|
||||
mFunObj = funobj;
|
||||
|
||||
// Create our arg array - leave an extra slot for a secret final argument
|
||||
// that indicates to the called function how "late" the timeout is. We
|
||||
// will fill that in when SetLateness is called.
|
||||
// Create our arg array. argc is the number of arguments passed
|
||||
// to setTimeout or setInterval; the first two are our callback
|
||||
// and the delay, so only arguments after that need to go in our
|
||||
// array.
|
||||
nsCOMPtr<nsIArray> array;
|
||||
rv = NS_CreateJSArgv(cx, (argc > 1) ? argc - 1 : argc, nsnull,
|
||||
// NS_MAX(argc - 2, 0) wouldn't work right because argc is unsigned.
|
||||
rv = NS_CreateJSArgv(cx, NS_MAX(argc, 2u) - 2, nsnull,
|
||||
getter_AddRefs(array));
|
||||
if (NS_FAILED(rv)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
@ -339,12 +339,14 @@ nsJSScriptTimeoutHandler::Init(nsGlobalWindow *aWindow, bool *aIsInterval,
|
|||
nsCOMPtr<nsIJSArgArray> jsarray(do_QueryInterface(array));
|
||||
jsarray->GetArgs(&dummy, reinterpret_cast<void **>(&jsargv));
|
||||
|
||||
// must have worked - we own the impl! :)
|
||||
NS_ASSERTION(jsargv, "No argv!");
|
||||
// jsargv might be null if we have argc <= 2
|
||||
if (jsargv) {
|
||||
for (PRInt32 i = 2; (PRUint32)i < argc; ++i) {
|
||||
jsargv[i - 2] = argv[i];
|
||||
}
|
||||
// final arg slot remains null, array has rooted vals.
|
||||
} else {
|
||||
NS_ASSERTION(argc <= 2, "Why do we have no jsargv when we have arguments?");
|
||||
}
|
||||
mArgv = array;
|
||||
} else {
|
||||
NS_WARNING("No func and no expr - why are we here?");
|
||||
|
@ -353,20 +355,6 @@ nsJSScriptTimeoutHandler::Init(nsGlobalWindow *aWindow, bool *aIsInterval,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
void nsJSScriptTimeoutHandler::SetLateness(PRIntervalTime aHowLate)
|
||||
{
|
||||
nsCOMPtr<nsIJSArgArray> jsarray(do_QueryInterface(mArgv));
|
||||
if (jsarray) {
|
||||
PRUint32 argc;
|
||||
jsval *jsargv;
|
||||
nsresult rv = jsarray->GetArgs(&argc, reinterpret_cast<void **>(&jsargv));
|
||||
if (NS_SUCCEEDED(rv) && jsargv && argc)
|
||||
jsargv[argc-1] = INT_TO_JSVAL((jsint) aHowLate);
|
||||
} else {
|
||||
NS_ERROR("How can our argv not handle this?");
|
||||
}
|
||||
}
|
||||
|
||||
const PRUnichar *
|
||||
nsJSScriptTimeoutHandler::GetHandlerText()
|
||||
{
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
interface nsIVariant;
|
||||
interface nsIDOMFileList;
|
||||
|
||||
[scriptable, uuid(34042440-60A8-4992-AE5C-798E69148955)]
|
||||
[scriptable, uuid(E929ACB6-435C-4CB8-9AD1-AE3B9353BCC5)]
|
||||
interface nsIDOMDataTransfer : nsISupports
|
||||
{
|
||||
/**
|
||||
|
@ -160,28 +160,6 @@ interface nsIDOMDataTransfer : nsISupports
|
|||
* @throws NO_MODIFICATION_ALLOWED_ERR if the item cannot be modified
|
||||
*/
|
||||
void addElement(in nsIDOMElement element);
|
||||
};
|
||||
|
||||
[scriptable, uuid(AE6DF4E2-FA37-4701-A33E-A5678F826EED)]
|
||||
interface nsIDOMNSDataTransfer : nsISupports
|
||||
{
|
||||
/*
|
||||
* Integer version of dropEffect, set to one of the constants in nsIDragService.
|
||||
*/
|
||||
[noscript] attribute unsigned long dropEffectInt;
|
||||
|
||||
/*
|
||||
* Integer version of effectAllowed, set to one or a combination of the
|
||||
* constants in nsIDragService.
|
||||
*/
|
||||
[noscript] attribute unsigned long effectAllowedInt;
|
||||
|
||||
/**
|
||||
* Creates a copy of the data transfer object, for the given event type and
|
||||
* user cancelled flag.
|
||||
*/
|
||||
[noscript] nsIDOMDataTransfer clone(in PRUint32 aEventType,
|
||||
in boolean aUserCancelled);
|
||||
|
||||
/**
|
||||
* The number of items being dragged.
|
||||
|
@ -272,4 +250,22 @@ interface nsIDOMNSDataTransfer : nsISupports
|
|||
* drags, or if the caller cannot access this node, this will be null.
|
||||
*/
|
||||
readonly attribute nsIDOMNode mozSourceNode;
|
||||
|
||||
/*
|
||||
* Integer version of dropEffect, set to one of the constants in nsIDragService.
|
||||
*/
|
||||
[noscript] attribute unsigned long dropEffectInt;
|
||||
|
||||
/*
|
||||
* Integer version of effectAllowed, set to one or a combination of the
|
||||
* constants in nsIDragService.
|
||||
*/
|
||||
[noscript] attribute unsigned long effectAllowedInt;
|
||||
|
||||
/**
|
||||
* Creates a copy of the data transfer object, for the given event type and
|
||||
* user cancelled flag.
|
||||
*/
|
||||
[noscript] nsIDOMDataTransfer clone(in PRUint32 aEventType,
|
||||
in boolean aUserCancelled);
|
||||
};
|
||||
|
|
|
@ -73,6 +73,7 @@ _TEST_FILES = \
|
|||
test_bug384122.html \
|
||||
test_bug389366.html \
|
||||
test_bug393974.html \
|
||||
test_bug394769.html \
|
||||
test_bug396843.html \
|
||||
test_bug397571.html \
|
||||
test_bug400204.html \
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=394769
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 394769</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=394769">Mozilla Bug 394769</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug 394769 **/
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
function h() {
|
||||
is(arguments.length, 1, "Should only have one argument");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function g() {
|
||||
is(arguments.length, 0, "Should not have lateness argument for function with delay");
|
||||
setTimeout(h, 0, "arg");
|
||||
}
|
||||
|
||||
function f() {
|
||||
is(arguments.length, 0, "Should not have lateness argument for function with no delay");
|
||||
setTimeout(g, 0);
|
||||
}
|
||||
|
||||
setTimeout(f);
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -79,6 +79,7 @@ _TEST_FILES = \
|
|||
test_browserFrame4.html \
|
||||
test_browserFrame5.html \
|
||||
test_browserFrame6.html \
|
||||
test_for_of.html \
|
||||
$(NULL)
|
||||
|
||||
_CHROME_FILES = \
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Tests for for-of loops</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body onload="doTest()">
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<script>
|
||||
function doTest() {
|
||||
// DOM NodeLists are iterable.
|
||||
var a = [];
|
||||
for (var e of document.body.childNodes)
|
||||
if (e.nodeType === 1)
|
||||
a.push(e.tagName);
|
||||
is("P DIV SCRIPT", a.join(" "), "for-of should see each element in the body");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -831,14 +831,11 @@ nsEditorEventListener::CanDrop(nsIDOMDragEvent* aEvent)
|
|||
|
||||
NS_ENSURE_TRUE(typeSupported, false);
|
||||
|
||||
nsCOMPtr<nsIDOMNSDataTransfer> dataTransferNS(do_QueryInterface(dataTransfer));
|
||||
NS_ENSURE_TRUE(dataTransferNS, false);
|
||||
|
||||
// If there is no source node, this is probably an external drag and the
|
||||
// drop is allowed. The later checks rely on checking if the drag target
|
||||
// is the same as the drag source.
|
||||
nsCOMPtr<nsIDOMNode> sourceNode;
|
||||
dataTransferNS->GetMozSourceNode(getter_AddRefs(sourceNode));
|
||||
dataTransfer->GetMozSourceNode(getter_AddRefs(sourceNode));
|
||||
if (!sourceNode)
|
||||
return true;
|
||||
|
||||
|
|
|
@ -1867,6 +1867,7 @@ Parser::statements()
|
|||
tc->blockNode = saveBlock;
|
||||
|
||||
pn->pn_pos.end = tokenStream.currentToken().pos.end;
|
||||
JS_ASSERT(pn->pn_pos.begin <= pn->pn_pos.end);
|
||||
return pn;
|
||||
}
|
||||
|
||||
|
@ -3136,6 +3137,23 @@ Parser::switchStatement()
|
|||
return pn;
|
||||
}
|
||||
|
||||
bool
|
||||
Parser::matchInOrOf(bool *isForOfp)
|
||||
{
|
||||
if (tokenStream.matchToken(TOK_IN)) {
|
||||
*isForOfp = false;
|
||||
return true;
|
||||
}
|
||||
if (tokenStream.matchToken(TOK_NAME)) {
|
||||
if (tokenStream.currentToken().name() == context->runtime->atomState.ofAtom) {
|
||||
*isForOfp = true;
|
||||
return true;
|
||||
}
|
||||
tokenStream.ungetToken();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ParseNode *
|
||||
Parser::forStatement()
|
||||
{
|
||||
|
@ -3241,18 +3259,27 @@ Parser::forStatement()
|
|||
ParseNode *forHead; /* initialized by both branches. */
|
||||
StmtInfo letStmt; /* used if blockObj != NULL. */
|
||||
ParseNode *pn2, *pn3; /* forHead->pn_kid1 and pn_kid2. */
|
||||
if (pn1 && tokenStream.matchToken(TOK_IN)) {
|
||||
bool forOf;
|
||||
if (pn1 && matchInOrOf(&forOf)) {
|
||||
/*
|
||||
* Parse the rest of the for/in head.
|
||||
* Parse the rest of the for/in or for/of head.
|
||||
*
|
||||
* Here pn1 is everything to the left of 'in'. At the end of this block,
|
||||
* pn1 is a decl or NULL, pn2 is the assignment target that receives the
|
||||
* enumeration value each iteration, and pn3 is the rhs of 'in'.
|
||||
* Here pn1 is everything to the left of 'in' or 'of'. At the end of
|
||||
* this block, pn1 is a decl or NULL, pn2 is the assignment target that
|
||||
* receives the enumeration value each iteration, and pn3 is the rhs of
|
||||
* 'in'.
|
||||
*/
|
||||
pn->pn_iflags |= JSITER_ENUMERATE;
|
||||
forStmt.type = STMT_FOR_IN_LOOP;
|
||||
|
||||
/* Check that the left side of the 'in' is valid. */
|
||||
/* Set pn_iflags and rule out invalid combinations. */
|
||||
if (forOf && pn->pn_iflags != 0) {
|
||||
JS_ASSERT(pn->pn_iflags == JSITER_FOREACH);
|
||||
reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_FOR_EACH_LOOP);
|
||||
return NULL;
|
||||
}
|
||||
pn->pn_iflags |= (forOf ? JSITER_FOR_OF : JSITER_ENUMERATE);
|
||||
|
||||
/* Check that the left side of the 'in' or 'of' is valid. */
|
||||
if (forDecl
|
||||
? (pn1->pn_count > 1 || pn1->isOp(JSOP_DEFCONST)
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
|
@ -4323,7 +4350,9 @@ Parser::variables(ParseNodeKind kind, StaticBlockObject *blockObj, VarContext va
|
|||
|
||||
if (!CheckDestructuring(context, &data, pn2, tc))
|
||||
return NULL;
|
||||
if ((tc->flags & TCF_IN_FOR_INIT) && tokenStream.peekToken() == TOK_IN) {
|
||||
bool ignored;
|
||||
if ((tc->flags & TCF_IN_FOR_INIT) && matchInOrOf(&ignored)) {
|
||||
tokenStream.ungetToken();
|
||||
pn->append(pn2);
|
||||
continue;
|
||||
}
|
||||
|
@ -5373,7 +5402,7 @@ Parser::comprehensionTail(ParseNode *kid, uintN blockid, bool isGenexp,
|
|||
/*
|
||||
* FOR node is binary, left is loop control and right is body. Use
|
||||
* index to count each block-local let-variable on the left-hand side
|
||||
* of the IN.
|
||||
* of the in/of.
|
||||
*/
|
||||
pn2 = BinaryNode::create(PNK_FOR, tc);
|
||||
if (!pn2)
|
||||
|
@ -5427,7 +5456,20 @@ Parser::comprehensionTail(ParseNode *kid, uintN blockid, bool isGenexp,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_IN, JSMSG_IN_AFTER_FOR_NAME);
|
||||
bool forOf;
|
||||
if (!matchInOrOf(&forOf)) {
|
||||
reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_IN_AFTER_FOR_NAME);
|
||||
return NULL;
|
||||
}
|
||||
if (forOf) {
|
||||
if (pn2->pn_iflags != JSITER_ENUMERATE) {
|
||||
JS_ASSERT(pn2->pn_iflags == (JSITER_FOREACH | JSITER_ENUMERATE));
|
||||
reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_FOR_EACH_LOOP);
|
||||
return NULL;
|
||||
}
|
||||
pn2->pn_iflags = JSITER_FOR_OF;
|
||||
}
|
||||
|
||||
ParseNode *pn4 = expr();
|
||||
if (!pn4)
|
||||
return NULL;
|
||||
|
|
|
@ -280,6 +280,7 @@ struct Parser : private AutoGCRooter
|
|||
#endif /* JS_HAS_XML_SUPPORT */
|
||||
|
||||
bool setAssignmentLhsOps(ParseNode *pn, JSOp op);
|
||||
bool matchInOrOf(bool *isForOfp);
|
||||
};
|
||||
|
||||
inline bool
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
// for-of can iterate arguments objects.
|
||||
|
||||
var s;
|
||||
function test() {
|
||||
for (var v of arguments)
|
||||
s += v;
|
||||
}
|
||||
|
||||
s = '';
|
||||
test();
|
||||
assertEq(s, '');
|
||||
|
||||
s = '';
|
||||
test('x', 'y');
|
||||
assertEq(s, 'xy');
|
|
@ -0,0 +1,11 @@
|
|||
// for-of can iterate arguments objects after returning.
|
||||
|
||||
function f() {
|
||||
return arguments;
|
||||
}
|
||||
|
||||
var s = '';
|
||||
var args = f('a', 'b', 'c');
|
||||
for (var v of args)
|
||||
s += v;
|
||||
assertEq(s, 'abc');
|
|
@ -0,0 +1,16 @@
|
|||
// for-of can iterate strict arguments objects.
|
||||
|
||||
var s;
|
||||
function test() {
|
||||
"use strict";
|
||||
for (var v of arguments)
|
||||
s += v;
|
||||
}
|
||||
|
||||
s = '';
|
||||
test();
|
||||
assertEq(s, '');
|
||||
|
||||
s = '';
|
||||
test('a', 'b');
|
||||
assertEq(s, 'ab');
|
|
@ -0,0 +1,15 @@
|
|||
// for-of can iterate arguments objects for other active frames.
|
||||
|
||||
var s;
|
||||
function g(obj) {
|
||||
for (var v of obj)
|
||||
s += v;
|
||||
}
|
||||
|
||||
function f() {
|
||||
g(arguments);
|
||||
}
|
||||
|
||||
s = '';
|
||||
f(1, 2, 3);
|
||||
assertEq(s, '123');
|
|
@ -0,0 +1,16 @@
|
|||
// for-of can iterate strict arguments objects in non-strict code.
|
||||
|
||||
var s;
|
||||
function g(obj) {
|
||||
for (var v of obj)
|
||||
s += v;
|
||||
}
|
||||
|
||||
function f() {
|
||||
"use strict";
|
||||
g(arguments);
|
||||
}
|
||||
|
||||
s = '';
|
||||
f(1, 2, 3);
|
||||
assertEq(s, '123');
|
|
@ -0,0 +1,12 @@
|
|||
// Changing arguments.length affects a for-of loop iterating over arguments.
|
||||
|
||||
var s;
|
||||
function f() {
|
||||
arguments.length = 2;
|
||||
for (var v of arguments)
|
||||
s += v;
|
||||
}
|
||||
|
||||
s = '';
|
||||
f('a', 'b', 'c', 'd', 'e');
|
||||
assertEq(s, 'ab');
|
|
@ -0,0 +1,13 @@
|
|||
// Changing arguments.length during a for-of loop iterating over arguments affects the loop.
|
||||
|
||||
var s;
|
||||
function f() {
|
||||
for (var v of arguments) {
|
||||
s += v;
|
||||
arguments.length--;
|
||||
}
|
||||
}
|
||||
|
||||
s = '';
|
||||
f('a', 'b', 'c', 'd', 'e');
|
||||
assertEq(s, 'abc');
|
|
@ -0,0 +1,3 @@
|
|||
// for-of can be used in array comprehensions.
|
||||
|
||||
assertEq([x*x for (x of [1, 2, 3])].join(), "1,4,9");
|
|
@ -0,0 +1,10 @@
|
|||
// for-of does not skip Array holes. The value at a hole is undefined.
|
||||
|
||||
var a = [0, , 2, 3];
|
||||
var log = [];
|
||||
for (var x of a) {
|
||||
assertEq(x, a[log.length]);
|
||||
log.push(x);
|
||||
}
|
||||
assertEq(log[1], undefined);
|
||||
assertEq(log.join(), "0,,2,3");
|
|
@ -0,0 +1,8 @@
|
|||
// for-of does not consult Object.prototype when it encounters a hole.
|
||||
|
||||
Object.prototype[1] = 'FAIL';
|
||||
var log = [];
|
||||
for (var x of [0, , 2, 3])
|
||||
log.push(x);
|
||||
assertEq(log[1], undefined);
|
||||
assertEq(log.join(), "0,,2,3");
|
|
@ -0,0 +1,8 @@
|
|||
// for-of does not consult Array.prototype when it encounters a hole.
|
||||
|
||||
Array.prototype[1] = 'FAIL';
|
||||
var log = [];
|
||||
for (var x of [0, , 2, 3])
|
||||
log.push(x);
|
||||
assertEq(log[1], undefined);
|
||||
assertEq(log.join(), "0,,2,3");
|
|
@ -0,0 +1,10 @@
|
|||
// for-of on an Array does not consult the prototype chain when it encounters a hole.
|
||||
|
||||
var m = {1: 'FAIL'};
|
||||
var a = [0, , 2, 3];
|
||||
a.__proto__ = m;
|
||||
var log = [];
|
||||
for (var x of a)
|
||||
log.push(x);
|
||||
assertEq(log[1], undefined);
|
||||
assertEq(log.join(), "0,,2,3");
|
|
@ -0,0 +1,13 @@
|
|||
// for-of on a slow Array does not consult the prototype chain when it encounters a hole.
|
||||
|
||||
var a = [0, , , 3];
|
||||
a.slow = true;
|
||||
Object.prototype[1] = 'FAIL1';
|
||||
Array.prototype[2] = 'FAIL2';
|
||||
|
||||
var log = [];
|
||||
for (var x of a)
|
||||
log.push(x);
|
||||
assertEq(log[1], undefined);
|
||||
assertEq(log[2], undefined);
|
||||
assertEq(log.join(), "0,,,3");
|
|
@ -0,0 +1,11 @@
|
|||
// for-of works on Array.prototype.
|
||||
|
||||
var v;
|
||||
for (v of Array.prototype)
|
||||
throw "FAIL";
|
||||
|
||||
var s = '';
|
||||
Array.prototype.push('a', 'b');
|
||||
for (v of Array.prototype)
|
||||
s += v;
|
||||
assertEq(s, 'ab');
|
|
@ -0,0 +1,7 @@
|
|||
// for-of works on arrays.
|
||||
|
||||
var a = ['a', 'b', 'c'];
|
||||
var s = '';
|
||||
for (var v of a)
|
||||
s += v;
|
||||
assertEq(s, 'abc');
|
|
@ -0,0 +1,10 @@
|
|||
// A for-of loop over an array does not take a snapshot of the array elements.
|
||||
// Instead, each time the loop needs an element from the array, it gets its current value.
|
||||
|
||||
var a = [3, 5, 5, 4, 0, 5];
|
||||
var s = '';
|
||||
for (var i of a) {
|
||||
s += i;
|
||||
a[i] = 'X';
|
||||
}
|
||||
assertEq(s, '355X0X');
|
|
@ -0,0 +1,9 @@
|
|||
// Two for-of loops on the same array get distinct iterators.
|
||||
|
||||
var a = [1, 2, 3];
|
||||
var s = '';
|
||||
for (var x of a)
|
||||
s += x;
|
||||
for (var y of a)
|
||||
s += y;
|
||||
assertEq(s, '123123');
|
|
@ -0,0 +1,8 @@
|
|||
// Nested for-of loops on the same array get distinct iterators.
|
||||
|
||||
var a = [1, 2, 3];
|
||||
var s = '';
|
||||
for (var x of a)
|
||||
for (var y of a)
|
||||
s += '' + x + y + ',';
|
||||
assertEq(s, '11,12,13,21,22,23,31,32,33,');
|
|
@ -0,0 +1,4 @@
|
|||
// for-of on an empty array does nothing.
|
||||
|
||||
for (var x of [])
|
||||
fail();
|
|
@ -0,0 +1,10 @@
|
|||
// A for-of loop over an array continues to the end if the array grows during iteration.
|
||||
|
||||
var a = [0, 1, 1, 0, 1, 0, 0];
|
||||
var s = '';
|
||||
for (var v of a) {
|
||||
s += v;
|
||||
if (v === 1)
|
||||
a.push(2);
|
||||
}
|
||||
assertEq(s, '0110100222');
|
|
@ -0,0 +1,10 @@
|
|||
// Inserting values in an array does not change the next index of an existing iterator.
|
||||
|
||||
var a = [1, 2, 3, 4];
|
||||
var s = '';
|
||||
for (var v of a) {
|
||||
s += v;
|
||||
if (s.length === 2)
|
||||
a.unshift('x');
|
||||
}
|
||||
assertEq(s, '12234');
|
|
@ -0,0 +1,13 @@
|
|||
// A for-of loop over an array stops at the new end of the array if it shrinks during iteration.
|
||||
|
||||
function ispal(arr) {
|
||||
for (var v of arr) {
|
||||
if (v !== arr.pop())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
assertEq(ispal([1, 2, 3, 4, 3, 2, 1]), true);
|
||||
assertEq(ispal([1, 2, 3, 3, 2, 1]), true);
|
||||
assertEq(ispal([1, 2, 3, 4, 2, 1]), false);
|
|
@ -0,0 +1,9 @@
|
|||
// Using shift to cut values out of an array does not change the next index of an existing iterator.
|
||||
|
||||
var a = [1, 2, 3, 4, 5, 6, 7, 8];
|
||||
var s = '';
|
||||
for (var v of a) {
|
||||
s += v;
|
||||
a.shift();
|
||||
}
|
||||
assertEq(s, '1357');
|
|
@ -0,0 +1,8 @@
|
|||
// for-of works on slow arrays.
|
||||
|
||||
var a = ['a', 'b', 'c'];
|
||||
a.slow = true;
|
||||
var log = '';
|
||||
for (var x of a)
|
||||
log += x;
|
||||
assertEq(log, 'abc');
|
|
@ -0,0 +1,10 @@
|
|||
// Two for-of loops on the same slow array get distinct iterators.
|
||||
|
||||
var a = [1, 2, 3];
|
||||
a.slow = true;
|
||||
var s = '';
|
||||
for (var x of a)
|
||||
s += x;
|
||||
for (var y of a)
|
||||
s += y;
|
||||
assertEq(s, '123123');
|
|
@ -0,0 +1,9 @@
|
|||
// Nested for-of loops on the same slow array get distinct iterators.
|
||||
|
||||
var a = [1, 2, 3];
|
||||
a.slow = true;
|
||||
var s = '';
|
||||
for (var x of a)
|
||||
for (var y of a)
|
||||
s += '' + x + y + ',';
|
||||
assertEq(s, '11,12,13,21,22,23,31,32,33,');
|
|
@ -0,0 +1,6 @@
|
|||
// for-of on an empty slow array does nothing.
|
||||
|
||||
var a = [];
|
||||
a.slow = true;
|
||||
for (var x of a)
|
||||
fail();
|
|
@ -0,0 +1,10 @@
|
|||
// Slowifying an array while it is being iterated does not affect iteration.
|
||||
|
||||
var a = [9, 8, 7, 6, 5, 4, 3];
|
||||
var log = '';
|
||||
for (var x of a) {
|
||||
log += x;
|
||||
if (x === 6)
|
||||
a.slow = true;
|
||||
}
|
||||
assertEq(log, "9876543");
|
|
@ -0,0 +1,9 @@
|
|||
// A break statement leaves a for-of loop.
|
||||
|
||||
var log = '';
|
||||
for (var x of ['a', 'b', 'c']) {
|
||||
log += x;
|
||||
if (x === 'b')
|
||||
break;
|
||||
}
|
||||
assertEq(log, "ab");
|
|
@ -0,0 +1,10 @@
|
|||
// A break statement leaves only a single for-of loop.
|
||||
|
||||
var log = '';
|
||||
for (var x of [1, 2, 3]) {
|
||||
for (var y of ['.', ':']) {
|
||||
log += x + y;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assertEq(log, "1.2.3.");
|
|
@ -0,0 +1,12 @@
|
|||
// A labeled break statement can leave multiple for-loops
|
||||
|
||||
var log = '';
|
||||
for (var i = 0; i < 3; i++) {
|
||||
a: for (var x of [1, 2, 3]) {
|
||||
for (var y of ['.', ':']) {
|
||||
log += x + y;
|
||||
break a;
|
||||
}
|
||||
}
|
||||
}
|
||||
assertEq(log, "1.1.1.");
|
|
@ -0,0 +1,36 @@
|
|||
// The decompiler correctly handles for-of loops.
|
||||
|
||||
function tokens(code) {
|
||||
var arr = [];
|
||||
var s = code.replace(/\w+|[^\s]/g, function (tok) { arr.push(tok); return ""; });
|
||||
assertEq(s.trim(), "", "tokens() should find all tokens in code: " + uneval(code));
|
||||
return arr;
|
||||
}
|
||||
|
||||
function test(code) {
|
||||
var before = "function f() { " + code + " }";
|
||||
var after = eval("(" + before + ")").toString();
|
||||
assertEq(tokens(before).join(" "), tokens(after).join(" "), "decompiler failed to round-trip");
|
||||
}
|
||||
|
||||
// statements
|
||||
test("for (a of b) { f(a); }");
|
||||
test("for (a of b) { f(a); g(a); }");
|
||||
|
||||
// for-of with "in" operator nearby
|
||||
test("for (a of b in c ? c : c.items()) { f(a); }");
|
||||
|
||||
// destructuring
|
||||
test("for ([a, b] of c) { a.m(b); }");
|
||||
|
||||
// for-let-of
|
||||
test("for (let a of b) { f(a); }");
|
||||
test("for (let [a, b] of c) { a.m(b); }");
|
||||
|
||||
// array comprehensions
|
||||
test("return [a for (a of b)];");
|
||||
test("return [[b, a] for ([a, b] of c.items())];");
|
||||
|
||||
// generator expressions
|
||||
test("return (a for (a of b));");
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
// for-of works with generators.
|
||||
|
||||
function range(n) {
|
||||
for (var i = 0; i < n; i++)
|
||||
yield i;
|
||||
}
|
||||
|
||||
var s = '';
|
||||
for (var a of range(4))
|
||||
s += a;
|
||||
assertEq(s, '0123');
|
|
@ -0,0 +1,15 @@
|
|||
// Generator-iterators are consumed the first time they are iterated.
|
||||
|
||||
function range(n) {
|
||||
for (var i = 0; i < n; i++)
|
||||
yield i;
|
||||
}
|
||||
|
||||
var r = range(10);
|
||||
var i = 0;
|
||||
for (var x in r)
|
||||
assertEq(x, i++);
|
||||
assertEq(i, 10);
|
||||
for (var y in r)
|
||||
throw "FAIL";
|
||||
assertEq(y, undefined);
|
|
@ -0,0 +1,18 @@
|
|||
// Nested for-of loops can use the same generator-iterator.
|
||||
|
||||
function range(n) {
|
||||
for (var i = 0; i < n; i++)
|
||||
yield i;
|
||||
}
|
||||
|
||||
var r = range(10);
|
||||
for (var a of r)
|
||||
for (var b of r)
|
||||
for (var c of r)
|
||||
for (var d of r)
|
||||
;
|
||||
|
||||
assertEq(a, 0);
|
||||
assertEq(b, 1);
|
||||
assertEq(c, 2);
|
||||
assertEq(d, 9);
|
|
@ -0,0 +1,15 @@
|
|||
// for-of can iterate over generator-iterators produced by generator-expressions.
|
||||
|
||||
function g() {
|
||||
yield 1;
|
||||
yield 2;
|
||||
}
|
||||
|
||||
var it = g();
|
||||
for (var i = 0; i < 10; i++) {
|
||||
let prev = it;
|
||||
it = (k + 1 for (k of prev));
|
||||
}
|
||||
|
||||
var arr = [v for (v of it)];
|
||||
assertEq(arr.join(), "11,12");
|
|
@ -0,0 +1,18 @@
|
|||
// Breaking out of a for-of loop over a generator-iterator closes the iterator.
|
||||
|
||||
function range(n) {
|
||||
for (var i = 0; i < n; i++)
|
||||
yield i;
|
||||
}
|
||||
|
||||
var r = range(10);
|
||||
var s = '';
|
||||
for (var x of r) {
|
||||
s += x;
|
||||
if (x == 4)
|
||||
break;
|
||||
}
|
||||
s += '/';
|
||||
for (var y of r)
|
||||
s += y;
|
||||
assertEq(s, '01234/');
|
|
@ -0,0 +1,43 @@
|
|||
// Named break closes the right generator-iterators.
|
||||
|
||||
function g() {
|
||||
for (;;)
|
||||
yield 1;
|
||||
}
|
||||
|
||||
function isClosed(it) {
|
||||
try {
|
||||
it.next();
|
||||
return false;
|
||||
} catch (exc) {
|
||||
if (exc !== StopIteration)
|
||||
throw exc;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
var a = g(), b = g(), c = g(), d = g(), e = g(), f = g();
|
||||
|
||||
for (var aa of a) {
|
||||
b_: for (var bb of b) {
|
||||
c_: for (var cc of c) {
|
||||
d_: for (var dd of d) {
|
||||
e_: for (var ee of e) {
|
||||
for (var ff of f)
|
||||
break c_;
|
||||
}
|
||||
}
|
||||
}
|
||||
assertEq(isClosed(a), false);
|
||||
assertEq(isClosed(b), false);
|
||||
assertEq(isClosed(c), true);
|
||||
assertEq(isClosed(d), true);
|
||||
assertEq(isClosed(e), true);
|
||||
assertEq(isClosed(f), true);
|
||||
break b_;
|
||||
}
|
||||
assertEq(isClosed(a), false);
|
||||
assertEq(isClosed(b), true);
|
||||
break;
|
||||
}
|
||||
assertEq(isClosed(a), true);
|
|
@ -0,0 +1,21 @@
|
|||
// Iterating over non-iterable values throws a TypeError.
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
var misc = [
|
||||
{}, {x: 1}, Math, isNaN,
|
||||
Object.create(null),
|
||||
Object.create(Array.prototype),
|
||||
null, undefined,
|
||||
true, 0, 3.1416, "", "ponies",
|
||||
new Boolean(true), new Number(0), new String("ponies")];
|
||||
|
||||
for (var i = 0; i < misc.length; i++) {
|
||||
let v = misc[i];
|
||||
var testfn = function () {
|
||||
for (var _ of v)
|
||||
throw 'FAIL';
|
||||
throw 'BAD';
|
||||
};
|
||||
assertThrowsInstanceOf(testfn, TypeError);
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
// Basic for-of test with Proxy.
|
||||
|
||||
function iter(arr) {
|
||||
var i = 0;
|
||||
return {
|
||||
next: function () {
|
||||
if (i < arr.length)
|
||||
return arr[i++];
|
||||
throw StopIteration;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function iterableProxy(arr) {
|
||||
return Proxy.create({iterate: function () { return iter(arr); }});
|
||||
}
|
||||
|
||||
var s = '';
|
||||
var arr = ['a', 'b', 'c', 'd'];
|
||||
var p = iterableProxy(arr);
|
||||
|
||||
// Test the same proxy twice. Its iterate method should be called each time.
|
||||
for (var i = 0; i < 2; i++) {
|
||||
var j = 0;
|
||||
for (var x of p)
|
||||
assertEq(x, arr[j++]);
|
||||
assertEq(j, arr.length);
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
// Basic for-of test with Proxy whose iterate method is a generator.
|
||||
|
||||
var arr = ['a', 'b', 'c', 'd'];
|
||||
var proxy = Proxy.create({
|
||||
iterate: function () {
|
||||
for (var i = 0; i < arr.length; i++)
|
||||
yield arr[i];
|
||||
}
|
||||
});
|
||||
|
||||
for (var i = 0; i < 2; i++)
|
||||
assertEq([v for (v of proxy)].join(","), "a,b,c,d");
|
|
@ -0,0 +1,6 @@
|
|||
// An exception thrown from an iterate trap is propagated.
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
var p = Proxy.create({iterate: function () { throw "fit"; }});
|
||||
assertThrowsValue(function () { for (var v of p) {} }, "fit");
|
|
@ -0,0 +1,10 @@
|
|||
// for-of on a fixed (non-trapping) proxy does not call the iterate trap.
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
var p = Proxy.create({
|
||||
iterate: function () { throw "FAIL"; },
|
||||
fix: function () { return {}; }
|
||||
});
|
||||
Object.preventExtensions(p);
|
||||
assertThrowsInstanceOf(function () { for (var v of p) {} }, TypeError);
|
|
@ -0,0 +1,14 @@
|
|||
// Control can exit a for-of loop via return.
|
||||
|
||||
function f() {
|
||||
for (var a of [1, 2, 3]) {
|
||||
for (var b of [1, 2, 3]) {
|
||||
for (var c of [1, 2, 3]) {
|
||||
if (a !== b && b !== c && c !== a)
|
||||
return "" + a + b + c;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertEq(f(), "123");
|
|
@ -0,0 +1,22 @@
|
|||
// We correctly reject bogus for-of loop syntax.
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
function assertSyntaxError(code) {
|
||||
assertThrowsInstanceOf(function () { Function(code); }, SyntaxError, "Function:" + code);
|
||||
assertThrowsInstanceOf(function () { eval(code); }, SyntaxError, "eval:" + code);
|
||||
var ieval = eval;
|
||||
assertThrowsInstanceOf(function () { ieval(code); }, SyntaxError, "indirect eval:" + code);
|
||||
}
|
||||
|
||||
function test(badForHead) {
|
||||
assertSyntaxError(badForHead + " {}"); // apply directly to forHead
|
||||
assertSyntaxError("[0 " + badForHead + "];");
|
||||
}
|
||||
|
||||
var a, b, c;
|
||||
test("for (a in b of c)");
|
||||
test("for each (a of b)");
|
||||
test("for (a of b of c)");
|
||||
test("for (let (a = 1) a of b)");
|
||||
test("for (let {a: 1} of b)");
|
|
@ -0,0 +1,9 @@
|
|||
// "of" is not a keyword.
|
||||
|
||||
var of;
|
||||
|
||||
Function("var of;");
|
||||
|
||||
let (of = 12) {}
|
||||
|
||||
function of(of) {}
|
|
@ -0,0 +1,20 @@
|
|||
// Control can exit a for-of loop via throw.
|
||||
|
||||
function f() {
|
||||
for (var a of [1, 2, 3]) {
|
||||
for (var b of [1, 2, 3]) {
|
||||
for (var c of [1, 2, 3]) {
|
||||
if (a !== b && b !== c && c !== a)
|
||||
throw [a, b, c];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var x = null;
|
||||
try {
|
||||
f();
|
||||
} catch (exc) {
|
||||
x = exc.join("");
|
||||
}
|
||||
assertEq(x, "123");
|
|
@ -0,0 +1,7 @@
|
|||
// for-of can iterate over typed arrays.
|
||||
|
||||
var a = Int8Array([0, 1, -7, 3])
|
||||
var s = '';
|
||||
for (var v of a)
|
||||
s += v + ',';
|
||||
assertEq(s, '0,1,-7,3,');
|
|
@ -0,0 +1,11 @@
|
|||
// The body of a for-of loop does not run if the target is an empty typed array.
|
||||
|
||||
for (x of Int16Array(0))
|
||||
throw "FAIL";
|
||||
for (x of Float32Array(0))
|
||||
throw "FAIL";
|
||||
|
||||
var a = Int8Array([0, 1, 2, 3]).subarray(2, 2);
|
||||
assertEq(a.length, 0);
|
||||
for (v of a)
|
||||
throw "FAIL";
|
|
@ -0,0 +1,4 @@
|
|||
// Destructuring does not occur when the target of for-of is an empty typed array.
|
||||
|
||||
for (var [[x]] of Int32Array(0))
|
||||
throw "FAIL";
|
|
@ -0,0 +1,7 @@
|
|||
// for-of throws if the target is a typed array prototype object.
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
assertThrowsInstanceOf(function () {
|
||||
for (var v of Int8Array.prototype)
|
||||
throw "FAIL";
|
||||
}, TypeError);
|
|
@ -0,0 +1,7 @@
|
|||
// for-of throws if the target is an ArrayBuffer.
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
assertThrowsInstanceOf(function () {
|
||||
for (var v of Int8Array([0, 1, 2, 3]).buffer)
|
||||
throw "FAIL";
|
||||
}, TypeError);
|
|
@ -0,0 +1,9 @@
|
|||
// for-of can iterate over float typed arrays containing infinities or NaNs.
|
||||
|
||||
var values = [Infinity, -Infinity, -0, NaN];
|
||||
for (var C of [Float32Array, Float64Array]) {
|
||||
var i = 0;
|
||||
for (var v of C(values))
|
||||
assertEq(v, values[i++]);
|
||||
assertEq(i, values.length);
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
// for-of works on cross-compartment wrappers of Arrays.
|
||||
|
||||
var g = newGlobal('new-compartment');
|
||||
var s = '';
|
||||
for (var x of g.Array(1, 1, 2, 3, 5))
|
||||
s += x;
|
||||
assertEq(s, '11235');
|
|
@ -294,7 +294,7 @@ MSG_DEF(JSMSG_WRONG_CONSTRUCTOR, 207, 1, JSEXN_TYPEERR, "wrong constructor
|
|||
MSG_DEF(JSMSG_BAD_GENERATOR_RETURN, 208, 1, JSEXN_TYPEERR, "generator function {0} returns a value")
|
||||
MSG_DEF(JSMSG_BAD_ANON_GENERATOR_RETURN, 209, 0, JSEXN_TYPEERR, "anonymous generator function returns a value")
|
||||
MSG_DEF(JSMSG_NAME_AFTER_FOR_PAREN, 210, 0, JSEXN_SYNTAXERR, "missing name after for (")
|
||||
MSG_DEF(JSMSG_IN_AFTER_FOR_NAME, 211, 0, JSEXN_SYNTAXERR, "missing in after for")
|
||||
MSG_DEF(JSMSG_IN_AFTER_FOR_NAME, 211, 0, JSEXN_SYNTAXERR, "missing 'in' or 'of' after for")
|
||||
MSG_DEF(JSMSG_BAD_TRAP_RETURN_VALUE, 212, 2, JSEXN_TYPEERR,"trap {1} for {0} returned a primitive value")
|
||||
MSG_DEF(JSMSG_KEYWORD_NOT_NS, 213, 0, JSEXN_SYNTAXERR, "keyword is used as namespace")
|
||||
MSG_DEF(JSMSG_BAD_GENERATOR_YIELD, 214, 1, JSEXN_TYPEERR, "yield from closing generator {0}")
|
||||
|
@ -374,3 +374,4 @@ MSG_DEF(JSMSG_CANT_WATCH_PROP, 287, 0, JSEXN_TYPEERR, "properties whose n
|
|||
MSG_DEF(JSMSG_CSP_BLOCKED_EVAL, 288, 0, JSEXN_ERR, "call to eval() blocked by CSP")
|
||||
MSG_DEF(JSMSG_DEBUG_NO_SCOPE_OBJECT, 289, 0, JSEXN_TYPEERR, "declarative Environments don't have binding objects")
|
||||
MSG_DEF(JSMSG_EMPTY_CONSEQUENT, 290, 0, JSEXN_SYNTAXERR, "mistyped ; after conditional?")
|
||||
MSG_DEF(JSMSG_NOT_ITERABLE, 291, 1, JSEXN_TYPEERR, "{0} is not iterable")
|
||||
|
|
|
@ -1,37 +1,39 @@
|
|||
#include "tests.h"
|
||||
#include "jsutil.h"
|
||||
|
||||
const jschar arr[] = { 'h', 'i', ',', 'd', 'o', 'n', '\'', 't', ' ', 'd', 'e', 'l', 'e', 't', 'e', ' ', 'm', 'e', '\0' };
|
||||
size_t arrlen = sizeof(arr) / sizeof(arr[0]) - 1;
|
||||
void *magic = (void *)0x42;
|
||||
static const jschar arr[] = {
|
||||
'h', 'i', ',', 'd', 'o', 'n', '\'', 't', ' ', 'd', 'e', 'l', 'e', 't', 'e', ' ', 'm', 'e', '\0'
|
||||
};
|
||||
static const size_t arrlen = sizeof(arr) / sizeof(arr[0]) - 1;
|
||||
|
||||
int finalized_noclosure = 0;
|
||||
int finalized_closure = 0;
|
||||
static int finalized1 = 0;
|
||||
static int finalized2 = 0;
|
||||
|
||||
void finalize_str(JSContext *cx, JSString *str)
|
||||
static void
|
||||
finalize_str(const JSStringFinalizer *fin, jschar *chars);
|
||||
|
||||
static const JSStringFinalizer finalizer1 = { finalize_str };
|
||||
static const JSStringFinalizer finalizer2 = { finalize_str };
|
||||
|
||||
static void
|
||||
finalize_str(const JSStringFinalizer *fin, jschar *chars)
|
||||
{
|
||||
size_t len;
|
||||
const jschar *chars = JS_GetStringCharsAndLength(cx, str, &len);
|
||||
if (chars && len == arrlen && js::PodEqual(chars, arr, len)) {
|
||||
void *closure = JS_GetExternalStringClosure(cx, str);
|
||||
if (closure) {
|
||||
if (closure == magic)
|
||||
++finalized_closure;
|
||||
} else {
|
||||
++finalized_noclosure;
|
||||
if (chars && js::PodEqual(const_cast<const jschar *>(chars), arr, arrlen)) {
|
||||
if (fin == &finalizer1) {
|
||||
++finalized1;
|
||||
} else if (fin == &finalizer2) {
|
||||
++finalized2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BEGIN_TEST(testExternalStrings)
|
||||
{
|
||||
intN op = JS_AddExternalStringFinalizer(finalize_str);
|
||||
|
||||
const unsigned N = 1000;
|
||||
|
||||
for (unsigned i = 0; i < N; ++i) {
|
||||
CHECK(JS_NewExternalString(cx, arr, arrlen, op));
|
||||
CHECK(JS_NewExternalStringWithClosure(cx, arr, arrlen, op, magic));
|
||||
CHECK(JS_NewExternalString(cx, arr, arrlen, &finalizer1));
|
||||
CHECK(JS_NewExternalString(cx, arr, arrlen, &finalizer2));
|
||||
}
|
||||
|
||||
// clear that newborn root
|
||||
|
@ -42,8 +44,8 @@ BEGIN_TEST(testExternalStrings)
|
|||
// a generous fudge factor to account for strings rooted by conservative gc
|
||||
const unsigned epsilon = 10;
|
||||
|
||||
CHECK((N - finalized_noclosure) < epsilon);
|
||||
CHECK((N - finalized_closure) < epsilon);
|
||||
CHECK((N - finalized1) < epsilon);
|
||||
CHECK((N - finalized2) < epsilon);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
* data member. On gcc, this doesn't seem to matter. On MSVC, this prevents
|
||||
* returning a jsval from a function between C and C++ because it will use a
|
||||
* retparam in C++ and a direct return value in C.
|
||||
*
|
||||
* Bug 712289 - jsval alignment was different on 32-bit platforms between C and
|
||||
* C++ because the default alignments of js::Value and jsval_layout differ.
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
|
@ -15,9 +18,12 @@ C_ValueToObject(JSContext *cx, jsval v, JSObject **obj);
|
|||
extern jsval
|
||||
C_GetEmptyStringValue(JSContext *cx);
|
||||
|
||||
extern size_t
|
||||
C_jsvalAlignmentTest();
|
||||
|
||||
}
|
||||
|
||||
BEGIN_TEST(testValueABI)
|
||||
BEGIN_TEST(testValueABI_retparam)
|
||||
{
|
||||
JSObject* obj = JS_GetGlobalObject(cx);
|
||||
jsval v = OBJECT_TO_JSVAL(obj);
|
||||
|
@ -32,4 +38,13 @@ BEGIN_TEST(testValueABI)
|
|||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testValueABI)
|
||||
END_TEST(testValueABI_retparam)
|
||||
|
||||
BEGIN_TEST(testValueABI_alignment)
|
||||
{
|
||||
typedef struct { char c; jsval v; } AlignTest;
|
||||
CHECK(C_jsvalAlignmentTest() == sizeof(AlignTest));
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testValueABI_alignment)
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
#include "jsapi.h"
|
||||
|
||||
JSBool C_ValueToObject(JSContext *cx, jsval v, JSObject **obj)
|
||||
// See testValueABI.cpp
|
||||
|
||||
JSBool
|
||||
C_ValueToObject(JSContext *cx, jsval v, JSObject **obj)
|
||||
{
|
||||
return JS_ValueToObject(cx, v, obj);
|
||||
}
|
||||
|
@ -10,3 +13,10 @@ C_GetEmptyStringValue(JSContext *cx)
|
|||
{
|
||||
return JS_GetEmptyStringValue(cx);
|
||||
}
|
||||
|
||||
size_t
|
||||
C_jsvalAlignmentTest()
|
||||
{
|
||||
typedef struct { char c; jsval v; } AlignTest;
|
||||
return sizeof(AlignTest);
|
||||
}
|
||||
|
|
|
@ -2966,51 +2966,27 @@ JS_FlushCaches(JSContext *cx)
|
|||
{
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(intN)
|
||||
JS_AddExternalStringFinalizer(JSStringFinalizeOp finalizer)
|
||||
{
|
||||
return JSExternalString::changeFinalizer(NULL, finalizer);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(intN)
|
||||
JS_RemoveExternalStringFinalizer(JSStringFinalizeOp finalizer)
|
||||
{
|
||||
return JSExternalString::changeFinalizer(finalizer, NULL);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSString *)
|
||||
JS_NewExternalString(JSContext *cx, const jschar *chars, size_t length, intN type)
|
||||
JS_NewExternalString(JSContext *cx, const jschar *chars, size_t length,
|
||||
const JSStringFinalizer *fin)
|
||||
{
|
||||
AssertNoGC(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
JSString *s = JSExternalString::new_(cx, chars, length, type, NULL);
|
||||
JSString *s = JSExternalString::new_(cx, chars, length, fin);
|
||||
Probes::createString(cx, s, length);
|
||||
return s;
|
||||
}
|
||||
|
||||
extern JS_PUBLIC_API(JSString *)
|
||||
JS_NewExternalStringWithClosure(JSContext *cx, const jschar *chars, size_t length,
|
||||
intN type, void *closure)
|
||||
{
|
||||
AssertNoGC(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
return JSExternalString::new_(cx, chars, length, type, closure);
|
||||
}
|
||||
|
||||
extern JS_PUBLIC_API(JSBool)
|
||||
JS_IsExternalString(JSContext *cx, JSString *str)
|
||||
JS_IsExternalString(JSString *str)
|
||||
{
|
||||
AssertNoGC(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
return str->isExternal();
|
||||
}
|
||||
|
||||
extern JS_PUBLIC_API(void *)
|
||||
JS_GetExternalStringClosure(JSContext *cx, JSString *str)
|
||||
extern JS_PUBLIC_API(const JSStringFinalizer *)
|
||||
JS_GetExternalStringFinalizer(JSString *str)
|
||||
{
|
||||
AssertNoGCOrFlatString(cx, str);
|
||||
CHECK_REQUEST(cx);
|
||||
return str->asExternal().externalClosure();
|
||||
return str->asExternal().externalFinalizer();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
|
@ -4410,6 +4386,22 @@ JS_NextProperty(JSContext *cx, JSObject *iterobj, jsid *idp)
|
|||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSObject *)
|
||||
JS_NewElementIterator(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
AssertNoGC(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
assertSameCompartment(cx, obj);
|
||||
return ElementIteratorObject::create(cx, obj);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSObject *)
|
||||
JS_ElementIteratorStub(JSContext *cx, JSObject *obj, JSBool keysonly)
|
||||
{
|
||||
JS_ASSERT(!keysonly);
|
||||
return JS_NewElementIterator(cx, obj);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_GetReservedSlot(JSContext *cx, JSObject *obj, uint32_t index, jsval *vp)
|
||||
{
|
||||
|
|
|
@ -1330,11 +1330,13 @@ typedef void
|
|||
(* JSFinalizeOp)(JSContext *cx, JSObject *obj);
|
||||
|
||||
/*
|
||||
* Used by JS_AddExternalStringFinalizer and JS_RemoveExternalStringFinalizer
|
||||
* to extend and reduce the set of string types finalized by the GC.
|
||||
* Finalizes external strings created by JS_NewExternalString.
|
||||
*/
|
||||
typedef void
|
||||
(* JSStringFinalizeOp)(JSContext *cx, JSString *str);
|
||||
typedef struct JSStringFinalizer JSStringFinalizer;
|
||||
|
||||
struct JSStringFinalizer {
|
||||
void (*finalize)(const JSStringFinalizer *fin, jschar *chars);
|
||||
};
|
||||
|
||||
/*
|
||||
* JSClass.checkAccess type: check whether obj[id] may be accessed per mode,
|
||||
|
@ -3305,70 +3307,27 @@ JS_GetGCParameterForThread(JSContext *cx, JSGCParamKey key);
|
|||
extern JS_PUBLIC_API(void)
|
||||
JS_FlushCaches(JSContext *cx);
|
||||
|
||||
/*
|
||||
* Add a finalizer for external strings created by JS_NewExternalString (see
|
||||
* below) using a type-code returned from this function, and that understands
|
||||
* how to free or release the memory pointed at by JS_GetStringChars(str).
|
||||
*
|
||||
* Return a nonnegative type index if there is room for finalizer in the
|
||||
* global GC finalizers table, else return -1. If the engine is compiled
|
||||
* JS_THREADSAFE and used in a multi-threaded environment, this function must
|
||||
* be invoked on the primordial thread only, at startup -- or else the entire
|
||||
* program must single-thread itself while loading a module that calls this
|
||||
* function.
|
||||
*/
|
||||
extern JS_PUBLIC_API(intN)
|
||||
JS_AddExternalStringFinalizer(JSStringFinalizeOp finalizer);
|
||||
|
||||
/*
|
||||
* Remove finalizer from the global GC finalizers table, returning its type
|
||||
* code if found, -1 if not found.
|
||||
*
|
||||
* As with JS_AddExternalStringFinalizer, there is a threading restriction
|
||||
* if you compile the engine JS_THREADSAFE: this function may be called for a
|
||||
* given finalizer pointer on only one thread; different threads may call to
|
||||
* remove distinct finalizers safely.
|
||||
*
|
||||
* You must ensure that all strings with finalizer's type have been collected
|
||||
* before calling this function. Otherwise, string data will be leaked by the
|
||||
* GC, for want of a finalizer to call.
|
||||
*/
|
||||
extern JS_PUBLIC_API(intN)
|
||||
JS_RemoveExternalStringFinalizer(JSStringFinalizeOp finalizer);
|
||||
|
||||
/*
|
||||
* Create a new JSString whose chars member refers to external memory, i.e.,
|
||||
* memory requiring type-specific finalization. The type code must be a
|
||||
* nonnegative return value from JS_AddExternalStringFinalizer.
|
||||
* memory requiring application-specific finalization.
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSString *)
|
||||
JS_NewExternalString(JSContext *cx, const jschar *chars, size_t length, intN type);
|
||||
|
||||
/*
|
||||
* Like JS_NewExternalString, except that 'closure' can be retrieved later via
|
||||
* JS_GetExternalStringClosure. This closure data is a black blox to the JS
|
||||
* engine and may be used by the embedding to associate extra data with an
|
||||
* external string. E.g., an embedding may want to associate a pointer to the
|
||||
* object that owns the chars of an external string so that, when this external
|
||||
* string is finalized, the owner object can be deleted.
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSString *)
|
||||
JS_NewExternalStringWithClosure(JSContext *cx, const jschar *chars, size_t length,
|
||||
intN type, void *closure);
|
||||
JS_NewExternalString(JSContext *cx, const jschar *chars, size_t length,
|
||||
const JSStringFinalizer *fin);
|
||||
|
||||
/*
|
||||
* Return whether 'str' was created with JS_NewExternalString or
|
||||
* JS_NewExternalStringWithClosure.
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSBool)
|
||||
JS_IsExternalString(JSContext *cx, JSString *str);
|
||||
JS_IsExternalString(JSString *str);
|
||||
|
||||
/*
|
||||
* Return the 'closure' arg passed to JS_NewExternalStringWithClosure or NULL
|
||||
* if the external string was created via JS_NewExternalString.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void *)
|
||||
JS_GetExternalStringClosure(JSContext *cx, JSString *str);
|
||||
extern JS_PUBLIC_API(const JSStringFinalizer *)
|
||||
JS_GetExternalStringFinalizer(JSString *str);
|
||||
|
||||
/*
|
||||
* Set the size of the native stack that should not be exceed. To disable
|
||||
|
@ -3438,7 +3397,12 @@ struct JSClass {
|
|||
#define JSCLASS_HIGH_FLAGS_SHIFT (JSCLASS_RESERVED_SLOTS_SHIFT + \
|
||||
JSCLASS_RESERVED_SLOTS_WIDTH)
|
||||
|
||||
#define JSCLASS_INTERNAL_FLAG1 (1<<(JSCLASS_HIGH_FLAGS_SHIFT+0))
|
||||
/*
|
||||
* Call the iteratorObject hook only to iterate over contents (for-of), not to
|
||||
* enumerate properties (for-in, for-each, Object.keys, etc.)
|
||||
*/
|
||||
#define JSCLASS_FOR_OF_ITERATION (1<<(JSCLASS_HIGH_FLAGS_SHIFT+0))
|
||||
|
||||
#define JSCLASS_IS_ANONYMOUS (1<<(JSCLASS_HIGH_FLAGS_SHIFT+1))
|
||||
#define JSCLASS_IS_GLOBAL (1<<(JSCLASS_HIGH_FLAGS_SHIFT+2))
|
||||
#define JSCLASS_INTERNAL_FLAG2 (1<<(JSCLASS_HIGH_FLAGS_SHIFT+3))
|
||||
|
@ -4042,6 +4006,21 @@ JS_NewPropertyIterator(JSContext *cx, JSObject *obj);
|
|||
extern JS_PUBLIC_API(JSBool)
|
||||
JS_NextProperty(JSContext *cx, JSObject *iterobj, jsid *idp);
|
||||
|
||||
/*
|
||||
* Create an object to iterate over the elements of obj in for-of order. This
|
||||
* can be used to implement the iteratorObject hook for an array-like Class.
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSObject *)
|
||||
JS_NewElementIterator(JSContext *cx, JSObject *obj);
|
||||
|
||||
/*
|
||||
* To make your array-like class iterable using the for-of loop, set the
|
||||
* JSCLASS_FOR_OF_ITERATION bit in the class's flags field and set its
|
||||
* .ext.iteratorObject hook to this function.
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSObject *)
|
||||
JS_ElementIteratorStub(JSContext *cx, JSObject *obj, JSBool keysonly);
|
||||
|
||||
extern JS_PUBLIC_API(JSBool)
|
||||
JS_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
|
||||
jsval *vp, uintN *attrsp);
|
||||
|
|
|
@ -1215,7 +1215,7 @@ array_fix(JSContext *cx, JSObject *obj, bool *success, AutoIdVector *props)
|
|||
|
||||
Class js::ArrayClass = {
|
||||
"Array",
|
||||
Class::NON_NATIVE | JSCLASS_HAS_CACHED_PROTO(JSProto_Array),
|
||||
Class::NON_NATIVE | JSCLASS_HAS_CACHED_PROTO(JSProto_Array) | JSCLASS_FOR_OF_ITERATION,
|
||||
JS_PropertyStub, /* addProperty */
|
||||
JS_PropertyStub, /* delProperty */
|
||||
JS_PropertyStub, /* getProperty */
|
||||
|
@ -1231,7 +1231,14 @@ Class js::ArrayClass = {
|
|||
NULL, /* xdrObject */
|
||||
NULL, /* hasInstance */
|
||||
array_trace, /* trace */
|
||||
JS_NULL_CLASS_EXT,
|
||||
{
|
||||
NULL, /* equality */
|
||||
NULL, /* outerObject */
|
||||
NULL, /* innerObject */
|
||||
JS_ElementIteratorStub,
|
||||
NULL, /* unused */
|
||||
false, /* isWrappedNative */
|
||||
},
|
||||
{
|
||||
array_lookupGeneric,
|
||||
array_lookupProperty,
|
||||
|
@ -1272,14 +1279,30 @@ Class js::ArrayClass = {
|
|||
|
||||
Class js::SlowArrayClass = {
|
||||
"Array",
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_Array),
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_Array) | JSCLASS_FOR_OF_ITERATION,
|
||||
slowarray_addProperty,
|
||||
JS_PropertyStub, /* delProperty */
|
||||
JS_PropertyStub, /* getProperty */
|
||||
JS_StrictPropertyStub, /* setProperty */
|
||||
JS_EnumerateStub,
|
||||
JS_ResolveStub,
|
||||
JS_ConvertStub
|
||||
JS_ConvertStub,
|
||||
NULL,
|
||||
NULL, /* reserved0 */
|
||||
NULL, /* checkAccess */
|
||||
NULL, /* call */
|
||||
NULL, /* construct */
|
||||
NULL, /* xdrObject */
|
||||
NULL, /* hasInstance */
|
||||
NULL, /* trace */
|
||||
{
|
||||
NULL, /* equality */
|
||||
NULL, /* outerObject */
|
||||
NULL, /* innerObject */
|
||||
JS_ElementIteratorStub,
|
||||
NULL, /* unused */
|
||||
false, /* isWrappedNative */
|
||||
}
|
||||
};
|
||||
|
||||
bool
|
||||
|
|
|
@ -154,6 +154,7 @@ const char *const js_common_atom_names[] = {
|
|||
js_noSuchMethod_str, /* noSuchMethodAtom */
|
||||
"[object Null]", /* objectNullAtom */
|
||||
"[object Undefined]", /* objectUndefinedAtom */
|
||||
"of", /* ofAtom */
|
||||
js_proto_str, /* protoAtom */
|
||||
js_set_str, /* setAtom */
|
||||
js_source_str, /* sourceAtom */
|
||||
|
|
|
@ -314,6 +314,7 @@ struct JSAtomState
|
|||
js::PropertyName *noSuchMethodAtom;
|
||||
js::PropertyName *objectNullAtom;
|
||||
js::PropertyName *objectUndefinedAtom;
|
||||
js::PropertyName *ofAtom;
|
||||
js::PropertyName *protoAtom;
|
||||
js::PropertyName *setAtom;
|
||||
js::PropertyName *sourceAtom;
|
||||
|
|
|
@ -1007,7 +1007,6 @@ JSContext::JSContext(JSRuntime *rt)
|
|||
, stackIterAssertionEnabled(true)
|
||||
#endif
|
||||
{
|
||||
PodZero(&sharpObjectMap);
|
||||
PodZero(&link);
|
||||
#ifdef JS_THREADSAFE
|
||||
PodZero(&threadLinks);
|
||||
|
|
|
@ -81,6 +81,8 @@ struct JSSharpObjectMap {
|
|||
jsrefcount depth;
|
||||
uint32_t sharpgen;
|
||||
JSHashTable *table;
|
||||
|
||||
JSSharpObjectMap() : depth(0), sharpgen(0), table(NULL) {}
|
||||
};
|
||||
|
||||
namespace js {
|
||||
|
|
|
@ -517,6 +517,7 @@ IsObjectInContextCompartment(const JSObject *obj, const JSContext *cx);
|
|||
#define JSITER_KEYVALUE 0x4 /* destructuring for-in wants [key, value] */
|
||||
#define JSITER_OWNONLY 0x8 /* iterate over obj's own properties only */
|
||||
#define JSITER_HIDDEN 0x10 /* also enumerate non-enumerable properties */
|
||||
#define JSITER_FOR_OF 0x20 /* harmony for-of loop */
|
||||
|
||||
inline uintptr_t
|
||||
GetContextStackLimit(const JSContext *cx)
|
||||
|
|
|
@ -559,7 +559,8 @@ Class js::NormalArgumentsObjectClass = {
|
|||
"Arguments",
|
||||
JSCLASS_NEW_RESOLVE |
|
||||
JSCLASS_HAS_RESERVED_SLOTS(NormalArgumentsObject::RESERVED_SLOTS) |
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
|
||||
JSCLASS_FOR_OF_ITERATION,
|
||||
JS_PropertyStub, /* addProperty */
|
||||
args_delProperty,
|
||||
JS_PropertyStub, /* getProperty */
|
||||
|
@ -574,7 +575,15 @@ Class js::NormalArgumentsObjectClass = {
|
|||
NULL, /* construct */
|
||||
NULL, /* xdrObject */
|
||||
NULL, /* hasInstance */
|
||||
args_trace
|
||||
args_trace,
|
||||
{
|
||||
NULL, /* equality */
|
||||
NULL, /* outerObject */
|
||||
NULL, /* innerObject */
|
||||
JS_ElementIteratorStub,
|
||||
NULL, /* unused */
|
||||
false, /* isWrappedNative */
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -586,7 +595,8 @@ Class js::StrictArgumentsObjectClass = {
|
|||
"Arguments",
|
||||
JSCLASS_NEW_RESOLVE |
|
||||
JSCLASS_HAS_RESERVED_SLOTS(StrictArgumentsObject::RESERVED_SLOTS) |
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
|
||||
JSCLASS_FOR_OF_ITERATION,
|
||||
JS_PropertyStub, /* addProperty */
|
||||
args_delProperty,
|
||||
JS_PropertyStub, /* getProperty */
|
||||
|
@ -601,7 +611,15 @@ Class js::StrictArgumentsObjectClass = {
|
|||
NULL, /* construct */
|
||||
NULL, /* xdrObject */
|
||||
NULL, /* hasInstance */
|
||||
args_trace
|
||||
args_trace,
|
||||
{
|
||||
NULL, /* equality */
|
||||
NULL, /* outerObject */
|
||||
NULL, /* innerObject */
|
||||
JS_ElementIteratorStub,
|
||||
NULL, /* unused */
|
||||
false, /* isWrappedNative */
|
||||
}
|
||||
};
|
||||
|
||||
namespace js {
|
||||
|
|
|
@ -115,6 +115,33 @@ Class js::IteratorClass = {
|
|||
}
|
||||
};
|
||||
|
||||
Class js::ElementIteratorClass = {
|
||||
"ElementIterator",
|
||||
JSCLASS_HAS_RESERVED_SLOTS(ElementIteratorObject::NumSlots),
|
||||
JS_PropertyStub, /* addProperty */
|
||||
JS_PropertyStub, /* delProperty */
|
||||
JS_PropertyStub, /* getProperty */
|
||||
JS_StrictPropertyStub, /* setProperty */
|
||||
JS_EnumerateStub,
|
||||
JS_ResolveStub,
|
||||
JS_ConvertStub,
|
||||
NULL, /* finalize */
|
||||
NULL, /* reserved */
|
||||
NULL, /* checkAccess */
|
||||
NULL, /* call */
|
||||
NULL, /* construct */
|
||||
NULL, /* xdrObject */
|
||||
NULL, /* hasInstance */
|
||||
NULL, /* trace */
|
||||
{
|
||||
NULL, /* equality */
|
||||
NULL, /* outerObject */
|
||||
NULL, /* innerObject */
|
||||
iterator_iterator,
|
||||
NULL /* unused */
|
||||
}
|
||||
};
|
||||
|
||||
static const gc::AllocKind ITERATOR_FINALIZE_KIND = gc::FINALIZE_OBJECT2;
|
||||
|
||||
void
|
||||
|
@ -423,6 +450,16 @@ GetCustomIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp)
|
|||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
|
||||
/*
|
||||
* for-of iteration does not fall back on __iterator__ or property
|
||||
* enumeration. This is more conservative than the current proposed spec.
|
||||
*/
|
||||
if (flags == JSITER_FOR_OF) {
|
||||
js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_NOT_ITERABLE,
|
||||
JSDVG_SEARCH_STACK, ObjectValue(*obj), NULL, NULL, NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check whether we have a valid __iterator__ method. */
|
||||
JSAtom *atom = cx->runtime->atomState.iteratorAtom;
|
||||
if (!js_GetMethod(cx, obj, ATOM_TO_JSID(atom), JSGET_NO_METHOD_BARRIER, vp))
|
||||
|
@ -658,13 +695,23 @@ GetIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp)
|
|||
if (obj) {
|
||||
/* Enumerate Iterator.prototype directly. */
|
||||
if (JSIteratorOp op = obj->getClass()->ext.iteratorObject) {
|
||||
JSObject *iterobj = op(cx, obj, !(flags & JSITER_FOREACH));
|
||||
/*
|
||||
* Arrays and other classes representing iterable collections have
|
||||
* the JSCLASS_FOR_OF_ITERATION flag. This flag means that the
|
||||
* object responds to all other kinds of enumeration (for-in,
|
||||
* for-each, Object.keys, Object.getOwnPropertyNames, etc.) in the
|
||||
* default way, ignoring the hook. The hook is used only when
|
||||
* iterating in the style of a for-of loop.
|
||||
*/
|
||||
if (!(obj->getClass()->flags & JSCLASS_FOR_OF_ITERATION) || flags == JSITER_FOR_OF) {
|
||||
JSObject *iterobj = op(cx, obj, !(flags & (JSITER_FOREACH | JSITER_FOR_OF)));
|
||||
if (!iterobj)
|
||||
return false;
|
||||
vp->setObject(*iterobj);
|
||||
types::MarkIteratorUnknown(cx);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (keysOnly) {
|
||||
/*
|
||||
|
@ -1044,6 +1091,110 @@ js_SuppressDeletedElements(JSContext *cx, JSObject *obj, uint32_t begin, uint32_
|
|||
return SuppressDeletedPropertyHelper(cx, obj, IndexRangePredicate(begin, end));
|
||||
}
|
||||
|
||||
const uint32_t CLOSED_INDEX = UINT32_MAX;
|
||||
|
||||
JSObject *
|
||||
ElementIteratorObject::create(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
JS_ASSERT(obj);
|
||||
JSObject *iterobj = NewObjectWithGivenProto(cx, &ElementIteratorClass, NULL, obj);
|
||||
if (iterobj) {
|
||||
iterobj->setReservedSlot(TargetSlot, ObjectValue(*obj));
|
||||
iterobj->setReservedSlot(IndexSlot, Int32Value(0));
|
||||
}
|
||||
return iterobj;
|
||||
}
|
||||
|
||||
inline uint32_t
|
||||
ElementIteratorObject::getIndex() const
|
||||
{
|
||||
return uint32_t(getReservedSlot(IndexSlot).toInt32());
|
||||
}
|
||||
|
||||
inline JSObject *
|
||||
ElementIteratorObject::getTargetObject() const
|
||||
{
|
||||
return &getReservedSlot(TargetSlot).toObject();
|
||||
}
|
||||
|
||||
inline void
|
||||
ElementIteratorObject::setIndex(uint32_t index)
|
||||
{
|
||||
setReservedSlot(IndexSlot, Int32Value(int32_t(index)));
|
||||
}
|
||||
|
||||
bool
|
||||
ElementIteratorObject::iteratorNext(JSContext *cx, Value *vp)
|
||||
{
|
||||
uint32_t i, length;
|
||||
JSObject *obj = getTargetObject();
|
||||
if (!js_GetLengthProperty(cx, obj, &length))
|
||||
goto error;
|
||||
|
||||
i = getIndex();
|
||||
if (i >= length) {
|
||||
setIndex(CLOSED_INDEX);
|
||||
vp->setMagic(JS_NO_ITER_VALUE);
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_ASSERT(i + 1 > i);
|
||||
|
||||
/* Simple fast path for dense arrays. */
|
||||
if (obj->isDenseArray()) {
|
||||
*vp = obj->getDenseArrayElement(i);
|
||||
if (vp->isMagic(JS_ARRAY_HOLE))
|
||||
vp->setUndefined();
|
||||
} else {
|
||||
/* Make a jsid for this index. */
|
||||
jsid id;
|
||||
if (i < uint32_t(INT32_MAX) && INT_FITS_IN_JSID(i)) {
|
||||
id = INT_TO_JSID(i);
|
||||
} else {
|
||||
Value v = DoubleValue(i);
|
||||
if (!js_ValueToStringId(cx, v, &id))
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Find out if this object has an element i. */
|
||||
bool has;
|
||||
if (obj->isProxy()) {
|
||||
/* js_HasOwnProperty does not work on proxies. */
|
||||
if (!Proxy::hasOwn(cx, obj, id, &has))
|
||||
goto error;
|
||||
} else {
|
||||
JSObject *obj2;
|
||||
JSProperty *prop;
|
||||
if (!js_HasOwnProperty(cx, obj->getOps()->lookupGeneric, obj, id, &obj2, &prop))
|
||||
goto error;
|
||||
has = !!prop;
|
||||
}
|
||||
|
||||
/* Populate *vp. */
|
||||
if (has) {
|
||||
if (!obj->getElement(cx, obj, i, vp))
|
||||
goto error;
|
||||
} else {
|
||||
vp->setUndefined();
|
||||
}
|
||||
}
|
||||
|
||||
/* On success, bump the index. */
|
||||
setIndex(i + 1);
|
||||
return true;
|
||||
|
||||
error:
|
||||
setIndex(CLOSED_INDEX);
|
||||
return false;
|
||||
}
|
||||
|
||||
inline js::ElementIteratorObject *
|
||||
JSObject::asElementIterator()
|
||||
{
|
||||
JS_ASSERT(isElementIterator());
|
||||
return static_cast<js::ElementIteratorObject *>(this);
|
||||
}
|
||||
|
||||
JSBool
|
||||
js_IteratorMore(JSContext *cx, JSObject *iterobj, Value *rval)
|
||||
{
|
||||
|
@ -1069,7 +1220,38 @@ js_IteratorMore(JSContext *cx, JSObject *iterobj, Value *rval)
|
|||
JS_CHECK_RECURSION(cx, return false);
|
||||
|
||||
/* Fetch and cache the next value from the iterator. */
|
||||
if (!ni) {
|
||||
if (ni) {
|
||||
JS_ASSERT(!ni->isKeyIter());
|
||||
jsid id;
|
||||
if (!ValueToId(cx, StringValue(*ni->current()), &id))
|
||||
return false;
|
||||
id = js_CheckForStringIndex(id);
|
||||
ni->incCursor();
|
||||
if (!ni->obj->getGeneric(cx, id, rval))
|
||||
return false;
|
||||
if ((ni->flags & JSITER_KEYVALUE) && !NewKeyValuePair(cx, id, *rval, rval))
|
||||
return false;
|
||||
} else if (iterobj->isElementIterator()) {
|
||||
/*
|
||||
* Like native iterators, element iterators do not have a .next
|
||||
* method, so this fast path is necessary for correctness.
|
||||
*/
|
||||
if (!iterobj->asElementIterator()->iteratorNext(cx, rval))
|
||||
return false;
|
||||
if (rval->isMagic(JS_NO_ITER_VALUE)) {
|
||||
cx->iterValue.setMagic(JS_NO_ITER_VALUE);
|
||||
rval->setBoolean(false);
|
||||
return true;
|
||||
}
|
||||
} else if (iterobj->isProxy()) {
|
||||
if (!Proxy::iteratorNext(cx, iterobj, rval))
|
||||
return false;
|
||||
if (rval->isMagic(JS_NO_ITER_VALUE)) {
|
||||
rval->setBoolean(false);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
/* Call the iterator object's .next method. */
|
||||
jsid id = ATOM_TO_JSID(cx->runtime->atomState.nextAtom);
|
||||
if (!js_GetMethod(cx, iterobj, id, JSGET_METHOD_BARRIER, rval))
|
||||
return false;
|
||||
|
@ -1083,17 +1265,6 @@ js_IteratorMore(JSContext *cx, JSObject *iterobj, Value *rval)
|
|||
rval->setBoolean(false);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
JS_ASSERT(!ni->isKeyIter());
|
||||
jsid id;
|
||||
if (!ValueToId(cx, StringValue(*ni->current()), &id))
|
||||
return false;
|
||||
id = js_CheckForStringIndex(id);
|
||||
ni->incCursor();
|
||||
if (!ni->obj->getGeneric(cx, id, rval))
|
||||
return false;
|
||||
if ((ni->flags & JSITER_KEYVALUE) && !NewKeyValuePair(cx, id, *rval, rval))
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Cache the value returned by iterobj.next() so js_IteratorNext() can find it. */
|
||||
|
|
|
@ -101,6 +101,67 @@ struct NativeIterator {
|
|||
void mark(JSTracer *trc);
|
||||
};
|
||||
|
||||
class ElementIteratorObject : public JSObject {
|
||||
public:
|
||||
enum {
|
||||
TargetSlot,
|
||||
IndexSlot,
|
||||
NumSlots
|
||||
};
|
||||
|
||||
static JSObject *create(JSContext *cx, JSObject *target);
|
||||
|
||||
inline uint32_t getIndex() const;
|
||||
inline void setIndex(uint32_t index);
|
||||
inline JSObject *getTargetObject() const;
|
||||
|
||||
/*
|
||||
Array iterators are like this:
|
||||
|
||||
Array.prototype[iterate] = function () {
|
||||
for (var i = 0; i < (this.length >>> 0); i++) {
|
||||
var desc = Object.getOwnPropertyDescriptor(this, i);
|
||||
yield desc === undefined ? undefined : this[i];
|
||||
}
|
||||
}
|
||||
|
||||
This has the following implications:
|
||||
|
||||
- Array iterators are generic; Array.prototype[iterate] can be transferred to
|
||||
any other object to create iterators over it.
|
||||
|
||||
- The next() method of an Array iterator is non-reentrant. Trying to reenter,
|
||||
e.g. by using it on an object with a length getter that calls it.next() on
|
||||
the same iterator, causes a TypeError.
|
||||
|
||||
- The iterator fetches obj.length every time its next() method is called.
|
||||
|
||||
- The iterator converts obj.length to a whole number using ToUint32. As a
|
||||
consequence the iterator can't go on forever; it can yield at most 2^32-1
|
||||
values. Then i will be 0xffffffff, and no possible length value will be
|
||||
greater than that.
|
||||
|
||||
- The iterator does not skip "array holes". When it encounters a hole, it
|
||||
yields undefined.
|
||||
|
||||
- The iterator never consults the prototype chain.
|
||||
|
||||
- If an element has a getter which throws, the exception is propagated, and
|
||||
the iterator is closed (that is, all future calls to next() will simply
|
||||
throw StopIteration).
|
||||
|
||||
Note that if next() were reentrant, even more details of its inner
|
||||
workings would be observable.
|
||||
*/
|
||||
|
||||
/*
|
||||
* If there are any more elements to visit, store the value of the next
|
||||
* element in *vp, increment the index, and return true. If not, call
|
||||
* vp->setMagic(JS_NO_ITER_VALUE) and return true. Return false on error.
|
||||
*/
|
||||
bool iteratorNext(JSContext *cx, Value *vp);
|
||||
};
|
||||
|
||||
bool
|
||||
VectorToIdArray(JSContext *cx, js::AutoIdVector &props, JSIdArray **idap);
|
||||
|
||||
|
|
197
js/src/jsobj.cpp
197
js/src/jsobj.cpp
|
@ -454,15 +454,11 @@ js_TraceSharpMap(JSTracer *trc, JSSharpObjectMap *map)
|
|||
static JSBool
|
||||
obj_toSource(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
JSBool ok;
|
||||
jschar *ochars, *vsharp;
|
||||
const jschar *idstrchars, *vchars;
|
||||
size_t nchars, idstrlength, gsoplength, vlength, vsharplength, curlen;
|
||||
const char *comma;
|
||||
bool comma = false;
|
||||
const jschar *vchars;
|
||||
size_t vlength;
|
||||
Value *val;
|
||||
JSString *gsop[2];
|
||||
JSString *valstr, *str;
|
||||
JSLinearString *idstr;
|
||||
|
||||
JS_CHECK_RECURSION(cx, return JS_FALSE);
|
||||
|
||||
|
@ -477,7 +473,6 @@ obj_toSource(JSContext *cx, uintN argc, Value *vp)
|
|||
if (!obj)
|
||||
return false;
|
||||
|
||||
jschar *chars;
|
||||
JSIdArray *ida;
|
||||
bool alreadySeen = false;
|
||||
JSHashEntry *he = js_EnterSharpObject(cx, obj, &ida, &alreadySeen);
|
||||
|
@ -497,22 +492,26 @@ obj_toSource(JSContext *cx, uintN argc, Value *vp)
|
|||
return true;
|
||||
}
|
||||
JS_ASSERT(!IS_SHARP(he));
|
||||
ok = JS_TRUE;
|
||||
|
||||
if (alreadySeen)
|
||||
MAKE_SHARP(he);
|
||||
|
||||
/* If outermost, allocate 4 + 1 for "({})" and the terminator. */
|
||||
chars = (jschar *) cx->malloc_(((outermost ? 4 : 2) + 1) * sizeof(jschar));
|
||||
nchars = 0;
|
||||
if (!chars)
|
||||
goto error;
|
||||
if (outermost)
|
||||
chars[nchars++] = '(';
|
||||
/* Automatically call js_LeaveSharpObject when we leave this frame. */
|
||||
class AutoLeaveSharpObject {
|
||||
JSContext *cx;
|
||||
JSIdArray *ida;
|
||||
public:
|
||||
AutoLeaveSharpObject(JSContext *cx, JSIdArray *ida) : cx(cx), ida(ida) {}
|
||||
~AutoLeaveSharpObject() {
|
||||
js_LeaveSharpObject(cx, &ida);
|
||||
}
|
||||
} autoLeaveSharpObject(cx, ida);
|
||||
|
||||
chars[nchars++] = '{';
|
||||
|
||||
comma = NULL;
|
||||
StringBuffer buf(cx);
|
||||
if (outermost && !buf.append('('))
|
||||
return false;
|
||||
if (!buf.append('{'))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* We have four local roots for cooked and raw value GC safety. Hoist the
|
||||
|
@ -521,28 +520,26 @@ obj_toSource(JSContext *cx, uintN argc, Value *vp)
|
|||
*/
|
||||
val = localroot + 2;
|
||||
|
||||
for (jsint i = 0, length = ida->length; i < length; i++) {
|
||||
for (jsint i = 0; i < ida->length; i++) {
|
||||
/* Get strings for id and value and GC-root them via vp. */
|
||||
jsid id = ida->vector[i];
|
||||
JSLinearString *idstr;
|
||||
|
||||
JSObject *obj2;
|
||||
JSProperty *prop;
|
||||
ok = obj->lookupGeneric(cx, id, &obj2, &prop);
|
||||
if (!ok)
|
||||
goto error;
|
||||
if (!obj->lookupGeneric(cx, id, &obj2, &prop))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Convert id to a value and then to a string. Decide early whether we
|
||||
* prefer get/set or old getter/setter syntax.
|
||||
*/
|
||||
JSString *s = ToString(cx, IdToValue(id));
|
||||
if (!s || !(idstr = s->ensureLinear(cx))) {
|
||||
ok = JS_FALSE;
|
||||
goto error;
|
||||
}
|
||||
if (!s || !(idstr = s->ensureLinear(cx)))
|
||||
return false;
|
||||
vp->setString(idstr); /* local root */
|
||||
|
||||
jsint valcnt = 0;
|
||||
int valcnt = 0;
|
||||
if (prop) {
|
||||
bool doGet = true;
|
||||
if (obj2->isNative()) {
|
||||
|
@ -564,9 +561,8 @@ obj_toSource(JSContext *cx, uintN argc, Value *vp)
|
|||
if (doGet) {
|
||||
valcnt = 1;
|
||||
gsop[0] = NULL;
|
||||
ok = obj->getGeneric(cx, id, &val[0]);
|
||||
if (!ok)
|
||||
goto error;
|
||||
if (!obj->getGeneric(cx, id, &val[0]))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -574,25 +570,16 @@ obj_toSource(JSContext *cx, uintN argc, Value *vp)
|
|||
* If id is a string that's not an identifier, or if it's a negative
|
||||
* integer, then it must be quoted.
|
||||
*/
|
||||
bool idIsLexicalIdentifier = IsIdentifier(idstr);
|
||||
if (JSID_IS_ATOM(id)
|
||||
? !idIsLexicalIdentifier
|
||||
? !IsIdentifier(idstr)
|
||||
: (!JSID_IS_INT(id) || JSID_TO_INT(id) < 0)) {
|
||||
s = js_QuoteString(cx, idstr, jschar('\''));
|
||||
if (!s || !(idstr = s->ensureLinear(cx))) {
|
||||
ok = JS_FALSE;
|
||||
goto error;
|
||||
}
|
||||
if (!s || !(idstr = s->ensureLinear(cx)))
|
||||
return false;
|
||||
vp->setString(idstr); /* local root */
|
||||
}
|
||||
idstrlength = idstr->length();
|
||||
idstrchars = idstr->getChars(cx);
|
||||
if (!idstrchars) {
|
||||
ok = JS_FALSE;
|
||||
goto error;
|
||||
}
|
||||
|
||||
for (jsint j = 0; j < valcnt; j++) {
|
||||
for (int j = 0; j < valcnt; j++) {
|
||||
/*
|
||||
* Censor an accessor descriptor getter or setter part if it's
|
||||
* undefined.
|
||||
|
@ -601,27 +588,15 @@ obj_toSource(JSContext *cx, uintN argc, Value *vp)
|
|||
continue;
|
||||
|
||||
/* Convert val[j] to its canonical source form. */
|
||||
valstr = js_ValueToSource(cx, val[j]);
|
||||
if (!valstr) {
|
||||
ok = JS_FALSE;
|
||||
goto error;
|
||||
}
|
||||
JSString *valstr = js_ValueToSource(cx, val[j]);
|
||||
if (!valstr)
|
||||
return false;
|
||||
localroot[j].setString(valstr); /* local root */
|
||||
vchars = valstr->getChars(cx);
|
||||
if (!vchars) {
|
||||
ok = JS_FALSE;
|
||||
goto error;
|
||||
}
|
||||
if (!vchars)
|
||||
return false;
|
||||
vlength = valstr->length();
|
||||
|
||||
/*
|
||||
* If val[j] is a non-sharp object, and we're not serializing an
|
||||
* accessor (ECMA syntax can't accommodate sharpened accessors),
|
||||
* consider sharpening it.
|
||||
*/
|
||||
vsharp = NULL;
|
||||
vsharplength = 0;
|
||||
|
||||
/*
|
||||
* Remove '(function ' from the beginning of valstr and ')' from the
|
||||
* end so that we can put "get" in front of the function definition.
|
||||
|
@ -657,100 +632,34 @@ obj_toSource(JSContext *cx, uintN argc, Value *vp)
|
|||
}
|
||||
}
|
||||
|
||||
#define SAFE_ADD(n) \
|
||||
JS_BEGIN_MACRO \
|
||||
size_t n_ = (n); \
|
||||
curlen += n_; \
|
||||
if (curlen < n_) \
|
||||
goto overflow; \
|
||||
JS_END_MACRO
|
||||
if (comma && !buf.append(", "))
|
||||
return false;
|
||||
comma = true;
|
||||
|
||||
curlen = nchars;
|
||||
if (comma)
|
||||
SAFE_ADD(2);
|
||||
SAFE_ADD(idstrlength + 1);
|
||||
if (gsop[j])
|
||||
SAFE_ADD(gsop[j]->length() + 1);
|
||||
SAFE_ADD(vsharplength);
|
||||
SAFE_ADD(vlength);
|
||||
/* Account for the trailing null. */
|
||||
SAFE_ADD((outermost ? 2 : 1) + 1);
|
||||
#undef SAFE_ADD
|
||||
if (!buf.append(gsop[j]) || !buf.append(' '))
|
||||
return false;
|
||||
|
||||
if (curlen > size_t(-1) / sizeof(jschar))
|
||||
goto overflow;
|
||||
if (!buf.append(idstr))
|
||||
return false;
|
||||
if (!buf.append(gsop[j] ? ' ' : ':'))
|
||||
return false;
|
||||
|
||||
/* Allocate 1 + 1 at end for closing brace and terminating 0. */
|
||||
chars = (jschar *) cx->realloc_((ochars = chars), curlen * sizeof(jschar));
|
||||
if (!chars) {
|
||||
chars = ochars;
|
||||
goto overflow;
|
||||
}
|
||||
|
||||
if (comma) {
|
||||
chars[nchars++] = comma[0];
|
||||
chars[nchars++] = comma[1];
|
||||
}
|
||||
comma = ", ";
|
||||
|
||||
if (gsop[j]) {
|
||||
gsoplength = gsop[j]->length();
|
||||
const jschar *gsopchars = gsop[j]->getChars(cx);
|
||||
if (!gsopchars)
|
||||
goto overflow;
|
||||
js_strncpy(&chars[nchars], gsopchars, gsoplength);
|
||||
nchars += gsoplength;
|
||||
chars[nchars++] = ' ';
|
||||
}
|
||||
js_strncpy(&chars[nchars], idstrchars, idstrlength);
|
||||
nchars += idstrlength;
|
||||
/* Extraneous space after id here will be extracted later */
|
||||
chars[nchars++] = gsop[j] ? ' ' : ':';
|
||||
|
||||
if (vsharplength) {
|
||||
js_strncpy(&chars[nchars], vsharp, vsharplength);
|
||||
nchars += vsharplength;
|
||||
}
|
||||
js_strncpy(&chars[nchars], vchars, vlength);
|
||||
nchars += vlength;
|
||||
|
||||
if (vsharp)
|
||||
cx->free_(vsharp);
|
||||
}
|
||||
}
|
||||
|
||||
chars[nchars++] = '}';
|
||||
if (outermost)
|
||||
chars[nchars++] = ')';
|
||||
chars[nchars] = 0;
|
||||
|
||||
error:
|
||||
js_LeaveSharpObject(cx, &ida);
|
||||
|
||||
if (!ok) {
|
||||
if (chars)
|
||||
Foreground::free_(chars);
|
||||
if (!buf.append(vchars, vlength))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!chars) {
|
||||
JS_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
str = js_NewString(cx, chars, nchars);
|
||||
if (!str) {
|
||||
cx->free_(chars);
|
||||
if (!buf.append('}'))
|
||||
return false;
|
||||
if (outermost && !buf.append(')'))
|
||||
return false;
|
||||
|
||||
JSString *str = buf.finishString();
|
||||
if (!str)
|
||||
return false;
|
||||
}
|
||||
vp->setString(str);
|
||||
return true;
|
||||
|
||||
overflow:
|
||||
cx->free_(vsharp);
|
||||
cx->free_(chars);
|
||||
chars = NULL;
|
||||
goto error;
|
||||
}
|
||||
#endif /* JS_HAS_TOSOURCE */
|
||||
|
||||
|
|
|
@ -354,6 +354,7 @@ extern Class BooleanClass;
|
|||
extern Class CallableObjectClass;
|
||||
extern Class DateClass;
|
||||
extern Class ErrorClass;
|
||||
extern Class ElementIteratorClass;
|
||||
extern Class GeneratorClass;
|
||||
extern Class IteratorClass;
|
||||
extern Class JSONClass;
|
||||
|
@ -377,6 +378,7 @@ class BlockObject;
|
|||
class BooleanObject;
|
||||
class ClonedBlockObject;
|
||||
class DeclEnvObject;
|
||||
class ElementIteratorObject;
|
||||
class GlobalObject;
|
||||
class NestedScopeObject;
|
||||
class NewObjectCache;
|
||||
|
@ -1046,6 +1048,8 @@ struct JSObject : js::gc::Cell
|
|||
return sizeof(JSObject) + sizeof(js::ObjectElements);
|
||||
}
|
||||
|
||||
inline js::ElementIteratorObject *asElementIterator();
|
||||
|
||||
/*
|
||||
* Array-specific getters and setters (for both dense and slow arrays).
|
||||
*/
|
||||
|
@ -1406,6 +1410,7 @@ struct JSObject : js::gc::Cell
|
|||
inline bool isArray() const;
|
||||
inline bool isDate() const;
|
||||
inline bool isDenseArray() const;
|
||||
inline bool isElementIterator() const;
|
||||
inline bool isError() const;
|
||||
inline bool isFunction() const;
|
||||
inline bool isGenerator() const;
|
||||
|
@ -1550,13 +1555,9 @@ class ValueArray {
|
|||
|
||||
/* For manipulating JSContext::sharpObjectMap. */
|
||||
#define SHARP_BIT ((jsatomid) 1)
|
||||
#define BUSY_BIT ((jsatomid) 2)
|
||||
#define SHARP_ID_SHIFT 2
|
||||
#define IS_SHARP(he) (uintptr_t((he)->value) & SHARP_BIT)
|
||||
#define MAKE_SHARP(he) ((he)->value = (void *) (uintptr_t((he)->value)|SHARP_BIT))
|
||||
#define IS_BUSY(he) (uintptr_t((he)->value) & BUSY_BIT)
|
||||
#define MAKE_BUSY(he) ((he)->value = (void *) (uintptr_t((he)->value)|BUSY_BIT))
|
||||
#define CLEAR_BUSY(he) ((he)->value = (void *) (uintptr_t((he)->value)&~BUSY_BIT))
|
||||
|
||||
extern JSHashEntry *
|
||||
js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, bool *alreadySeen);
|
||||
|
|
|
@ -926,6 +926,7 @@ inline bool JSObject::isCall() const { return hasClass(&js::CallClass); }
|
|||
inline bool JSObject::isClonedBlock() const { return isBlock() && !!getProto(); }
|
||||
inline bool JSObject::isDate() const { return hasClass(&js::DateClass); }
|
||||
inline bool JSObject::isDeclEnv() const { return hasClass(&js::DeclEnvClass); }
|
||||
inline bool JSObject::isElementIterator() const { return hasClass(&js::ElementIteratorClass); }
|
||||
inline bool JSObject::isError() const { return hasClass(&js::ErrorClass); }
|
||||
inline bool JSObject::isFunction() const { return hasClass(&js::FunctionClass); }
|
||||
inline bool JSObject::isFunctionProxy() const { return hasClass(&js::FunctionProxyClass); }
|
||||
|
|
|
@ -2712,6 +2712,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
|||
saveop = JSOP_NOP;
|
||||
sn = NULL;
|
||||
rval = NULL;
|
||||
bool forOf = false;
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
foreach = inXML = quoteAttr = JS_FALSE;
|
||||
#endif
|
||||
|
@ -3854,6 +3855,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
|||
break;
|
||||
|
||||
case JSOP_ITER:
|
||||
forOf = (GET_UINT8(pc) == JSITER_FOR_OF);
|
||||
foreach = (GET_UINT8(pc) & (JSITER_FOREACH | JSITER_KEYVALUE)) ==
|
||||
JSITER_FOREACH;
|
||||
todo = -2;
|
||||
|
@ -3925,9 +3927,11 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
|||
rval = POP_STR();
|
||||
if (ss->top >= 1 && ss->opcodes[ss->top - 1] == JSOP_FORLOCAL) {
|
||||
ss->sprinter.setOffset(ss->offsets[ss->top] - PAREN_SLOP);
|
||||
if (Sprint(&ss->sprinter, " %s (%s in %s)",
|
||||
if (Sprint(&ss->sprinter, " %s (%s %s %s)",
|
||||
foreach ? js_for_each_str : js_for_str,
|
||||
lval, rval) < 0) {
|
||||
lval,
|
||||
forOf ? "of" : "in",
|
||||
rval) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -3939,9 +3943,11 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
|||
*/
|
||||
todo = ss->offsets[ss->top - 1];
|
||||
} else {
|
||||
todo = Sprint(&ss->sprinter, " %s (%s in %s)",
|
||||
todo = Sprint(&ss->sprinter, " %s (%s %s %s)",
|
||||
foreach ? js_for_each_str : js_for_str,
|
||||
lval, rval);
|
||||
lval,
|
||||
forOf ? "of" : "in",
|
||||
rval);
|
||||
}
|
||||
if (todo < 0 || !PushOff(ss, todo, JSOP_FORLOCAL))
|
||||
return NULL;
|
||||
|
@ -3953,9 +3959,12 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
|||
*/
|
||||
rval = GetStr(ss, ss->top - 1);
|
||||
xval = VarPrefix(js_GetSrcNote(jp->script, pc + next));
|
||||
js_printf(jp, "\t%s (%s%s in %s) {\n",
|
||||
js_printf(jp, "\t%s (%s%s %s %s) {\n",
|
||||
foreach ? js_for_each_str : js_for_str,
|
||||
xval, lval, rval);
|
||||
xval,
|
||||
lval,
|
||||
forOf ? "of" : "in",
|
||||
rval);
|
||||
jp->indent += 4;
|
||||
DECOMPILE_CODE(pc + next + JSOP_POP_LENGTH, cond - next - JSOP_POP_LENGTH);
|
||||
jp->indent -= 4;
|
||||
|
|
|
@ -314,6 +314,13 @@ ProxyHandler::defaultValue(JSContext *cx, JSObject *proxy, JSType hint, Value *v
|
|||
return DefaultValue(cx, proxy, hint, vp);
|
||||
}
|
||||
|
||||
bool
|
||||
ProxyHandler::iteratorNext(JSContext *cx, JSObject *proxy, Value *vp)
|
||||
{
|
||||
vp->setMagic(JS_NO_ITER_VALUE);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ProxyHandler::call(JSContext *cx, JSObject *proxy, uintN argc, Value *vp)
|
||||
{
|
||||
|
@ -969,6 +976,14 @@ Proxy::defaultValue(JSContext *cx, JSObject *proxy, JSType hint, Value *vp)
|
|||
return GetProxyHandler(proxy)->defaultValue(cx, proxy, hint, vp);
|
||||
}
|
||||
|
||||
bool
|
||||
Proxy::iteratorNext(JSContext *cx, JSObject *proxy, Value *vp)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return NULL);
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
return GetProxyHandler(proxy)->iteratorNext(cx, proxy, vp);
|
||||
}
|
||||
|
||||
static JSObject *
|
||||
proxy_innerObject(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
|
|
|
@ -86,6 +86,7 @@ class JS_FRIEND_API(ProxyHandler) {
|
|||
virtual JSString *fun_toString(JSContext *cx, JSObject *proxy, uintN indent);
|
||||
virtual RegExpShared *regexp_toShared(JSContext *cx, JSObject *proxy);
|
||||
virtual bool defaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp);
|
||||
virtual bool iteratorNext(JSContext *cx, JSObject *proxy, Value *vp);
|
||||
virtual void finalize(JSContext *cx, JSObject *proxy);
|
||||
virtual void trace(JSTracer *trc, JSObject *proxy);
|
||||
virtual bool getElementIfPresent(JSContext *cx, JSObject *obj, JSObject *receiver,
|
||||
|
@ -140,6 +141,7 @@ class Proxy {
|
|||
static JSString *fun_toString(JSContext *cx, JSObject *proxy, uintN indent);
|
||||
static RegExpShared *regexp_toShared(JSContext *cx, JSObject *proxy);
|
||||
static bool defaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp);
|
||||
static bool iteratorNext(JSContext *cx, JSObject *proxy, Value *vp);
|
||||
};
|
||||
|
||||
inline bool IsObjectProxy(const JSObject *obj)
|
||||
|
|
|
@ -209,11 +209,9 @@ StringBuffer::appendInflated(const char *cstr, size_t cstrlen)
|
|||
size_t lengthBefore = length();
|
||||
if (!cb.growByUninitialized(cstrlen))
|
||||
return false;
|
||||
#if DEBUG
|
||||
size_t oldcstrlen = cstrlen;
|
||||
bool ok =
|
||||
#endif
|
||||
InflateStringToBuffer(context(), cstr, cstrlen, begin() + lengthBefore, &cstrlen);
|
||||
DebugOnly<size_t> oldcstrlen = cstrlen;
|
||||
DebugOnly<bool> ok = InflateStringToBuffer(context(), cstr, cstrlen,
|
||||
begin() + lengthBefore, &cstrlen);
|
||||
JS_ASSERT(ok && oldcstrlen == cstrlen);
|
||||
return true;
|
||||
}
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче