mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
## Summary - Fix debug assertion failure in `JSWrappingFunction` when `expect.extend()` is called with objects containing non-`JSFunction` callables - The crash occurred because `jsCast<JSFunction*>` was used, which asserts the value inherits from `JSFunction`, but callable class constructors (like `Expect`) inherit from `InternalFunction` instead ## Changes - Change `JSWrappingFunction` to store `JSObject*` instead of `JSFunction*` - Use `jsDynamicCast` instead of `jsCast` in `getWrappedFunction` - Use `getObject()` instead of `jsCast` in `create()` ## Reproduction ```js const jest = Bun.jest(); jest.expect.extend(jest); ``` Before fix (debug build): ``` ASSERTION FAILED: !from || from->JSCell::inherits(std::remove_pointer<To>::type::info()) JSCast.h(40) : To JSC::jsCast(From *) [To = JSC::JSFunction *, From = JSC::JSCell] ``` After fix: Properly throws `TypeError: expect.extend: 'jest' is not a valid matcher` ## Test plan - [x] Added regression test `test/regression/issue/fuzzer-ENG-22942.test.ts` - [x] Existing `expect-extend.test.js` tests pass (27 tests) - [x] Build succeeds Fixes ENG-22942 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Bot <claude-bot@bun.sh> Co-authored-by: Claude <noreply@anthropic.com>
41 lines
1.2 KiB
TypeScript
41 lines
1.2 KiB
TypeScript
import { expect, test } from "bun:test";
|
|
|
|
// Regression test for ENG-22942: Crash when calling expect.extend with non-function values
|
|
// The crash occurred because JSWrappingFunction assumed all callable objects are JSFunction,
|
|
// but class constructors like Expect are callable but not JSFunction instances.
|
|
|
|
test("expect.extend with jest object should throw TypeError, not crash", () => {
|
|
const jest = Bun.jest(import.meta.path);
|
|
|
|
expect(() => {
|
|
jest.expect.extend(jest);
|
|
}).toThrow(TypeError);
|
|
});
|
|
|
|
test("expect.extend with object containing non-function values should throw", () => {
|
|
const jest = Bun.jest(import.meta.path);
|
|
|
|
expect(() => {
|
|
jest.expect.extend({
|
|
notAFunction: "string value",
|
|
});
|
|
}).toThrow("expect.extend: `notAFunction` is not a valid matcher");
|
|
});
|
|
|
|
test("expect.extend with valid matchers still works", () => {
|
|
const jest = Bun.jest(import.meta.path);
|
|
|
|
jest.expect.extend({
|
|
toBeEven(received: number) {
|
|
const pass = received % 2 === 0;
|
|
return {
|
|
message: () => `expected ${received} ${pass ? "not " : ""}to be even`,
|
|
pass,
|
|
};
|
|
},
|
|
});
|
|
|
|
jest.expect(4).toBeEven();
|
|
jest.expect(3).not.toBeEven();
|
|
});
|