Remove unnecessary unchecked gates.

This commit is contained in:
Stephen Chung 2022-10-05 15:07:54 +08:00
parent 80f95b6f2a
commit 42c0eeed57
12 changed files with 178 additions and 211 deletions

View File

@ -1,12 +1,31 @@
//! Settings for [`Engine`]'s limitations.
#![cfg(not(feature = "unchecked"))]
use super::default_limits;
use crate::Engine;
use std::num::{NonZeroU64, NonZeroUsize};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
pub mod default_limits {
#[cfg(debug_assertions)]
#[cfg(not(feature = "no_function"))]
pub const MAX_CALL_STACK_DEPTH: usize = 8;
#[cfg(debug_assertions)]
pub const MAX_EXPR_DEPTH: usize = 32;
#[cfg(not(feature = "no_function"))]
#[cfg(debug_assertions)]
pub const MAX_FUNCTION_EXPR_DEPTH: usize = 16;
#[cfg(not(debug_assertions))]
#[cfg(not(feature = "no_function"))]
pub const MAX_CALL_STACK_DEPTH: usize = 64;
#[cfg(not(debug_assertions))]
pub const MAX_EXPR_DEPTH: usize = 64;
#[cfg(not(feature = "no_function"))]
#[cfg(not(debug_assertions))]
pub const MAX_FUNCTION_EXPR_DEPTH: usize = 32;
}
/// A type containing all the limits imposed by the [`Engine`].
///
/// Not available under `unchecked`.

View File

@ -43,28 +43,7 @@ use std::prelude::v1::*;
pub mod default_limits {
#[cfg(not(feature = "unchecked"))]
#[cfg(debug_assertions)]
#[cfg(not(feature = "no_function"))]
pub const MAX_CALL_STACK_DEPTH: usize = 8;
#[cfg(not(feature = "unchecked"))]
#[cfg(debug_assertions)]
pub const MAX_EXPR_DEPTH: usize = 32;
#[cfg(not(feature = "unchecked"))]
#[cfg(not(feature = "no_function"))]
#[cfg(debug_assertions)]
pub const MAX_FUNCTION_EXPR_DEPTH: usize = 16;
#[cfg(not(feature = "unchecked"))]
#[cfg(not(debug_assertions))]
#[cfg(not(feature = "no_function"))]
pub const MAX_CALL_STACK_DEPTH: usize = 64;
#[cfg(not(feature = "unchecked"))]
#[cfg(not(debug_assertions))]
pub const MAX_EXPR_DEPTH: usize = 64;
#[cfg(not(feature = "unchecked"))]
#[cfg(not(feature = "no_function"))]
#[cfg(not(debug_assertions))]
pub const MAX_FUNCTION_EXPR_DEPTH: usize = 32;
pub use super::limits::default_limits::*;
pub const MAX_DYNAMIC_PARAMETERS: usize = 16;
}
@ -236,55 +215,3 @@ impl Engine {
self
}
}
#[cfg(feature = "unchecked")]
impl Engine {
/// The maximum levels of function calls allowed for a script.
#[inline(always)]
#[must_use]
pub const fn max_call_levels(&self) -> usize {
usize::MAX
}
/// The maximum number of operations allowed for a script to run (0 for unlimited).
#[inline]
#[must_use]
pub const fn max_operations(&self) -> u64 {
0
}
/// The maximum number of imported [modules][crate::Module] allowed for a script.
#[inline(always)]
#[must_use]
pub const fn max_modules(&self) -> usize {
usize::MAX
}
/// The depth limit for expressions (0 for unlimited).
#[inline(always)]
#[must_use]
pub const fn max_expr_depth(&self) -> usize {
0
}
/// The depth limit for expressions in functions (0 for unlimited).
#[inline(always)]
#[must_use]
pub const fn max_function_expr_depth(&self) -> usize {
0
}
/// The maximum length of [strings][crate::ImmutableString] (0 for unlimited).
#[inline(always)]
#[must_use]
pub const fn max_string_size(&self) -> usize {
0
}
/// The maximum length of [arrays][crate::Array] (0 for unlimited).
#[inline(always)]
#[must_use]
pub const fn max_array_size(&self) -> usize {
0
}
/// The maximum size of [object maps][crate::Map] (0 for unlimited).
#[inline(always)]
#[must_use]
pub const fn max_map_size(&self) -> usize {
0
}
}

View File

@ -347,9 +347,9 @@ impl Engine {
}
/// Check a result to ensure that it is valid.
#[cfg(not(feature = "unchecked"))]
#[inline]
pub(crate) fn check_return_value(&self, result: RhaiResult, _pos: Position) -> RhaiResult {
#[cfg(not(feature = "unchecked"))]
if let Ok(ref r) = result {
self.check_data_size(r, _pos)?;
}
@ -357,3 +357,81 @@ impl Engine {
result
}
}
#[cfg(feature = "unchecked")]
impl Engine {
/// The maximum levels of function calls allowed for a script.
#[inline(always)]
#[must_use]
pub const fn max_call_levels(&self) -> usize {
usize::MAX
}
/// The maximum number of operations allowed for a script to run (0 for unlimited).
#[inline(always)]
#[must_use]
pub const fn max_operations(&self) -> u64 {
0
}
/// The maximum number of imported [modules][crate::Module] allowed for a script.
#[inline(always)]
#[must_use]
pub const fn max_modules(&self) -> usize {
usize::MAX
}
/// The depth limit for expressions (0 for unlimited).
#[inline(always)]
#[must_use]
pub const fn max_expr_depth(&self) -> usize {
0
}
/// The depth limit for expressions in functions (0 for unlimited).
#[inline(always)]
#[must_use]
pub const fn max_function_expr_depth(&self) -> usize {
0
}
/// The maximum length of [strings][crate::ImmutableString] (0 for unlimited).
#[inline(always)]
#[must_use]
pub const fn max_string_size(&self) -> usize {
0
}
/// The maximum length of [arrays][crate::Array] (0 for unlimited).
#[inline(always)]
#[must_use]
pub const fn max_array_size(&self) -> usize {
0
}
/// The maximum size of [object maps][crate::Map] (0 for unlimited).
#[inline(always)]
#[must_use]
pub const fn max_map_size(&self) -> usize {
0
}
/// Check if the number of operations stay within limit.
#[inline(always)]
pub(crate) const fn track_operation(
&self,
_: &crate::GlobalRuntimeState,
_: Position,
) -> crate::RhaiResultOf<()> {
Ok(())
}
/// Check whether the size of a [`Dynamic`] is within limits.
#[inline(always)]
pub(crate) const fn check_data_size(
&self,
_: &Dynamic,
_: Position,
) -> crate::RhaiResultOf<()> {
Ok(())
}
/// Check a result to ensure that it is valid.
#[inline(always)]
pub(crate) const fn check_return_value(&self, result: RhaiResult, _: Position) -> RhaiResult {
result
}
}

View File

@ -131,7 +131,6 @@ impl Engine {
self.eval_op_assignment(
global, caches, lib, op_info, obj_ptr, root, new_val, level,
)?;
#[cfg(not(feature = "unchecked"))]
self.check_data_size(obj_ptr, op_info.pos)?;
None
}
@ -159,7 +158,6 @@ impl Engine {
)?;
// Replace new value
new_val = val.take_or_clone();
#[cfg(not(feature = "unchecked"))]
self.check_data_size(&new_val, op_info.pos)?;
}
}
@ -246,7 +244,6 @@ impl Engine {
global, caches, lib, op_info, val_target, root, new_val, level,
)?;
}
#[cfg(not(feature = "unchecked"))]
self.check_data_size(target.source(), op_info.pos)?;
Ok((Dynamic::UNIT, true))
}
@ -606,9 +603,7 @@ impl Engine {
Expr::Variable(x, .., var_pos) => {
#[cfg(feature = "debugging")]
self.run_debugger(scope, global, lib, this_ptr, lhs, level)?;
#[cfg(not(feature = "unchecked"))]
self.inc_operations(&mut global.num_operations, *var_pos)?;
self.track_operation(global, *var_pos)?;
let (mut target, ..) =
self.search_namespace(scope, global, lib, this_ptr, lhs, level)?;
@ -655,8 +650,7 @@ impl Engine {
idx_values: &mut FnArgsVec<Dynamic>,
level: usize,
) -> RhaiResultOf<()> {
#[cfg(not(feature = "unchecked"))]
self.inc_operations(&mut global.num_operations, expr.position())?;
self.track_operation(global, expr.position())?;
match expr {
#[cfg(not(feature = "no_object"))]
@ -814,8 +808,7 @@ impl Engine {
use_indexers: bool,
level: usize,
) -> RhaiResultOf<Target<'t>> {
#[cfg(not(feature = "unchecked"))]
self.inc_operations(&mut global.num_operations, Position::NONE)?;
self.track_operation(global, Position::NONE)?;
match target {
#[cfg(not(feature = "no_index"))]

View File

@ -1,6 +1,7 @@
//! Data size checks during evaluation.
#![cfg(not(feature = "unchecked"))]
use super::GlobalRuntimeState;
use crate::types::dynamic::Union;
use crate::{Dynamic, Engine, Position, RhaiResultOf, ERR};
use std::num::NonZeroUsize;
@ -15,7 +16,6 @@ impl Engine {
/// # Panics
///
/// Panics if any interior data is shared (should never happen).
#[cfg(not(feature = "unchecked"))]
pub(crate) fn calc_data_sizes(value: &Dynamic, _top: bool) -> (usize, usize, usize) {
match value.0 {
#[cfg(not(feature = "no_index"))]
@ -71,24 +71,11 @@ impl Engine {
}
/// Is there a data size limit set?
#[cfg(not(feature = "unchecked"))]
pub(crate) const fn has_data_size_limit(&self) -> bool {
let mut _limited = self.limits.max_string_size.is_some();
#[cfg(not(feature = "no_index"))]
{
_limited = _limited || self.limits.max_array_size.is_some();
}
#[cfg(not(feature = "no_object"))]
{
_limited = _limited || self.limits.max_map_size.is_some();
}
_limited
self.max_string_size() > 0 || self.max_array_size() > 0 || self.max_map_size() > 0
}
/// Raise an error if any data size exceeds limit.
#[cfg(not(feature = "unchecked"))]
pub(crate) fn raise_err_if_over_data_size_limit(
&self,
sizes: (usize, usize, usize),
@ -128,7 +115,6 @@ impl Engine {
}
/// Check whether the size of a [`Dynamic`] is within limits.
#[cfg(not(feature = "unchecked"))]
pub(crate) fn check_data_size(&self, value: &Dynamic, pos: Position) -> RhaiResultOf<()> {
// If no data size limits, just return
if !self.has_data_size_limit() {
@ -143,29 +129,30 @@ impl Engine {
/// Raise an error if the size of a [`Dynamic`] is out of limits (if any).
///
/// Not available under `unchecked`.
#[cfg(not(feature = "unchecked"))]
#[inline(always)]
pub fn ensure_data_size_within_limits(&self, value: &Dynamic) -> RhaiResultOf<()> {
self.check_data_size(value, Position::NONE)
}
/// Check if the number of operations stay within limit.
#[cfg(not(feature = "unchecked"))]
pub(crate) fn inc_operations(
pub(crate) fn track_operation(
&self,
num_operations: &mut u64,
global: &mut GlobalRuntimeState,
pos: Position,
) -> RhaiResultOf<()> {
*num_operations += 1;
global.num_operations += 1;
// Guard against too many operations
if self.max_operations() > 0 && *num_operations > self.max_operations() {
let max = self.max_operations();
let num_operations = global.num_operations;
if max > 0 && num_operations > max {
return Err(ERR::ErrorTooManyOperations(pos).into());
}
// Report progress - only in steps
if let Some(ref progress) = self.progress {
if let Some(token) = progress(*num_operations) {
if let Some(token) = progress(num_operations) {
// Terminate script if progress returns a termination token
return Err(ERR::ErrorTerminated(token, pos).into());
}

View File

@ -318,8 +318,7 @@ impl Engine {
let reset_debugger =
self.run_debugger_with_reset(scope, global, lib, this_ptr, expr, level)?;
#[cfg(not(feature = "unchecked"))]
self.inc_operations(&mut global.num_operations, expr.position())?;
self.track_operation(global, expr.position())?;
let result =
self.eval_fn_call_expr(scope, global, caches, lib, this_ptr, x, x.pos, level);
@ -337,8 +336,7 @@ impl Engine {
#[cfg(feature = "debugging")]
self.run_debugger(scope, global, lib, this_ptr, expr, level)?;
#[cfg(not(feature = "unchecked"))]
self.inc_operations(&mut global.num_operations, expr.position())?;
self.track_operation(global, expr.position())?;
return if index.is_none() && x.0.is_none() && x.3 == KEYWORD_THIS {
this_ptr
@ -355,8 +353,7 @@ impl Engine {
let reset_debugger =
self.run_debugger_with_reset(scope, global, lib, this_ptr, expr, level)?;
#[cfg(not(feature = "unchecked"))]
self.inc_operations(&mut global.num_operations, expr.position())?;
self.track_operation(global, expr.position())?;
let result = match expr {
// Constants
@ -410,7 +407,7 @@ impl Engine {
let mut result = Ok(Dynamic::UNIT);
#[cfg(not(feature = "unchecked"))]
let mut sizes = (0, 0, 0);
let mut total_data_sizes = (0, 0, 0);
for item_expr in &**x {
let value = match self
@ -424,19 +421,21 @@ impl Engine {
};
#[cfg(not(feature = "unchecked"))]
if self.has_data_size_limit() {
let val_sizes = Self::calc_data_sizes(&value, true);
array.push(value);
#[cfg(not(feature = "unchecked"))]
if self.has_data_size_limit() {
sizes = (
sizes.0 + val_sizes.0,
sizes.1 + val_sizes.1,
sizes.2 + val_sizes.2,
total_data_sizes = (
total_data_sizes.0 + val_sizes.0,
total_data_sizes.1 + val_sizes.1,
total_data_sizes.2 + val_sizes.2,
);
self.raise_err_if_over_data_size_limit(sizes, item_expr.position())?;
self.raise_err_if_over_data_size_limit(
total_data_sizes,
item_expr.position(),
)?;
}
array.push(value);
}
result.map(|_| array.into())
@ -448,7 +447,7 @@ impl Engine {
let mut result = Ok(Dynamic::UNIT);
#[cfg(not(feature = "unchecked"))]
let mut sizes = (0, 0, 0);
let mut total_data_sizes = (0, 0, 0);
for (key, value_expr) in &x.0 {
let value = match self
@ -462,15 +461,20 @@ impl Engine {
};
#[cfg(not(feature = "unchecked"))]
if self.has_data_size_limit() {
let delta = Self::calc_data_sizes(&value, true);
total_data_sizes = (
total_data_sizes.0 + delta.0,
total_data_sizes.1 + delta.1,
total_data_sizes.2 + delta.2,
);
self.raise_err_if_over_data_size_limit(
total_data_sizes,
value_expr.position(),
)?;
}
*map.get_mut(key.as_str()).unwrap() = value;
#[cfg(not(feature = "unchecked"))]
if self.has_data_size_limit() {
sizes = (sizes.0 + delta.0, sizes.1 + delta.1, sizes.2 + delta.2);
self.raise_err_if_over_data_size_limit(sizes, value_expr.position())?;
}
}
result.map(|_| map.into())

View File

@ -152,7 +152,6 @@ impl Engine {
let context = (self, op, None, &*global, lib, *op_pos, level).into();
let result = func(context, args).map(|_| ());
#[cfg(not(feature = "unchecked"))]
self.check_data_size(args[0], root.1)?;
return result;
@ -165,10 +164,7 @@ impl Engine {
match self.call_native_fn(
global, caches, lib, op_assign, hash, args, true, true, *op_pos, level,
) {
Ok(_) => {
#[cfg(not(feature = "unchecked"))]
self.check_data_size(args[0], root.1)?;
}
Ok(_) => self.check_data_size(args[0], root.1)?,
Err(err) if matches!(*err, ERR::ErrorFunctionNotFound(ref f, ..) if f.starts_with(op_assign)) =>
{
// Expand to `var = var op rhs`
@ -218,8 +214,7 @@ impl Engine {
// Function calls should account for a relatively larger portion of statements.
if let Stmt::FnCall(x, ..) = stmt {
#[cfg(not(feature = "unchecked"))]
self.inc_operations(&mut global.num_operations, stmt.position())?;
self.track_operation(global, stmt.position())?;
let result =
self.eval_fn_call_expr(scope, global, caches, lib, this_ptr, x, x.pos, level);
@ -236,8 +231,7 @@ impl Engine {
if let Stmt::Assignment(x, ..) = stmt {
let (op_info, BinaryExpr { lhs, rhs }) = &**x;
#[cfg(not(feature = "unchecked"))]
self.inc_operations(&mut global.num_operations, stmt.position())?;
self.track_operation(global, stmt.position())?;
let result = if let Expr::Variable(x, ..) = lhs {
let rhs_result = self
@ -267,8 +261,7 @@ impl Engine {
);
}
#[cfg(not(feature = "unchecked"))]
self.inc_operations(&mut global.num_operations, pos)?;
self.track_operation(global, pos)?;
let root = (var_name, pos);
let lhs_ptr = &mut lhs_ptr;
@ -339,8 +332,7 @@ impl Engine {
return result;
}
#[cfg(not(feature = "unchecked"))]
self.inc_operations(&mut global.num_operations, stmt.position())?;
self.track_operation(global, stmt.position())?;
let result = match stmt {
// No-op
@ -512,8 +504,7 @@ impl Engine {
let (.., body) = &**x;
if body.is_empty() {
#[cfg(not(feature = "unchecked"))]
self.inc_operations(&mut global.num_operations, body.position())?;
self.track_operation(global, body.position())?;
} else {
match self
.eval_stmt_block(scope, global, caches, lib, this_ptr, body, true, level)
@ -668,10 +659,7 @@ impl Engine {
*scope.get_mut_by_index(index).write_lock().unwrap() = value;
#[cfg(not(feature = "unchecked"))]
if let Err(err) = self
.inc_operations(&mut global.num_operations, statements.position())
{
if let Err(err) = self.track_operation(global, statements.position()) {
loop_result = Err(err);
break;
}

View File

@ -298,7 +298,7 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
($x:ty, $xx:ident, $y:ty, $yy:ident) => {
if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
#[cfg(not(feature = "unchecked"))]
use crate::packages::arithmetic::decimal_functions::*;
use crate::packages::arithmetic::decimal_functions::builtin::*;
#[cfg(not(feature = "unchecked"))]
match op {
@ -697,7 +697,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
($x:ident, $xx:ident, $y:ty, $yy:ident) => {
if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
#[cfg(not(feature = "unchecked"))]
use crate::packages::arithmetic::decimal_functions::*;
use crate::packages::arithmetic::decimal_functions::builtin::*;
#[cfg(not(feature = "unchecked"))]
return match op {

View File

@ -358,8 +358,7 @@ impl Engine {
pos: Position,
level: usize,
) -> RhaiResultOf<(Dynamic, bool)> {
#[cfg(not(feature = "unchecked"))]
self.inc_operations(&mut global.num_operations, pos)?;
self.track_operation(global, pos)?;
let parent_source = global.source.clone();
let op_assign = if is_op_assign {
@ -1232,8 +1231,7 @@ impl Engine {
target = target.into_owned();
}
#[cfg(not(feature = "unchecked"))]
self.inc_operations(&mut global.num_operations, _pos)?;
self.track_operation(global, _pos)?;
#[cfg(not(feature = "no_closure"))]
let target_is_shared = target.is_shared();
@ -1312,8 +1310,7 @@ impl Engine {
let (target, _pos) =
self.search_scope_only(scope, global, lib, this_ptr, first_arg, level)?;
#[cfg(not(feature = "unchecked"))]
self.inc_operations(&mut global.num_operations, _pos)?;
self.track_operation(global, _pos)?;
#[cfg(not(feature = "no_closure"))]
let target_is_shared = target.is_shared();
@ -1350,9 +1347,7 @@ impl Engine {
let mut func = match module.get_qualified_fn(hash) {
// Then search native Rust functions
None => {
#[cfg(not(feature = "unchecked"))]
self.inc_operations(&mut global.num_operations, pos)?;
self.track_operation(global, pos)?;
let hash_params = calc_fn_params_hash(args.iter().map(|a| a.type_id()));
let hash_qualified_fn = combine_hashes(hash, hash_params);
@ -1384,8 +1379,7 @@ impl Engine {
}));
let hash_qualified_fn = combine_hashes(hash, hash_params);
#[cfg(not(feature = "unchecked"))]
self.inc_operations(&mut global.num_operations, pos)?;
self.track_operation(global, pos)?;
if let Some(f) = module.get_qualified_fn(hash_qualified_fn) {
func = Some(f);
@ -1461,8 +1455,7 @@ impl Engine {
_pos: Position,
level: usize,
) -> RhaiResult {
#[cfg(not(feature = "unchecked"))]
self.inc_operations(&mut global.num_operations, _pos)?;
self.track_operation(global, _pos)?;
let script = script.trim();

View File

@ -63,8 +63,7 @@ impl Engine {
assert!(fn_def.params.len() == args.len());
#[cfg(not(feature = "unchecked"))]
self.inc_operations(&mut global.num_operations, pos)?;
self.track_operation(global, pos)?;
// Check for stack overflow
if level > self.max_call_levels() {

View File

@ -488,39 +488,29 @@ mod f64_functions {
#[cfg(feature = "decimal")]
#[export_module]
pub mod decimal_functions {
use num_traits::Pow;
use rust_decimal::{prelude::Zero, Decimal, MathematicalOps};
use rust_decimal::{prelude::Zero, Decimal};
#[rhai_fn(skip, return_raw)]
#[cfg(not(feature = "unchecked"))]
pub mod builtin {
use rust_decimal::MathematicalOps;
#[rhai_fn(return_raw)]
pub fn add(x: Decimal, y: Decimal) -> RhaiResultOf<Decimal> {
if cfg!(not(feature = "unchecked")) {
x.checked_add(y)
.ok_or_else(|| make_err(format!("Addition overflow: {x} + {y}")))
} else {
Ok(x + y)
}
}
#[rhai_fn(skip, return_raw)]
#[rhai_fn(return_raw)]
pub fn subtract(x: Decimal, y: Decimal) -> RhaiResultOf<Decimal> {
if cfg!(not(feature = "unchecked")) {
x.checked_sub(y)
.ok_or_else(|| make_err(format!("Subtraction overflow: {x} - {y}")))
} else {
Ok(x - y)
}
}
#[rhai_fn(skip, return_raw)]
#[rhai_fn(return_raw)]
pub fn multiply(x: Decimal, y: Decimal) -> RhaiResultOf<Decimal> {
if cfg!(not(feature = "unchecked")) {
x.checked_mul(y)
.ok_or_else(|| make_err(format!("Multiplication overflow: {x} * {y}")))
} else {
Ok(x * y)
}
}
#[rhai_fn(skip, return_raw)]
#[rhai_fn(return_raw)]
pub fn divide(x: Decimal, y: Decimal) -> RhaiResultOf<Decimal> {
if cfg!(not(feature = "unchecked")) {
// Detect division by zero
if y == Decimal::zero() {
Err(make_err(format!("Division by zero: {x} / {y}")))
@ -528,26 +518,16 @@ pub mod decimal_functions {
x.checked_div(y)
.ok_or_else(|| make_err(format!("Division overflow: {x} / {y}")))
}
} else {
Ok(x / y)
}
}
#[rhai_fn(skip, return_raw)]
#[rhai_fn(return_raw)]
pub fn modulo(x: Decimal, y: Decimal) -> RhaiResultOf<Decimal> {
if cfg!(not(feature = "unchecked")) {
x.checked_rem(y)
.ok_or_else(|| make_err(format!("Modulo division by zero or overflow: {x} % {y}")))
} else {
Ok(x % y)
}
}
#[rhai_fn(skip, return_raw)]
#[rhai_fn(return_raw)]
pub fn power(x: Decimal, y: Decimal) -> RhaiResultOf<Decimal> {
if cfg!(not(feature = "unchecked")) {
x.checked_powd(y)
.ok_or_else(|| make_err(format!("Exponential overflow: {x} ** {y}")))
} else {
Ok(x.pow(y))
}
}
#[rhai_fn(name = "-")]

View File

@ -236,9 +236,8 @@ pub mod array_functions {
}
let check_sizes = match item.0 {
crate::types::dynamic::Union::Array(..) | crate::types::dynamic::Union::Str(..) => {
true
}
crate::types::dynamic::Union::Str(..) => true,
crate::types::dynamic::Union::Array(..) => true,
#[cfg(not(feature = "no_object"))]
crate::types::dynamic::Union::Map(..) => true,
_ => false,