Merge mozilla-central to mozilla-inbound

This commit is contained in:
Ed Morley 2012-02-07 21:05:31 +00:00
Родитель fc417d773a 832b856be0
Коммит 5483df1d20
157 изменённых файлов: 2045 добавлений и 726 удалений

Просмотреть файл

@ -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,11 +4874,8 @@ 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,
getter_AddRefs(aDragEvent->dataTransfer));
initialDataTransfer->Clone(aDragEvent->message, aDragEvent->userCancelled,
getter_AddRefs(aDragEvent->dataTransfer));
NS_ENSURE_TRUE(aDragEvent->dataTransfer, NS_ERROR_OUT_OF_MEMORY);
// for the dragenter and dragover events, initialize the drop effect
@ -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!");
for (PRInt32 i = 2; (PRUint32)i < argc; ++i) {
jsargv[i - 2] = argv[i];
// jsargv might be null if we have argc <= 2
if (jsargv) {
for (PRInt32 i = 2; (PRUint32)i < argc; ++i) {
jsargv[i - 2] = argv[i];
}
} else {
NS_ASSERTION(argc <= 2, "Why do we have no jsargv when we have arguments?");
}
// final arg slot remains null, array has rooted vals.
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,12 +695,22 @@ 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));
if (!iterobj)
return false;
vp->setObject(*iterobj);
types::MarkIteratorUnknown(cx);
return true;
/*
* 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);

Просмотреть файл

@ -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);
if (!buf.append(vchars, vlength))
return false;
}
}
chars[nchars++] = '}';
if (outermost)
chars[nchars++] = ')';
chars[nchars] = 0;
error:
js_LeaveSharpObject(cx, &ida);
if (!ok) {
if (chars)
Foreground::free_(chars);
if (!buf.append('}'))
return false;
}
if (!chars) {
JS_ReportOutOfMemory(cx);
if (outermost && !buf.append(')'))
return false;
}
str = js_NewString(cx, chars, nchars);
if (!str) {
cx->free_(chars);
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;
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше