Add examples (#475)

This commit is contained in:
Hikaru Terazono 2020-10-17 00:15:16 +09:00 committed by GitHub
parent 1988c98f3c
commit 35dbd2c0ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 711 additions and 0 deletions

View File

@ -60,3 +60,12 @@ which = "4.0.2"
[dev-dependencies]
trybuild = "1.0.33"
[[example]]
name = "hello_world"
[[example]]
name = "shell"
[[example]]
name = "process"

42
examples/count-hosts.js Normal file
View File

@ -0,0 +1,42 @@
// Copyright 2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
function Initialize() { }
function Process(request) {
if (options.verbose) {
log("Processing " + request.host + request.path +
" from " + request.referrer + "@" + request.userAgent);
}
if (!output[request.host]) {
output[request.host] = 1;
} else {
output[request.host]++
}
}
Initialize();

65
examples/hello_world.rs Normal file
View File

@ -0,0 +1,65 @@
use rusty_v8 as v8;
fn main() {
// Initialize V8.
let platform = v8::new_default_platform().unwrap();
v8::V8::initialize_platform(platform);
v8::V8::initialize();
// Create a new Isolate and make it the current one.
let isolate = &mut v8::Isolate::new(v8::CreateParams::default());
// Create a stack-allocated handle scope.
let handle_scope = &mut v8::HandleScope::new(isolate);
// Create a new context.
let context = v8::Context::new(handle_scope);
// Enter the context for compiling and running the hello world script.
let scope = &mut v8::ContextScope::new(handle_scope, context);
// Create a string containing the JavaScript source code.
let code = v8::String::new(scope, "'Hello' + ' World!'").unwrap();
// Compile the source code.
let script = v8::Script::compile(scope, code, None).unwrap();
// Run the script to get the result.
let result = script.run(scope).unwrap();
// Convert the result to a string and print it.
let result = result.to_string(scope).unwrap();
println!("{}", result.to_rust_string_lossy(scope));
// Use the JavaScript API to generate a WebAssembly module.
//
// |bytes| contains the binary format for the following module:
//
// (func (export "add") (param i32 i32) (result i32)
// get_local 0
// get_local 1
// i32.add)
//
let c_source = r#"
let bytes = new Uint8Array([
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x07, 0x01,
0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7f, 0x03, 0x02, 0x01, 0x00, 0x07,
0x07, 0x01, 0x03, 0x61, 0x64, 0x64, 0x00, 0x00, 0x0a, 0x09, 0x01,
0x07, 0x00, 0x20, 0x00, 0x20, 0x01, 0x6a, 0x0b
]);
let module = new WebAssembly.Module(bytes);
let instance = new WebAssembly.Instance(module);
instance.exports.add(3, 4);
"#;
// Create a string containing the JavaScript source code.
let source = v8::String::new(scope, c_source).unwrap();
// Compile the source code.
let script = v8::Script::compile(scope, source, None).unwrap();
// Run the script to get the result.
let result = script.run(scope).unwrap();
// Print the result.
let result = result.to_uint32(scope).unwrap();
println!("3 + 4 = {}", result.value());
}

373
examples/process.rs Normal file
View File

@ -0,0 +1,373 @@
use rusty_v8 as v8;
use std::collections::HashMap;
use std::convert::TryFrom;
#[allow(clippy::needless_pass_by_value)] // this function should follow the callback type
fn log_callback(
scope: &mut v8::HandleScope,
args: v8::FunctionCallbackArguments,
mut _retval: v8::ReturnValue,
) {
let message = args
.get(0)
.to_string(scope)
.unwrap()
.to_rust_string_lossy(scope);
println!("Logged: {}", message);
}
fn main() {
// Initialize V8.
let platform = v8::new_default_platform().unwrap();
v8::V8::initialize_platform(platform);
v8::V8::initialize();
// Parse options.
let (options, file) = parse_args();
if file.is_empty() {
panic!("no script was specified");
}
let mut isolate = v8::Isolate::new(v8::CreateParams::default());
let mut scope = v8::HandleScope::new(&mut isolate);
let source = std::fs::read_to_string(&file)
.unwrap_or_else(|err| panic!("failed to open {}: {}", file, err));
let source = v8::String::new(&mut scope, &source).unwrap();
let mut processor = JsHttpRequestProcessor::new(&mut scope, source, options);
let requests = vec![
StringHttpRequest::new("/process.cc", "localhost", "google.com", "firefox"),
StringHttpRequest::new("/", "localhost", "google.net", "firefox"),
StringHttpRequest::new("/", "localhost", "google.org", "safari"),
StringHttpRequest::new("/", "localhost", "yahoo.com", "ie"),
StringHttpRequest::new("/", "localhost", "yahoo.com", "safari"),
StringHttpRequest::new("/", "localhost", "yahoo.com", "firefox"),
];
for req in requests {
processor.process(req);
}
processor.print_output();
}
fn parse_args() -> (HashMap<String, String>, String) {
use std::env;
let args: Vec<String> = env::args().collect();
let mut options = HashMap::new();
let mut file = String::new();
for arg in &args {
if let Some(pos) = arg.find('=') {
let (key, value) = arg.split_at(pos);
let value = &value[1..];
options.insert(key.into(), value.into());
} else {
file = arg.into();
}
}
(options, file)
}
/// A simplified HTTP request.
trait HttpRequest {
fn path(&self) -> &str;
fn referrer(&self) -> &str;
fn host(&self) -> &str;
fn user_agent(&self) -> &str;
}
/// A simplified HTTP request.
struct StringHttpRequest {
pub path: String,
pub referrer: String,
pub host: String,
pub user_agent: String,
}
impl StringHttpRequest {
/// Creates a `StringHttpRequest`.
pub fn new(
path: impl Into<String>,
referrer: impl Into<String>,
host: impl Into<String>,
user_agent: impl Into<String>,
) -> Self {
Self {
path: path.into(),
referrer: referrer.into(),
host: host.into(),
user_agent: user_agent.into(),
}
}
}
impl HttpRequest for StringHttpRequest {
fn path(&self) -> &str {
&self.path
}
fn referrer(&self) -> &str {
&self.referrer
}
fn host(&self) -> &str {
&self.host
}
fn user_agent(&self) -> &str {
&self.user_agent
}
}
/// An http request processor that is scriptable using JavaScript.
struct JsHttpRequestProcessor<'s, 'i> {
context: v8::Local<'s, v8::Context>,
context_scope: v8::ContextScope<'i, v8::HandleScope<'s>>,
process_fn: Option<v8::Local<'s, v8::Function>>,
request_template: v8::Global<v8::ObjectTemplate>,
_map_template: Option<v8::Global<v8::ObjectTemplate>>,
}
impl<'s, 'i> JsHttpRequestProcessor<'s, 'i>
where
's: 'i,
{
/// Creates a scriptable HTTP request processor.
pub fn new(
isolate_scope: &'i mut v8::HandleScope<'s, ()>,
source: v8::Local<'s, v8::String>,
options: HashMap<String, String>,
) -> Self {
let global = v8::ObjectTemplate::new(isolate_scope);
global.set(
v8::String::new(isolate_scope, "log").unwrap().into(),
v8::FunctionTemplate::new(isolate_scope, log_callback).into(),
);
let context = v8::Context::new_from_template(isolate_scope, global);
let mut context_scope = v8::ContextScope::new(isolate_scope, context);
let request_template = v8::ObjectTemplate::new(&mut context_scope);
request_template.set_internal_field_count(1);
// make it global
let request_template =
v8::Global::new(&mut context_scope, request_template);
let mut self_ = JsHttpRequestProcessor {
context,
context_scope,
process_fn: None,
request_template,
_map_template: None,
};
// loads options and output
let options = self_.wrap_map(options);
let options_str =
v8::String::new(&mut self_.context_scope, "options").unwrap();
self_.context.global(&mut self_.context_scope).set(
&mut self_.context_scope,
options_str.into(),
options.into(),
);
let output = v8::Object::new(&mut self_.context_scope);
let output_str =
v8::String::new(&mut self_.context_scope, "output").unwrap();
self_.context.global(&mut self_.context_scope).set(
&mut self_.context_scope,
output_str.into(),
output.into(),
);
// execute script
self_.execute_script(source);
let process_str =
v8::String::new(&mut self_.context_scope, "Process").unwrap();
let process_fn = self_
.context
.global(&mut self_.context_scope)
.get(&mut self_.context_scope, process_str.into())
.expect("missing function Process");
let process_fn = v8::Local::<v8::Function>::try_from(process_fn)
.expect("function expected");
self_.process_fn = Some(process_fn);
self_
}
fn execute_script(&mut self, script: v8::Local<'s, v8::String>) {
let scope = &mut v8::HandleScope::new(&mut self.context_scope);
let try_catch = &mut v8::TryCatch::new(scope);
let script = v8::Script::compile(try_catch, script, None)
.expect("failed to compile script");
if script.run(try_catch).is_none() {
let exception = try_catch.exception().unwrap();
let exception_string = exception
.to_string(try_catch)
.unwrap()
.to_rust_string_lossy(try_catch);
panic!("{}", exception_string);
}
}
/// Processes the given HTTP request.
pub fn process<R>(&mut self, request: R)
where
R: HttpRequest + 'static,
{
let request: Box<dyn HttpRequest> = Box::new(request);
let request = self.wrap_request(request);
let scope = &mut v8::HandleScope::new(&mut self.context_scope);
let try_catch = &mut v8::TryCatch::new(scope);
let process_fn = self.process_fn.as_mut().unwrap();
let global = self.context.global(try_catch).into();
if process_fn
.call(try_catch, global, &[request.into()])
.is_none()
{
let exception = try_catch.exception().unwrap();
let exception_string = exception
.to_string(try_catch)
.unwrap()
.to_rust_string_lossy(try_catch);
panic!("{}", exception_string);
}
}
/// Utility function that wraps a http request object in a JavaScript object.
fn wrap_request(
&mut self,
request: Box<dyn HttpRequest>,
) -> v8::Local<'s, v8::Object> {
// TODO: fix memory leak
use std::ffi::c_void;
// Dobule-box to get C-sized reference of Box<dyn HttpRequest>
let request = Box::new(request);
// Local scope for temporary handles.
let scope = &mut self.context_scope;
let request_template = v8::Local::new(scope, &self.request_template);
let result = request_template.new_instance(scope).unwrap();
let external = v8::External::new(
scope,
Box::leak(request) as *mut Box<dyn HttpRequest> as *mut c_void,
);
result.set_internal_field(0, external.into());
let name = v8::String::new(scope, "path").unwrap().into();
result.set_accessor(scope, name, Self::request_prop_handler);
let name = v8::String::new(scope, "userAgent").unwrap().into();
result.set_accessor(scope, name, Self::request_prop_handler);
let name = v8::String::new(scope, "referrer").unwrap().into();
result.set_accessor(scope, name, Self::request_prop_handler);
let name = v8::String::new(scope, "host").unwrap().into();
result.set_accessor(scope, name, Self::request_prop_handler);
result
}
/// This handles the properties of `HttpRequest`
#[allow(clippy::needless_pass_by_value)] // this function should follow the callback type
fn request_prop_handler(
scope: &mut v8::HandleScope,
key: v8::Local<v8::Name>,
args: v8::PropertyCallbackArguments,
mut rv: v8::ReturnValue,
) {
let this = args.this();
let external = Self::unwrap_request(scope, this);
assert!(
!external.is_null(),
"the pointer to Box<dyn HttpRequest> should not be null"
);
let request = unsafe { &mut *external };
let key = key.to_string(scope).unwrap().to_rust_string_lossy(scope);
let value = match &*key {
"path" => request.path(),
"userAgent" => request.user_agent(),
"referrer" => request.referrer(),
"host" => request.host(),
_ => {
return;
}
};
rv.set(v8::String::new(scope, value).unwrap().into());
}
/// Utility function that extracts the http request object from a wrapper object.
fn unwrap_request<'a>(
scope: &mut v8::HandleScope,
request: v8::Local<'a, v8::Object>,
) -> *mut Box<dyn HttpRequest> {
let external = request.get_internal_field(scope, 0).unwrap();
let external = unsafe { v8::Local::<v8::External>::cast(external) };
external.value() as *mut Box<dyn HttpRequest>
}
fn wrap_map(
&mut self,
options: HashMap<String, String>,
) -> v8::Local<'s, v8::Object> {
// TODO: wrap map, not convert into Object
let scope = &mut self.context_scope;
let result = v8::Object::new(scope);
for (key, value) in options {
let key = v8::String::new(scope, &key).unwrap().into();
let value = v8::String::new(scope, &value).unwrap().into();
result.set(scope, key, value);
}
result
}
/// Prints the output.
pub fn print_output(&mut self) {
let scope = &mut v8::HandleScope::new(&mut self.context_scope);
let key = v8::String::new(scope, "output").unwrap();
let output = self
.context
.global(scope)
.get(scope, key.into())
.unwrap()
.to_object(scope)
.unwrap();
let props = output.get_property_names(scope).unwrap();
for i in 0..props.length() {
let key = props.get_index(scope, i).unwrap();
let value = output.get(scope, key).unwrap();
let key = key.to_string(scope).unwrap().to_rust_string_lossy(scope);
let value = value.to_string(scope).unwrap().to_rust_string_lossy(scope);
println!("{}: {}", key, value);
}
}
}

222
examples/shell.rs Normal file
View File

@ -0,0 +1,222 @@
use rusty_v8 as v8;
fn main() {
// Initialize V8.
let platform = v8::new_default_platform().unwrap();
v8::V8::initialize_platform(platform);
v8::V8::initialize();
// Pass command line arguments to V8.
let args: Vec<String> = std::env::args().collect();
let args = v8::V8::set_flags_from_command_line(args);
let mut run_shell_flag = args.len() == 1;
let isolate = &mut v8::Isolate::new(v8::CreateParams::default());
let handle_scope = &mut v8::HandleScope::new(isolate);
let context = v8::Context::new(handle_scope);
let context_scope = &mut v8::ContextScope::new(handle_scope, context);
let scope = &mut v8::HandleScope::new(context_scope);
run_main(scope, &*args, &mut run_shell_flag);
if run_shell_flag {
run_shell(scope);
}
}
/// Process remaining command line arguments and execute files
fn run_shell(scope: &mut v8::HandleScope) {
use std::io::{self, Write};
println!("V8 version {} [sample shell]", v8::V8::get_version());
loop {
print!("> ");
io::stdout().flush().unwrap();
let mut buf = String::new();
match io::stdin().read_line(&mut buf) {
Ok(n) => {
if n == 0 {
println!();
return;
}
execute_string(scope, &buf, "(shell)", true, true);
}
Err(error) => println!("error: {}", error),
}
}
}
/// Process remaining command line arguments and execute files
fn run_main(
scope: &mut v8::HandleScope,
args: &[String],
run_shell: &mut bool,
) {
let mut skip_next = false;
// Parse command-line arguments.
for (i, arg) in args.iter().enumerate().skip(1) {
if skip_next {
continue;
}
match &**arg {
"--shell" => {
// Enables the shell.
*run_shell = true;
}
"-f" => {
// Ignore any -f flags for compatibility with the other stand-
// alone JavaScript engines.
}
"-e" => {
// Execute script.
let script: &str = &args[i + 1];
skip_next = true;
// TODO: pump event loop (not implemented on rusty_v8?)
// while v8::Platform::pump_message_loop(&platform, isolate) {
// // do nothing
// }
execute_string(scope, script, "unnamed", false, true);
}
arg => {
if arg.starts_with("--") {
eprintln!("Warning: unknown flag {}.\nTry --help for options", arg);
continue;
}
// Use all other arguments as names of files to load and run.
let script = std::fs::read_to_string(arg).expect("failed to read file");
execute_string(scope, &script, arg, false, true);
}
}
}
}
fn execute_string(
scope: &mut v8::HandleScope,
script: &str,
filename: &str,
print_result: bool,
report_exceptions_flag: bool,
) {
let mut scope = v8::TryCatch::new(scope);
let script = v8::String::new(&mut scope, script).unwrap();
let origin = v8::ScriptOrigin::new(
v8::String::new(&mut scope, filename).unwrap().into(),
v8::Integer::new(&mut scope, 0),
v8::Integer::new(&mut scope, 0),
v8::Boolean::new(&mut scope, false),
v8::Integer::new(&mut scope, 0),
v8::undefined(&mut scope).into(),
v8::Boolean::new(&mut scope, false),
v8::Boolean::new(&mut scope, false),
v8::Boolean::new(&mut scope, false),
);
let script = if let Some(script) =
v8::Script::compile(&mut scope, script, Some(&origin))
{
script
} else {
assert!(scope.has_caught());
if report_exceptions_flag {
report_exceptions(scope);
}
return;
};
if let Some(result) = script.run(&mut scope) {
if print_result {
println!(
"{}",
result
.to_string(&mut scope)
.unwrap()
.to_rust_string_lossy(&mut scope)
);
}
} else {
assert!(scope.has_caught());
if report_exceptions_flag {
report_exceptions(scope);
}
}
}
fn report_exceptions(mut try_catch: v8::TryCatch<v8::HandleScope>) {
let exception = try_catch.exception().unwrap();
let exception_string = exception
.to_string(&mut try_catch)
.unwrap()
.to_rust_string_lossy(&mut try_catch);
let message = if let Some(message) = try_catch.message() {
message
} else {
eprintln!("{}", exception_string);
return;
};
// Print (filename):(line number): (message).
let filename = message
.get_script_resource_name(&mut try_catch)
.map_or_else(
|| "(unknown)".into(),
|s| {
s.to_string(&mut try_catch)
.unwrap()
.to_rust_string_lossy(&mut try_catch)
},
);
let line_number = message.get_line_number(&mut try_catch).unwrap_or_default();
eprintln!("{}:{}: {}", filename, line_number, exception_string);
// Print line of source code.
let source_line = message
.get_source_line(&mut try_catch)
.map(|s| {
s.to_string(&mut try_catch)
.unwrap()
.to_rust_string_lossy(&mut try_catch)
})
.unwrap();
eprintln!("{}", source_line);
// Print wavy underline (GetUnderline is deprecated).
let start_column = message.get_start_column();
let end_column = message.get_end_column();
for _ in 0..start_column {
eprint!(" ");
}
for _ in start_column..end_column {
eprint!("^");
}
eprintln!();
// Print stack trace
let stack_trace = if let Some(stack_trace) = try_catch.stack_trace() {
stack_trace
} else {
return;
};
let stack_trace = unsafe { v8::Local::<v8::String>::cast(stack_trace) };
let stack_trace = stack_trace
.to_string(&mut try_catch)
.map(|s| s.to_rust_string_lossy(&mut try_catch));
if let Some(stack_trace) = stack_trace {
eprintln!("{}", stack_trace);
}
}