Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Minor fixups for string slice selector. #846

Merged
merged 4 commits into from
Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ Deprecated API's
New features
------------

* Sub-strings can now be selected from full strings by indexing via ranges (e.g. `s[1..4]`).
* New options `Engine::set_max_strings_interned` and `Engine::max_strings_interned` are added to limit the maximum number of strings interned in the `Engine`'s string interner.
* A new advanced callback can now be registered (gated under the `internals` feature), `Engine::on_invalid_array_index`, to handle access to missing properties in object maps.
* A new advanced callback can now be registered (gated under the `internals` feature), `Engine::on_missing_map_property`, to handle out-of-bound index into arrays.
* A new advanced callback, `Engine::on_invalid_array_index`, is added (gated under the `internals` feature) to handle access to missing properties in object maps.
* A new advanced callback, `Engine::on_missing_map_property`, is added (gated under the `internals` feature) to handle out-of-bound index into arrays.

Enhancements
------------
Expand Down
Binary file not shown.
1 change: 0 additions & 1 deletion fuzz/clusterfuzz-testcase-scripting-4756460147900416

This file was deleted.

4 changes: 1 addition & 3 deletions src/api/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,7 @@ impl Engine {
global.lib.push(ast.shared_lib().clone());

#[cfg(not(feature = "no_module"))]
{
global.embedded_module_resolver = ast.resolver.clone();
}
global.embedded_module_resolver.clone_from(&ast.resolver);

let _ = self.eval_global_statements(global, caches, scope, ast.statements(), true)?;

Expand Down
6 changes: 3 additions & 3 deletions src/ast/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -479,8 +479,8 @@ impl AST {
other.resolver.as_deref().map_or(true, |r| r.is_empty()),
) {
(true, true) => (),
(false, true) => _ast.resolver = self.resolver.clone(),
(true, false) => _ast.resolver = other.resolver.clone(),
(false, true) => _ast.resolver.clone_from(&self.resolver),
(true, false) => _ast.resolver.clone_from(&other.resolver),
(false, false) => {
let mut resolver = self.resolver.as_deref().unwrap().clone();
for (k, v) in other.resolver.as_deref().unwrap() {
Expand Down Expand Up @@ -573,7 +573,7 @@ impl AST {
other.resolver.as_deref().map_or(true, |r| r.is_empty()),
) {
(_, true) => (),
(true, false) => self.resolver = other.resolver.clone(),
(true, false) => self.resolver.clone_from(&other.resolver),
(false, false) => {
let resolver = crate::func::shared_make_mut(self.resolver.as_mut().unwrap());
let other_resolver = crate::func::shared_take_or_clone(other.resolver.unwrap());
Expand Down
6 changes: 6 additions & 0 deletions src/ast/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,9 @@ impl Expr {
(Self::IntegerConstant(ref start, ..), Self::Unit(..)) => {
(*start..INT::MAX).into()
}
(Self::Unit(..), Self::IntegerConstant(ref start, ..)) => {
(0..*start).into()
}
_ => return None,
},
// x..=y
Expand All @@ -544,6 +547,9 @@ impl Expr {
(Self::IntegerConstant(ref start, ..), Self::Unit(..)) => {
(*start..=INT::MAX).into()
}
(Self::Unit(..), Self::IntegerConstant(ref start, ..)) => {
(0..=*start).into()
}
_ => return None,
},
_ => return None,
Expand Down
135 changes: 65 additions & 70 deletions src/eval/chaining.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@
use super::{Caches, GlobalRuntimeState, Target};
use crate::ast::{ASTFlags, BinaryExpr, Expr, OpAssignment};
use crate::engine::{FN_IDX_GET, FN_IDX_SET};
use crate::tokenizer::Token;
use crate::types::dynamic::Union;
use crate::{
calc_fn_hash, Dynamic, Engine, FnArgsVec, OnceCell, Position, RhaiResult, RhaiResultOf, Scope,
ERR,
calc_fn_hash, Dynamic, Engine, ExclusiveRange, FnArgsVec, InclusiveRange, OnceCell, Position,

Check warning on line 9 in src/eval/chaining.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, --features testing-environ,no_index,serde,metadata,internals,debugging, sta...

unused imports: `ExclusiveRange`, `InclusiveRange`
RhaiResult, RhaiResultOf, Scope, ERR,
};
use std::hash::Hash;
#[cfg(feature = "no_std")]
Expand Down Expand Up @@ -347,75 +346,71 @@
index: offset,
})
}
Err(typ) => {
if typ == "core::ops::range::Range<i64>" {
// val_str[range]
let idx = idx.read_lock::<core::ops::Range<i64>>().unwrap();
let range = &*idx;
let chars_count = s.chars().count();
let start = if range.start >= 0 {
range.start as usize
} else {
super::calc_index(chars_count, range.start, true, || {
ERR::ErrorStringBounds(chars_count, range.start, idx_pos).into()
})?
};
let end = if range.end >= 0 {
range.end as usize
} else {
super::calc_index(chars_count, range.end, true, || {
ERR::ErrorStringBounds(chars_count, range.end, idx_pos).into()
})
.unwrap_or(0)
};

let take = if end > start { end - start } else { 0 };

let value = s.chars().skip(start).take(take).collect::<String>();
return Ok(Target::StringSlice {
source: target,
value: value.into(),
start: start,
end: end,
exclusive: true,
});
} else if typ == "core::ops::range::RangeInclusive<i64>" {
// val_str[range]
let idx = idx.read_lock::<core::ops::RangeInclusive<i64>>().unwrap();
let range = &*idx;
let chars_count = s.chars().count();
let start = if *range.start() >= 0 {
*range.start() as usize
} else {
super::calc_index(chars_count, *range.start(), true, || {
ERR::ErrorStringBounds(chars_count, *range.start(), idx_pos)
.into()
})?
};
let end = if *range.end() >= 0 {
*range.end() as usize
} else {
super::calc_index(chars_count, *range.end(), true, || {
ERR::ErrorStringBounds(chars_count, *range.end(), idx_pos)
.into()
})
.unwrap_or(0)
};
Err(typ) if typ == std::any::type_name::<ExclusiveRange>() => {
// val_str[range]
let idx = idx.read_lock::<ExclusiveRange>().unwrap();
let range = &*idx;
let chars_count = s.chars().count();
let start = if range.start >= 0 {
range.start as usize
} else {
super::calc_index(chars_count, range.start, true, || {
ERR::ErrorStringBounds(chars_count, range.start, idx_pos).into()
})?
};
let end = if range.end >= 0 {
range.end as usize
} else {
super::calc_index(chars_count, range.end, true, || {
ERR::ErrorStringBounds(chars_count, range.end, idx_pos).into()
})
.unwrap_or(0)
};

let take = if end > start { end - start + 1 } else { 0 };
let take = if end > start { end - start } else { 0 };
let value = s.chars().skip(start).take(take).collect::<String>();

let value = s.chars().skip(start).take(take).collect::<String>();
return Ok(Target::StringSlice {
source: target,
value: value.into(),
start,
end,
exclusive: false,
});
Ok(Target::StringSlice {
source: target,
value: value.into(),
start: start,
end: end,
exclusive: true,
})
}
Err(typ) if typ == std::any::type_name::<InclusiveRange>() => {
// val_str[range]
let idx = idx.read_lock::<InclusiveRange>().unwrap();
let range = &*idx;
let chars_count = s.chars().count();
let start = if *range.start() >= 0 {
*range.start() as usize
} else {
return Err(self.make_type_mismatch_err::<crate::INT>(typ, idx_pos));
}
super::calc_index(chars_count, *range.start(), true, || {
ERR::ErrorStringBounds(chars_count, *range.start(), idx_pos).into()
})?
};
let end = if *range.end() >= 0 {
*range.end() as usize
} else {
super::calc_index(chars_count, *range.end(), true, || {
ERR::ErrorStringBounds(chars_count, *range.end(), idx_pos).into()
})
.unwrap_or(0)
};

let take = if end > start { end - start + 1 } else { 0 };
let value = s.chars().skip(start).take(take).collect::<String>();

Ok(Target::StringSlice {
source: target,
value: value.into(),
start,
end,
exclusive: false,
})
}
Err(typ) => Err(self.make_type_mismatch_err::<crate::INT>(typ, idx_pos)),
}
}

Expand Down Expand Up @@ -476,8 +471,8 @@
}
#[cfg(not(feature = "no_index"))]
(Expr::FnCall(fnc, _), ChainType::Indexing)
if fnc.op_token == Some(Token::InclusiveRange)
|| fnc.op_token == Some(Token::ExclusiveRange) =>
if fnc.op_token == Some(crate::tokenizer::Token::InclusiveRange)
|| fnc.op_token == Some(crate::tokenizer::Token::ExclusiveRange) =>
{
idx_values.push(rhs.get_literal_value().unwrap())
}
Expand Down
45 changes: 16 additions & 29 deletions src/eval/target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,14 +189,13 @@ impl<'a> Target<'a> {
Self::RefMut(..) => true,
#[cfg(not(feature = "no_closure"))]
Self::SharedValue { .. } => true,
#[cfg(not(feature = "no_index"))]
Self::StringSlice { .. } => true,
Self::TempValue(..) => false,
#[cfg(not(feature = "no_index"))]
Self::Bit { .. }
| Self::BitField { .. }
| Self::BlobByte { .. }
| Self::StringChar { .. } => false,
| Self::StringChar { .. }
| Self::StringSlice { .. } => false,
}
}
/// Is the [`Target`] a temp value?
Expand All @@ -207,14 +206,13 @@ impl<'a> Target<'a> {
Self::RefMut(..) => false,
#[cfg(not(feature = "no_closure"))]
Self::SharedValue { .. } => false,
#[cfg(not(feature = "no_index"))]
Self::StringSlice { .. } => true,
Self::TempValue(..) => true,
#[cfg(not(feature = "no_index"))]
Self::Bit { .. }
| Self::BitField { .. }
| Self::BlobByte { .. }
| Self::StringChar { .. } => false,
| Self::StringChar { .. }
| Self::StringSlice { .. } => false,
}
}
/// Is the [`Target`] a shared value?
Expand All @@ -225,13 +223,13 @@ impl<'a> Target<'a> {
return match self {
Self::RefMut(r) => r.is_shared(),
Self::SharedValue { .. } => true,
Self::StringSlice { .. } => true,
Self::TempValue(value) => value.is_shared(),
#[cfg(not(feature = "no_index"))]
Self::Bit { .. }
| Self::BitField { .. }
| Self::BlobByte { .. }
| Self::StringChar { .. } => false,
| Self::StringChar { .. }
| Self::StringSlice { .. } => false,
};
#[cfg(feature = "no_closure")]
return false;
Expand All @@ -245,18 +243,11 @@ impl<'a> Target<'a> {
Self::SharedValue { shared_value, .. } => shared_value, // Original shared value is simply taken
Self::TempValue(value) => value, // Owned value is simply taken
#[cfg(not(feature = "no_index"))]
Self::Bit { value, .. } => value, // boolean is taken
#[cfg(not(feature = "no_index"))]
Self::BitField { value, .. } => value, // INT is taken
#[cfg(not(feature = "no_index"))]
Self::BlobByte { value, .. } => value, // byte is taken
#[cfg(not(feature = "no_index"))]
Self::StringChar { value, .. } => value, // char is taken
#[cfg(not(feature = "no_index"))]
Self::StringSlice { value, .. } => {
// Slice of a string is cloned
Dynamic::from(value.to_string())
}
Self::Bit { value, .. }
| Self::BitField { value, .. }
| Self::BlobByte { value, .. }
| Self::StringChar { value, .. }
| Self::StringSlice { value, .. } => value, // Intermediate value is simply taken
}
}
/// Take a `&mut Dynamic` reference from the `Target`.
Expand Down Expand Up @@ -288,15 +279,11 @@ impl<'a> Target<'a> {
Self::SharedValue { guard, .. } => guard,
Self::TempValue(value) => value,
#[cfg(not(feature = "no_index"))]
Self::Bit { source, .. } => source,
#[cfg(not(feature = "no_index"))]
Self::BitField { source, .. } => source,
#[cfg(not(feature = "no_index"))]
Self::BlobByte { source, .. } => source,
#[cfg(not(feature = "no_index"))]
Self::StringChar { source, .. } => source,
#[cfg(not(feature = "no_index"))]
Self::StringSlice { source, .. } => source,
Self::Bit { source, .. }
| Self::BitField { source, .. }
| Self::BlobByte { source, .. }
| Self::StringChar { source, .. }
| Self::StringSlice { source, .. } => source,
}
}
/// Propagate a changed value back to the original source.
Expand Down
23 changes: 23 additions & 0 deletions src/func/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,29 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
impl_decimal!(INT, as_int, Decimal, as_decimal);
}

// Ranges
if *op == ExclusiveRange && type1 == TypeId::of::<INT>() && type2 == TypeId::of::<()>() {
return Some((
|_ctx, args| Ok((args[0].as_int().unwrap()..INT::MAX).into()),
false,
));
} else if *op == ExclusiveRange && type1 == TypeId::of::<()>() && type2 == TypeId::of::<INT>() {
return Some((
|_ctx, args| Ok((0..args[1].as_int().unwrap()).into()),
false,
));
} else if *op == ExclusiveRange && type1 == TypeId::of::<INT>() && type2 == TypeId::of::<()>() {
return Some((
|_ctx, args| Ok((args[0].as_int().unwrap()..=INT::MAX).into()),
false,
));
} else if *op == ExclusiveRange && type1 == TypeId::of::<()>() && type2 == TypeId::of::<INT>() {
return Some((
|_ctx, args| Ok((0..=args[1].as_int().unwrap()).into()),
false,
));
}

// char op string
if (type1, type2) == (TypeId::of::<char>(), TypeId::of::<ImmutableString>()) {
fn get_s1s2(args: &FnCallArgs) -> ([Option<char>; 2], [Option<char>; 2]) {
Expand Down
2 changes: 1 addition & 1 deletion src/module/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2029,7 +2029,7 @@ impl Module {
})
.map(|(&k, f)| (k, f.clone())),
),
None => self.functions = other.functions.clone(),
None => self.functions.clone_from(&other.functions),
}
}
self.dynamic_functions_filter += &other.dynamic_functions_filter;
Expand Down