Skip to content

Commit

Permalink
js: add inherits builtin for generic cross-realm instanceof
Browse files Browse the repository at this point in the history
  • Loading branch information
nektro committed Jan 11, 2025
1 parent 18f9ecf commit 5ff6db0
Show file tree
Hide file tree
Showing 9 changed files with 54 additions and 5 deletions.
1 change: 1 addition & 0 deletions src/bun.js/bindings/ZigGlobalObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3609,6 +3609,7 @@ void GlobalObject::addBuiltinGlobals(JSC::VM& vm)
GlobalPropertyInfo(builtinNames.TextEncoderStreamEncoderPrivateName(), JSTextEncoderStreamEncoderConstructor(), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly | 0),
GlobalPropertyInfo(builtinNames.makeErrorWithCodePrivateName(), JSFunction::create(vm, this, 2, String(), jsFunctionMakeErrorWithCode, ImplementationVisibility::Public), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly),
GlobalPropertyInfo(builtinNames.toClassPrivateName(), JSFunction::create(vm, this, 1, String(), jsFunctionToClass, ImplementationVisibility::Public), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly),
GlobalPropertyInfo(builtinNames.inheritsPrivateName(), JSFunction::create(vm, this, 1, String(), jsFunctionInherits, ImplementationVisibility::Public), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly),
};
addStaticGlobals(staticGlobals, std::size(staticGlobals));

Expand Down
4 changes: 4 additions & 0 deletions src/bun.js/bindings/js_classes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export default [
//
"Blob",
];
5 changes: 5 additions & 0 deletions src/codegen/bundle-modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { getJS2NativeCPP, getJS2NativeZig } from "./generate-js2native";
import { cap, declareASCIILiteral, writeIfNotChanged } from "./helpers";
import { createInternalModuleRegistry } from "./internal-module-registry-scanner";
import { define } from "./replacements";
import jsclasses from "./../bun.js/bindings/js_classes";

const BASE = path.join(import.meta.dir, "../js");
const debug = process.argv[2] === "--debug=ON";
Expand Down Expand Up @@ -464,6 +465,10 @@ declare function $${code}(msg: string, ...args: any[]): ${name};
`;
}

for (const name of jsclasses) {
dts += `\ndeclare function $inherits${name}(value: any): boolean;`;
}

return dts;
})(),
);
Expand Down
29 changes: 29 additions & 0 deletions src/codegen/generate-classes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -910,6 +910,7 @@ function renderStaticDecls(symbolName, typeName, fields, supportsObjectCreate =

return rows.join("\n");
}

function writeBarrier(symbolName, typeName, name, cacheName) {
return `
Expand All @@ -928,6 +929,7 @@ extern JSC_CALLCONV JSC::EncodedJSValue ${symbolName(typeName, name)}GetCachedVa
`.trim();
}

function renderFieldsImpl(
symbolName: (typeName: string, name: string) => string,
typeName: string,
Expand Down Expand Up @@ -1176,6 +1178,7 @@ JSC_DEFINE_HOST_FUNCTION(${symbolName(typeName, name)}Callback, (JSGlobalObject

return rows.map(a => a.trim()).join("\n");
}

function allCachedValues(obj: ClassDefinition) {
let values = (obj.values ?? []).slice().map(name => [name, `m_${name}`]);
for (const name in obj.proto) {
Expand All @@ -1193,6 +1196,7 @@ function allCachedValues(obj: ClassDefinition) {

return values;
}

var extraIncludes = [];
function generateClassHeader(typeName, obj: ClassDefinition) {
var { klass, proto, JSType = "ObjectType", values = [], callbacks = {}, zigOnly = false } = obj;
Expand Down Expand Up @@ -2101,6 +2105,9 @@ const GENERATED_CLASSES_HEADER = [
#include "root.h"
namespace Zig {
JSC_DEFINE_HOST_FUNCTION(jsFunctionInherits, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame));
}
#include "JSDOMWrapper.h"
Expand Down Expand Up @@ -2168,6 +2175,25 @@ const GENERATED_CLASSES_IMPL_FOOTER = `
`;

function jsInheritsCppImpl() {
return `
JSC_DEFINE_HOST_FUNCTION(Zig::jsFunctionInherits, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
{
auto id = callFrame->argument(0).toInt32(globalObject);
auto value = callFrame->argument(1);
auto cell = value.asCell();
switch (id) {
${classes
.map(v => v.name)
.toSorted()
.map((v, i) => ` case ${i}: return JSValue::encode(jsBoolean(cell->inherits<WebCore::JS${v}>()));`)
.join("\n")}
}
return JSValue::encode(jsBoolean(false));
}`;
}

function initLazyClasses(initLaterFunctions) {
return `
Expand Down Expand Up @@ -2342,6 +2368,7 @@ comptime {
`,
]);

if (!process.env.ONLY_ZIG) {
const allHeaders = classes.map(a => generateHeader(a.name, a));
await writeIfNotChanged(`${outBase}/ZigGeneratedClasses.h`, [
Expand All @@ -2360,7 +2387,9 @@ if (!process.env.ONLY_ZIG) {
allImpls.join("\n"),
writeCppSerializers(classes),
GENERATED_CLASSES_IMPL_FOOTER,
jsInheritsCppImpl(),
]);

await writeIfNotChanged(
`${outBase}/ZigGeneratedClasses+lazyStructureHeader.h`,
classes.map(a => generateLazyClassStructureHeader(a.name, a)).join("\n"),
Expand Down
9 changes: 9 additions & 0 deletions src/codegen/replacements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { LoaderKeys } from "../api/schema";
import NodeErrors from "../bun.js/bindings/ErrorCode.ts";
import { sliceSourceCode } from "./builtin-parser";
import { registerNativeCall } from "./generate-js2native";
import jsclasses from "./../bun.js/bindings/js_classes";

// This is a list of extra syntax replacements to do. Kind of like macros
// These are only run on code itself, not string contents or comments.
Expand All @@ -21,6 +22,14 @@ for (let i = 0; i < NodeErrors.length; i++) {
});
}

for (let id = 0; id < jsclasses.length; id++) {
const name = jsclasses[id];
replacements.push({
from: new RegExp(`\\b\\__intrinsic__inherits${name}\\(`, "g"),
to: `$inherits(${id}, `,
});
}

// These rules are run on the entire file, including within strings.
export const globalReplacements: ReplacementRule[] = [
{
Expand Down
1 change: 1 addition & 0 deletions src/js/builtins/BunBuiltinNames.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ using namespace JSC;
macro(inFlightCloseRequest) \
macro(inFlightWriteRequest) \
macro(initializeWith) \
macro(inherits) \
macro(internalModuleRegistry) \
macro(internalRequire) \
macro(internalStream) \
Expand Down
2 changes: 1 addition & 1 deletion src/js/bun/ffi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,7 @@ function normalizePath(path) {
// This is mostly for import.meta.resolve()
// https://github.com/oven-sh/bun/issues/10304
path = Bun.fileURLToPath(path as URL);
} else if (path instanceof Blob) {
} else if ($inheritsBlob(path)) {
// must be a Bun.file() blob
// https://discord.com/channels/876711213126520882/1230114905898614794/1230114905898614794
path = path.name;
Expand Down
4 changes: 2 additions & 2 deletions src/js/node/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,11 @@ const kfakeSocket = Symbol("kfakeSocket");
const kEmptyBuffer = Buffer.alloc(0);

function isValidTLSArray(obj) {
if (typeof obj === "string" || isTypedArray(obj) || obj instanceof ArrayBuffer || obj instanceof Blob) return true;
if (typeof obj === "string" || isTypedArray(obj) || obj instanceof ArrayBuffer || $inheritsBlob(obj)) return true;
if (Array.isArray(obj)) {
for (var i = 0; i < obj.length; i++) {
const item = obj[i];
if (typeof item !== "string" && !isTypedArray(item) && !(item instanceof ArrayBuffer) && !(item instanceof Blob))
if (typeof item !== "string" && !isTypedArray(item) && !(item instanceof ArrayBuffer) && !$inheritsBlob(item))
return false;
}
return true;
Expand Down
4 changes: 2 additions & 2 deletions src/js/node/tls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ function parseCertString() {
const rejectUnauthorizedDefault =
process.env.NODE_TLS_REJECT_UNAUTHORIZED !== "0" && process.env.NODE_TLS_REJECT_UNAUTHORIZED !== "false";
function isValidTLSArray(obj) {
if (typeof obj === "string" || isTypedArray(obj) || obj instanceof ArrayBuffer || obj instanceof Blob) return true;
if (typeof obj === "string" || isTypedArray(obj) || obj instanceof ArrayBuffer || $inheritsBlob(obj)) return true;
if (Array.isArray(obj)) {
for (var i = 0; i < obj.length; i++) {
const item = obj[i];
if (typeof item !== "string" && !isTypedArray(item) && !(item instanceof ArrayBuffer) && !(item instanceof Blob))
if (typeof item !== "string" && !isTypedArray(item) && !(item instanceof ArrayBuffer) && !$inheritsBlob(item))
return false;
}
return true;
Expand Down

0 comments on commit 5ff6db0

Please sign in to comment.