Link libqjs
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
const QUICKJS: &str = "../third_party/quickjs/build";
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rustc-link-search=native={QUICKJS}");
|
||||
println!("cargo:rustc-link-lib=static=qjs");
|
||||
|
||||
println!("cargo:rustc-link-lib=dylib=m");
|
||||
println!("cargo:rustc-link-lib=dylib=dl");
|
||||
println!("cargo:rustc-link-lib=dylib=pthread");
|
||||
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
println!("cargo:rerun-if-changed={QUICKJS}/libqjs.a");
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
use std::ffi::{CStr, CString, c_char, c_int, c_void};
|
||||
|
||||
/// Compile `source` to quickjs bytecode, as `qjsc -b` does.
|
||||
///
|
||||
/// `name` is the module name quickjs records and reports errors against; it
|
||||
/// does not have to be a path that exists.
|
||||
pub fn compile(source: &str, name: &str) -> Result<Vec<u8>, String> {
|
||||
// A runtime and a context per call: compiling holds no state worth reusing,
|
||||
// and this way nothing outlives the returned bytes.
|
||||
unsafe {
|
||||
let rt = JS_NewRuntime();
|
||||
let ctx = JS_NewContext(rt);
|
||||
|
||||
let result = compile_in(ctx, source, name);
|
||||
|
||||
JS_FreeContext(ctx);
|
||||
JS_FreeRuntime(rt);
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
/// The body of `compile`, so the runtime is freed on either outcome.
|
||||
unsafe fn compile_in(ctx: *mut JSContext, source: &str, name: &str) -> Result<Vec<u8>, String> {
|
||||
// JS_Eval requires `input[input_len] == 0`, which a &str does not promise:
|
||||
// the byte past the end is whatever the allocator left there. Read as a
|
||||
// token, that byte decides whether the source parses.
|
||||
let source = CString::new(source).map_err(|_| "source has a nul byte".to_string())?;
|
||||
let name = CString::new(name).map_err(|_| "module name has a nul byte".to_string())?;
|
||||
|
||||
unsafe {
|
||||
// COMPILE_ONLY stops at the bytecode, so nothing in the source runs.
|
||||
let obj = JS_Eval(
|
||||
ctx,
|
||||
source.as_ptr(),
|
||||
source.as_bytes().len(),
|
||||
name.as_ptr(),
|
||||
JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY,
|
||||
);
|
||||
|
||||
if obj.tag == JS_TAG_EXCEPTION {
|
||||
return Err(exception(ctx));
|
||||
}
|
||||
|
||||
let mut len = 0;
|
||||
let buf = JS_WriteObject(ctx, &mut len, obj, JS_WRITE_OBJ_BYTECODE);
|
||||
JS_FreeValue(ctx, obj);
|
||||
|
||||
if buf.is_null() {
|
||||
return Err(exception(ctx));
|
||||
}
|
||||
|
||||
let bytes = std::slice::from_raw_parts(buf, len).to_vec();
|
||||
js_free(ctx, buf.cast::<c_void>());
|
||||
|
||||
Ok(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
/// The pending exception, as text. Taking it also clears it.
|
||||
unsafe fn exception(ctx: *mut JSContext) -> String {
|
||||
unsafe {
|
||||
let err = JS_GetException(ctx);
|
||||
let text = JS_ToCStringLen2(ctx, std::ptr::null_mut(), err, false);
|
||||
|
||||
let message = if text.is_null() {
|
||||
"quickjs raised an exception it could not describe".to_string()
|
||||
} else {
|
||||
CStr::from_ptr(text).to_string_lossy().into_owned()
|
||||
};
|
||||
|
||||
if !text.is_null() {
|
||||
JS_FreeCString(ctx, text);
|
||||
}
|
||||
|
||||
JS_FreeValue(ctx, err);
|
||||
message
|
||||
}
|
||||
}
|
||||
|
||||
// ── the C side ─────────────────────────────────────────────────────────────
|
||||
|
||||
enum JSRuntime {}
|
||||
enum JSContext {}
|
||||
|
||||
/// 16 bytes passed and returned BY VALUE. quickjs only nan-boxes on 32-bit
|
||||
/// targets (`JS_NAN_BOXING`, quickjs.h), where this layout would be wrong.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
struct JSValue {
|
||||
u: JSValueUnion,
|
||||
tag: i64,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
union JSValueUnion {
|
||||
int32: i32,
|
||||
float64: f64,
|
||||
ptr: *mut c_void,
|
||||
}
|
||||
|
||||
// `JS_IsException` and `JS_ToCString` are `static inline` in the header, so
|
||||
// they are not symbols to link against. These are the constants they read.
|
||||
const JS_TAG_EXCEPTION: i64 = 6;
|
||||
const JS_EVAL_TYPE_MODULE: c_int = 1 << 0;
|
||||
const JS_EVAL_FLAG_COMPILE_ONLY: c_int = 1 << 5;
|
||||
const JS_WRITE_OBJ_BYTECODE: c_int = 1 << 0;
|
||||
|
||||
unsafe extern "C" {
|
||||
fn JS_NewRuntime() -> *mut JSRuntime;
|
||||
fn JS_FreeRuntime(rt: *mut JSRuntime);
|
||||
fn JS_NewContext(rt: *mut JSRuntime) -> *mut JSContext;
|
||||
fn JS_FreeContext(ctx: *mut JSContext);
|
||||
|
||||
fn JS_Eval(
|
||||
ctx: *mut JSContext,
|
||||
input: *const c_char,
|
||||
input_len: usize,
|
||||
filename: *const c_char,
|
||||
eval_flags: c_int,
|
||||
) -> JSValue;
|
||||
|
||||
fn JS_WriteObject(
|
||||
ctx: *mut JSContext,
|
||||
psize: *mut usize,
|
||||
obj: JSValue,
|
||||
flags: c_int,
|
||||
) -> *mut u8;
|
||||
|
||||
fn JS_GetException(ctx: *mut JSContext) -> JSValue;
|
||||
fn JS_ToCStringLen2(
|
||||
ctx: *mut JSContext,
|
||||
plen: *mut usize,
|
||||
val: JSValue,
|
||||
cesu8: bool,
|
||||
) -> *const c_char;
|
||||
fn JS_FreeCString(ctx: *mut JSContext, ptr: *const c_char);
|
||||
fn JS_FreeValue(ctx: *mut JSContext, v: JSValue);
|
||||
fn js_free(ctx: *mut JSContext, ptr: *mut c_void);
|
||||
}
|
||||
+3
-21
@@ -3,11 +3,10 @@ mod declosure;
|
||||
mod demutate;
|
||||
mod flatten;
|
||||
mod infer;
|
||||
mod libqjs;
|
||||
mod parse;
|
||||
mod regify;
|
||||
|
||||
use std::process::Command;
|
||||
|
||||
use declass::declass;
|
||||
use declosure::declosure;
|
||||
use demutate::demutate;
|
||||
@@ -16,24 +15,15 @@ use infer::infer;
|
||||
use parse::QuickJSBinary;
|
||||
use regify::regify;
|
||||
|
||||
const QJSC: &str = "../third_party/quickjs/build/qjsc";
|
||||
const BIN_OUT: &str = "/tmp/qjsrt.bin";
|
||||
|
||||
fn main() {
|
||||
let args = std::env::args().collect::<Vec<String>>();
|
||||
let filename = args.get(1).unwrap();
|
||||
|
||||
// Compile source file
|
||||
let output = Command::new(QJSC)
|
||||
.args(["-b", "-o", BIN_OUT])
|
||||
.arg(filename)
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
assert!(output.status.success());
|
||||
let source = std::fs::read_to_string(filename).unwrap();
|
||||
let bin = libqjs::compile(&source, filename).unwrap();
|
||||
|
||||
// Parse, then flatten
|
||||
let bin = std::fs::read(BIN_OUT).unwrap();
|
||||
let (_, parsed) = QuickJSBinary::parse(&bin).unwrap();
|
||||
let (flat_fns, atoms) = flatten(parsed);
|
||||
|
||||
@@ -65,12 +55,4 @@ fn main() {
|
||||
}
|
||||
|
||||
println!("{atoms:#?}");
|
||||
|
||||
/*println!("=== after deprototype ===");
|
||||
let deprototyped = deprototype(declassed);
|
||||
println!("{deprototyped:#?}");
|
||||
|
||||
println!("=== after declosure ===");
|
||||
let declosured = declosure(deprototyped);
|
||||
println!("{declosured:#?}");*/
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user