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 demutate;
|
||||||
mod flatten;
|
mod flatten;
|
||||||
mod infer;
|
mod infer;
|
||||||
|
mod libqjs;
|
||||||
mod parse;
|
mod parse;
|
||||||
mod regify;
|
mod regify;
|
||||||
|
|
||||||
use std::process::Command;
|
|
||||||
|
|
||||||
use declass::declass;
|
use declass::declass;
|
||||||
use declosure::declosure;
|
use declosure::declosure;
|
||||||
use demutate::demutate;
|
use demutate::demutate;
|
||||||
@@ -16,24 +15,15 @@ use infer::infer;
|
|||||||
use parse::QuickJSBinary;
|
use parse::QuickJSBinary;
|
||||||
use regify::regify;
|
use regify::regify;
|
||||||
|
|
||||||
const QJSC: &str = "../third_party/quickjs/build/qjsc";
|
|
||||||
const BIN_OUT: &str = "/tmp/qjsrt.bin";
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let args = std::env::args().collect::<Vec<String>>();
|
let args = std::env::args().collect::<Vec<String>>();
|
||||||
let filename = args.get(1).unwrap();
|
let filename = args.get(1).unwrap();
|
||||||
|
|
||||||
// Compile source file
|
// Compile source file
|
||||||
let output = Command::new(QJSC)
|
let source = std::fs::read_to_string(filename).unwrap();
|
||||||
.args(["-b", "-o", BIN_OUT])
|
let bin = libqjs::compile(&source, filename).unwrap();
|
||||||
.arg(filename)
|
|
||||||
.output()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert!(output.status.success());
|
|
||||||
|
|
||||||
// Parse, then flatten
|
// Parse, then flatten
|
||||||
let bin = std::fs::read(BIN_OUT).unwrap();
|
|
||||||
let (_, parsed) = QuickJSBinary::parse(&bin).unwrap();
|
let (_, parsed) = QuickJSBinary::parse(&bin).unwrap();
|
||||||
let (flat_fns, atoms) = flatten(parsed);
|
let (flat_fns, atoms) = flatten(parsed);
|
||||||
|
|
||||||
@@ -65,12 +55,4 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
println!("{atoms:#?}");
|
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