Section 4/101 menit
4. Cara Kerja Swift Macro (Teori Internal)
4. Cara Kerja Swift Macro (Teori Internal)
Pipeline Kompilasi dengan Macro
Ketika compiler menemukan macro, proses kompilasi berjalan seperti ini:
swift
Source Code (.swift)
│
▼
Swift Parser
│ menghasilkan
▼
Abstract Syntax Tree (AST)
│
▼
Macro Expansion Phase
┌────┴────────────────────────┐
│ Compiler menemukan @Macro │
│ atau #macro │
└─────────────┬──────────────┘
│ kirim subtree AST ke
▼
Macro Plugin (executable)
[berjalan dalam sandbox]
│ kembalikan AST baru
▼
Expanded AST (macro sudah diganti
dengan kode yang di-generate)
│
▼
Type Checker & Optimizer
│
▼
Compiled Binary
AST (Abstract Syntax Tree)
AST adalah representasi terstruktur dari kode sumber sebagai pohon node. Setiap konstruk Swift (class, func, property, ekspresi) direpresentasikan sebagai node AST.
swift
// Kode sumber:
struct Point { var x: Int }
// Representasi AST (disederhanakan):
StructDecl(name: "Point") {
VarDecl(
name: "x",
type: SimpleTypeIdentifier("Int")
)
}
Macro menerima node AST sebagai input dan mengembalikan node AST baru. Ini mengapa macro type-safe — bukan manipulasi string seperti C preprocessor.
SwiftSyntax
SwiftSyntax adalah library Apple yang menyediakan API untuk membaca dan memodifikasi AST Swift. Semua macro plugin ditulis menggunakan SwiftSyntax.
swift
// Contoh: membaca nama semua stored property dari StructDecl
import SwiftSyntax
import SwiftSyntaxMacros
struct MyMacro: MemberMacro {
static func expansion(
of node: AttributeSyntax,
providingMembersOf declaration: some DeclGroupSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
// Baca semua stored property dari deklarasi struct/class
let storedProperties = declaration.memberBlock.members
.compactMap { $0.decl.as(VariableDeclSyntax.self) }
.filter { $0.bindings.first?.accessorBlock == nil } // hanya stored, bukan computed
// Generate kode baru berdasarkan property tersebut
let initParams = storedProperties.map { variable -> String in
let name = variable.bindings.first?.pattern.description ?? ""
let type = variable.bindings.first?.typeAnnotation?.type.description ?? "Any"
return "\(name): \(type)"
}.joined(separator: ", ")
return [
"init(\(raw: initParams)) { \(raw: storedProperties.map { "self.\($0.bindings.first?.pattern.description ?? "") = \($0.bindings.first?.pattern.description ?? "")" }.joined(separator: "; ")) }"
]
}
}
Sandbox Execution
Macro plugin berjalan sebagai proses terpisah yang terisolasi (sandbox). Ini berarti:
- Macro tidak bisa membaca file sistem secara bebas
- Macro tidak bisa melakukan network request
- Macro tidak bisa memodifikasi state global
- Jika macro crash, compiler memberikan error yang jelas — tidak crash seluruh build