Bug 1066432. Update ForOfIterator to the changes in Web IDL's handling of sequences as iterables. We now commit to an iterable if we get a non-undefined value for the Symbol.iterator property, not just if we get a callable value. r=jorendorff

This commit is contained in:
Boris Zbarsky 2014-09-30 21:26:28 -04:00
parent 209307a037
commit 9804f71306
9 changed files with 136 additions and 11 deletions

View File

@ -56,6 +56,9 @@ TestInterfaceJS.prototype = {
get cachedAttr() { return this._cachedAttr; },
setCachedAttr: function(n) { this._cachedAttr = n; },
clearCachedAttrCache: function () { this.__DOM_IMPL__._clearCachedCachedAttrValue(); },
testSequenceOverload: function(arg) {},
testSequenceUnion: function(arg) {},
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TestInterfaceJS])

View File

@ -50,3 +50,5 @@ skip-if = debug == false
[test_throwing_method_noDCE.html]
[test_treat_non_object_as_null.html]
[test_traceProtos.html]
[test_sequence_detection.html]
skip-if = debug == false

View File

@ -0,0 +1,54 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1066432
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1066432</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript">
/** Test for Bug 1066432 **/
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]}, function() {
var testInterfaceJS = new TestInterfaceJS();
ok(testInterfaceJS, "got a TestInterfaceJS object");
try {
testInterfaceJS.testSequenceOverload(
{ "@@iterator": 5, [Symbol.iterator]: Array.prototype[Symbol.iterator] });
ok(false, "Should have thrown in the overload case");
} catch (e) {
ise(e.name, "TypeError", "Should get a TypeError for the overload case");
alert(e.message);
ok(e.message.contains("not iterable"),
"Should have a message about being non-iterable in the overload case");
}
try {
testInterfaceJS.testSequenceUnion(
{ "@@iterator": 5, [Symbol.iterator]: Array.prototype[Symbol.iterator] });
ok(false, "Should have thrown in the union case");
} catch (e) {
ise(e.name, "TypeError", "Should get a TypeError for the union case");
alert(e.message);
ok(e.message.contains("not iterable"),
"Should have a message about being non-iterable in the union case");
}
SimpleTest.finish();
});
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1066432">Mozilla Bug 1066432</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
</body>
</html>

View File

@ -42,4 +42,10 @@ interface TestInterfaceJS {
readonly attribute short cachedAttr;
void setCachedAttr(short n);
void clearCachedAttrCache();
// Test for sequence overloading and union behavior
void testSequenceOverload(sequence<DOMString> arg);
void testSequenceOverload(DOMString arg);
void testSequenceUnion((sequence<DOMString> or DOMString) arg);
};

View File

@ -7,17 +7,19 @@ The tests in this directory exercise the JSAPI.
If you built JS, you already built the tests.
If you did `make check` in your JS objdir, you already ran them.
The tests are built by default when you build JS. All the tests are compiled
into a single binary named jsapi-tests. They all run in a single process.
To run the tests:
cd $OBJDIR/dist/bin
./jsapi-tests
To run the tests in a debugger:
cd $OBJDIR/jsapi-tests
cd $OBJDIR/dist/bin
gdb ./jsapi-tests
--- Creating new tests
1. You can either add to an existing test*.cpp file or make a new one.

View File

@ -29,6 +29,7 @@ UNIFIED_SOURCES += [
'testException.cpp',
'testExternalStrings.cpp',
'testFindSCCs.cpp',
'testForOfIterator.cpp',
'testFreshGlobalEvalRedefinition.cpp',
'testFuncCallback.cpp',
'testFunctionProperties.cpp',

View File

@ -0,0 +1,53 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "jsapi-tests/tests.h"
BEGIN_TEST(testForOfIterator_basicNonIterable)
{
JS::RootedValue v(cx);
// Hack to make it simple to produce an object that has a property
// named Symbol.iterator.
EVAL("var obj = { '@@iterator': 5, [Symbol.iterator]: Array.prototype[Symbol.iterator] }; obj;", &v);
JS::ForOfIterator iter(cx);
bool ok = iter.init(v);
CHECK(!ok);
JS_ClearPendingException(cx);
return true;
}
END_TEST(testForOfIterator_basicNonIterable)
BEGIN_TEST(testForOfIterator_bug515273_part1)
{
JS::RootedValue v(cx);
// Hack to make it simple to produce an object that has a property
// named Symbol.iterator.
EVAL("var obj = { '@@iterator': 5, [Symbol.iterator]: Array.prototype[Symbol.iterator] }; obj;", &v);
JS::ForOfIterator iter(cx);
bool ok = iter.init(v, JS::ForOfIterator::AllowNonIterable);
CHECK(!ok);
JS_ClearPendingException(cx);
return true;
}
END_TEST(testForOfIterator_bug515273_part1)
BEGIN_TEST(testForOfIterator_bug515273_part2)
{
JS::RootedObject obj(cx,
JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
CHECK(obj);
JS::RootedValue v(cx, JS::ObjectValue(*obj));
JS::ForOfIterator iter(cx);
bool ok = iter.init(v, JS::ForOfIterator::AllowNonIterable);
CHECK(ok);
CHECK(!iter.valueIsIterable());
return true;
}
END_TEST(testForOfIterator_bug515273_part2)

View File

@ -5191,10 +5191,10 @@ class MOZ_STACK_CLASS JS_PUBLIC_API(ForOfIterator) {
};
/*
* Initialize the iterator. If AllowNonIterable is passed then if iterable
* does not have a callable @@iterator init() will just return true instead
* of throwing. Callers should then check valueIsIterable() before
* continuing with the iteration.
* Initialize the iterator. If AllowNonIterable is passed then if getting
* the @@iterator property from iterable returns undefined init() will just
* return true instead of throwing. Callers must then check
* valueIsIterable() before continuing with the iteration.
*/
bool init(JS::HandleValue iterable,
NonIterableBehavior nonIterableBehavior = ThrowOnNonIterable);

View File

@ -1366,13 +1366,17 @@ ForOfIterator::init(HandleValue iterable, NonIterableBehavior nonIterableBehavio
if (!JSObject::getProperty(cx, iterableObj, iterableObj, cx->names().std_iterator, &callee))
return false;
// Throw if obj[@@iterator] isn't callable if we were asked to do so.
// If obj[@@iterator] is undefined and we were asked to allow non-iterables,
// bail out now without setting iterator. This will make valueIsIterable(),
// which our caller should check, return false.
if (nonIterableBehavior == AllowNonIterable && callee.isUndefined())
return true;
// Throw if obj[@@iterator] isn't callable.
// js::Invoke is about to check for this kind of error anyway, but it would
// throw an inscrutable error message about |method| rather than this nice
// one about |obj|.
if (!callee.isObject() || !callee.toObject().isCallable()) {
if (nonIterableBehavior == AllowNonIterable)
return true;
char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, iterable, NullPtr());
if (!bytes)
return false;