mirror of
https://github.com/icewind1991/ivory.git
synced 2026-06-03 18:54:07 +02:00
argument extracting basics
This commit is contained in:
parent
1e6e05c135
commit
5b95e4370a
13 changed files with 5327 additions and 10 deletions
|
|
@ -1,20 +1,38 @@
|
|||
use std::intrinsics::transmute;
|
||||
|
||||
use ivory::*;
|
||||
use ivory::externs::printf;
|
||||
use ivory::zend::{ExecuteData, Value};
|
||||
use ivory::zend::{ExecuteData, ZVal};
|
||||
|
||||
#[ivory_export]
|
||||
fn hello_other(_other: String) {
|
||||
printf(format!("Hello ",));
|
||||
printf(format!("Hello ", ));
|
||||
}
|
||||
|
||||
#[ivory_export]
|
||||
fn hello_world() {
|
||||
printf("Hello world, Rust!");
|
||||
printf("Hello world, Rust2!");
|
||||
}
|
||||
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn dump(data: *const ExecuteData, retval: *mut ZVal) {
|
||||
let data: &ExecuteData = unsafe { data.as_ref() }.unwrap();
|
||||
let num = data.num_args();
|
||||
for arg in data.args() {
|
||||
printf(format!("{}\n", unsafe { arg.as_i64() }));
|
||||
}
|
||||
}
|
||||
|
||||
const FUNCTION_META_DUMP: ::ivory::zend::FunctionMeta = ::ivory::zend::FunctionMeta {
|
||||
name: { concat!("dump", "\0").as_ptr() as *const ::libc::c_char },
|
||||
func: dump,
|
||||
args: &[],
|
||||
};
|
||||
|
||||
ivory_module!({
|
||||
name: "demo",
|
||||
version: "0.0.1",
|
||||
functions: &[hello_world, hello_other],
|
||||
functions: &[hello_world, hello_other, dump],
|
||||
info: &[("demo extension", "enabled")]
|
||||
});
|
||||
|
|
@ -11,6 +11,7 @@ edition = "2018"
|
|||
[dependencies]
|
||||
libc = "0.2.50"
|
||||
ivory-macro = { version = "0.1", path = "macro" }
|
||||
ivory-sys = { version = "7.3", path = "sys" }
|
||||
|
||||
[lib]
|
||||
name = "ivory"
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ fn export_fn(item: ItemFn) -> TokenStream {
|
|||
|
||||
quote! {
|
||||
#[no_mangle]
|
||||
pub extern "C" fn #name(data: &ExecuteData, retval: &Value) {
|
||||
pub extern "C" fn #name(data: *const ::ivory::zend::ExecuteData, retval: *mut ::ivory::zend::ZVal) {
|
||||
let result = #body;
|
||||
}
|
||||
|
||||
|
|
|
|||
4845
ivory/src/zend/bindings.rs
Normal file
4845
ivory/src/zend/bindings.rs
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,5 +1,8 @@
|
|||
pub use self::module::*;
|
||||
pub use self::function::*;
|
||||
pub use self::zval::{ExecuteData, ZVal};
|
||||
|
||||
mod module;
|
||||
mod function;
|
||||
mod zval;
|
||||
mod bindings;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use std::mem;
|
|||
use libc::*;
|
||||
|
||||
use crate::zend::function::{Function, ArgInfo};
|
||||
use crate::zend::{ExecuteData, ZVal};
|
||||
|
||||
pub(crate) type StartupFunc = extern "C" fn(type_: c_int, module_number: c_int) -> c_int;
|
||||
pub(crate) type ShutdownFunc = extern "C" fn(type_: c_int, module_number: c_int) -> c_int;
|
||||
|
|
@ -11,11 +12,7 @@ pub(crate) type InfoFunc = extern "C" fn();
|
|||
pub(crate) type GlobalsCtorFunc = extern "C" fn(global: *const c_void) -> c_void;
|
||||
pub(crate) type GlobalsDtorFunc = extern "C" fn(global: *const c_void) -> c_void;
|
||||
pub(crate) type PostDeactivateFunc = extern "C" fn() -> c_int;
|
||||
pub(crate) type HandlerFunc = extern "C" fn(execute_data: &ExecuteData, retval: &Value);
|
||||
|
||||
pub struct ExecuteData {}
|
||||
|
||||
pub struct Value {}
|
||||
pub(crate) type HandlerFunc = extern "C" fn(execute_data: *const ExecuteData, retval: *mut ZVal);
|
||||
|
||||
pub struct ModuleDep {}
|
||||
|
||||
|
|
|
|||
89
ivory/src/zend/zval.rs
Normal file
89
ivory/src/zend/zval.rs
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
use std::intrinsics::transmute;
|
||||
use std::mem::size_of;
|
||||
|
||||
use crate::externs::printf;
|
||||
|
||||
use super::bindings::{zend_execute_data, zval};
|
||||
|
||||
#[derive(Clone)]
|
||||
#[repr(transparent)]
|
||||
pub struct ZVal(zval);
|
||||
|
||||
impl ZVal {
|
||||
pub unsafe fn as_i64(&self) -> i64 {
|
||||
self.0.value.lval as i64
|
||||
//self.0.u1.type_info as i64
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct ExecuteData(zend_execute_data);
|
||||
|
||||
impl ExecuteData {
|
||||
pub fn num_args(&self) -> u32 {
|
||||
unsafe { self.0.This.u2.num_args }
|
||||
}
|
||||
|
||||
fn get_arg_base(&self) -> *const ZVal {
|
||||
let offset = (size_of::<zend_execute_data>() + size_of::<zval>() - 1) / size_of::<zval>();
|
||||
let self_ptr: *const zend_execute_data = &self.0;
|
||||
unsafe {
|
||||
transmute::<_, *const ZVal>(self_ptr).add(5)
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn get_arg(&self, i: u32) -> &ZVal {
|
||||
unsafe {
|
||||
let base = self.get_arg_base();
|
||||
let val_ptr = base.add(i as usize);
|
||||
&*val_ptr
|
||||
}
|
||||
}
|
||||
|
||||
pub fn args(&self) -> ArgIterator {
|
||||
ArgIterator {
|
||||
base: self.get_arg_base(),
|
||||
count: self.num_args(),
|
||||
item: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ArgIterator {
|
||||
base: *const ZVal,
|
||||
count: u32,
|
||||
item: u32,
|
||||
}
|
||||
|
||||
impl Iterator for ArgIterator {
|
||||
type Item = ZVal;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.item < self.count {
|
||||
let val = unsafe { &*(self.base.add(self.item as usize)) };
|
||||
self.item += 1;
|
||||
Some((*val).clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
enum ZValType {
|
||||
Undef = 0,
|
||||
Null = 1,
|
||||
False = 2,
|
||||
True = 3,
|
||||
Long = 4,
|
||||
Double = 5,
|
||||
String = 6,
|
||||
Array = 7,
|
||||
Object = 8,
|
||||
Resource = 9,
|
||||
Reference = 10,
|
||||
}
|
||||
|
||||
//impl From<ZVal> for Option<i64> {
|
||||
// fn from(val: ZVal) -> Self {}
|
||||
//}
|
||||
13
ivory/sys/Cargo.toml
Normal file
13
ivory/sys/Cargo.toml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
[package]
|
||||
description = "Rust bindings for libphp"
|
||||
name = "ivory-sys"
|
||||
version = "7.3.3"
|
||||
authors = ["Herman J. Radtke III <herman@hermanradtke.com>", "Heinz Gies <heinz@licenser.net>"]
|
||||
links = "php7"
|
||||
build = "build.rs"
|
||||
license = "MIT"
|
||||
|
||||
[build-dependencies]
|
||||
bindgen = "0.43"
|
||||
cc = "1.0"
|
||||
num_cpus="1.0"
|
||||
48
ivory/sys/README.md
Normal file
48
ivory/sys/README.md
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
# php-sys
|
||||
|
||||
Bindings to php.
|
||||
|
||||
Note: I have only tested this without ZTS.
|
||||
|
||||
## PHP
|
||||
|
||||
In order to compile php-sys, we need development headers and the libphp7 library. That library may come in the form of `libphp7.so` or `libphp7.a` depending on how you install/compile PHP.
|
||||
|
||||
### From Package
|
||||
|
||||
* For Ubuntu, please refer to the [.travis.yml](../.travis.yml) _install_ section for the commands.
|
||||
* For Mac OS X, I could not find a set of packages that worked.
|
||||
|
||||
### From Source
|
||||
|
||||
Some basic instructions on how to install PHP so you can embed it into Rust.
|
||||
|
||||
#### Mac OS X
|
||||
|
||||
I had to use brew to install bison. I believe autoconf and other tools were either already installed or provided by Mac OS X. Brew installed some modified version of libiconv which confused PHP. I also had some problems, so I stopped building xml related stuff. To build I had to do:
|
||||
|
||||
```
|
||||
$ ./genfiles
|
||||
$ ./buildconf --force
|
||||
$ PATH="/usr/local/opt/bison/bin:$PATH" ./configure --enable-debug --enable-embed=static --without-iconv --disable-libxml --disable-dom --disable-xml --disable-simplexml --disable-xmlwriter --disable-xmlreader --without-pear
|
||||
$ PATH="/usr/local/opt/bison/bin:$PATH" make
|
||||
$ PATH="/usr/local/opt/bison/bin:$PATH" make test
|
||||
```
|
||||
|
||||
Note: I embed a static library on Mac OS X. If you want to do embed PHP with a shared library, then use `--enable-embed=shared`.
|
||||
|
||||
#### Linux
|
||||
|
||||
Here are the dependencies needed (in apt-get form):
|
||||
|
||||
```bash
|
||||
$ apt-get install git make gcc libxml2-dev autoconf bison valgrind clang re2c
|
||||
```
|
||||
|
||||
```
|
||||
$ ./genfiles
|
||||
$ ./buildconf --force
|
||||
$ ./configure --enable-debug --enable-embed=shared
|
||||
$ make
|
||||
$ make test
|
||||
```
|
||||
231
ivory/sys/build.rs
Normal file
231
ivory/sys/build.rs
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
extern crate bindgen;
|
||||
extern crate cc;
|
||||
extern crate num_cpus;
|
||||
|
||||
use bindgen::callbacks::{MacroParsingBehavior, ParseCallbacks};
|
||||
use bindgen::Builder;
|
||||
use std::collections::HashSet;
|
||||
use std::env;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
const PHP_VERSION: &'static str = concat!("php-", env!("CARGO_PKG_VERSION"));
|
||||
|
||||
/// println_stderr and run_command_or_fail are copied from rdkafka-sys
|
||||
macro_rules! println_stderr(
|
||||
($($arg:tt)*) => { {
|
||||
let r = writeln!(&mut ::std::io::stderr(), $($arg)*);
|
||||
r.expect("failed printing to stderr");
|
||||
} }
|
||||
);
|
||||
|
||||
fn run_command_or_fail(dir: String, cmd: &str, args: &[&str]) {
|
||||
println_stderr!(
|
||||
"Running command: \"{} {}\" in dir: {}",
|
||||
cmd,
|
||||
args.join(" "),
|
||||
dir
|
||||
);
|
||||
let ret = Command::new(cmd).current_dir(dir).args(args).env("CC", "clang").status();
|
||||
match ret.map(|status| (status.success(), status.code())) {
|
||||
Ok((true, _)) => return,
|
||||
Ok((false, Some(c))) => panic!("Command failed with error code {}", c),
|
||||
Ok((false, None)) => panic!("Command got killed"),
|
||||
Err(e) => panic!("Command failed with error: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
fn target(path: &str) -> String {
|
||||
let osdir = env::var("PWD").unwrap();
|
||||
let pfx = match env::var("CARGO_TARGET_DIR") {
|
||||
Ok(d) => d,
|
||||
Err(_) => String::from("target"),
|
||||
};
|
||||
let profile = env::var("PROFILE").unwrap();
|
||||
format!("{}/{}/{}/native/{}", osdir, pfx, profile, path)
|
||||
}
|
||||
|
||||
fn exists(path: &str) -> bool {
|
||||
Path::new(target(path).as_str()).exists()
|
||||
}
|
||||
|
||||
/// This is needed to prevent bindgen to create multiple definitions of the same macro and fail
|
||||
#[derive(Debug)]
|
||||
struct MacroCallback {
|
||||
macros: Arc<RwLock<HashSet<String>>>,
|
||||
}
|
||||
|
||||
impl ParseCallbacks for MacroCallback {
|
||||
fn will_parse_macro(&self, name: &str) -> MacroParsingBehavior {
|
||||
self.macros.write().unwrap().insert(name.into());
|
||||
|
||||
match name {
|
||||
"FP_NAN" | "FP_INFINITE" | "FP_ZERO" | "FP_SUBNORMAL" | "FP_NORMAL" => {
|
||||
MacroParsingBehavior::Ignore
|
||||
}
|
||||
_ => MacroParsingBehavior::Default,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let cpus = format!("{}", num_cpus::get());
|
||||
#[cfg(all(target_os = "linux"))]
|
||||
let default_link_static = false;
|
||||
#[cfg(all(target_os = "macos"))]
|
||||
let default_link_static = true;
|
||||
let php_version = option_env!("PHP_VERSION").unwrap_or(PHP_VERSION);
|
||||
let macros = Arc::new(RwLock::new(HashSet::new()));
|
||||
|
||||
println!("cargo:rerun-if-env-changed=PHP_VERSION");
|
||||
println!("cargo:rerun-if-env-changed=PHP_LINK_STATIC");
|
||||
|
||||
let link_dynamic = env::var_os("PHP_LINK_DYNAMIC")
|
||||
.map(|_| true)
|
||||
.unwrap_or(false);
|
||||
let link_static = env::var_os("PHP_LINK_STATIC")
|
||||
.map(|_| true)
|
||||
.unwrap_or(default_link_static && !link_dynamic);
|
||||
|
||||
if !exists("php-src/LICENSE") {
|
||||
println_stderr!("Setting up PHP {}", php_version);
|
||||
run_command_or_fail(
|
||||
"/".to_string(),
|
||||
"mkdir",
|
||||
&[
|
||||
"-p",
|
||||
&target("")
|
||||
],
|
||||
);
|
||||
run_command_or_fail(
|
||||
target(""),
|
||||
"git",
|
||||
&[
|
||||
"clone",
|
||||
"https://github.com/php/php-src",
|
||||
format!("--branch={}", php_version).as_str(),
|
||||
],
|
||||
);
|
||||
run_command_or_fail(
|
||||
target("php-src"),
|
||||
"sed",
|
||||
&[
|
||||
"-e",
|
||||
"s/void zend_signal_startup/ZEND_API void zend_signal_startup/g",
|
||||
"-ibk",
|
||||
"Zend/zend_signal.c",
|
||||
"Zend/zend_signal.h",
|
||||
],
|
||||
);
|
||||
run_command_or_fail(target("php-src"), "./genfiles", &[]);
|
||||
run_command_or_fail(target("php-src"), "./buildconf", &["--force"]);
|
||||
|
||||
let embed_type = if link_static { "static" } else { "shared" };
|
||||
|
||||
#[cfg(all(target_os = "linux"))]
|
||||
let config = &[
|
||||
"--enable-debug",
|
||||
&format!("--enable-embed={}", embed_type),
|
||||
"--disable-cli",
|
||||
"--disable-cgi",
|
||||
"--enable-maintainer-zts",
|
||||
// "--without-iconv",
|
||||
"--disable-libxml",
|
||||
"--disable-dom",
|
||||
"--disable-xml",
|
||||
"--disable-simplexml",
|
||||
"--disable-xmlwriter",
|
||||
"--disable-xmlreader",
|
||||
// "--without-pear",
|
||||
// "--with-libdir=lib64",
|
||||
// "--with-pic",
|
||||
];
|
||||
#[cfg(all(target_os = "macos"))]
|
||||
let config = &[
|
||||
"--enable-debug",
|
||||
&format!("--enable-embed={}", embed_type),
|
||||
"--disable-cli",
|
||||
"--disable-cgi",
|
||||
"--enable-maintainer-zts",
|
||||
"--without-iconv",
|
||||
"--disable-libxml",
|
||||
"--disable-dom",
|
||||
"--disable-xml",
|
||||
"--disable-simplexml",
|
||||
"--disable-xmlwriter",
|
||||
"--disable-xmlreader",
|
||||
// "--without-pear",
|
||||
// "--with-libdir=lib64",
|
||||
// "--with-pic",
|
||||
];
|
||||
run_command_or_fail(target("php-src"), "./configure", config);
|
||||
run_command_or_fail(target("php-src"), "make", &["-j", cpus.as_str()]);
|
||||
}
|
||||
|
||||
let include_dir = target("php-src");
|
||||
let lib_dir = target("php-src/libs");
|
||||
|
||||
let link_type = if link_static { "=static" } else { "" };
|
||||
|
||||
println!("cargo:rustc-link-lib{}=php7", link_type);
|
||||
println!("cargo:rustc-link-search=native={}", lib_dir);
|
||||
|
||||
let includes = ["/", "/TSRM", "/Zend", "/main"]
|
||||
.iter()
|
||||
.map(|d| format!("-I{}{}", include_dir, d))
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let bindings = Builder::default()
|
||||
.rustfmt_bindings(true)
|
||||
.clang_args(includes)
|
||||
.whitelist_function("_zend_file_handle__bindgen_ty_1")
|
||||
.whitelist_function("php_execute_script")
|
||||
.whitelist_function("php_module_startup")
|
||||
.whitelist_function("php_request_shutdown")
|
||||
.whitelist_function("php_request_startup")
|
||||
.whitelist_function("phprpm_fopen")
|
||||
.whitelist_function("sapi_send_headers")
|
||||
.whitelist_function("sapi_startup")
|
||||
.whitelist_function("sg_request_info")
|
||||
.whitelist_function("sg_sapi_headers")
|
||||
.whitelist_function("sg_server_context")
|
||||
.whitelist_function("sg_server_context")
|
||||
.whitelist_function("sg_set_server_context")
|
||||
.whitelist_function("sg_set_server_context")
|
||||
.whitelist_function("ts_resource_ex")
|
||||
.whitelist_function("tsrm_startup")
|
||||
.whitelist_function("zend_error")
|
||||
.whitelist_function("zend_signal_startup")
|
||||
.whitelist_function("zend_tsrmls_cache_update")
|
||||
.whitelist_var("SAPI_HEADER_SENT_SUCCESSFULLY")
|
||||
.whitelist_type("sapi_headers_struc")
|
||||
.whitelist_type("sapi_module_struc")
|
||||
.whitelist_type("sapi_request_info")
|
||||
.whitelist_type("ZEND_RESULT_CODE")
|
||||
.whitelist_type("zval")
|
||||
.whitelist_type("zend_execute_data")
|
||||
.whitelist_var("zend_stream_type_ZEND_HANDLE_FP")
|
||||
.parse_callbacks(Box::new(MacroCallback {
|
||||
macros: macros.clone(),
|
||||
})).derive_default(true)
|
||||
.header("wrapper.h")
|
||||
.generate()
|
||||
.expect("Unable to generate bindings");
|
||||
|
||||
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
bindings
|
||||
.write_to_file(out_path.join("bindings.rs"))
|
||||
.expect("Couldn't write bindings!");
|
||||
cc::Build::new()
|
||||
.file("src/shim.c")
|
||||
.include(&include_dir)
|
||||
.flag("-fPIC")
|
||||
.flag("-m64")
|
||||
.include(&format!("{}/TSRM", include_dir))
|
||||
.include(&format!("{}/Zend", include_dir))
|
||||
.include(&format!("{}/main", include_dir))
|
||||
.compile("foo");
|
||||
}
|
||||
17
ivory/sys/src/lib.rs
Normal file
17
ivory/sys/src/lib.rs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#![allow(non_upper_case_globals)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
use std::os::raw::{c_char, c_uchar, c_void};
|
||||
|
||||
//extern "C" {
|
||||
//// pub fn sg_request_info() -> *mut sapi_request_info;
|
||||
// pub fn sg_server_context() -> *mut c_void;
|
||||
// pub fn sg_set_server_context(context: *mut c_void);
|
||||
//// pub fn sg_sapi_headers() -> *mut sapi_headers_struct;
|
||||
// pub fn sg_headers_sent() -> c_uchar;
|
||||
// pub fn sg_set_headers_sent(is_sent: c_uchar);
|
||||
// pub fn zend_tsrmls_cache_update();
|
||||
//// pub fn phprpm_fopen(filename: *const c_char, mode: *const c_char) -> *mut FILE;
|
||||
//}
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
||||
48
ivory/sys/src/shim.c
Normal file
48
ivory/sys/src/shim.c
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/**
|
||||
* This file defines some simple C functions to make ffi interop easier. The
|
||||
* majority of the functions are wrapping some pre-processor define that is
|
||||
* critical to PHP internals.
|
||||
*/
|
||||
|
||||
#include <Zend/zend.h>
|
||||
#include <main/php.h>
|
||||
#include <sapi/embed/php_embed.h>
|
||||
|
||||
//sapi_request_info * sg_request_info() {
|
||||
// return &SG(request_info);
|
||||
//}
|
||||
|
||||
void * sg_server_context() {
|
||||
return SG(server_context);
|
||||
}
|
||||
|
||||
void sg_set_server_context(void *context) {
|
||||
SG(server_context) = context;
|
||||
}
|
||||
|
||||
//sapi_headers_struct * sg_sapi_headers() {
|
||||
// return &SG(sapi_headers);
|
||||
//}
|
||||
|
||||
unsigned char sg_headers_sent() {
|
||||
return SG(headers_sent);
|
||||
}
|
||||
|
||||
void sg_set_headers_sent(unsigned char is_sent) {
|
||||
SG(headers_sent) = is_sent;
|
||||
}
|
||||
|
||||
void zend_tsrmls_cache_update() {
|
||||
ZEND_TSRMLS_CACHE_UPDATE();
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a wrapper around fopen
|
||||
*
|
||||
* I tried to use libc, but libc defines FILE different from the php bindings
|
||||
* generated by bindgen. Thus, it was easier to wrap this and expose it in Rust
|
||||
* via an extern fn definition.
|
||||
*/
|
||||
FILE *phprpm_fopen(const char *filename, const char *mode) {
|
||||
return fopen(filename, mode);
|
||||
}
|
||||
7
ivory/sys/wrapper.h
Normal file
7
ivory/sys/wrapper.h
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#ifndef PHP_RS_WRAPPER_H
|
||||
#define PHP_RS_WRAPPER_H
|
||||
#include <Zend/zend.h>
|
||||
#include <Zend/zend_compile.h>
|
||||
//#include <main/php.h>
|
||||
//#include <sapi/embed/php_embed.h>
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue