Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions JSTests/BUN-TEST-DIFFERENCES.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ Not upstream's: a sync neither brings nor changes them. Most are named `stress/b
| `stress/inline-cache-stub-freed-during-call-to-custom-setter.js` | an inline cache stub that has called native code stays until the call returns, also when the callee signed the return address that it saved (`JITStubRoutineSet::mark`). The setter of `$vm`'s `customAccessor` is compiled with `pac-ret` on ARM64 Linux in every build. On a processor without pointer authentication nothing is signed and the test passes whatever the code does |
| `stress/bun-return-address-of-function-is-signed.js` | with `ENABLE(RETURN_ADDRESS_SIGNING)` a function signs the return address that it saves in LLInt, Baseline, the DFG and the FTL alike: `$vm.returnAddressOfCallerIsSigned()` signs the address again and compares. Elsewhere, and on a processor without pointer authentication (`$vm.functionsSignReturnAddress()`), the test ends at once |
| `stress/bun-return-address-with-wrong-signature-is-not-returned-to.js` | with `ENABLE(RETURN_ADDRESS_SIGNING)` a function does not return once a bit of the signature of its saved return address was flipped, in each of the four tiers: the run must crash. Where nothing is signed the test crashes by itself |
| `stress/super-property-assignment-value-in-derived-constructor-arrow-function.js` | in an arrow function of a derived constructor that reads `this` again before each use, `super[key] = value` gives the assigned value and the setter gets it, and a `for`-`of` loop with a `super[key]` target uses its key: the value and the key keep their registers while `this` is read (`AssignBracketNode::emitBytecode`, `ForOfNode::emitBytecode`). The same bug is in WebKit/WebKit: once a sync brings a fix from there, this row and the fork's lines go |
| the fr-CA block of `stress/intl-date-pattern-includes-literal-text.js` (the file is upstream's) | keeps the file's coverage on CLDR 49, where fr's pattern no longer has literal text: an `h` inside literal text is not mistaken for the hour field |
| the last block of `stress/regexp-test-sticky-first-char-filter.js` (the file is upstream's; nothing of upstream's text changes) | the first-character filter of a sticky `test()` comes from the first sets of the alternatives (`yarr/YarrFirstFollowSets.*`): behind a leading lookbehind, forward reference or lookahead, and in groups nested more than eight deep, a byte that passes the filter still reaches the matcher |

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// In a derived constructor, an arrow function that has eval code or an arrow function with super(), and the body of an
// async arrow function, read |this| again before each use. The read needs a register. The value of an assignment to a
// super property, and the key of a for-of target, stay in their own registers while |this| is read.

function shouldBe(actual, expected, name)
{
if (actual !== expected)
throw new Error(`${name}: expected ${String(expected)} but got ${typeof actual === "object" ? Object.prototype.toString.call(actual) : String(actual)}`);
}

class Base {
set setter(value) { this.stored = value; }
}

const settled = [];

class Derived extends Base {
constructor()
{
super();
const key = "setter";
const withEval = () => { eval(""); return super[key] = 21; };
const withEvalAndConstantKey = () => { eval(""); return super["setter"] = 22; };
const withSuperCall = () => { const notCalled = () => { super(); }; return super[key] = 23; };
const forOfWithEval = () => { eval(""); for (super["la" + "st"] of [5, 6]); return this.last; };
const asyncBody = async () => { await 0; return super[key] = 24; };
const forOfInAsyncBody = async () => { await 0; for (super["la" + "st"] of [7, 8]); return this.last; };

shouldBe(withEval(), 21, "arrow function with eval code, value of the assignment");
shouldBe(this.stored, 21, "arrow function with eval code, value that the setter got");
shouldBe(withEvalAndConstantKey(), 22, "arrow function with eval code and a constant key, value of the assignment");
shouldBe(this.stored, 22, "arrow function with eval code and a constant key, value that the setter got");
shouldBe(withSuperCall(), 23, "arrow function with an arrow function that has super(), value of the assignment");
shouldBe(this.stored, 23, "arrow function with an arrow function that has super(), value that the setter got");
shouldBe(forOfWithEval(), 6, "arrow function with eval code, for-of to a super property");

const name = "body of an async arrow function";
asyncBody().then(value => {
settled.push(name);
shouldBe(value, 24, `${name}, value of the assignment`);
shouldBe(this.stored, 24, `${name}, value that the setter got`);
return forOfInAsyncBody();
}).then(value => {
settled.push(name);
shouldBe(value, 8, `${name}, for-of to a super property`);
}).catch(error => settled.push(error));
}
}

for (let i = 0; i < testLoopCount; ++i)
new Derived;

drainMicrotasks();
for (const result of settled) {
if (result instanceof Error)
throw result;
}
shouldBe(settled.length, 2 * testLoopCount, "async arrow functions settle");
22 changes: 11 additions & 11 deletions Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4306,28 +4306,28 @@ RegisterID* AssignBracketNode::emitBytecode(BytecodeGenerator& generator, Regist
RefPtr<RegisterID> result = generator.emitNode(value.get(), m_right);

generator.emitExpressionInfo(divot(), divotStart(), divotEnd());
RegisterID* forwardResult = (dst == generator.ignoredResult()) ? result.get() : generator.move(generator.tempDestination(result.get()), result.get());
RefPtr<RegisterID> forwardResult = (dst == generator.ignoredResult()) ? result.get() : generator.move(generator.tempDestination(result.get()), result.get());

if (isNonIndexStringElement(*m_subscript)) {
if (m_base->isSuperNode()) {
RefPtr<RegisterID> thisValue = generator.ensureThis();
Comment thread
robobun marked this conversation as resolved.
Resolved
generator.emitPutById(base.get(), thisValue.get(), static_cast<StringNode*>(m_subscript)->value(), forwardResult);
generator.emitPutById(base.get(), thisValue.get(), static_cast<StringNode*>(m_subscript)->value(), forwardResult.get());
} else
generator.emitPutById(base.get(), static_cast<StringNode*>(m_subscript)->value(), forwardResult);
generator.emitPutById(base.get(), static_cast<StringNode*>(m_subscript)->value(), forwardResult.get());
} else {
if (m_base->isSuperNode()) {
RefPtr<RegisterID> thisValue = generator.ensureThis();
generator.emitPutByVal(base.get(), thisValue.get(), property.get(), forwardResult);
generator.emitPutByVal(base.get(), thisValue.get(), property.get(), forwardResult.get());
} else {
if (context)
generator.emitEnumeratorPutByVal(*context, base.get(), property.get(), forwardResult);
generator.emitEnumeratorPutByVal(*context, base.get(), property.get(), forwardResult.get());
else
generator.emitPutByVal(base.get(), property.get(), forwardResult);
generator.emitPutByVal(base.get(), property.get(), forwardResult.get());
}
}

generator.emitProfileType(forwardResult, divotStart(), divotEnd());
return generator.move(dst, forwardResult);
generator.emitProfileType(forwardResult.get(), divotStart(), divotEnd());
return generator.move(dst, forwardResult.get());
}

// ------------------------------ ReadModifyBracketNode -----------------------------------
Expand Down Expand Up @@ -4941,14 +4941,14 @@ void ForOfNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
} else if (m_lexpr->isBracketAccessorNode()) {
BracketAccessorNode* assignNode = static_cast<BracketAccessorNode*>(m_lexpr);
RefPtr<RegisterID> base = generator.emitNode(assignNode->base());
RegisterID* subscript = generator.emitNodeForProperty(assignNode->subscript());
RefPtr<RegisterID> subscript = generator.emitNodeForProperty(assignNode->subscript());

generator.emitExpressionInfo(assignNode->divot(), assignNode->divotStart(), assignNode->divotEnd());
if (assignNode->base()->isSuperNode()) {
RefPtr<RegisterID> thisValue = generator.ensureThis();
generator.emitPutByVal(base.get(), thisValue.get(), subscript, value);
generator.emitPutByVal(base.get(), thisValue.get(), subscript.get(), value);
} else
generator.emitPutByVal(base.get(), subscript, value);
generator.emitPutByVal(base.get(), subscript.get(), value);
generator.emitProfileType(value, assignNode->divotStart(), assignNode->divotEnd());
} else {
ASSERT(m_lexpr->isDestructuringNode());
Expand Down
Loading