Những Thay Đổi Chi Tiết Trong Lua 5.2
Gần đây mình có ý định thử nghiệm xem LuaJIT 2.0 sẽ mang lại cải thiện hiệu năng nào cho hệ thống hiện tại. Tuy nhiên, vấn đề phát sinh ngay từ đầu là hệ thống chúng tôi được xây dựng trên nền tảng Lua 5.2, trong khi LuaJIT 2.0 chỉ hỗ trợ API của Lua 5.1. Dự kiến trong tương lai gần, phiên bản này cũng khó có khả năng hỗ trợ Lua 5.2.
Do đó, mình buộc phải tìm cách triển khai tính năng hỗ trợ ngược cho Lua 5.1.
Sự thay đổi lớn về mặt cú pháp
Điều đầu tiên cần lưu ý là Lua 5.2 đã loại bỏ khái niệm bảng môi trường (environment table) và thay thế bằng kỹ thuật cú pháp tiện lợi _ENV
. Những thay đổi nhỏ khác chủ yếu nằm ở API cấp độ C, khiến các thư viện C viết theo chuẩn Lua 5.2 không thể biên dịch được trong môi trường Lua 5.1. Mình dự định sẽ mô phỏng các tính năng này bằng API của Lua 5.1.
Mình không cố gắng triển khai đầy đủ tất cả các API mới của Lua 5.2, ví dụ như hỗ trợ về continuation function – một tính năng gần như không thể thực hiện được trên nền tảng Lua 5.1 hiện có. Một số API đơn giản hơn đã được mình chia sẻ tại đây:
luaL_checkversion
tuy hữu ích nhưng rất khó để hoàn thiện chức năng đầy đủ, đặc biệt là không thể phát hiện việc liên kết trùng lặp.lua_arith
về mặt lý thuyết có thể triển khai nhưng nếu dùng API của Lua 5.1 để mô phỏng sẽ dẫn đến mã nguồn phức tạp và hiệu năng kém. Vì không sử dụng tính năng này nên mình quyết định bỏ qua.- Các hàm
lua_upvalueid
vàlua_upvaluejoin
ít khả năng được dùng đến, do đó mình không tập trung vào việc triển khai chúng. - Cấu trúc
luaL_Buffer
trong Lua 5.2 đã thay đổi, khiến các hàm mới nhưluaL_buffinitsize
vàluaL_prepbuffsize
không thể duy trì ngữ nghĩa tương tự. Để tránh làm ảnh hưởng đến hành vi hiện tại của thư viện auxlib trong Lua 5.1, mình đành phải tránh dùng các API mới này.
Khắc phục vấn đề về API cấp độ Lua
Thay đổi lớn nhất liên quan đến hàm load
– được điều chỉnh cùng với _ENV
. Việc hỗ trợ đầy đủ tính năng này khá phức tạp, vì vậy mình bắt đầu bằng cách mô phỏng phiên bản đơn giản nhất.
Ban đầu, mình định sửa đổi mã nguồn do người dùng cung cấp bằng cách thêm một đoạn mã nhỏ ở đầu để giả lập hành vi của _ENV
. Tuy nhiên, mình phát hiện ra một số thư viện của chúng tôi sử dụng định dạng binary để truyền function giữa các State. Mã nhị phân không thể bị thay đổi bằng cách thêm vào đoạn mã text, đây là một vấn đề cần giải quyết trong tương lai.
Xử lý sự khác biệt trong string.format
Trong Lua 5.2, %s
trong string.format
sẽ tự động gọi phương thức __tostring
của đối tượng, trong khi Lua 5.1 không làm như vậy. Mình không muốn cập nhật lại hàm format
trong Lua 5.1, thay vào đó sẽ sửa mã nguồn Lua để gọi __tostring
một cách tường minh. Phần lớn các trường hợp dùng format
là để ghi log, chỉ cần tránh truyền giá trị nil
là đủ.
Khắc phục lỗi liên quan đến __pairs
Lua 5.2 cải thiện đáng kể hỗ trợ cho coroutine, may mắn là LuaJIT 2.0 cũng hỗ trợ tính năng này. Tuy nhiên, mình phát hiện LuaJIT 2 có lỗi khi sử dụng __pairs
(mặc định bị tắt). Sau thời gian dài gỡ lỗi, mình khẳng định đây không phải lỗi trong mã nguồn của chúng tôi.
Ví dụ đơn giản để kiểm chứng lỗi này:
|
|
Khi chạy trên LuaJIT 2.0, kết quả đầu ra sẽ là:
|
|
Trường a
bị lặp qua hai lần.
Xử lý sự khác biệt trong thư viện debug
Lua 5.2 mở rộng khả năng của hàm debug.getinfo
, cung cấp thêm trường nparams
để lấy số lượng tham số của hàm. Trong Lua 5.1, số lượng tham số và biến local được xử lý đồng nhất.
Ví dụ mã nguồn cũ dùng để lấy tên các tham số hàm:
|
|
Vì Lua 5.1 không có nparams
, đồng thời debug.getlocal
yêu cầu số nguyên đại diện cho độ sâu ngăn xếp chứ không phải đối tượng hàm, nên mình đã áp dụng giải pháp thay thế sau:
|
|