Fix unsafe violation in for loop.

This commit is contained in:
Stephen Chung 2022-01-15 10:18:16 +08:00
parent 2677e7dc8a
commit a8ec984b0f
3 changed files with 34 additions and 11 deletions

View File

@ -11,6 +11,7 @@ Bug fixes
* `chop` for arrays and BLOB's now works properly. * `chop` for arrays and BLOB's now works properly.
* `set_bit` for bit-flags with negative index now works correctly. * `set_bit` for bit-flags with negative index now works correctly.
* Misnamed `params` field `name` in the JSON output of `Engine::gen_fn_metadata_to_json` is fixed (was incorrectly named `type`). * Misnamed `params` field `name` in the JSON output of `Engine::gen_fn_metadata_to_json` is fixed (was incorrectly named `type`).
* Fixes a potential `unsafe` violation in `for` loop.
Enhancements Enhancements
------------ ------------

View File

@ -464,7 +464,7 @@ impl Engine {
// For loop // For loop
Stmt::For(expr, x, _) => { Stmt::For(expr, x, _) => {
let (Ident { name, .. }, counter, statements) = x.as_ref(); let (Ident { name: var_name, .. }, counter, statements) = x.as_ref();
let iter_obj = self let iter_obj = self
.eval_expr(scope, global, state, lib, this_ptr, expr, level)? .eval_expr(scope, global, state, lib, this_ptr, expr, level)?
.flatten(); .flatten();
@ -492,14 +492,23 @@ impl Engine {
// Add the loop variables // Add the loop variables
let orig_scope_len = scope.len(); let orig_scope_len = scope.len();
let counter_index = if let Some(counter) = counter { let counter_index = if let Some(counter) = counter {
scope.push(unsafe_cast_var_name_to_lifetime(&counter.name), 0 as INT); // Loop variables are always removed at the end of the statement
// so this cast is safe.
let counter_name = unsafe_cast_var_name_to_lifetime(&counter.name);
scope.push(counter_name, 0 as INT);
scope.len() - 1 scope.len() - 1
} else { } else {
usize::MAX usize::MAX
}; };
scope.push(unsafe_cast_var_name_to_lifetime(name), ());
// Loop variables are always removed at the end of the statement
// so this cast is safe.
let var_name = unsafe_cast_var_name_to_lifetime(var_name);
scope.push(var_name, ());
let index = scope.len() - 1; let index = scope.len() - 1;
let mut loop_result = Ok(Dynamic::UNIT);
for (x, iter_value) in func(iter_obj).enumerate() { for (x, iter_value) in func(iter_obj).enumerate() {
// Increment counter // Increment counter
if counter_index < usize::MAX { if counter_index < usize::MAX {
@ -546,7 +555,12 @@ impl Engine {
} }
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
self.inc_operations(&mut global.num_operations, statements.position())?; if let Err(err) =
self.inc_operations(&mut global.num_operations, statements.position())
{
loop_result = Err(err);
break;
}
if statements.is_empty() { if statements.is_empty() {
continue; continue;
@ -567,7 +581,8 @@ impl Engine {
} }
scope.rewind(orig_scope_len); scope.rewind(orig_scope_len);
Ok(Dynamic::UNIT)
loop_result
} else { } else {
Err(ERR::ErrorFor(expr.position()).into()) Err(ERR::ErrorFor(expr.position()).into())
} }
@ -580,7 +595,7 @@ impl Engine {
// Try/Catch statement // Try/Catch statement
Stmt::TryCatch(x, _) => { Stmt::TryCatch(x, _) => {
let (try_stmt, err_var, catch_stmt) = x.as_ref(); let (try_stmt, err_var_name, catch_stmt) = x.as_ref();
let result = self let result = self
.eval_stmt_block(scope, global, state, lib, this_ptr, try_stmt, true, level) .eval_stmt_block(scope, global, state, lib, this_ptr, try_stmt, true, level)
@ -630,8 +645,11 @@ impl Engine {
let orig_scope_len = scope.len(); let orig_scope_len = scope.len();
err_var.as_ref().map(|Ident { name, .. }| { err_var_name.as_ref().map(|Ident { name, .. }| {
scope.push(unsafe_cast_var_name_to_lifetime(name), err_value) // Catch error variables are always removed from after the block
// so this cast is safe.
let var_name = unsafe_cast_var_name_to_lifetime(name);
scope.push(var_name, err_value)
}); });
let result = self.eval_stmt_block( let result = self.eval_stmt_block(

View File

@ -74,14 +74,18 @@ impl Engine {
let orig_mods_len = global.num_imported_modules(); let orig_mods_len = global.num_imported_modules();
// Put arguments into scope as variables // Put arguments into scope as variables
// Actually consume the arguments instead of cloning them
scope.extend( scope.extend(
fn_def fn_def
.params .params
.iter() .iter()
.zip(args.iter_mut().map(|v| mem::take(*v))) .zip(args.into_iter().map(|v| {
// Actually consume the arguments instead of cloning them
mem::take(*v)
}))
.map(|(name, value)| { .map(|(name, value)| {
let var_name: std::borrow::Cow<'_, str> = // Arguments are always removed at the end of the call,
// so this cast is safe.
let var_name: std::borrow::Cow<_> =
unsafe_cast_var_name_to_lifetime(name).into(); unsafe_cast_var_name_to_lifetime(name).into();
(var_name, value) (var_name, value)
}), }),