Skip to content

Commit

Permalink
Merge pull request #545 from schungx/master
Browse files Browse the repository at this point in the history
Minor refactor.
  • Loading branch information
schungx committed Mar 29, 2022
2 parents d5083bc + a268105 commit 82cc4b5
Show file tree
Hide file tree
Showing 34 changed files with 328 additions and 187 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Expand Up @@ -17,6 +17,11 @@ Bug fixes
* Exporting a variable that contains a local function pointer (including anonymous function or closure) now raises a runtime error.
* Full optimization is now skipped for method calls.

New features
------------

* [Type aliases](https://doc.rust-lang.org/reference/items/type-aliases.html) in plugin modules are now used as friendly names for custom types. This makes plugin modules more self-contained when they are used to define a custom type's API.

Enhancements
------------

Expand All @@ -26,6 +31,10 @@ Enhancements
* Separation of constants in function calls is removed as its performance benefit is dubious.
* A function `sleep` is added to block the current thread by a specified number of seconds.
* `Scope::set_alias` is added to export a variable under a particular alias name.
* `starts_with` and `ends_with` are added for strings.
* Variables in modules registered via `register_global_module` can now be accessed in the global namespace.
* `Dynamic::into_read_only` is added to convert a `Dynamic` value into constant.
* `Module` now holds a collection of custom types with an API.


Version 1.5.0
Expand Down
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -61,6 +61,7 @@ Protected against attacks
* [Sand-boxed](https://rhai.rs/book/safety/sandbox.html) - the scripting engine, if declared immutable, cannot mutate the containing environment unless [explicitly permitted](https://rhai.rs/book/patterns/control.html).
* Rugged - protected against malicious attacks (such as [stack-overflow](https://rhai.rs/book/safety/max-call-stack.html), [over-sized data](https://rhai.rs/book/safety/max-string-size.html), and [runaway scripts](https://rhai.rs/book/safety/max-operations.html) etc.) that may come from untrusted third-party user-land scripts.
* Track script evaluation [progress](https://rhai.rs/book/safety/progress.html) and manually terminate a script run.
* Passes Miri.


For those who actually want their own language
Expand Down
2 changes: 1 addition & 1 deletion codegen/Cargo.toml
Expand Up @@ -16,7 +16,7 @@ default = []
metadata = []

[dev-dependencies]
rhai = { path = "..", version = "1.4", features = ["metadata"] }
rhai = { path = "..", version = "1.6", features = ["metadata"] }
trybuild = "1"

[dependencies]
Expand Down
8 changes: 4 additions & 4 deletions codegen/src/lib.rs
Expand Up @@ -269,7 +269,7 @@ pub fn exported_module(module_path: proc_macro::TokenStream) -> proc_macro::Toke
pub fn combine_with_exported_module(args: proc_macro::TokenStream) -> proc_macro::TokenStream {
match crate::register::parse_register_macro(args) {
Ok((module_expr, _export_name, module_path)) => proc_macro::TokenStream::from(quote! {
#module_path::rhai_generate_into_module(#module_expr, true);
#module_path::rhai_generate_into_module(#module_expr, true)
}),
Err(e) => e.to_compile_error().into(),
}
Expand Down Expand Up @@ -303,7 +303,7 @@ pub fn register_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenS
Ok((engine_expr, export_name, rust_mod_path)) => {
let gen_mod_path = crate::register::generated_module_path(&rust_mod_path);
proc_macro::TokenStream::from(quote! {
#engine_expr.register_result_fn(#export_name, #gen_mod_path::dynamic_result_fn);
#engine_expr.register_result_fn(#export_name, #gen_mod_path::dynamic_result_fn)
})
}
Err(e) => e.to_compile_error().into(),
Expand Down Expand Up @@ -352,7 +352,7 @@ pub fn set_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream
#module_expr.set_fn(#export_name, FnNamespace::Internal, FnAccess::Public,
#param_names,
&#gen_mod_path::Token::param_types(),
#gen_mod_path::Token().into());
#gen_mod_path::Token().into())
})
}
Err(e) => e.to_compile_error().into(),
Expand Down Expand Up @@ -401,7 +401,7 @@ pub fn set_exported_global_fn(args: proc_macro::TokenStream) -> proc_macro::Toke
#module_expr.set_fn(#export_name, FnNamespace::Global, FnAccess::Public,
#param_names,
&#gen_mod_path::Token::param_types(),
#gen_mod_path::Token().into());
#gen_mod_path::Token().into())
})
}
Err(e) => e.to_compile_error().into(),
Expand Down
30 changes: 13 additions & 17 deletions codegen/src/module.rs
Expand Up @@ -140,39 +140,35 @@ impl Parse for Module {
for item in content.iter() {
match item {
syn::Item::Const(syn::ItemConst {
vis,
vis: syn::Visibility::Public(..),
ref expr,
ident,
attrs,
ty,
..
}) if matches!(vis, syn::Visibility::Public(..)) => {
consts.push(ExportedConst {
name: ident.to_string(),
typ: ty.clone(),
expr: expr.as_ref().clone(),
cfg_attrs: crate::attrs::collect_cfg_attr(&attrs),
})
}
}) => consts.push(ExportedConst {
name: ident.to_string(),
typ: ty.clone(),
expr: expr.as_ref().clone(),
cfg_attrs: crate::attrs::collect_cfg_attr(&attrs),
}),
_ => {}
}
}
// Gather and parse type definitions.
for item in content.iter() {
match item {
syn::Item::Type(syn::ItemType {
vis,
vis: syn::Visibility::Public(..),
ident,
attrs,
ty,
..
}) if matches!(vis, syn::Visibility::Public(..)) => {
custom_types.push(ExportedType {
name: ident.to_string(),
typ: ty.clone(),
cfg_attrs: crate::attrs::collect_cfg_attr(&attrs),
})
}
}) => custom_types.push(ExportedType {
name: ident.to_string(),
typ: ty.clone(),
cfg_attrs: crate::attrs::collect_cfg_attr(&attrs),
}),
_ => {}
}
}
Expand Down
6 changes: 2 additions & 4 deletions src/api/call_fn.rs
Expand Up @@ -178,7 +178,7 @@ impl Engine {
#[cfg(not(feature = "no_closure"))]
crate::func::call::ensure_no_data_race(name, &mut args, false)?;

let result = self.call_script_fn(
self.call_script_fn(
scope,
global,
state,
Expand All @@ -189,8 +189,6 @@ impl Engine {
rewind_scope,
Position::NONE,
0,
);

result
)
}
}
56 changes: 33 additions & 23 deletions src/api/compile.rs
Expand Up @@ -115,33 +115,34 @@ impl Engine {

let mut ast = self.compile_scripts_with_scope(scope, &[script])?;

if let Some(ref module_resolver) = self.module_resolver {
let mut resolver = StaticModuleResolver::new();
let mut imports = BTreeSet::new();
let mut resolver = StaticModuleResolver::new();
let mut imports = BTreeSet::new();

collect_imports(&ast, &resolver, &mut imports);
collect_imports(&ast, &resolver, &mut imports);

if !imports.is_empty() {
while let Some(path) = imports.iter().next() {
let path = path.clone();
if !imports.is_empty() {
while let Some(path) = imports.iter().next() {
let path = path.clone();

match module_resolver.resolve_ast(self, None, &path, crate::Position::NONE) {
Some(Ok(module_ast)) => {
collect_imports(&module_ast, &resolver, &mut imports)
}
Some(err) => return err,
None => (),
}
match self
.module_resolver
.resolve_ast(self, None, &path, crate::Position::NONE)
{
Some(Ok(module_ast)) => collect_imports(&module_ast, &resolver, &mut imports),
Some(err) => return err,
None => (),
}

let module =
module_resolver.resolve(self, None, &path, crate::Position::NONE)?;
let module = shared_take_or_clone(module);
let module =
self.module_resolver
.resolve(self, None, &path, crate::Position::NONE)?;

imports.remove(&path);
resolver.insert(path, module);
}
ast.set_resolver(resolver);
let module = shared_take_or_clone(module);

imports.remove(&path);
resolver.insert(path, module);
}
ast.set_resolver(resolver);
}

Ok(ast)
Expand Down Expand Up @@ -200,7 +201,11 @@ impl Engine {
scope: &Scope,
scripts: impl AsRef<[S]>,
) -> ParseResult<AST> {
self.compile_with_scope_and_optimization_level(scope, scripts, self.optimization_level)
self.compile_with_scope_and_optimization_level(
scope,
scripts,
self.options.optimization_level,
)
}
/// Join a list of strings and compile into an [`AST`] using own scope at a specific optimization level.
///
Expand Down Expand Up @@ -296,7 +301,12 @@ impl Engine {

let mut peekable = stream.peekable();
let mut state = ParseState::new(self, tokenizer_control);
self.parse_global_expr(&mut peekable, &mut state, scope, self.optimization_level)
self.parse_global_expr(
&mut peekable,
&mut state,
scope,
self.options.optimization_level,
)
}
/// Parse a JSON string into an [object map][crate::Map].
/// This is a light-weight alternative to using, say,
Expand Down
6 changes: 3 additions & 3 deletions src/api/eval.rs
Expand Up @@ -67,7 +67,7 @@ impl Engine {
let ast = self.compile_with_scope_and_optimization_level(
scope,
&[script],
self.optimization_level,
self.options.optimization_level,
)?;
self.eval_ast_with_scope(scope, &ast)
}
Expand Down Expand Up @@ -227,9 +227,9 @@ impl Engine {
ast.as_ref(),
];
let lib = if lib.first().map(|m: &&Module| m.is_empty()).unwrap_or(true) {
&lib[0..0]
&[]
} else {
&lib
&lib[..]
};
self.eval_global_statements(scope, global, &mut state, statements, lib, level)
}
Expand Down
4 changes: 2 additions & 2 deletions src/api/events.rs
Expand Up @@ -282,7 +282,7 @@ impl Engine {
/// ```
#[inline(always)]
pub fn on_print(&mut self, callback: impl Fn(&str) + SendSync + 'static) -> &mut Self {
self.print = Some(Box::new(callback));
self.print = Box::new(callback);
self
}
/// Override default action of `debug` (print to stdout using [`println!`])
Expand Down Expand Up @@ -332,7 +332,7 @@ impl Engine {
&mut self,
callback: impl Fn(&str, Option<&str>, Position) + SendSync + 'static,
) -> &mut Self {
self.debug = Some(Box::new(callback));
self.debug = Box::new(callback);
self
}
/// _(debugging)_ Register a callback for debugging.
Expand Down
2 changes: 1 addition & 1 deletion src/api/mod.rs
Expand Up @@ -71,7 +71,7 @@ impl Engine {
&mut self,
resolver: impl crate::ModuleResolver + 'static,
) -> &mut Self {
self.module_resolver = Some(Box::new(resolver));
self.module_resolver = Box::new(resolver);
self
}

Expand Down
4 changes: 2 additions & 2 deletions src/api/optimize.rs
Expand Up @@ -9,7 +9,7 @@ impl Engine {
/// Not available under `no_optimize`.
#[inline(always)]
pub fn set_optimization_level(&mut self, optimization_level: OptimizationLevel) -> &mut Self {
self.optimization_level = optimization_level;
self.options.optimization_level = optimization_level;
self
}

Expand All @@ -20,7 +20,7 @@ impl Engine {
#[inline(always)]
#[must_use]
pub const fn optimization_level(&self) -> OptimizationLevel {
self.optimization_level
self.options.optimization_level
}

/// Optimize the [`AST`] with constants defined in an external Scope.
Expand Down
9 changes: 8 additions & 1 deletion src/api/options.rs
@@ -1,12 +1,14 @@
//! Settings for [`Engine`]'s language options.

use crate::Engine;
use crate::{Engine, OptimizationLevel};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;

/// A type containing all language options for the [`Engine`].
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct LanguageOptions {
/// Script optimization level.
pub optimization_level: OptimizationLevel,
/// Is `if`-expression allowed?
pub allow_if_expr: bool,
/// Is `switch` expression allowed?
Expand All @@ -33,6 +35,11 @@ impl LanguageOptions {
#[inline(always)]
pub const fn new() -> Self {
Self {
#[cfg(not(feature = "no_optimize"))]
optimization_level: OptimizationLevel::Simple,
#[cfg(feature = "no_optimize")]
optimization_level: (),

allow_if_expr: true,
allow_switch_expr: true,
allow_stmt_expr: true,
Expand Down
5 changes: 3 additions & 2 deletions src/api/register.rs
Expand Up @@ -273,7 +273,7 @@ impl Engine {
/// ```
#[inline(always)]
pub fn register_type_with_name<T: Variant + Clone>(&mut self, name: &str) -> &mut Self {
self.custom_types.add_type::<T>(name);
self.global_namespace_mut().set_custom_type::<T>(name);
self
}
/// Register a custom type for use with the [`Engine`], with a pretty-print name
Expand All @@ -289,7 +289,8 @@ impl Engine {
name: impl Into<Identifier>,
) -> &mut Self {
// Add the pretty-print type name into the map
self.custom_types.add(fully_qualified_type_path, name);
self.global_namespace_mut()
.set_custom_type_raw(fully_qualified_type_path, name);
self
}
/// Register an type iterator for an iterable type with the [`Engine`].
Expand Down
2 changes: 1 addition & 1 deletion src/api/run.rs
Expand Up @@ -30,7 +30,7 @@ impl Engine {
&mut stream.peekable(),
&mut state,
scope,
self.optimization_level,
self.options.optimization_level,
)?;

self.run_ast_with_scope(scope, &ast)
Expand Down
15 changes: 12 additions & 3 deletions src/api/type_names.rs
Expand Up @@ -97,7 +97,6 @@ impl Engine {
#[cfg(feature = "no_module")]
return None;
})
.or_else(|| self.custom_types.get(name))
.unwrap_or_else(|| map_std_type_name(name, true))
}

Expand All @@ -119,8 +118,18 @@ impl Engine {
};
}

self.custom_types
.get(name)
self.global_modules
.iter()
.find_map(|m| m.get_custom_type(name))
.or_else(|| {
#[cfg(not(feature = "no_module"))]
return self
.global_sub_modules
.iter()
.find_map(|(_, m)| m.get_custom_type(name));
#[cfg(feature = "no_module")]
return None;
})
.unwrap_or_else(|| match name {
"INT" => return type_name::<crate::INT>(),
#[cfg(not(feature = "no_float"))]
Expand Down
7 changes: 4 additions & 3 deletions src/ast/ast.rs
Expand Up @@ -150,9 +150,10 @@ impl AST {
#[inline(always)]
#[must_use]
pub fn source(&self) -> Option<&str> {
match self.source.as_str() {
"" => None,
s => Some(s),
if self.source.is_empty() {
None
} else {
Some(self.source.as_str())
}
}
/// Get a reference to the source.
Expand Down

0 comments on commit 82cc4b5

Please sign in to comment.