NOTE
이 문서는 Bun 의 유지관리자와 기여자를 위한 것으로, 내부 구현 세부사항을 설명합니다.2024 년 12 월에 코드베이스에 도입된 새로운 바인딩 생성기는 *.bind.ts 파일을 스캔하여 함수와 클래스 정의를 찾고, JavaScript 와 네이티브 코드 간 상호 운용을 위한 글루 코드를 생성합니다.
현재 유사한 목적을 달성하는 다른 코드 생성기와 시스템이 있습니다. 다음 항목들은 결국 이 하나로 완전히 대체될 것입니다.
- "클래스 생성기", 커스텀 클래스를 위해
*.classes.ts변환 - "JS2Native",
src/js에서 네이티브 코드로 임시 호출 허용
Zig 에서 JS 함수 생성
간단한 함수 (예: add) 를 구현하는 파일이 주어졌을 때
pub fn add(global: *jsc.JSGlobalObject, a: i32, b: i32) !i32 {
return std.math.add(i32, a, b) catch {
// 바인딩 함수는 `error.OutOfMemory` 와 `error.JSError` 를 반환할 수 있습니다.
// `std.math.add` 의 `error.Overflow` 와 같은 다른 오류는 변환해야 합니다.
// 설명을 구체적으로 작성하세요.
return global.throwPretty("Integer overflow while adding", .{});
};
}
const gen = bun.gen.math; // 이 파일의 기본 이름인 "math"
const std = @import("std");
const bun = @import("bun");
const jsc = bun.jsc;그런 다음 .bind.ts 함수를 사용하여 API 스키마를 설명합니다. 바인딩 파일은 Zig 파일 옆에 위치합니다.
import { t, fn } from "bindgen";
export const add = fn({
args: {
global: t.globalObject,
a: t.i32,
b: t.i32.default(1),
},
ret: t.i32,
});이 함수 선언은 다음과 동일합니다.
/**
* 인수가 0 개이면 예외를 발생시킵니다.
* 범위를 벗어난 숫자는 모듈로를 사용하여 래핑합니다.
*/
declare function add(a: number, b: number = 1): number;코드 생성기는 네이티브 함수 구현인 bun.gen.math.jsAdd 를 제공합니다. JavaScript 에 전달하려면 bun.gen.math.createAddCallback(global) 를 사용합니다. src/js/ 의 JS 파일은 $bindgenFn("math.bind.ts", "add") 를 사용하여 구현에 대한 핸들을 얻을 수 있습니다.
문자열
문자열을 수신하기 위한 타입은 t.DOMString, t.ByteString, t.USVString 중 하나입니다. 이들은 WebIDL 대응 항목에 직접 매핑되며 약간 다른 변환 로직을 가집니다. Bindgen 은 모든 경우에 BunString 을 네이티브 코드에 전달합니다.
잘 모르겠으면 DOMString 을 사용하세요.
t.UTF8String 은 t.DOMString 대신 사용할 수 있지만 bun.String.toUTF8 을 호출합니다. 네이티브 콜백은 []const u8(WTF-8 데이터) 를 네이티브 코드에 전달하고 함수 반환 후 해제합니다.
WebIDL 스펙의 요약:
- ByteString 은 유효한 latin1 문자만 포함할 수 있습니다. bun.String 이 이미 8 비트 형식이라고 가정하는 것은 안전하지 않지만 매우 가능성이 높습니다.
- USVString 은 유효하지 않은 서로게이트 쌍, 즉 UTF-8 으로 올바르게 표현할 수 있는 텍스트를 포함하지 않습니다.
- DOMString 은 가장 느슨하지만 가장 권장되는 전략입니다.
함수 변형
variants 는 여러 변형 (오버로드라고도 함) 을 지정할 수 있습니다.
import { t, fn } from "bindgen";
export const action = fn({
variants: [
{
args: {
a: t.i32,
},
ret: t.i32,
},
{
args: {
a: t.DOMString,
},
ret: t.DOMString,
},
],
});Zig 에서 각 변형은 스키마가 정의한 순서에 따라 번호를 받습니다.
fn action1(a: i32) i32 {
return a;
}
fn action2(a: bun.String) bun.String {
return a;
}t.dictionary
dictionary 는 JavaScript 객체에 대한 정의로, 일반적으로 함수 입력으로 사용됩니다. 함수 출력의 경우 함수와 디스트럭처링을 추가하기 위해 클래스 타입을 선언하는 것이 더 현명한 아이디어입니다.
열거형
WebIDL 의 열거형 타입을 사용하려면 다음 중 하나를 사용하세요.
t.stringEnum: 새로운 enum 타입을 생성하고 코드 생성t.zigEnum: 코드베이스의 기존 enum 에서 bindgen 타입 파생
fmt.zig / bun:internal-for-testing 에서 사용되는 stringEnum 의 예
export const Formatter = t.stringEnum("highlight-javascript", "escape-powershell");
export const fmtString = fn({
args: {
global: t.globalObject,
code: t.UTF8String,
formatter: Formatter,
},
ret: t.DOMString,
});WebIDL 은 기존 Web API 와 일관성을 유지하기 위해 열거형 값에 케밥 케이스를 사용하는 것을 강력히 권장합니다.
Zig 코드에서 enum 파생
TODO: zigEnum
t.oneOf
oneOf 는 두 개 이상의 타입 간 유니온입니다. Zig 에서 union(enum) 으로 표현됩니다.
TODO:
속성
t.* 타입에 체이닝할 수 있는 속성 집합이 있습니다. 모든 타입에서 사용 가능한 속성은 다음과 같습니다.
.required, 딕셔너리 파라미터에서만 사용.optional, 함수 인수에서만 사용.default(T)
값이 선택적일 경우 Zig optional 로 낮아집니다.
타입에 따라 사용 가능한 속성이 더 있습니다. 자세한 내용은 자동 완성의 타입 정의를 참조하세요. 위 세 가지 중 하나만 적용할 수 있으며 마지막에 적용해야 합니다.
정수 속성
정수 타입은 clamp 또는 enforceRange 를 사용하여 오버플로 동작을 커스터마이징할 수 있습니다.
import { t, fn } from "bindgen";
export const add = fn({
args: {
global: t.globalObject,
// i32 범위 강제
a: t.i32.enforceRange(),
// u16 범위로 클램프
b: t.u16,
// 제공되지 않으면 기본값과 함께 임의 범위 강제
c: t.i32.enforceRange(0, 1000).default(5),
// 임의 범위로 클램프 또는 null
d: t.u16.clamp(0, 10).optional,
},
ret: t.i32,
});validateInteger, validateNumber 등 다양한 Node.js 검증 함수를 사용할 수 있습니다. Node.js API 를 구현할 때 오류 메시지가 Node 의 동작과 1:1 로 일치하도록 이러한 함수를 사용하세요.
enforceRange 와 달리 WebIDL 에서 가져온 validate* 함수는 허용하는 입력에 대해 훨씬 더 엄격합니다. 예를 들어 Node 의 숫자 검증기는 typeof value === 'number' 를 확인하는 반면 WebIDL 은 손실 있는 변환을 위해 ToNumber 를 사용합니다.
import { t, fn } from "bindgen";
export const add = fn({
args: {
global: t.globalObject,
// 숫자가 아니면 예외 발생
a: t.f64.validateNumber(),
// i32 범위에서 유효
a: t.i32.validateInt32(),
// 안전한 정수 범위의 f64
b: t.f64.validateInteger(),
// 주어진 범위의 f64
c: t.f64.validateNumber(-10000, 10000),
},
ret: t.i32,
});콜백
TODO
클래스
TODO