implementing-jsc-class…
Creates JavaScript classes using Bun's Rust bindings generator (.classes.ts). Use when implementing new JS APIs in Rust with JSC integration, prototypes, or…
Implements JavaScript classes in C++ using JavaScriptCore. Use when creating new JS classes with C++ bindings, prototypes, or constructors.
$ npx -y skills add oven-sh/bun --skill implementing-jsc-classes-cpp --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/implementing-jsc-classes-cppContext preview
The summary Claude sees to decide when to auto-load this skill.
Implements JavaScript classes in C++ using JavaScriptCore. Use when creating new JS classes with C++ bindings, prototypes, or constructors.
name: implementing-jsc-classes-cpp description: Implements JavaScript classes in C++ using JavaScriptCore. Use when creating new JS classes with C++ bindings, prototypes, or constructors.
For publicly accessible Constructor and Prototype, create 3 classes:
1. **`class Foo : public JSC::DestructibleObject`** - if C++ fields exist; otherwise use `JSC::constructEmptyObject` with `putDirectOffset` 2. **`class FooPrototype : public JSC::JSNonFinalObject`** 3. **`class FooConstructor : public JSC::InternalFunction`**
No public constructor? Only Prototype and class needed.
Classes with C++ fields need subspaces in:
template<typename MyClassT, JSC::SubspaceAccess mode>
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) {
if constexpr (mode == JSC::SubspaceAccess::Concurrently)
return nullptr;
return WebCore::subspaceForImpl<MyClassT, WebCore::UseCustomHeapCellType::No>(
vm, BUN_SUBSPACE_SLOTS(m_clientSubspaceForMyClassT, m_subspaceForMyClassT));
}`subspaceForImpl` only reads the slots inline. The subspaces themselves are created by `subspaceForImplSlow` in `src/jsc/bindings/BunClientData.cpp`, which is the only place that constructs an `IsoSubspace` (`test/internal/source-lints/iso-subspace-creation.test.ts` checks this).
static JSC_DECLARE_HOST_FUNCTION(jsFooProtoFuncMethod);
static JSC_DECLARE_CUSTOM_GETTER(jsFooGetter_property);
static const HashTableValue JSFooPrototypeTableValues[] = {
{ "property"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsFooGetter_property, 0 } },
{ "method"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsFooProtoFuncMethod, 1 } },
};class JSFooPrototype final : public JSC::JSNonFinalObject {
public:
using Base = JSC::JSNonFinalObject;
static constexpr unsigned StructureFlags = Base::StructureFlags;
static JSFooPrototype* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure) {
JSFooPrototype* prototype = new (NotNull, allocateCell<JSFooPrototype>(vm)) JSFooPrototype(vm, structure);
prototype->finishCreation(vm);
return prototype;
}
template<typename, JSC::SubspaceAccess>
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) { return &vm.plainObjectSpace(); }
DECLARE_INFO;
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) {
auto* structure = JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
structure->setMayBePrototype(true);
return structure;
}
private:
JSFooPrototype(JSC::VM& vm, JSC::Structure* structure) : Base(vm, structure) {}
void finishCreation(JSC::VM& vm);
};
void JSFooPrototype::finishCreation(VM& vm) {
Base::finishCreation(vm);
reifyStaticProperties(vm, JSFoo::info(), JSFooPrototypeTableValues, *this);
JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
}// Getter
JSC_DEFINE_CUSTOM_GETTER(jsFooGetter_prop, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName)) {
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
JSFoo* thisObject = jsDynamicCast<JSFoo*>(JSValue::decode(thisValue));
if (UNLIKELY(!thisObject)) {
Bun::throwThisTypeError(*globalObject, scope, "JSFoo"_s, "prop"_s);
return {};
}
return JSValue::encode(jsBoolean(thisObject->value()));
}
// Function
JSC_DEFINE_HOST_FUNCTION(jsFooProtoFuncMethod, (JSGlobalObject* globalObject, CallFrame* callFrame)) {
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
auto* thisObject = jsDynamicCast<JSFoo*>(callFrame->thisValue());
if (UNLIKELY(!thisObject)) {
Bun::throwThisTypeError(*globalObject, scope, "Foo"_s, "method"_s);
return {};
}
return JSValue::encode(thisObject->doSomething(vm, globalObject));
}class JSFooConstructor final : public JSC::InternalFunction {
public:
using Base = JSC::InternalFunction;
static constexpr unsigned StructureFlags = Base::StructureFlags;
static JSFooConstructor* create(JSC::VM& vm, JSC::Structure* structure, JSC::JSObject* prototype) {
JSFooConstructor* constructor = new (NotNull, JSC::allocateCell<JSFooConstructor>(vm)) JSFooConstructor(vm, structure);
constructor->finishCreation(vm, prototype);
return constructor;
}
DECLARE_INFO;
template<typename CellType, JSC::SubspaceAccess>
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) { return &vm.internalFunctionSpace(); }
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) {
return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::InternalFunctionType, StructureFlags), info());
}
private:
JSFooConstructor(JSC::VM& vm, JSC::Structure* structure) : Base(vm, structure, callFoo, constructFoo) {}
void finishCreation(JSC::VM& vm, JSC::JSObject* prototype) {
Base::finishCreation(vm, 0, "Foo"_s);
putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly);
}
};Add to `ZigGlobalObject.h`:
JSC::LazyClassStructure m_JSFooClassStructure;
Initialize in `ZigGlobalObject.cpp`:
m_JSFooClassStructure.initLater([](LazyClassStructure::Initializer& init) {
Bun::initJSFooClassIncredibly fast JavaScript runtime, bundler, test runner, and package manager – all in one
Repo: oven-sh/bun
Creates JavaScript classes using Bun's Rust bindings generator (.classes.ts). Use when implementing new JS APIs in Rust with JSC integration, prototypes, or…
JSC GC reference for Bun. Use for use-after-free, JS object leaks, "collected too early", or when touching WriteBarrier, visitChildren,…
Guides using bun_sys for system calls and file I/O in Rust. Use when implementing file operations, opening fds, or any syscall path instead of std::fs or libc.
Find the top-N slowest test files in CI from a recent BuildKite run, optionally posting the results to a Slack channel as a formatted table. Use when asked to…
Verify a Bun runtime change by driving the debug binary end-to-end.
Guides writing bundler tests using itBundled/expectBundled in test/bundler/. Use when creating or modifying bundler, transpiler, or code transformation tests.