ext_php_rs/
flags.rs

1//! Flags and enums used in PHP and the Zend engine.
2
3use bitflags::bitflags;
4
5#[cfg(php81)]
6use crate::ffi::ZEND_ACC_ENUM;
7#[cfg(not(php82))]
8use crate::ffi::ZEND_ACC_REUSE_GET_ITERATOR;
9use crate::ffi::{
10    CONST_CS, CONST_DEPRECATED, CONST_NO_FILE_CACHE, CONST_PERSISTENT, E_COMPILE_ERROR,
11    E_COMPILE_WARNING, E_CORE_ERROR, E_CORE_WARNING, E_DEPRECATED, E_ERROR, E_NOTICE, E_PARSE,
12    E_RECOVERABLE_ERROR, E_STRICT, E_USER_DEPRECATED, E_USER_ERROR, E_USER_NOTICE, E_USER_WARNING,
13    E_WARNING, IS_ARRAY, IS_CALLABLE, IS_CONSTANT_AST, IS_DOUBLE, IS_FALSE, IS_INDIRECT,
14    IS_ITERABLE, IS_LONG, IS_MIXED, IS_NULL, IS_OBJECT, IS_PTR, IS_REFERENCE, IS_RESOURCE,
15    IS_STRING, IS_TRUE, IS_TYPE_COLLECTABLE, IS_TYPE_REFCOUNTED, IS_UNDEF, IS_VOID, PHP_INI_ALL,
16    PHP_INI_PERDIR, PHP_INI_SYSTEM, PHP_INI_USER, ZEND_ACC_ABSTRACT, ZEND_ACC_ANON_CLASS,
17    ZEND_ACC_CALL_VIA_TRAMPOLINE, ZEND_ACC_CHANGED, ZEND_ACC_CLOSURE, ZEND_ACC_CONSTANTS_UPDATED,
18    ZEND_ACC_CTOR, ZEND_ACC_DEPRECATED, ZEND_ACC_DONE_PASS_TWO, ZEND_ACC_EARLY_BINDING,
19    ZEND_ACC_FAKE_CLOSURE, ZEND_ACC_FINAL, ZEND_ACC_GENERATOR, ZEND_ACC_HAS_FINALLY_BLOCK,
20    ZEND_ACC_HAS_RETURN_TYPE, ZEND_ACC_HAS_TYPE_HINTS, ZEND_ACC_HEAP_RT_CACHE, ZEND_ACC_IMMUTABLE,
21    ZEND_ACC_IMPLICIT_ABSTRACT_CLASS, ZEND_ACC_INTERFACE, ZEND_ACC_LINKED, ZEND_ACC_NEARLY_LINKED,
22    ZEND_ACC_NEVER_CACHE, ZEND_ACC_NO_DYNAMIC_PROPERTIES, ZEND_ACC_PRELOADED, ZEND_ACC_PRIVATE,
23    ZEND_ACC_PROMOTED, ZEND_ACC_PROTECTED, ZEND_ACC_PUBLIC, ZEND_ACC_RESOLVED_INTERFACES,
24    ZEND_ACC_RESOLVED_PARENT, ZEND_ACC_RETURN_REFERENCE, ZEND_ACC_STATIC, ZEND_ACC_STRICT_TYPES,
25    ZEND_ACC_TOP_LEVEL, ZEND_ACC_TRAIT, ZEND_ACC_TRAIT_CLONE, ZEND_ACC_UNRESOLVED_VARIANCE,
26    ZEND_ACC_USES_THIS, ZEND_ACC_USE_GUARDS, ZEND_ACC_VARIADIC, ZEND_EVAL_CODE,
27    ZEND_HAS_STATIC_IN_METHODS, ZEND_INTERNAL_FUNCTION, ZEND_USER_FUNCTION, Z_TYPE_FLAGS_SHIFT,
28    _IS_BOOL,
29};
30
31use std::{convert::TryFrom, fmt::Display};
32
33use crate::error::{Error, Result};
34
35bitflags! {
36    /// Flags used for setting the type of Zval.
37    #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
38    pub struct ZvalTypeFlags: u32 {
39        /// Undefined
40        const Undef = IS_UNDEF;
41        /// Null
42        const Null = IS_NULL;
43        /// `false`
44        const False = IS_FALSE;
45        /// `true`
46        const True = IS_TRUE;
47        /// Integer
48        const Long = IS_LONG;
49        /// Floating point number
50        const Double = IS_DOUBLE;
51        /// String
52        const String = IS_STRING;
53        /// Array
54        const Array = IS_ARRAY;
55        /// Object
56        const Object = IS_OBJECT;
57        /// Resource
58        const Resource = IS_RESOURCE;
59        /// Reference
60        const Reference = IS_REFERENCE;
61        /// Callable
62        const Callable = IS_CALLABLE;
63        /// Constant expression
64        const ConstantExpression = IS_CONSTANT_AST;
65        /// Void
66        const Void = IS_VOID;
67        /// Pointer
68        const Ptr = IS_PTR;
69        /// Iterable
70        const Iterable = IS_ITERABLE;
71
72        /// Interned string extended
73        const InternedStringEx = Self::String.bits();
74        /// String extended
75        const StringEx = Self::String.bits() | Self::RefCounted.bits();
76        /// Array extended
77        const ArrayEx = Self::Array.bits() | Self::RefCounted.bits() | Self::Collectable.bits();
78        /// Object extended
79        const ObjectEx = Self::Object.bits() | Self::RefCounted.bits() | Self::Collectable.bits();
80        /// Resource extended
81        const ResourceEx = Self::Resource.bits() | Self::RefCounted.bits();
82        /// Reference extended
83        const ReferenceEx = Self::Reference.bits() | Self::RefCounted.bits();
84        /// Constant ast extended
85        const ConstantAstEx = Self::ConstantExpression.bits() | Self::RefCounted.bits();
86
87        /// Reference counted
88        const RefCounted = (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT);
89        /// Collectable
90        const Collectable = (IS_TYPE_COLLECTABLE << Z_TYPE_FLAGS_SHIFT);
91    }
92}
93
94bitflags! {
95    /// Flags for building classes.
96    #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
97    pub struct ClassFlags: u32 {
98        /// Final class or method
99        const Final = ZEND_ACC_FINAL;
100        /// Abstract method
101        const Abstract = ZEND_ACC_ABSTRACT;
102        /// Immutable `op_array` and class_entries
103        /// (implemented only for lazy loading of `op_array`s)
104        const Immutable = ZEND_ACC_IMMUTABLE;
105        /// Function has typed arguments / class has typed props
106        const HasTypeHints = ZEND_ACC_HAS_TYPE_HINTS;
107        /// Top-level class or function declaration
108        const TopLevel = ZEND_ACC_TOP_LEVEL;
109        /// op_array or class is preloaded
110        const Preloaded = ZEND_ACC_PRELOADED;
111
112        /// Class entry is an interface
113        const Interface = ZEND_ACC_INTERFACE;
114        /// Class entry is a trait
115        const Trait = ZEND_ACC_TRAIT;
116        /// Anonymous class
117        const AnonymousClass = ZEND_ACC_ANON_CLASS;
118        /// Class is an Enum
119        #[cfg(php81)]
120        const Enum = ZEND_ACC_ENUM;
121        /// Class linked with parent, interfaces and traits
122        const Linked = ZEND_ACC_LINKED;
123        /// Class is abstract, since it is set by any abstract method
124        const ImplicitAbstractClass = ZEND_ACC_IMPLICIT_ABSTRACT_CLASS;
125        /// Class has magic methods `__get`/`__set`/`__unset`/`__isset` that use guards
126        const UseGuards = ZEND_ACC_USE_GUARDS;
127
128        /// Class constants updated
129        const ConstantsUpdated = ZEND_ACC_CONSTANTS_UPDATED;
130        /// Objects of this class may not have dynamic properties
131        const NoDynamicProperties = ZEND_ACC_NO_DYNAMIC_PROPERTIES;
132        /// User class has methods with static variables
133        const HasStaticInMethods = ZEND_HAS_STATIC_IN_METHODS;
134        /// Children must reuse parent `get_iterator()`
135        #[cfg(not(php82))]
136        const ReuseGetIterator = ZEND_ACC_REUSE_GET_ITERATOR;
137        /// Parent class is resolved (CE)
138        const ResolvedParent = ZEND_ACC_RESOLVED_PARENT;
139        /// Interfaces are resolved (CE)
140        const ResolvedInterfaces = ZEND_ACC_RESOLVED_INTERFACES;
141        /// Class has unresolved variance obligations
142        const UnresolvedVariance = ZEND_ACC_UNRESOLVED_VARIANCE;
143        /// Class is linked apart from variance obligations
144        const NearlyLinked = ZEND_ACC_NEARLY_LINKED;
145
146        /// Class cannot be serialized or unserialized
147        #[cfg(php81)]
148        const NotSerializable = crate::ffi::ZEND_ACC_NOT_SERIALIZABLE;
149    }
150}
151
152bitflags! {
153    /// Flags for building methods.
154    #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
155    pub struct MethodFlags: u32 {
156        /// Visibility public
157        const Public = ZEND_ACC_PUBLIC;
158        /// Visibility protected
159        const Protected = ZEND_ACC_PROTECTED;
160        /// Visibility private
161        const Private = ZEND_ACC_PRIVATE;
162        /// Method or property overrides private one
163        const Changed = ZEND_ACC_CHANGED;
164        /// Static method
165        const Static = ZEND_ACC_STATIC;
166        /// Final method
167        const Final = ZEND_ACC_FINAL;
168        /// Abstract method
169        const Abstract = ZEND_ACC_ABSTRACT;
170        /// Immutable `op_array` and class_entries
171        /// (implemented only for lazy loading of op_arrays)
172        const Immutable = ZEND_ACC_IMMUTABLE;
173        /// Function has typed arguments / class has typed props
174        const HasTypeHints = ZEND_ACC_HAS_TYPE_HINTS;
175        /// Top-level class or function declaration
176        const TopLevel = ZEND_ACC_TOP_LEVEL;
177        /// `op_array` or class is preloaded
178        const Preloaded = ZEND_ACC_PRELOADED;
179
180        /// Deprecation flag
181        const Deprecated = ZEND_ACC_DEPRECATED;
182        /// Function returning by reference
183        const ReturnReference = ZEND_ACC_RETURN_REFERENCE;
184        /// Function has a return type
185        const HasReturnType = ZEND_ACC_HAS_RETURN_TYPE;
186        /// Function with variable number of arguments
187        const Variadic = ZEND_ACC_VARIADIC;
188        /// `op_array` has finally blocks (user only)
189        const HasFinallyBlock = ZEND_ACC_HAS_FINALLY_BLOCK;
190        /// "main" `op_array` with `ZEND_DECLARE_CLASS_DELAYED` opcodes
191        const EarlyBinding = ZEND_ACC_EARLY_BINDING;
192        /// Closure uses `$this`
193        const UsesThis = ZEND_ACC_USES_THIS;
194        /// Call through user function trampoline
195        ///
196        /// # Example
197        /// - `__call`
198        /// - `__callStatic`
199        const CallViaTrampoline = ZEND_ACC_CALL_VIA_TRAMPOLINE;
200        /// Disable inline caching
201        const NeverCache = ZEND_ACC_NEVER_CACHE;
202        /// `op_array` is a clone of trait method
203        const TraitClone = ZEND_ACC_TRAIT_CLONE;
204        /// Function is a constructor
205        const IsConstructor = ZEND_ACC_CTOR;
206        /// Function is a closure
207        const Closure = ZEND_ACC_CLOSURE;
208        /// Function is a fake closure
209        const FakeClosure = ZEND_ACC_FAKE_CLOSURE;
210        /// Function is a generator
211        const Generator = ZEND_ACC_GENERATOR;
212        /// Function was processed by pass two (user only)
213        const DonePassTwo = ZEND_ACC_DONE_PASS_TWO;
214        /// `run_time_cache` allocated on heap (user only)
215        const HeapRTCache = ZEND_ACC_HEAP_RT_CACHE;
216        /// `op_array` uses strict mode types
217        const StrictTypes = ZEND_ACC_STRICT_TYPES;
218    }
219}
220
221bitflags! {
222    /// Flags for building properties.
223    #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
224    pub struct PropertyFlags: u32 {
225        /// Visibility public
226        const Public = ZEND_ACC_PUBLIC;
227        /// Visibility protected
228        const Protected = ZEND_ACC_PROTECTED;
229        /// Visibility private
230        const Private = ZEND_ACC_PRIVATE;
231        /// Property or method overrides private one
232        const Changed = ZEND_ACC_CHANGED;
233        /// Static property
234        const Static = ZEND_ACC_STATIC;
235        /// Promoted property
236        const Promoted = ZEND_ACC_PROMOTED;
237    }
238}
239
240bitflags! {
241    /// Flags for building constants.
242    #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
243    pub struct ConstantFlags: u32 {
244        /// Visibility public
245        const Public = ZEND_ACC_PUBLIC;
246        /// Visibility protected
247        const Protected = ZEND_ACC_PROTECTED;
248        /// Visibility private
249        const Private = ZEND_ACC_PRIVATE;
250        /// Promoted constant
251        const Promoted = ZEND_ACC_PROMOTED;
252    }
253}
254
255bitflags! {
256    /// Flags for building module global constants.
257    #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
258    pub struct GlobalConstantFlags: u32 {
259        /// No longer used -- always case-sensitive
260        #[deprecated(note = "No longer used -- always case-sensitive")]
261        const CaseSensitive = CONST_CS;
262        /// Persistent
263        const Persistent = CONST_PERSISTENT;
264        /// Can't be saved in file cache
265        const NoFileCache = CONST_NO_FILE_CACHE;
266        /// Deprecated (this flag is not deprecated, it literally means the constant is deprecated)
267        const Deprecated = CONST_DEPRECATED;
268    }
269}
270
271bitflags! {
272    /// Represents the result of a function.
273    #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
274    pub struct ZendResult: i32 {
275        /// Function call was successful.
276        const Success = 0;
277        /// Function call failed.
278        const Failure = -1;
279    }
280}
281
282bitflags! {
283    /// Represents permissions for where a configuration setting may be set.
284    pub struct IniEntryPermission: u32 {
285        /// User
286        const User = PHP_INI_USER;
287        /// Per directory
288        const PerDir = PHP_INI_PERDIR;
289        /// System
290        const System = PHP_INI_SYSTEM;
291        /// All
292        const All = PHP_INI_ALL;
293    }
294}
295
296bitflags! {
297    /// Represents error types when used via php_error_docref for example.
298    pub struct ErrorType: u32 {
299        /// Error
300        const Error = E_ERROR;
301        /// Warning
302        const Warning = E_WARNING;
303        /// Parse
304        const Parse = E_PARSE;
305        /// Notice
306        const Notice = E_NOTICE;
307        /// Core error
308        const CoreError = E_CORE_ERROR;
309        /// Core warning
310        const CoreWarning = E_CORE_WARNING;
311        /// Compile error
312        const CompileError = E_COMPILE_ERROR;
313        /// Compile warning
314        const CompileWarning = E_COMPILE_WARNING;
315        /// User error
316        #[cfg_attr(php84, deprecated = "`E_USER_ERROR` is deprecated since PHP 8.4. Throw an exception instead.")]
317        const UserError = E_USER_ERROR;
318        /// User warning
319        const UserWarning = E_USER_WARNING;
320        /// User notice
321        const UserNotice = E_USER_NOTICE;
322        /// Strict
323        const Strict = E_STRICT;
324        /// Recoverable error
325        const RecoverableError = E_RECOVERABLE_ERROR;
326        /// Deprecated
327        const Deprecated = E_DEPRECATED;
328        /// User deprecated
329        const UserDeprecated = E_USER_DEPRECATED;
330    }
331}
332
333/// Represents the type of a function.
334#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy)]
335pub enum FunctionType {
336    /// Internal function
337    Internal,
338    /// User function
339    User,
340    /// Eval code
341    Eval,
342}
343
344impl From<u8> for FunctionType {
345    #[allow(clippy::bad_bit_mask)]
346    fn from(value: u8) -> Self {
347        match value.into() {
348            ZEND_INTERNAL_FUNCTION => Self::Internal,
349            ZEND_USER_FUNCTION => Self::User,
350            ZEND_EVAL_CODE => Self::Eval,
351            _ => panic!("Unknown function type: {value}"),
352        }
353    }
354}
355
356/// Valid data types for PHP.
357#[repr(C, u8)]
358#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
359pub enum DataType {
360    /// Undefined
361    Undef,
362    /// `null`
363    Null,
364    /// `false`
365    False,
366    /// `true`
367    True,
368    /// Integer (the irony)
369    Long,
370    /// Floating point number
371    Double,
372    /// String
373    String,
374    /// Array
375    Array,
376    /// Iterable
377    Iterable,
378    /// Object
379    Object(Option<&'static str>),
380    /// Resource
381    Resource,
382    /// Reference
383    Reference,
384    /// Callable
385    Callable,
386    /// Constant expression
387    ConstantExpression,
388    /// Void
389    Void,
390    /// Mixed
391    Mixed,
392    /// Boolean
393    Bool,
394    /// Pointer
395    Ptr,
396    /// Indirect (internal)
397    Indirect,
398}
399
400impl Default for DataType {
401    fn default() -> Self {
402        Self::Void
403    }
404}
405
406impl DataType {
407    /// Returns the integer representation of the data type.
408    #[must_use]
409    pub const fn as_u32(&self) -> u32 {
410        match self {
411            DataType::Undef => IS_UNDEF,
412            DataType::Null => IS_NULL,
413            DataType::False => IS_FALSE,
414            DataType::True => IS_TRUE,
415            DataType::Long => IS_LONG,
416            DataType::Double => IS_DOUBLE,
417            DataType::String => IS_STRING,
418            DataType::Array => IS_ARRAY,
419            DataType::Object(_) => IS_OBJECT,
420            DataType::Resource | DataType::Reference => IS_RESOURCE,
421            DataType::Indirect => IS_INDIRECT,
422            DataType::Callable => IS_CALLABLE,
423            DataType::ConstantExpression => IS_CONSTANT_AST,
424            DataType::Void => IS_VOID,
425            DataType::Mixed => IS_MIXED,
426            DataType::Bool => _IS_BOOL,
427            DataType::Ptr => IS_PTR,
428            DataType::Iterable => IS_ITERABLE,
429        }
430    }
431}
432
433// TODO: Ideally want something like this
434// pub struct Type {
435//     data_type: DataType,
436//     is_refcounted: bool,
437//     is_collectable: bool,
438//     is_immutable: bool,
439//     is_persistent: bool,
440// }
441//
442// impl From<u32> for Type { ... }
443
444impl TryFrom<ZvalTypeFlags> for DataType {
445    type Error = Error;
446
447    fn try_from(value: ZvalTypeFlags) -> Result<Self> {
448        macro_rules! contains {
449            ($t: ident) => {
450                if value.contains(ZvalTypeFlags::$t) {
451                    return Ok(DataType::$t);
452                }
453            };
454        }
455
456        contains!(Undef);
457        contains!(Null);
458        contains!(False);
459        contains!(True);
460        contains!(False);
461        contains!(Long);
462        contains!(Double);
463        contains!(String);
464        contains!(Array);
465        contains!(Resource);
466        contains!(Callable);
467        contains!(ConstantExpression);
468        contains!(Void);
469
470        if value.contains(ZvalTypeFlags::Object) {
471            return Ok(DataType::Object(None));
472        }
473
474        Err(Error::UnknownDatatype(0))
475    }
476}
477
478impl From<u32> for DataType {
479    #[allow(clippy::bad_bit_mask)]
480    fn from(value: u32) -> Self {
481        macro_rules! contains {
482            ($c: ident, $t: ident) => {
483                if (value & $c) == $c {
484                    return DataType::$t;
485                }
486            };
487        }
488
489        contains!(IS_VOID, Void);
490        contains!(IS_PTR, Ptr);
491        contains!(IS_INDIRECT, Indirect);
492        contains!(IS_CALLABLE, Callable);
493        contains!(IS_CONSTANT_AST, ConstantExpression);
494        contains!(IS_REFERENCE, Reference);
495        contains!(IS_RESOURCE, Resource);
496        contains!(IS_ARRAY, Array);
497        contains!(IS_STRING, String);
498        contains!(IS_DOUBLE, Double);
499        contains!(IS_LONG, Long);
500        contains!(IS_TRUE, True);
501        contains!(IS_FALSE, False);
502        contains!(IS_NULL, Null);
503
504        if (value & IS_OBJECT) == IS_OBJECT {
505            return DataType::Object(None);
506        }
507
508        contains!(IS_UNDEF, Undef);
509
510        DataType::Mixed
511    }
512}
513
514impl Display for DataType {
515    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
516        match self {
517            DataType::Undef => write!(f, "Undefined"),
518            DataType::Null => write!(f, "Null"),
519            DataType::False => write!(f, "False"),
520            DataType::True => write!(f, "True"),
521            DataType::Long => write!(f, "Long"),
522            DataType::Double => write!(f, "Double"),
523            DataType::String => write!(f, "String"),
524            DataType::Array => write!(f, "Array"),
525            DataType::Object(obj) => write!(f, "{}", obj.as_deref().unwrap_or("Object")),
526            DataType::Resource => write!(f, "Resource"),
527            DataType::Reference => write!(f, "Reference"),
528            DataType::Callable => write!(f, "Callable"),
529            DataType::ConstantExpression => write!(f, "Constant Expression"),
530            DataType::Void => write!(f, "Void"),
531            DataType::Bool => write!(f, "Bool"),
532            DataType::Mixed => write!(f, "Mixed"),
533            DataType::Ptr => write!(f, "Pointer"),
534            DataType::Indirect => write!(f, "Indirect"),
535            DataType::Iterable => write!(f, "Iterable"),
536        }
537    }
538}
539
540#[cfg(test)]
541mod tests {
542    #![allow(clippy::unnecessary_fallible_conversions)]
543    use super::DataType;
544    use crate::ffi::{
545        IS_ARRAY, IS_ARRAY_EX, IS_CONSTANT_AST, IS_CONSTANT_AST_EX, IS_DOUBLE, IS_FALSE,
546        IS_INDIRECT, IS_INTERNED_STRING_EX, IS_LONG, IS_NULL, IS_OBJECT, IS_OBJECT_EX, IS_PTR,
547        IS_REFERENCE, IS_REFERENCE_EX, IS_RESOURCE, IS_RESOURCE_EX, IS_STRING, IS_STRING_EX,
548        IS_TRUE, IS_UNDEF, IS_VOID,
549    };
550    use std::convert::TryFrom;
551
552    #[test]
553    fn test_datatype() {
554        macro_rules! test {
555            ($c: ident, $t: ident) => {
556                assert_eq!(DataType::try_from($c), Ok(DataType::$t));
557            };
558        }
559
560        test!(IS_UNDEF, Undef);
561        test!(IS_NULL, Null);
562        test!(IS_FALSE, False);
563        test!(IS_TRUE, True);
564        test!(IS_LONG, Long);
565        test!(IS_DOUBLE, Double);
566        test!(IS_STRING, String);
567        test!(IS_ARRAY, Array);
568        assert_eq!(DataType::try_from(IS_OBJECT), Ok(DataType::Object(None)));
569        test!(IS_RESOURCE, Resource);
570        test!(IS_REFERENCE, Reference);
571        test!(IS_CONSTANT_AST, ConstantExpression);
572        test!(IS_INDIRECT, Indirect);
573        test!(IS_VOID, Void);
574        test!(IS_PTR, Ptr);
575
576        test!(IS_INTERNED_STRING_EX, String);
577        test!(IS_STRING_EX, String);
578        test!(IS_ARRAY_EX, Array);
579        assert_eq!(DataType::try_from(IS_OBJECT_EX), Ok(DataType::Object(None)));
580        test!(IS_RESOURCE_EX, Resource);
581        test!(IS_REFERENCE_EX, Reference);
582        test!(IS_CONSTANT_AST_EX, ConstantExpression);
583    }
584}