mirror of
https://github.com/oven-sh/bun
synced 2026-02-03 15:38:46 +00:00
Compare commits
5 Commits
dylan/pyth
...
dylan/upda
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40acc7d524 | ||
|
|
d23312d3f6 | ||
|
|
de8c754c6a | ||
|
|
27e1363a66 | ||
|
|
eba4da23e6 |
110
bench/snippets/abort-signal.mjs
Normal file
110
bench/snippets/abort-signal.mjs
Normal file
@@ -0,0 +1,110 @@
|
||||
// Benchmark for AbortController/AbortSignal abort() performance
|
||||
// Tests the optimization of skipping Event creation when no listeners are registered
|
||||
|
||||
import { bench, group, run } from "../runner.mjs";
|
||||
|
||||
// Warmup: ensure JIT compilation
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
const controller = new AbortController();
|
||||
controller.abort();
|
||||
}
|
||||
|
||||
group("AbortController.abort()", () => {
|
||||
bench("no listener", () => {
|
||||
const controller = new AbortController();
|
||||
controller.abort();
|
||||
});
|
||||
|
||||
bench("with addEventListener", () => {
|
||||
const controller = new AbortController();
|
||||
controller.signal.addEventListener("abort", () => {});
|
||||
controller.abort();
|
||||
});
|
||||
|
||||
bench("with onabort property", () => {
|
||||
const controller = new AbortController();
|
||||
controller.signal.onabort = () => {};
|
||||
controller.abort();
|
||||
});
|
||||
|
||||
bench("with 3 listeners", () => {
|
||||
const controller = new AbortController();
|
||||
controller.signal.addEventListener("abort", () => {});
|
||||
controller.signal.addEventListener("abort", () => {});
|
||||
controller.signal.addEventListener("abort", () => {});
|
||||
controller.abort();
|
||||
});
|
||||
});
|
||||
|
||||
group("AbortSignal static methods", () => {
|
||||
bench("AbortSignal.abort() - pre-aborted", () => {
|
||||
const signal = AbortSignal.abort();
|
||||
// Signal is already aborted, no event dispatch needed
|
||||
});
|
||||
|
||||
bench("AbortSignal.any([]) - empty array", () => {
|
||||
const signal = AbortSignal.any([]);
|
||||
});
|
||||
|
||||
bench("AbortSignal.any([signal, signal]) - 2 signals", () => {
|
||||
const a = new AbortController();
|
||||
const b = new AbortController();
|
||||
const signal = AbortSignal.any([a.signal, b.signal]);
|
||||
});
|
||||
|
||||
bench("AbortSignal.any() then abort - no listener", () => {
|
||||
const a = new AbortController();
|
||||
const b = new AbortController();
|
||||
const signal = AbortSignal.any([a.signal, b.signal]);
|
||||
a.abort();
|
||||
});
|
||||
|
||||
bench("AbortSignal.any() then abort - with listener", () => {
|
||||
const a = new AbortController();
|
||||
const b = new AbortController();
|
||||
const signal = AbortSignal.any([a.signal, b.signal]);
|
||||
signal.addEventListener("abort", () => {});
|
||||
a.abort();
|
||||
});
|
||||
});
|
||||
|
||||
group("AbortController creation only", () => {
|
||||
bench("new AbortController()", () => {
|
||||
const controller = new AbortController();
|
||||
});
|
||||
|
||||
bench("new AbortController() + access signal", () => {
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
});
|
||||
});
|
||||
|
||||
group("AbortSignal.timeout()", () => {
|
||||
// Note: These don't actually wait for timeout, just measure creation overhead
|
||||
bench("AbortSignal.timeout(1000) creation", () => {
|
||||
const signal = AbortSignal.timeout(1000);
|
||||
});
|
||||
|
||||
bench("AbortSignal.timeout(0) creation", () => {
|
||||
const signal = AbortSignal.timeout(0);
|
||||
});
|
||||
});
|
||||
|
||||
group("abort with reason", () => {
|
||||
bench("abort() with no reason", () => {
|
||||
const controller = new AbortController();
|
||||
controller.abort();
|
||||
});
|
||||
|
||||
bench("abort() with string reason", () => {
|
||||
const controller = new AbortController();
|
||||
controller.abort("cancelled");
|
||||
});
|
||||
|
||||
bench("abort() with Error reason", () => {
|
||||
const controller = new AbortController();
|
||||
controller.abort(new Error("cancelled"));
|
||||
});
|
||||
});
|
||||
|
||||
await run();
|
||||
@@ -105,6 +105,10 @@ summary(() => {
|
||||
bench(`small (${small.length} chars) - Bun.markdown.render`, () => {
|
||||
return Bun.markdown.render(small, renderCallbacks);
|
||||
});
|
||||
|
||||
bench(`small (${small.length} chars) - Bun.markdown.react`, () => {
|
||||
return Bun.markdown.react(small);
|
||||
});
|
||||
}
|
||||
|
||||
bench(`small (${small.length} chars) - marked`, () => {
|
||||
@@ -125,6 +129,10 @@ summary(() => {
|
||||
bench(`medium (${medium.length} chars) - Bun.markdown.render`, () => {
|
||||
return Bun.markdown.render(medium, renderCallbacks);
|
||||
});
|
||||
|
||||
bench(`medium (${medium.length} chars) - Bun.markdown.react`, () => {
|
||||
return Bun.markdown.react(medium);
|
||||
});
|
||||
}
|
||||
|
||||
bench(`medium (${medium.length} chars) - marked`, () => {
|
||||
@@ -145,6 +153,10 @@ summary(() => {
|
||||
bench(`large (${large.length} chars) - Bun.markdown.render`, () => {
|
||||
return Bun.markdown.render(large, renderCallbacks);
|
||||
});
|
||||
|
||||
bench(`large (${large.length} chars) - Bun.markdown.react`, () => {
|
||||
return Bun.markdown.react(large);
|
||||
});
|
||||
}
|
||||
|
||||
bench(`large (${large.length} chars) - marked`, () => {
|
||||
|
||||
1
packages/bun-types/test.d.ts
vendored
1
packages/bun-types/test.d.ts
vendored
@@ -2179,6 +2179,7 @@ declare module "bun:test" {
|
||||
mockResolvedValueOnce(value: ResolveType<T>): this;
|
||||
mockRejectedValue(value: RejectType<T>): this;
|
||||
mockRejectedValueOnce(value: RejectType<T>): this;
|
||||
[Symbol.dispose](): void;
|
||||
}
|
||||
|
||||
// export type MockMetadata<T, MetadataType = MockMetadataType> = {
|
||||
|
||||
@@ -7,6 +7,7 @@ const VirtualMachine = @This();
|
||||
|
||||
export var has_bun_garbage_collector_flag_enabled = false;
|
||||
pub export var isBunTest: bool = false;
|
||||
pub export var Bun__defaultRemainingRunsUntilSkipReleaseAccess: c_int = 10;
|
||||
|
||||
// TODO: evaluate if this has any measurable performance impact.
|
||||
pub var synthetic_allocation_limit: usize = std.math.maxInt(u32);
|
||||
|
||||
@@ -464,8 +464,8 @@ const ParseRenderer = struct {
|
||||
const entry = self.#stack.pop().?;
|
||||
const g = self.#globalObject;
|
||||
|
||||
// Determine HTML tag name
|
||||
const type_str: []const u8 = blockTypeName(block_type, entry.data);
|
||||
// Determine HTML tag index for cached string
|
||||
const tag_index = getBlockTypeTag(block_type, entry.data);
|
||||
|
||||
// For headings, compute slug before counting props
|
||||
const slug: ?[]const u8 = if (block_type == .h) self.#heading_tracker.leaveHeading(bun.default_allocator) else null;
|
||||
@@ -496,7 +496,7 @@ const ParseRenderer = struct {
|
||||
|
||||
// Build React element — use component override as type if set
|
||||
const component = self.getBlockComponent(block_type, entry.data);
|
||||
const type_val: JSValue = if (component != .zero) component else try bun.String.createUTF8ForJS(g, type_str);
|
||||
const type_val: JSValue = if (component != .zero) component else getCachedTagString(g, tag_index);
|
||||
|
||||
const props = JSValue.createEmptyObject(g, props_count);
|
||||
self.#marked_args.append(props);
|
||||
@@ -572,7 +572,7 @@ const ParseRenderer = struct {
|
||||
const entry = self.#stack.pop().?;
|
||||
const g = self.#globalObject;
|
||||
|
||||
const type_str: []const u8 = spanTypeName(span_type);
|
||||
const tag_index = getSpanTypeTag(span_type);
|
||||
|
||||
// Count props fields: always children (or alt for img) + metadata
|
||||
var props_count: usize = 1; // children (or alt for img)
|
||||
@@ -592,7 +592,7 @@ const ParseRenderer = struct {
|
||||
|
||||
// Build React element: { $$typeof, type, key, ref, props }
|
||||
const component = self.getSpanComponent(span_type);
|
||||
const type_val: JSValue = if (component != .zero) component else try bun.String.createUTF8ForJS(g, type_str);
|
||||
const type_val: JSValue = if (component != .zero) component else getCachedTagString(g, tag_index);
|
||||
|
||||
const props = JSValue.createEmptyObject(g, props_count);
|
||||
self.#marked_args.append(props);
|
||||
@@ -675,7 +675,7 @@ const ParseRenderer = struct {
|
||||
switch (text_type) {
|
||||
.br => {
|
||||
const br_component = self.#components.br;
|
||||
const br_type: JSValue = if (br_component != .zero) br_component else try bun.String.createUTF8ForJS(g, "br");
|
||||
const br_type: JSValue = if (br_component != .zero) br_component else getCachedTagString(g, .br);
|
||||
const empty_props = JSValue.createEmptyObject(g, 0);
|
||||
self.#marked_args.append(empty_props);
|
||||
const obj = self.createElement(br_type, empty_props);
|
||||
@@ -705,53 +705,6 @@ const ParseRenderer = struct {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// Type name mappings
|
||||
// ========================================
|
||||
|
||||
fn blockTypeName(block_type: md.BlockType, data: u32) []const u8 {
|
||||
return switch (block_type) {
|
||||
.h => switch (data) {
|
||||
1 => "h1",
|
||||
2 => "h2",
|
||||
3 => "h3",
|
||||
4 => "h4",
|
||||
5 => "h5",
|
||||
else => "h6",
|
||||
},
|
||||
.p => "p",
|
||||
.quote => "blockquote",
|
||||
.ul => "ul",
|
||||
.ol => "ol",
|
||||
.li => "li",
|
||||
.code => "pre",
|
||||
.hr => "hr",
|
||||
.html => "html",
|
||||
.table => "table",
|
||||
.thead => "thead",
|
||||
.tbody => "tbody",
|
||||
.tr => "tr",
|
||||
.th => "th",
|
||||
.td => "td",
|
||||
.doc => "div",
|
||||
};
|
||||
}
|
||||
|
||||
fn spanTypeName(span_type: md.SpanType) []const u8 {
|
||||
return switch (span_type) {
|
||||
.em => "em",
|
||||
.strong => "strong",
|
||||
.a => "a",
|
||||
.img => "img",
|
||||
.code => "code",
|
||||
.del => "del",
|
||||
.latexmath => "math",
|
||||
.latexmath_display => "math",
|
||||
.wikilink => "a",
|
||||
.u => "u",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/// Renderer that calls JavaScript callbacks for each markdown element.
|
||||
@@ -1125,6 +1078,89 @@ fn extractLanguage(src_text: []const u8, info_beg: u32) []const u8 {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Cached tag string indices - must match BunMarkdownTagStrings.h
|
||||
const TagIndex = enum(u8) {
|
||||
h1 = 0,
|
||||
h2 = 1,
|
||||
h3 = 2,
|
||||
h4 = 3,
|
||||
h5 = 4,
|
||||
h6 = 5,
|
||||
p = 6,
|
||||
blockquote = 7,
|
||||
ul = 8,
|
||||
ol = 9,
|
||||
li = 10,
|
||||
pre = 11,
|
||||
hr = 12,
|
||||
html = 13,
|
||||
table = 14,
|
||||
thead = 15,
|
||||
tbody = 16,
|
||||
tr = 17,
|
||||
th = 18,
|
||||
td = 19,
|
||||
div = 20,
|
||||
em = 21,
|
||||
strong = 22,
|
||||
a = 23,
|
||||
img = 24,
|
||||
code = 25,
|
||||
del = 26,
|
||||
math = 27,
|
||||
u = 28,
|
||||
br = 29,
|
||||
};
|
||||
|
||||
extern fn BunMarkdownTagStrings__getTagString(*jsc.JSGlobalObject, u8) JSValue;
|
||||
|
||||
fn getCachedTagString(globalObject: *jsc.JSGlobalObject, tag: TagIndex) JSValue {
|
||||
return BunMarkdownTagStrings__getTagString(globalObject, @intFromEnum(tag));
|
||||
}
|
||||
|
||||
fn getBlockTypeTag(block_type: md.BlockType, data: u32) TagIndex {
|
||||
return switch (block_type) {
|
||||
.h => switch (data) {
|
||||
1 => .h1,
|
||||
2 => .h2,
|
||||
3 => .h3,
|
||||
4 => .h4,
|
||||
5 => .h5,
|
||||
else => .h6,
|
||||
},
|
||||
.p => .p,
|
||||
.quote => .blockquote,
|
||||
.ul => .ul,
|
||||
.ol => .ol,
|
||||
.li => .li,
|
||||
.code => .pre,
|
||||
.hr => .hr,
|
||||
.html => .html,
|
||||
.table => .table,
|
||||
.thead => .thead,
|
||||
.tbody => .tbody,
|
||||
.tr => .tr,
|
||||
.th => .th,
|
||||
.td => .td,
|
||||
.doc => .div,
|
||||
};
|
||||
}
|
||||
|
||||
fn getSpanTypeTag(span_type: md.SpanType) TagIndex {
|
||||
return switch (span_type) {
|
||||
.em => .em,
|
||||
.strong => .strong,
|
||||
.a => .a,
|
||||
.img => .img,
|
||||
.code => .code,
|
||||
.del => .del,
|
||||
.latexmath => .math,
|
||||
.latexmath_display => .math,
|
||||
.wikilink => .a,
|
||||
.u => .u,
|
||||
};
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
const bun = @import("bun");
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <JavaScriptCore/VM.h>
|
||||
#include <JavaScriptCore/Heap.h>
|
||||
|
||||
extern "C" int Bun__defaultRemainingRunsUntilSkipReleaseAccess;
|
||||
|
||||
extern "C" void Bun__JSC_onBeforeWait(JSC::VM* _Nonnull vm)
|
||||
{
|
||||
ASSERT(vm);
|
||||
@@ -46,7 +48,7 @@ extern "C" void Bun__JSC_onBeforeWait(JSC::VM* _Nonnull vm)
|
||||
// finalizers that might've been waiting to be run is a good idea.
|
||||
// But if you haven't, like if the process is just waiting on I/O
|
||||
// then don't bother.
|
||||
static constexpr int defaultRemainingRunsUntilSkipReleaseAccess = 10;
|
||||
const int defaultRemainingRunsUntilSkipReleaseAccess = Bun__defaultRemainingRunsUntilSkipReleaseAccess;
|
||||
|
||||
static thread_local int remainingRunsUntilSkipReleaseAccess = 0;
|
||||
|
||||
|
||||
59
src/bun.js/bindings/BunMarkdownTagStrings.cpp
Normal file
59
src/bun.js/bindings/BunMarkdownTagStrings.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#include "root.h"
|
||||
#include "BunMarkdownTagStrings.h"
|
||||
#include <JavaScriptCore/JSString.h>
|
||||
#include <JavaScriptCore/JSGlobalObject.h>
|
||||
#include <JavaScriptCore/LazyProperty.h>
|
||||
#include <JavaScriptCore/LazyPropertyInlines.h>
|
||||
#include "ZigGlobalObject.h"
|
||||
#include <JavaScriptCore/SlotVisitorInlines.h>
|
||||
#include <JavaScriptCore/VMTrapsInlines.h>
|
||||
|
||||
namespace Bun {
|
||||
using namespace JSC;
|
||||
|
||||
#define MARKDOWN_TAG_STRINGS_LAZY_PROPERTY_DEFINITION(name, str, idx) \
|
||||
this->m_strings[idx].initLater( \
|
||||
[](const JSC::LazyProperty<JSGlobalObject, JSString>::Initializer& init) { \
|
||||
init.set(jsOwnedString(init.vm, str)); \
|
||||
});
|
||||
|
||||
#define MARKDOWN_TAG_STRINGS_LAZY_PROPERTY_VISITOR(name, str, idx) \
|
||||
this->m_strings[idx].visit(visitor);
|
||||
|
||||
void MarkdownTagStrings::initialize()
|
||||
{
|
||||
MARKDOWN_TAG_STRINGS_EACH_NAME(MARKDOWN_TAG_STRINGS_LAZY_PROPERTY_DEFINITION)
|
||||
}
|
||||
|
||||
template<typename Visitor>
|
||||
void MarkdownTagStrings::visit(Visitor& visitor)
|
||||
{
|
||||
MARKDOWN_TAG_STRINGS_EACH_NAME(MARKDOWN_TAG_STRINGS_LAZY_PROPERTY_VISITOR)
|
||||
}
|
||||
|
||||
template void MarkdownTagStrings::visit(JSC::AbstractSlotVisitor&);
|
||||
template void MarkdownTagStrings::visit(JSC::SlotVisitor&);
|
||||
|
||||
} // namespace Bun
|
||||
|
||||
// C API for Zig bindings
|
||||
extern "C" JSC::EncodedJSValue BunMarkdownTagStrings__getTagString(Zig::GlobalObject* globalObject, uint8_t tagIndex)
|
||||
{
|
||||
if (tagIndex >= MARKDOWN_TAG_STRINGS_COUNT)
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
|
||||
auto& tagStrings = globalObject->markdownTagStrings();
|
||||
|
||||
// Use a switch to call the appropriate accessor
|
||||
switch (tagIndex) {
|
||||
#define MARKDOWN_TAG_STRINGS_CASE(name, str, idx) \
|
||||
case idx: \
|
||||
return JSC::JSValue::encode(tagStrings.name##String(globalObject));
|
||||
|
||||
MARKDOWN_TAG_STRINGS_EACH_NAME(MARKDOWN_TAG_STRINGS_CASE)
|
||||
|
||||
#undef MARKDOWN_TAG_STRINGS_CASE
|
||||
default:
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
}
|
||||
70
src/bun.js/bindings/BunMarkdownTagStrings.h
Normal file
70
src/bun.js/bindings/BunMarkdownTagStrings.h
Normal file
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
#include <JavaScriptCore/JSString.h>
|
||||
#include <JavaScriptCore/LazyProperty.h>
|
||||
|
||||
// Markdown HTML tag names cached as JSStrings
|
||||
// These are commonly reused when rendering markdown to React elements
|
||||
|
||||
// clang-format off
|
||||
#define MARKDOWN_TAG_STRINGS_EACH_NAME(macro) \
|
||||
macro(h1, "h1"_s, 0) \
|
||||
macro(h2, "h2"_s, 1) \
|
||||
macro(h3, "h3"_s, 2) \
|
||||
macro(h4, "h4"_s, 3) \
|
||||
macro(h5, "h5"_s, 4) \
|
||||
macro(h6, "h6"_s, 5) \
|
||||
macro(p, "p"_s, 6) \
|
||||
macro(blockquote, "blockquote"_s, 7) \
|
||||
macro(ul, "ul"_s, 8) \
|
||||
macro(ol, "ol"_s, 9) \
|
||||
macro(li, "li"_s, 10) \
|
||||
macro(pre, "pre"_s, 11) \
|
||||
macro(hr, "hr"_s, 12) \
|
||||
macro(html, "html"_s, 13) \
|
||||
macro(table, "table"_s, 14) \
|
||||
macro(thead, "thead"_s, 15) \
|
||||
macro(tbody, "tbody"_s, 16) \
|
||||
macro(tr, "tr"_s, 17) \
|
||||
macro(th, "th"_s, 18) \
|
||||
macro(td, "td"_s, 19) \
|
||||
macro(div, "div"_s, 20) \
|
||||
macro(em, "em"_s, 21) \
|
||||
macro(strong, "strong"_s, 22) \
|
||||
macro(a, "a"_s, 23) \
|
||||
macro(img, "img"_s, 24) \
|
||||
macro(code, "code"_s, 25) \
|
||||
macro(del, "del"_s, 26) \
|
||||
macro(math, "math"_s, 27) \
|
||||
macro(u, "u"_s, 28) \
|
||||
macro(br, "br"_s, 29)
|
||||
// clang-format on
|
||||
|
||||
#define MARKDOWN_TAG_STRINGS_COUNT 30
|
||||
|
||||
namespace Bun {
|
||||
|
||||
using namespace JSC;
|
||||
|
||||
class MarkdownTagStrings {
|
||||
public:
|
||||
#define MARKDOWN_TAG_STRINGS_ACCESSOR_DEFINITION(name, str, idx) \
|
||||
JSC::JSString* name##String(JSC::JSGlobalObject* globalObject) \
|
||||
{ \
|
||||
return m_strings[idx].getInitializedOnMainThread(globalObject); \
|
||||
}
|
||||
|
||||
MARKDOWN_TAG_STRINGS_EACH_NAME(MARKDOWN_TAG_STRINGS_ACCESSOR_DEFINITION)
|
||||
|
||||
#undef MARKDOWN_TAG_STRINGS_ACCESSOR_DEFINITION
|
||||
|
||||
void initialize();
|
||||
|
||||
template<typename Visitor>
|
||||
void visit(Visitor& visitor);
|
||||
|
||||
private:
|
||||
JSC::LazyProperty<JSC::JSGlobalObject, JSC::JSString> m_strings[MARKDOWN_TAG_STRINGS_COUNT];
|
||||
};
|
||||
|
||||
} // namespace Bun
|
||||
@@ -988,6 +988,10 @@ void JSMockFunctionPrototype::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* g
|
||||
JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
|
||||
|
||||
this->putDirect(vm, Identifier::fromString(vm, "_isMockFunction"_s), jsBoolean(true), 0);
|
||||
|
||||
// Support `using spy = spyOn(...)` — auto-restores when leaving scope.
|
||||
JSValue restoreFn = this->getDirect(vm, Identifier::fromString(vm, "mockRestore"_s));
|
||||
this->putDirect(vm, vm.propertyNames->disposeSymbol, restoreFn, static_cast<unsigned>(JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DontEnum));
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsMockFunctionGetMockImplementation, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe))
|
||||
|
||||
@@ -302,7 +302,6 @@ extern "C" void JSCInitialize(const char* envp[], size_t envc, void (*onCrash)(c
|
||||
JSC::Options::useJITCage() = false;
|
||||
JSC::Options::useShadowRealm() = true;
|
||||
JSC::Options::useV8DateParser() = true;
|
||||
JSC::Options::useMathSumPreciseMethod() = true;
|
||||
JSC::Options::evalMode() = evalMode;
|
||||
JSC::Options::heapGrowthSteepnessFactor() = 1.0;
|
||||
JSC::Options::heapGrowthMaxIncrease() = 2.0;
|
||||
@@ -1699,6 +1698,7 @@ void GlobalObject::finishCreation(VM& vm)
|
||||
m_commonStrings.initialize();
|
||||
m_http2CommonStrings.initialize();
|
||||
m_bakeAdditions.initialize();
|
||||
m_markdownTagStrings.initialize();
|
||||
|
||||
Bun::addNodeModuleConstructorProperties(vm, this);
|
||||
m_JSNodeHTTPServerSocketStructure.initLater(
|
||||
|
||||
@@ -58,6 +58,7 @@ struct node_module;
|
||||
#include "headers-handwritten.h"
|
||||
#include "BunCommonStrings.h"
|
||||
#include "BunHttp2CommonStrings.h"
|
||||
#include "BunMarkdownTagStrings.h"
|
||||
#include "BunGlobalScope.h"
|
||||
#include <js_native_api.h>
|
||||
#include <node_api.h>
|
||||
@@ -526,6 +527,7 @@ public:
|
||||
V(private, std::unique_ptr<WebCore::DOMConstructors>, m_constructors) \
|
||||
V(private, Bun::CommonStrings, m_commonStrings) \
|
||||
V(private, Bun::Http2CommonStrings, m_http2CommonStrings) \
|
||||
V(private, Bun::MarkdownTagStrings, m_markdownTagStrings) \
|
||||
\
|
||||
/* JSC's hashtable code-generator tries to access these properties, so we make them public. */ \
|
||||
/* However, we'd like it better if they could be protected. */ \
|
||||
@@ -716,6 +718,7 @@ public:
|
||||
|
||||
Bun::CommonStrings& commonStrings() { return m_commonStrings; }
|
||||
Bun::Http2CommonStrings& http2CommonStrings() { return m_http2CommonStrings; }
|
||||
Bun::MarkdownTagStrings& markdownTagStrings() { return m_markdownTagStrings; }
|
||||
#include "ZigGeneratedClasses+lazyStructureHeader.h"
|
||||
|
||||
void finishCreation(JSC::VM&);
|
||||
|
||||
@@ -171,8 +171,8 @@ void AbortSignal::runAbortSteps()
|
||||
algorithm.second(reason);
|
||||
|
||||
// 3. Fire an event named abort at signal.
|
||||
dispatchEvent(Event::create(eventNames().abortEvent, Event::CanBubble::No, Event::IsCancelable::No));
|
||||
|
||||
if (hasEventListeners(eventNames().abortEvent))
|
||||
dispatchEvent(Event::create(eventNames().abortEvent, Event::CanBubble::No, Event::IsCancelable::No));
|
||||
setIsFiringEventListeners(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -49,14 +49,14 @@ WebCoreTypedArrayController::WebCoreTypedArrayController(bool allowAtomicsWait)
|
||||
|
||||
WebCoreTypedArrayController::~WebCoreTypedArrayController() = default;
|
||||
|
||||
JSC::JSArrayBuffer* WebCoreTypedArrayController::toJS(JSC::JSGlobalObject* lexicalGlobalObject, JSC::JSGlobalObject* globalObject, JSC::ArrayBuffer* buffer)
|
||||
JSC::JSArrayBuffer* WebCoreTypedArrayController::toJS(JSC::JSGlobalObject* lexicalGlobalObject, JSC::JSGlobalObject* globalObject, JSC::ArrayBuffer& buffer)
|
||||
{
|
||||
return JSC::jsCast<JSC::JSArrayBuffer*>(WebCore::toJS(lexicalGlobalObject, getDefaultGlobal(globalObject), buffer));
|
||||
}
|
||||
|
||||
void WebCoreTypedArrayController::registerWrapper(JSC::JSGlobalObject* globalObject, JSC::ArrayBuffer* native, JSC::JSArrayBuffer* wrapper)
|
||||
void WebCoreTypedArrayController::registerWrapper(JSC::JSGlobalObject* globalObject, JSC::ArrayBuffer& native, JSC::JSArrayBuffer& wrapper)
|
||||
{
|
||||
cacheWrapper(static_cast<JSVMClientData*>(JSC::getVM(globalObject).clientData)->normalWorld(), native, wrapper);
|
||||
cacheWrapper(static_cast<JSVMClientData*>(JSC::getVM(globalObject).clientData)->normalWorld(), &native, &wrapper);
|
||||
}
|
||||
|
||||
bool WebCoreTypedArrayController::isAtomicsWaitAllowedOnCurrentThread()
|
||||
|
||||
@@ -35,8 +35,8 @@ public:
|
||||
WebCoreTypedArrayController(bool allowAtomicsWait);
|
||||
virtual ~WebCoreTypedArrayController();
|
||||
|
||||
JSC::JSArrayBuffer* toJS(JSC::JSGlobalObject*, JSC::JSGlobalObject*, JSC::ArrayBuffer*) override;
|
||||
void registerWrapper(JSC::JSGlobalObject*, ArrayBuffer*, JSC::JSArrayBuffer*) override;
|
||||
JSC::JSArrayBuffer* toJS(JSC::JSGlobalObject*, JSC::JSGlobalObject*, JSC::ArrayBuffer&) override;
|
||||
void registerWrapper(JSC::JSGlobalObject*, ArrayBuffer&, JSC::JSArrayBuffer&) override;
|
||||
bool isAtomicsWaitAllowedOnCurrentThread() override;
|
||||
|
||||
JSC::WeakHandleOwner* wrapperOwner() { return &m_owner; }
|
||||
|
||||
@@ -52,6 +52,14 @@ pub fn init(this: *GarbageCollectionController, vm: *VirtualMachine) void {
|
||||
}
|
||||
this.gc_timer_interval = gc_timer_interval;
|
||||
|
||||
if (vm.transpiler.env.get("BUN_GC_RUNS_UNTIL_SKIP_RELEASE_ACCESS")) |val| {
|
||||
if (std.fmt.parseInt(c_int, val, 10)) |parsed| {
|
||||
if (parsed >= 0) {
|
||||
VirtualMachine.Bun__defaultRemainingRunsUntilSkipReleaseAccess = parsed;
|
||||
}
|
||||
} else |_| {}
|
||||
}
|
||||
|
||||
this.disabled = vm.transpiler.env.has("BUN_GC_TIMER_DISABLE");
|
||||
|
||||
if (!this.disabled)
|
||||
|
||||
38
test/js/bun/test/mock-disposable.test.ts
Normal file
38
test/js/bun/test/mock-disposable.test.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { expect, mock, spyOn, test } from "bun:test";
|
||||
|
||||
test("spyOn returns a disposable that calls mockRestore", () => {
|
||||
const obj = { method: () => "original" };
|
||||
|
||||
{
|
||||
using spy = spyOn(obj, "method").mockReturnValue("mocked");
|
||||
expect(obj.method()).toBe("mocked");
|
||||
expect(spy).toHaveBeenCalledTimes(1);
|
||||
}
|
||||
|
||||
expect(obj.method()).toBe("original");
|
||||
});
|
||||
|
||||
test("mock() returns a disposable that calls mockRestore", () => {
|
||||
const fn = mock(() => "original");
|
||||
|
||||
fn();
|
||||
expect(fn).toHaveBeenCalledTimes(1);
|
||||
expect(fn[Symbol.dispose]).toBeFunction();
|
||||
fn[Symbol.dispose]();
|
||||
expect(fn).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
test("using with spyOn auto-restores prototype methods", () => {
|
||||
class Greeter {
|
||||
greet() {
|
||||
return "hello";
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
using spy = spyOn(Greeter.prototype, "greet").mockReturnValue("hola");
|
||||
expect(new Greeter().greet()).toBe("hola");
|
||||
}
|
||||
|
||||
expect(new Greeter().greet()).toBe("hello");
|
||||
});
|
||||
Reference in New Issue
Block a user