[JSC] add missing RequireObjectCoercible() step in destructuring
https://bugs.webkit.org/show_bug.cgi?id=151596
Patch by Caitlin Potter <caitp@igalia.com> on 2015-12-01
Reviewed by Darin Adler.
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitRequireObjectCoercible):
* bytecompiler/BytecodeGenerator.h:
* bytecompiler/NodesCodegen.cpp:
(JSC::ObjectPatternNode::bindValue):
* tests/stress/destructuring-assignment-require-object-coercible.js: Added.
(testTypeError):
(testOK):
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@192899 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index a397c30..bd944fb 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,19 @@
+2015-12-01 Caitlin Potter <caitp@igalia.com>
+
+ [JSC] add missing RequireObjectCoercible() step in destructuring
+ https://bugs.webkit.org/show_bug.cgi?id=151596
+
+ Reviewed by Darin Adler.
+
+ * bytecompiler/BytecodeGenerator.cpp:
+ (JSC::BytecodeGenerator::emitRequireObjectCoercible):
+ * bytecompiler/BytecodeGenerator.h:
+ * bytecompiler/NodesCodegen.cpp:
+ (JSC::ObjectPatternNode::bindValue):
+ * tests/stress/destructuring-assignment-require-object-coercible.js: Added.
+ (testTypeError):
+ (testOK):
+
2015-12-01 Mark Lam <mark.lam@apple.com>
Refactor FTL sub snippet code to support general binary op snippets.
diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
index 1abbba4..41a64c9 100644
--- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
+++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
@@ -3855,4 +3855,17 @@
return result;
}
+void BytecodeGenerator::emitRequireObjectCoercible(RegisterID* value, const String& error)
+{
+ // FIXME: op_jneq_null treats "undetectable" objects as null/undefined. RequireObjectCoercible
+ // thus incorrectly throws a TypeError for interfaces like HTMLAllCollection.
+ RefPtr<Label> target = newLabel();
+ size_t begin = instructions().size();
+ emitOpcode(op_jneq_null);
+ instructions().append(value->index());
+ instructions().append(target->bind(begin, instructions().size()));
+ emitThrowTypeError(error);
+ emitLabel(target.get());
+}
+
} // namespace JSC
diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
index 07316b1..6ab390e 100644
--- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
+++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
@@ -595,6 +595,7 @@
RegisterID* emitIsObject(RegisterID* dst, RegisterID* src);
RegisterID* emitIsUndefined(RegisterID* dst, RegisterID* src);
+ void emitRequireObjectCoercible(RegisterID* value, const String& error);
RegisterID* emitIteratorNext(RegisterID* dst, RegisterID* iterator, const ThrowableExpressionData* node);
void emitIteratorClose(RegisterID* iterator, const ThrowableExpressionData* node);
diff --git a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
index 242738e..6eee442 100644
--- a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
+++ b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
@@ -3337,6 +3337,7 @@
void ObjectPatternNode::bindValue(BytecodeGenerator& generator, RegisterID* rhs) const
{
+ generator.emitRequireObjectCoercible(rhs, ASCIILiteral("Right side of assignment cannot be destructured"));
for (size_t i = 0; i < m_targetPatterns.size(); i++) {
auto& target = m_targetPatterns[i];
RefPtr<RegisterID> temp = generator.newTemporary();
diff --git a/Source/JavaScriptCore/tests/stress/destructuring-assignment-require-object-coercible.js b/Source/JavaScriptCore/tests/stress/destructuring-assignment-require-object-coercible.js
new file mode 100644
index 0000000..c25bb1b
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/destructuring-assignment-require-object-coercible.js
@@ -0,0 +1,68 @@
+function testTypeError(script, message) {
+ var error = null;
+ try {
+ eval(script);
+ } catch (e) {
+ error = e;
+ }
+ if (!error)
+ throw new Error("Expected type error not thrown by `" + script + "`");
+
+ if (String(error) !== message)
+ throw new Error("Bad error: " + String(error));
+}
+
+function testOK(script) {
+ var error = null;
+ try {
+ eval(script);
+ } catch (e) {
+ error = e;
+ }
+ if (error)
+ throw new Error("Bad error: " + String(error));
+}
+
+testTypeError(`({ } = null)`, "TypeError: Right side of assignment cannot be destructured");
+testTypeError(`({ a } = null)`, "TypeError: Right side of assignment cannot be destructured");
+testTypeError(`({ a: { b } = null } = { })`, "TypeError: Right side of assignment cannot be destructured");
+testTypeError(`({ a: { b } } = { a: null })`, "TypeError: Right side of assignment cannot be destructured");
+testTypeError(`({ } = undefined)`, "TypeError: Right side of assignment cannot be destructured");
+testTypeError(`({ a } = undefined)`, "TypeError: Right side of assignment cannot be destructured");
+testTypeError(`({ a: { b } = undefined } = { })`, "TypeError: Right side of assignment cannot be destructured");
+testTypeError(`({ a: { b } } = { a: undefined })`, "TypeError: Right side of assignment cannot be destructured");
+
+testOK(`({ } = 123)`);
+testOK(`({ a } = 123)`);
+testOK(`({ a: { b } = 123 } = { })`);
+testOK(`({ a: { b } } = { a: 123 })`);
+
+testOK(`({ } = 0.5)`);
+testOK(`({ a } = 0.5)`);
+testOK(`({ a: { b } = 0.5 } = { })`);
+testOK(`({ a: { b } } = { a: 0.5 })`);
+
+testOK(`({ } = NaN)`);
+testOK(`({ a } = NaN)`);
+testOK(`({ a: { b } = NaN } = { })`);
+testOK(`({ a: { b } } = { a: NaN })`);
+
+testOK(`({ } = true)`);
+testOK(`({ a } = true)`);
+testOK(`({ a: { b } = true } = { })`);
+testOK(`({ a: { b } } = { a: true })`);
+
+testOK(`({ } = {})`);
+testOK(`({ a } = {})`);
+testOK(`({ a: { b } = {} } = { })`);
+testOK(`({ a: { b } } = { a: {} })`);
+
+testOK(`({ } = [])`);
+testOK(`({ a } = [])`);
+testOK(`({ a: { b } = [] } = { })`);
+testOK(`({ a: { b } } = { a: [] })`);
+
+testOK(`({ } = /1/)`);
+testOK(`({ a } = /1/)`);
+testOK(`({ a: { b } = /1/ } = { })`);
+testOK(`({ a: { b } } = { a: /1/ })`);