Mở Rộng Hành Vi Của Hàm Require Trong Lua
Hôm nay, một đồng nghiệp đưa ra yêu cầu khá thú vị: anh ấy muốn tạo một môi trường sandbox cho một số đoạn mã Lua (được viết bởi các nhân viên thiết kế game). Trong Lua, việc xây dựng sandbox tương đối đơn giản - chỉ cần kiểm soát môi trường thực thi của các hàm là đủ. Tuy nhiên, yêu cầu đặc biệt ở đây là những đoạn mã này vẫn phải có thể sử dụng require để tải module một cách bình thường.
Vấn đề đặt ra là làm thế nào để đạt được điều này mà không cần chỉnh sửa các API hệ thống?
Giải pháp sáng tạo
Đầu tiên, chúng tôi mong muốn cú pháp sử dụng trông như thế này:
|
|
Khác với cách dùng require truyền thống, ở đây chúng tôi cho phép thêm một tham số bổ sung myEnv
. Mỗi lần module “xxx” được require, nó sẽ được thực thi lại nhưng với môi trường _ENV
được ràng buộc khác nhau.
Hơn nữa, vì các module có thể được khởi tạo lại nhiều lần, chúng tôi còn thiết lập một quy ước rằng mỗi module trong sandbox này có thể nhận thêm các tham số từ require.
Cơ chế thực hiện
Việc này hoàn toàn khả thi bằng cách thêm một loader tùy chỉnh vào package.searchers
. Thay vì trả về trực tiếp chunk module đã tải, chúng tôi bao bọc nó trong một hàm để hoãn việc thực thi cho đến khi tham số myEnv
được cung cấp.
Điều này có nghĩa là việc tải và lưu trữ chunk vẫn dựa vào cơ chế bộ nhớ đệm của hệ thống require, nhưng mỗi lần gọi hàm với myEnv
khác nhau sẽ tạo ra một phiên bản module mới với môi trường _ENV
được ràng buộc riêng biệt.
Triển khai thực tế
Tôi đã chia sẻ đoạn mã minh họa kỹ thuật này trên gist: require với việc ràng buộc môi trường hoãn lại. Đoạn mã này cho phép bạn:
- Tạo nhiều phiên bản module độc lập từ cùng một mã nguồn
- Truyền tham số cấu hình khi yêu cầu module
- Duy trì tính năng bộ nhớ đệm tiêu chuẩn của Lua
Kỹ thuật này mở ra nhiều khả năng thú vị trong việc quản lý môi trường thực thi module một cách linh hoạt, đặc biệt hữu ích trong các hệ thống cần cách ly môi trường như game engine hay các nền tảng scripting an toàn.