aboutsummaryrefslogtreecommitdiff
path: root/src/bun.js/scripts/generate-classes.ts
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2022-11-07 00:51:36 -0800
committerGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2022-11-07 00:51:36 -0800
commitd925367cae1f22ad85a29ae02456286d9ccc859a (patch)
tree4ba8a0977756a5ec175716122c0ed15d48d4e0e9 /src/bun.js/scripts/generate-classes.ts
parent179cd18f70751e2d3959ec95def0f5ea697dd781 (diff)
downloadbun-d925367cae1f22ad85a29ae02456286d9ccc859a.tar.gz
bun-d925367cae1f22ad85a29ae02456286d9ccc859a.tar.zst
bun-d925367cae1f22ad85a29ae02456286d9ccc859a.zip
[bindings generator] Support cached values that do not correspond to properties and support call() functions
Diffstat (limited to 'src/bun.js/scripts/generate-classes.ts')
-rw-r--r--src/bun.js/scripts/generate-classes.ts215
1 files changed, 137 insertions, 78 deletions
diff --git a/src/bun.js/scripts/generate-classes.ts b/src/bun.js/scripts/generate-classes.ts
index 39608dc22..78a4e69a3 100644
--- a/src/bun.js/scripts/generate-classes.ts
+++ b/src/bun.js/scripts/generate-classes.ts
@@ -3,6 +3,12 @@ import { readdirSync } from "fs";
import { resolve } from "path";
import type { Field, ClassDefinition } from "./class-definitions";
+const directoriesToSearch = [
+ resolve(`${import.meta.dir}/../api`),
+ resolve(`${import.meta.dir}/../test`),
+ resolve(`${import.meta.dir}/../webcore`),
+];
+
function symbolName(typeName, name) {
return `${typeName}__${name}`;
}
@@ -283,7 +289,7 @@ function generatePrototype(typeName, obj) {
const { proto: protoFields } = obj;
return `
${
- "construct" in obj
+ obj.construct
? `extern "C" void* ${classSymbolName(
typeName,
"construct"
@@ -296,6 +302,14 @@ ${
? `extern "C" void ${classSymbolName(typeName, "finalize")}(void*);`
: ""
}
+${
+ obj.call
+ ? `extern "C" JSC_DECLARE_HOST_FUNCTION(${classSymbolName(
+ typeName,
+ "call"
+ )});`
+ : ""
+}
${renderDecls(protoSymbolName, typeName, protoFields)}
STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(${proto}, ${proto}::Base);
@@ -313,7 +327,13 @@ ${generateHashTable(
const ClassInfo ${proto}::s_info = { "${typeName}"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(${proto}) };
-${renderFieldsImpl(protoSymbolName, typeName, obj, protoFields)}
+${renderFieldsImpl(
+ protoSymbolName,
+ typeName,
+ obj,
+ protoFields,
+ obj.values || []
+)}
void ${proto}::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject)
{
@@ -407,14 +427,10 @@ function generateConstructorHeader(typeName) {
// Must be defined for each specialization class.
static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES construct(JSC::JSGlobalObject*, JSC::CallFrame*);
+
DECLARE_EXPORT_INFO;
private:
- ${name}(JSC::VM& vm, JSC::Structure* structure, JSC::NativeFunction nativeFunction)
- : Base(vm, structure, nativeFunction, nativeFunction)
-
- {
- }
-
+ ${name}(JSC::VM& vm, JSC::Structure* structure);
void finishCreation(JSC::VM&, JSC::JSGlobalObject* globalObject, ${prototypeName(
typeName
)}* prototype);
@@ -455,10 +471,16 @@ void ${name}::finishCreation(VM& vm, JSC::JSGlobalObject* globalObject, ${protot
ASSERT(inherits(info()));
}
+${name}::${name}(JSC::VM& vm, JSC::Structure* structure) : Base(vm, structure, ${
+ obj.call ? classSymbolName(typeName, "call") : "construct"
+ }, construct) {
+
+ }
+
${name}* ${name}::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, ${prototypeName(
typeName
)}* prototype) {
- ${name}* ptr = new (NotNull, JSC::allocateCell<${name}>(vm)) ${name}(vm, structure, construct);
+ ${name}* ptr = new (NotNull, JSC::allocateCell<${name}>(vm)) ${name}(vm, structure);
ptr->finishCreation(vm, globalObject, prototype);
return ptr;
}
@@ -548,8 +570,8 @@ ${
`;
}
-function renderCachedFieldsHeader(typeName, klass, proto) {
- const rows = [];
+function renderCachedFieldsHeader(typeName, klass, proto, values) {
+ const rows: string[] = [];
for (const name in klass) {
if (
("cache" in klass[name] && klass[name].cache === true) ||
@@ -565,6 +587,10 @@ function renderCachedFieldsHeader(typeName, klass, proto) {
}
}
+ for (const name of values) {
+ rows.push(`mutable JSC::WriteBarrier<JSC::Unknown> m_${name};`);
+ }
+
return rows.join("\n");
}
@@ -677,12 +703,40 @@ function renderStaticDecls(symbolName, typeName, fields) {
return rows.join("\n");
}
+function writeBarrier(symbolName, typeName, name, cacheName) {
+ return `
+ extern "C" void ${symbolName(
+ typeName,
+ name
+ )}SetCachedValue(JSC::EncodedJSValue thisValue, JSC::JSGlobalObject *globalObject, JSC::EncodedJSValue value)
+ {
+ auto& vm = globalObject->vm();
+ auto* thisObject = jsCast<${className(
+ typeName
+ )}*>(JSValue::decode(thisValue));
+ thisObject->${cacheName}.set(vm, thisObject, JSValue::decode(value));
+ }
+
+ extern "C" EncodedJSValue ${symbolName(
+ typeName,
+ name
+ )}GetCachedValue(JSC::EncodedJSValue thisValue)
+ {
+ auto* thisObject = jsCast<${className(
+ typeName
+ )}*>(JSValue::decode(thisValue));
+ return JSValue::encode(thisObject->${cacheName}.get());
+ }
+
+ `;
+}
function renderFieldsImpl(
symbolName: (typeName: string, name: string) => string,
typeName: string,
obj: ClassDefinition,
- proto: ClassDefinition["proto"]
+ proto: ClassDefinition["proto"],
+ cachedValues: string[]
) {
const rows: string[] = [];
@@ -741,32 +795,7 @@ JSC_DEFINE_CUSTOM_GETTER(${symbolName(
RELEASE_AND_RETURN(throwScope, JSValue::encode(result));
}`);
}
- rows.push(`
-
-extern "C" void ${symbolName(
- typeName,
- name
- )}SetCachedValue(JSC::EncodedJSValue thisValue, JSC::JSGlobalObject *globalObject, JSC::EncodedJSValue value)
-{
- auto& vm = globalObject->vm();
- auto* thisObject = jsCast<${className(
- typeName
- )}*>(JSValue::decode(thisValue));
- thisObject->${cacheName}.set(vm, thisObject, JSValue::decode(value));
-}
-
-extern "C" EncodedJSValue ${symbolName(
- typeName,
- name
- )}GetCachedValue(JSC::EncodedJSValue thisValue)
-{
- auto* thisObject = jsCast<${className(
- typeName
- )}*>(JSValue::decode(thisValue));
- return JSValue::encode(thisObject->${cacheName}.get());
-}
-
-`);
+ rows.push(writeBarrier(symbolName, typeName, name, cacheName));
} else if (
"getter" in proto[name] ||
("accessor" in proto[name] && proto[name].getter)
@@ -824,30 +853,38 @@ JSC_DEFINE_CUSTOM_SETTER(${symbolName(
if ("fn" in proto[name]) {
rows.push(`
-JSC_DEFINE_HOST_FUNCTION(${symbolName(
- typeName,
- name
- )}Callback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
-{
- auto& vm = lexicalGlobalObject->vm();
-
- ${className(typeName)}* thisObject = jsDynamicCast<${className(
+ JSC_DEFINE_HOST_FUNCTION(${symbolName(
+ typeName,
+ name
+ )}Callback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
+ {
+ auto& vm = lexicalGlobalObject->vm();
+
+ ${className(typeName)}* thisObject = jsDynamicCast<${className(
typeName
)}*>(callFrame->thisValue());
- if (UNLIKELY(!thisObject)) {
- auto throwScope = DECLARE_THROW_SCOPE(vm);
- return throwVMTypeError(lexicalGlobalObject, throwScope);
+ if (UNLIKELY(!thisObject)) {
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ return throwVMTypeError(lexicalGlobalObject, throwScope);
+ }
+
+ JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject);
+
+ return ${symbolName(
+ typeName,
+ proto[name].fn
+ )}(thisObject->wrapped(), lexicalGlobalObject, callFrame);
+ }
+ `);
}
+ }
- JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject);
-
- return ${symbolName(
- typeName,
- proto[name].fn
- )}(thisObject->wrapped(), lexicalGlobalObject, callFrame);
-}
-`);
+ if (cachedValues?.length) {
+ for (const cacheName of cachedValues) {
+ rows.push(
+ writeBarrier(symbolName, typeName, cacheName, "m_" + cacheName)
+ );
}
}
@@ -936,6 +973,7 @@ function generateClassHeader(typeName, obj: ClassDefinition) {
}
static JSObject* createPrototype(VM& vm, JSDOMGlobalObject* globalObject);
+ static JSObject* createConstructor(VM& vm, JSGlobalObject* globalObject, JSValue prototype);
~${name}();
@@ -965,7 +1003,7 @@ function generateClassHeader(typeName, obj: ClassDefinition) {
${DECLARE_VISIT_CHILDREN}
- ${renderCachedFieldsHeader(typeName, klass, proto)}
+ ${renderCachedFieldsHeader(typeName, klass, proto, values)}
};
`;
}
@@ -998,7 +1036,7 @@ function generateClassImpl(typeName, obj: ClassDefinition) {
})
.join("\n");
var DEFINE_VISIT_CHILDREN = "";
- if (DEFINE_VISIT_CHILDREN_LIST.length || estimatedSize) {
+ if (DEFINE_VISIT_CHILDREN_LIST.length || estimatedSize || values.length) {
DEFINE_VISIT_CHILDREN = `
template<typename Visitor>
void ${name}::visitChildrenImpl(JSCell* cell, Visitor& visitor)
@@ -1114,6 +1152,17 @@ void ${name}::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer)
Base::analyzeHeap(cell, analyzer);
}
+JSObject* ${name}::createConstructor(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
+{
+ return WebCore::${constructorName(
+ typeName
+ )}::create(vm, globalObject, WebCore::${constructorName(
+ typeName
+ )}::createStructure(vm, globalObject, globalObject->functionPrototype()), jsCast<WebCore::${prototypeName(
+ typeName
+ )}*>(prototype));
+}
+
JSObject* ${name}::createPrototype(VM& vm, JSDOMGlobalObject* globalObject)
{
return ${prototypeName(typeName)}::create(vm, globalObject, ${prototypeName(
@@ -1129,17 +1178,13 @@ ${DEFINE_VISIT_CHILDREN}
}
function generateHeader(typeName, obj) {
- return (
- generateClassHeader(typeName, obj).trim() +
- "\n" +
- (!obj.noConstructor ? generateConstructorHeader(typeName).trim() : "") +
- "\n"
- );
+ return generateClassHeader(typeName, obj).trim();
}
function generateImpl(typeName, obj) {
const proto = obj.proto;
return [
+ generateConstructorHeader(typeName).trim() + "\n",
Object.keys(proto).length > 0 && generatePrototype(typeName, obj).trim(),
!obj.noConstructor ? generateConstructorImpl(typeName, obj).trim() : null,
Object.keys(proto).length > 0 && generateClassImpl(typeName, obj).trim(),
@@ -1157,6 +1202,8 @@ function generateZig(
finalize,
noConstructor,
estimatedSize,
+ call = false,
+ values = [],
hasPendingActivity = false,
} = {} as ClassDefinition
) {
@@ -1166,6 +1213,10 @@ function generateZig(
exports.set(`constructor`, classSymbolName(typeName, "construct"));
}
+ if (call) {
+ exports.set(`call`, classSymbolName(typeName, "call"));
+ }
+
if (finalize) {
exports.set(`finalize`, classSymbolName(typeName, "finalize"));
}
@@ -1188,13 +1239,16 @@ function generateZig(
appendSymbols(exports, (name) => protoSymbolName(typeName, name), a)
);
- const externs = Object.entries(proto)
+ const externs = Object.entries({
+ ...proto,
+ ...Object.fromEntries((values || []).map((a) => [a, { internal: true }])),
+ })
.filter(
([name, { cache, internal }]) =>
(cache && typeof cache !== "string") || internal
)
.map(
- ([name, { cache }]) =>
+ ([name]) =>
`extern fn ${protoSymbolName(
typeName,
name
@@ -1336,6 +1390,14 @@ function generateZig(
}
});
+ if (!!call) {
+ output += `
+ if (@TypeOf(${typeName}.call) != StaticCallbackType)
+ @compileLog(
+ "Expected ${typeName}.call to be a static callback"
+ );`;
+ }
+
return output;
}
@@ -1489,13 +1551,9 @@ function generateLazyClassStructureImpl(typeName, { klass = {}, proto = {} }) {
init.setStructure(WebCore::${className(
typeName
)}::createStructure(init.vm, init.global, init.prototype));
- init.setConstructor(WebCore::${constructorName(
+ init.setConstructor(WebCore::${className(
typeName
- )}::create(init.vm, init.global, WebCore::${constructorName(
- typeName
- )}::createStructure(init.vm, init.global, init.global->functionPrototype()), jsCast<WebCore::${prototypeName(
- typeName
- )}*>(init.prototype)));
+ )}::createConstructor(init.vm, init.global, init.prototype));
});
@@ -1531,6 +1589,12 @@ const GENERATED_CLASSES_IMPL_HEADER = `
// Generated by make codegen
#include "root.h"
+#include <JavaScriptCore/JSFunction.h>
+#include <JavaScriptCore/InternalFunction.h>
+#include <JavaScriptCore/LazyClassStructure.h>
+#include <JavaScriptCore/LazyClassStructureInlines.h>
+#include <JavaScriptCore/FunctionPrototype.h>
+
#include "ZigGlobalObject.h"
#include <JavaScriptCore/DOMJITAbstractHeap.h>
@@ -1604,11 +1668,6 @@ pub const StaticCallbackType = fn(*JSC.JSGlobalObject, *JSC.CallFrame) callconv(
`;
-const directoriesToSearch = [
- resolve(`${import.meta.dir}/../api`),
- resolve(`${import.meta.dir}/../webcore`),
-];
-
function findClasses() {
var classes = [];
for (let directory of directoriesToSearch) {