Skip to content

Commit

Permalink
Separate compiler and codegen behind features, add codegen, add compi…
Browse files Browse the repository at this point in the history
…lation and loading
  • Loading branch information
bjcscat committed Dec 4, 2024
1 parent 0fa3ea1 commit 439260a
Show file tree
Hide file tree
Showing 6 changed files with 575 additions and 64 deletions.
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
[package]
name = "luau-rs"
name = "rs-luau"
version = "0.0.1"
edition = "2021"

[features]
default=["compiler", "codegen"]
compiler = []
codegen = []
luau_vector4=[]

[build-dependencies]
Expand Down
9 changes: 7 additions & 2 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,15 @@ fn main() {
);
}

println!("cargo:rustc-link-lib=static=Luau.Ast");
println!("cargo:rustc-link-lib=static=Luau.VM");

#[cfg(feature = "compiler")]
println!("cargo:rustc-link-lib=static=Luau.Compiler");
#[cfg(feature = "compiler")]
println!("cargo:rustc-link-lib=static=Luau.Ast");
// println!("cargo:rustc-link-lib=static=Luau.Analysis");
println!("cargo:rustc-link-lib=static=Luau.VM");

#[cfg(feature = "codegen")]
println!("cargo:rustc-link-lib=static=Luau.CodeGen");

// link to C++ stdlib, unless we're on windows, which is special
Expand Down
247 changes: 219 additions & 28 deletions src/compile.rs
Original file line number Diff line number Diff line change
@@ -1,53 +1,244 @@
use std::{ffi::c_int, fs, path::Path, ptr::null};
use core::str;
use std::{cell::UnsafeCell, ffi::c_int, fs, path::Path, ptr::null};

use crate::{ffi::luaucode, luau_compile, LuauCompileOptions};
use crate::{cstdlib_free, ffi::luaucode, luau_compile, LuauCompileOptions};

pub struct Compiler {
options: LuauCompileOptions,
_vector_lib: Option<Box<[u8]>>,
_vector_ctor: Option<Box<[u8]>>,
_vector_type: Option<Box<[u8]>>,
_mutable_globals: Option<Vec<Box<[u8]>>>,
_userdata_types: Option<Vec<Box<[u8]>>>,
options: UnsafeCell<LuauCompileOptions>,
}

impl Compiler {
pub fn compile(&mut self, source: impl AsRef<[u8]>) -> Box<[u8]> {
pub fn new() -> Self {
Self {
_vector_lib: None,
_vector_ctor: None,
_vector_type: None,
_mutable_globals: None,
_userdata_types: None,
options: UnsafeCell::new(LuauCompileOptions::default()),
}
}

fn options(&mut self) -> &mut LuauCompileOptions {
self.options.get_mut()
}

/// Sets the optimization level for the compiler
pub fn optimization_level(&mut self, level: c_int) -> &mut Self {
self.options().optimization_level = level;

self
}

/// Sets the debug level for the compiler
pub fn debug_level(&mut self, level: c_int) -> &mut Self {
self.options().debug_level = level;

self
}

/// Sets the type info level for the compiler
pub fn type_info_level(&mut self, level: c_int) -> &mut Self {
self.options().type_info_level = level;

self
}

/// Sets the coverage level for the compiler
pub fn coverage_level(&mut self, level: c_int) -> &mut Self {
self.options().coverage_level = level;

self
}

/// Sets the vector library ident for the compiler
pub fn vector_lib(&mut self, lib: impl AsRef<[u8]>) -> &mut Self {
let lib: Box<[u8]> = Box::from(lib.as_ref());

self.options().vector_lib = lib.as_ptr() as _;
self._vector_lib = Some(lib);

self
}

/// Sets the vector constructor ident for the compiler
pub fn vector_ctor(&mut self, ctor: impl AsRef<[u8]>) -> &mut Self {
let ctor: Box<[u8]> = Box::from(ctor.as_ref());

self.options().vector_ctor = ctor.as_ptr() as _;
self._vector_ctor = Some(ctor);

self
}

/// Sets the vector type ident for the compiler
pub fn vector_type(&mut self, vec_type: impl AsRef<[u8]>) -> &mut Self {
let vector_type: Box<[u8]> = Box::from(vec_type.as_ref());

self.options().vector_type = vector_type.as_ptr() as _;
self._vector_type = Some(vector_type);

self
}

/// Sets the mutable globals for the compiler
pub fn mutable_globals<T: AsRef<[u8]>>(&mut self, lib: impl AsRef<[T]>) -> &mut Self {
let mut vector: Vec<Box<[u8]>> = Vec::new();
let mut pointer_vectors: Vec<*const u8> = Vec::new();

for t in lib.as_ref() {
let boxed: Box<[u8]> = Box::from(t.as_ref());
pointer_vectors.push(boxed.as_ptr());
vector.push(boxed);
}

self.options().mutable_globals = pointer_vectors.as_ptr() as *const *const _;
self._mutable_globals = Some(vector);

self
}

/// Sets the userdata types for the compiler
pub fn userdata_types<T: AsRef<[u8]>>(&mut self, types: impl AsRef<[T]>) -> &mut Self {
let mut vector: Vec<Box<[u8]>> = Vec::new();
let mut pointer_vectors: Vec<*const u8> = Vec::new();

for t in types.as_ref() {
let boxed: Box<[u8]> = Box::from(t.as_ref());
pointer_vectors.push(boxed.as_ptr());
vector.push(boxed);
}

self.options().userdata_types = pointer_vectors.as_ptr() as *const *const _;
self._userdata_types = Some(vector);

self
}

pub fn compile(&self, source: impl AsRef<[u8]>) -> CompilerResult {
let source = source.as_ref();
unsafe {
let mut len = 0;
let data = luau_compile(
let bytecode = luau_compile(
source.as_ptr() as _,
source.len(),
&raw mut self.options,
self.options.get(),
&raw mut len,
);
luaucode::cstdlib_free(data as _);
panic!()

CompilerResult { bytecode, len }
}
}
}

pub struct CompilerBuilder(LuauCompileOptions);
pub struct CompilerResult {
bytecode: *const i8,
len: usize,
}

impl CompilerBuilder {
pub fn optimization_level(mut self, level: c_int) -> Self {
self.0.optimization_level = level;
self
impl CompilerResult {
// not technically unsafe
fn bytecode_unchecked(&self) -> &[u8] {
unsafe { std::slice::from_raw_parts(self.bytecode as _, self.len) }
}

pub fn bytecode(&self) -> Option<&[u8]> {
if self.is_err() {
None
} else {
Some(self.bytecode_unchecked())
}
}

pub fn error(&self) -> Option<&str> {
if self.is_ok() {
None
} else {
unsafe {
Some(
str::from_utf8(std::slice::from_raw_parts(
self.bytecode.add(1) as _,
self.len - 1,
))
.expect("Luau error was not valid UTF-8"),
)
}
}
}

pub fn build(self) -> Compiler {
Compiler { options: self.0 }
/// Returns true if the compiler result is an error
pub fn is_err(&self) -> bool {
unsafe { !self.bytecode.is_null() && self.bytecode.read() == 0 }
}

/// Returns true if the compiler result is not an error
pub fn is_ok(&self) -> bool {
!self.is_err()
}
}

impl Drop for CompilerResult {
fn drop(&mut self) {
unsafe {
cstdlib_free(self.bytecode as _);
}
}
}

impl Default for CompilerBuilder {
fn default() -> Self {
Self(LuauCompileOptions {
optimization_level: 1,
debug_level: 1,
type_info_level: 0,
coverage_level: 0,
vector_lib: null(),
vector_ctor: null(),
vector_type: null(),
mutable_globals: null(),
userdata_types: null(),
})
#[cfg(test)]
mod tests {
use crate::Luau;

use super::Compiler;

#[test]
fn compiler_success() {
let compiler = Compiler::new();

// has an effect so cant be optimized out entirely
let result = compiler.compile("v()");

assert!(result.is_ok(), "Expected result to be a success");
assert!(
result.bytecode().is_some(),
"Expected resultant bytecode to be some"
);
assert!(
result.bytecode().is_some_and(|v| !v.is_empty()),
"Expected resultant bytecode to be non-empty"
);

let luau = Luau::default();

let load_result = luau.load(None, result.bytecode().unwrap(), 0);

assert_eq!(load_result, Ok(()));
}

#[test]
fn compiler_error() {
let compiler = Compiler::new();

// will always be an error per an RFC
let result = compiler.compile("$");

assert!(
result.is_err(),
"Expected the compiler result to be an error"
);

assert!(
result.bytecode().is_none(),
"Expected the bytecode to be none"
);
assert!(
result.error().is_some(),
"Expected the compiler result output a string"
);
}
}
4 changes: 4 additions & 0 deletions src/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,18 @@ pub mod luauconf {
pub mod luau;
pub mod luaulib;
pub mod lauxlib;
#[cfg(feature="compiler")]
pub mod luaucode;
#[cfg(feature="codegen")]
pub mod luaucodegen;

#[allow(dead_code, unused)]
pub mod prelude {
pub use super::luau::*;
pub use super::lauxlib::*;
pub use super::luaulib::*;
#[cfg(feature="compiler")]
pub use super::luaucode::*;
#[cfg(feature="codegen")]
pub use super::luaucodegen::*;
}
Loading

0 comments on commit 439260a

Please sign in to comment.