1use crate::{Session, SessionGlobals, Span};
2use solar_data_structures::{index::BaseIndex32, trustme};
3use solar_macros::symbols;
4use std::{cmp, fmt, hash, str};
5
6#[derive(Clone, Copy)]
8pub struct Ident {
9 pub name: Symbol,
11 pub span: Span,
13}
14
15impl Default for Ident {
16 #[inline]
17 fn default() -> Self {
18 Self::DUMMY
19 }
20}
21
22impl PartialEq for Ident {
23 #[inline]
24 fn eq(&self, rhs: &Self) -> bool {
25 self.name == rhs.name
26 }
27}
28
29impl Eq for Ident {}
30
31impl PartialOrd for Ident {
32 #[inline]
33 fn partial_cmp(&self, rhs: &Self) -> Option<cmp::Ordering> {
34 Some(self.cmp(rhs))
35 }
36}
37
38impl Ord for Ident {
39 #[inline]
40 fn cmp(&self, rhs: &Self) -> cmp::Ordering {
41 self.name.cmp(&rhs.name)
42 }
43}
44
45impl hash::Hash for Ident {
46 #[inline]
47 fn hash<H: hash::Hasher>(&self, state: &mut H) {
48 self.name.hash(state);
49 }
50}
51
52impl fmt::Debug for Ident {
53 #[inline]
54 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55 fmt::Display::fmt(self, f)
56 }
57}
58
59impl fmt::Display for Ident {
60 #[inline]
61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 self.name.fmt(f)
63 }
64}
65
66impl Ident {
67 pub const DUMMY: Self = Self::new(Symbol::DUMMY, Span::DUMMY);
69
70 #[inline]
72 pub const fn new(name: Symbol, span: Span) -> Self {
73 Self { name, span }
74 }
75
76 #[inline]
78 pub const fn with_dummy_span(name: Symbol) -> Self {
79 Self::new(name, Span::DUMMY)
80 }
81
82 #[allow(clippy::should_implement_trait)]
84 pub fn from_str(string: &str) -> Self {
85 Self::with_dummy_span(Symbol::intern(string))
86 }
87
88 pub fn from_str_and_span(string: &str, span: Span) -> Self {
90 Self::new(Symbol::intern(string), span)
91 }
92
93 #[inline]
95 #[allow(clippy::inherent_to_string_shadow_display)]
96 #[cfg_attr(debug_assertions, track_caller)]
97 pub fn to_string(&self) -> String {
98 self.as_str().to_string()
99 }
100
101 #[cfg_attr(debug_assertions, track_caller)]
105 pub fn as_str(&self) -> &str {
106 self.name.as_str()
107 }
108
109 #[inline]
113 #[cfg_attr(debug_assertions, track_caller)]
114 pub fn as_str_in(self, session: &Session) -> &str {
115 self.name.as_str_in(session)
116 }
117
118 #[inline]
120 pub fn is_used_keyword(self) -> bool {
121 self.name.is_used_keyword()
122 }
123
124 #[inline]
126 pub fn is_unused_keyword(self) -> bool {
127 self.name.is_unused_keyword()
128 }
129
130 #[inline]
132 pub fn is_weak_keyword(self) -> bool {
133 self.name.is_weak_keyword()
134 }
135
136 #[inline]
138 pub fn is_yul_keyword(self) -> bool {
139 self.name.is_yul_keyword()
140 }
141
142 #[inline]
144 pub fn is_yul_evm_builtin(self) -> bool {
145 self.name.is_yul_builtin()
146 }
147
148 #[inline]
151 pub fn is_reserved(self, yul: bool) -> bool {
152 self.name.is_reserved(yul)
153 }
154
155 #[inline]
158 pub fn is_non_reserved(self, yul: bool) -> bool {
159 self.name.is_non_reserved(yul)
160 }
161
162 #[inline]
166 pub fn is_elementary_type(self) -> bool {
167 self.name.is_elementary_type()
168 }
169
170 #[inline]
172 pub fn is_bool_lit(self) -> bool {
173 self.name.is_bool_lit()
174 }
175
176 #[inline]
178 pub fn is_location_specifier(self) -> bool {
179 self.name.is_location_specifier()
180 }
181
182 #[inline]
184 pub fn is_mutability_specifier(self) -> bool {
185 self.name.is_mutability_specifier()
186 }
187
188 #[inline]
190 pub fn is_visibility_specifier(self) -> bool {
191 self.name.is_visibility_specifier()
192 }
193}
194
195#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
200pub struct Symbol(BaseIndex32);
201
202impl Default for Symbol {
203 #[inline]
204 fn default() -> Self {
205 Self::DUMMY
206 }
207}
208
209impl Symbol {
210 pub const DUMMY: Self = kw::Empty;
212
213 const fn new(n: u32) -> Self {
214 Self(BaseIndex32::new(n))
215 }
216
217 pub fn intern(string: &str) -> Self {
219 SessionGlobals::with(|g| g.symbol_interner.intern(string))
220 }
221
222 #[inline]
224 pub fn intern_in(string: &str, session: &Session) -> Self {
225 session.intern(string)
226 }
227
228 #[inline]
230 #[allow(clippy::inherent_to_string_shadow_display)]
231 pub fn to_string(&self) -> String {
232 self.as_str().to_string()
233 }
234
235 #[cfg_attr(debug_assertions, track_caller)]
243 pub fn as_str(&self) -> &str {
244 SessionGlobals::with(|g| unsafe { trustme::decouple_lt(g.symbol_interner.get(*self)) })
245 }
246
247 #[inline]
251 #[cfg_attr(debug_assertions, track_caller)]
252 pub fn as_str_in(self, session: &Session) -> &str {
253 session.resolve_symbol(self)
254 }
255
256 #[inline(always)]
258 pub const fn as_u32(self) -> u32 {
259 self.0.get()
260 }
261
262 #[inline]
266 pub fn is_used_keyword(self) -> bool {
267 self < kw::After
268 }
269
270 #[inline]
272 pub fn is_unused_keyword(self) -> bool {
273 self >= kw::After && self <= kw::Var
274 }
275
276 #[inline]
278 pub fn is_weak_keyword(self) -> bool {
279 self >= kw::Leave && self <= kw::Builtin
280 }
281
282 #[inline]
284 pub fn is_yul_keyword(self) -> bool {
285 matches!(
287 self,
288 kw::Function
289 | kw::Let
290 | kw::If
291 | kw::Switch
292 | kw::Case
293 | kw::Default
294 | kw::For
295 | kw::Break
296 | kw::Continue
297 | kw::Leave
298 | kw::True
299 | kw::False
300 )
301 }
302
303 #[inline]
305 pub fn is_yul_builtin(self) -> bool {
306 (self >= kw::Add && self <= kw::Xor)
307 | matches!(self, kw::Address | kw::Byte | kw::Return | kw::Revert)
308 }
309
310 #[inline]
313 pub fn is_reserved(self, yul: bool) -> bool {
314 if yul {
315 self.is_yul_keyword() | self.is_yul_builtin()
316 } else {
317 self.is_used_keyword() | self.is_unused_keyword()
318 }
319 }
320
321 #[inline]
324 pub fn is_non_reserved(self, yul: bool) -> bool {
325 !self.is_reserved(yul)
326 }
327
328 #[inline]
332 pub fn is_elementary_type(self) -> bool {
333 self >= kw::Int && self <= kw::UFixed
334 }
335
336 #[inline]
338 pub fn is_bool_lit(self) -> bool {
339 self == kw::False || self == kw::True
340 }
341
342 #[inline]
344 pub fn is_location_specifier(self) -> bool {
345 matches!(self, kw::Calldata | kw::Memory | kw::Storage)
346 }
347
348 #[inline]
350 pub fn is_mutability_specifier(self) -> bool {
351 matches!(self, kw::Immutable | kw::Constant)
352 }
353
354 #[inline]
356 pub fn is_visibility_specifier(self) -> bool {
357 matches!(self, kw::Public | kw::Private | kw::Internal | kw::External)
358 }
359
360 #[inline]
362 pub const fn is_preinterned(self) -> bool {
363 self.as_u32() < PREINTERNED_SYMBOLS_COUNT
364 }
365}
366
367impl fmt::Debug for Symbol {
368 #[inline]
369 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
370 fmt::Debug::fmt(self.as_str(), f)
371 }
372}
373
374impl fmt::Display for Symbol {
375 #[inline]
376 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
377 fmt::Display::fmt(self.as_str(), f)
378 }
379}
380
381#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
385pub struct ByteSymbol(BaseIndex32);
386
387impl ByteSymbol {
388 pub fn intern(byte_str: &[u8]) -> Self {
390 SessionGlobals::with(|g| g.symbol_interner.intern_byte_str(byte_str))
391 }
392
393 #[cfg_attr(debug_assertions, track_caller)]
397 pub fn as_byte_str(&self) -> &[u8] {
398 SessionGlobals::with(|g| unsafe {
399 trustme::decouple_lt(g.symbol_interner.get_byte_str(*self))
400 })
401 }
402
403 #[cfg_attr(debug_assertions, track_caller)]
407 pub fn as_byte_str_in(self, session: &Session) -> &[u8] {
408 session.resolve_byte_str(self)
409 }
410
411 #[inline(always)]
413 pub fn as_u32(self) -> u32 {
414 self.0.get()
415 }
416}
417
418impl fmt::Debug for ByteSymbol {
419 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
420 fmt::Debug::fmt(self.as_byte_str(), f)
421 }
422}
423
424pub(crate) struct Interner {
428 inner: inturn::BytesInterner<ByteSymbol, solar_data_structures::map::FxBuildHasher>,
429}
430
431impl Interner {
432 pub(crate) fn fresh() -> Self {
433 Self::prefill(PREINTERNED)
434 }
435
436 pub(crate) fn prefill(init: &[&'static str]) -> Self {
437 let mut inner =
438 inturn::BytesInterner::with_capacity_and_hasher(init.len() * 4, Default::default());
439 inner.intern_many_mut_static(init.iter().map(|s| s.as_bytes()));
440 Self { inner }
441 }
442
443 #[inline]
444 pub(crate) fn intern(&self, string: &str) -> Symbol {
445 Symbol(self.inner.intern(string.as_bytes()).0)
446 }
447
448 #[inline]
449 #[cfg_attr(debug_assertions, track_caller)]
450 pub(crate) fn get(&self, symbol: Symbol) -> &str {
451 let s = self.inner.resolve(ByteSymbol(symbol.0));
452 unsafe { std::str::from_utf8_unchecked(s) }
454 }
455
456 #[inline]
457 pub(crate) fn intern_byte_str(&self, byte_str: &[u8]) -> ByteSymbol {
458 self.inner.intern(byte_str)
459 }
460
461 #[inline]
462 #[cfg_attr(debug_assertions, track_caller)]
463 pub(crate) fn get_byte_str(&self, symbol: ByteSymbol) -> &[u8] {
464 self.inner.resolve(symbol)
465 }
466
467 fn trace_stats(&mut self) {
468 if enabled!(tracing::Level::TRACE) {
469 self.trace_stats_impl();
470 }
471 }
472
473 #[inline(never)]
474 fn trace_stats_impl(&mut self) {
475 let mut lengths = self.inner.iter().map(|(_, s)| s.len()).collect::<Vec<_>>();
476 lengths.sort_unstable();
477 let len = lengths.len();
478 assert!(len > 0);
479 let bytes = lengths.iter().copied().sum::<usize>();
480 trace!(
481 preinterned=PREINTERNED_SYMBOLS_COUNT,
482 len,
483 bytes,
484 max=lengths.last().copied().unwrap_or(0),
485 mean=%format!("{:.2}", (bytes as f64 / len as f64)),
486 median=%format!("{:.2}", if len.is_multiple_of(2) {
487 (lengths[len / 2 - 1] + lengths[len / 2]) as f64 / 2.0
488 } else {
489 lengths[len / 2] as f64
490 }),
491 "Interner stats",
492 );
493 }
494}
495
496impl inturn::InternerSymbol for ByteSymbol {
497 #[inline]
498 fn try_from_usize(n: usize) -> Option<Self> {
499 BaseIndex32::try_from_usize(n).map(Self)
500 }
501
502 #[inline]
503 fn to_usize(self) -> usize {
504 self.0.index()
505 }
506}
507
508impl Drop for Interner {
509 fn drop(&mut self) {
510 self.trace_stats();
511 }
512}
513
514pub mod kw {
520 use crate::Symbol;
521
522 #[doc(inline)]
523 pub use super::kw_generated::*;
524
525 #[inline]
527 pub const fn boolean(b: bool) -> Symbol {
528 if b { True } else { False }
529 }
530
531 #[inline]
539 #[track_caller]
540 pub const fn int(n: u8) -> Symbol {
541 assert!(n <= 32);
542 Symbol::new(Int.as_u32() + n as u32)
543 }
544
545 #[inline]
553 #[track_caller]
554 pub const fn uint(n: u8) -> Symbol {
555 assert!(n <= 32);
556 Symbol::new(UInt.as_u32() + n as u32)
557 }
558
559 #[inline]
565 #[track_caller]
566 pub const fn fixed_bytes(n: u8) -> Symbol {
567 assert!(n > 0 && n <= 32);
568 Symbol::new(Bytes.as_u32() + n as u32)
569 }
570}
571
572pub mod sym {
578 use super::Symbol;
579
580 #[doc(inline)]
581 pub use super::sym_generated::*;
582
583 pub fn integer<N: TryInto<usize> + Copy + itoa::Integer>(n: N) -> Symbol {
587 if let Ok(idx @ 0..=9) = n.try_into() {
588 return Symbol::new(super::SYMBOL_DIGITS_BASE + idx as u32);
589 }
590 Symbol::intern(itoa::Buffer::new().format(n))
591 }
592}
593
594symbols! {
596 Keywords {
600 Empty: "",
602
603 Abstract: "abstract",
604 Anonymous: "anonymous",
605 As: "as",
606 Assembly: "assembly",
607 Break: "break",
608 Calldata: "calldata",
609 Catch: "catch",
610 Constant: "constant",
611 Constructor: "constructor",
612 Continue: "continue",
613 Contract: "contract",
614 Delete: "delete",
615 Do: "do",
616 Else: "else",
617 Emit: "emit",
618 Enum: "enum",
619 Event: "event",
620 External: "external",
621 Fallback: "fallback",
622 For: "for",
623 Function: "function",
624 Hex: "hex",
625 If: "if",
626 Immutable: "immutable",
627 Import: "import",
628 Indexed: "indexed",
629 Interface: "interface",
630 Internal: "internal",
631 Is: "is",
632 Library: "library",
633 Mapping: "mapping",
634 Memory: "memory",
635 Modifier: "modifier",
636 New: "new",
637 Override: "override",
638 Payable: "payable",
639 Pragma: "pragma",
640 Private: "private",
641 Public: "public",
642 Pure: "pure",
643 Receive: "receive",
644 Return: "return",
645 Returns: "returns",
646 Storage: "storage",
647 Struct: "struct",
648 Throw: "throw",
649 Try: "try",
650 Type: "type",
651 Unchecked: "unchecked",
652 Unicode: "unicode",
653 Using: "using",
654 View: "view",
655 Virtual: "virtual",
656 While: "while",
657
658 Int: "int",
660 Int8: "int8",
661 Int16: "int16",
662 Int24: "int24",
663 Int32: "int32",
664 Int40: "int40",
665 Int48: "int48",
666 Int56: "int56",
667 Int64: "int64",
668 Int72: "int72",
669 Int80: "int80",
670 Int88: "int88",
671 Int96: "int96",
672 Int104: "int104",
673 Int112: "int112",
674 Int120: "int120",
675 Int128: "int128",
676 Int136: "int136",
677 Int144: "int144",
678 Int152: "int152",
679 Int160: "int160",
680 Int168: "int168",
681 Int176: "int176",
682 Int184: "int184",
683 Int192: "int192",
684 Int200: "int200",
685 Int208: "int208",
686 Int216: "int216",
687 Int224: "int224",
688 Int232: "int232",
689 Int240: "int240",
690 Int248: "int248",
691 Int256: "int256",
692 UInt: "uint",
693 UInt8: "uint8",
694 UInt16: "uint16",
695 UInt24: "uint24",
696 UInt32: "uint32",
697 UInt40: "uint40",
698 UInt48: "uint48",
699 UInt56: "uint56",
700 UInt64: "uint64",
701 UInt72: "uint72",
702 UInt80: "uint80",
703 UInt88: "uint88",
704 UInt96: "uint96",
705 UInt104: "uint104",
706 UInt112: "uint112",
707 UInt120: "uint120",
708 UInt128: "uint128",
709 UInt136: "uint136",
710 UInt144: "uint144",
711 UInt152: "uint152",
712 UInt160: "uint160",
713 UInt168: "uint168",
714 UInt176: "uint176",
715 UInt184: "uint184",
716 UInt192: "uint192",
717 UInt200: "uint200",
718 UInt208: "uint208",
719 UInt216: "uint216",
720 UInt224: "uint224",
721 UInt232: "uint232",
722 UInt240: "uint240",
723 UInt248: "uint248",
724 UInt256: "uint256",
725 Bytes: "bytes",
726 Bytes1: "bytes1",
727 Bytes2: "bytes2",
728 Bytes3: "bytes3",
729 Bytes4: "bytes4",
730 Bytes5: "bytes5",
731 Bytes6: "bytes6",
732 Bytes7: "bytes7",
733 Bytes8: "bytes8",
734 Bytes9: "bytes9",
735 Bytes10: "bytes10",
736 Bytes11: "bytes11",
737 Bytes12: "bytes12",
738 Bytes13: "bytes13",
739 Bytes14: "bytes14",
740 Bytes15: "bytes15",
741 Bytes16: "bytes16",
742 Bytes17: "bytes17",
743 Bytes18: "bytes18",
744 Bytes19: "bytes19",
745 Bytes20: "bytes20",
746 Bytes21: "bytes21",
747 Bytes22: "bytes22",
748 Bytes23: "bytes23",
749 Bytes24: "bytes24",
750 Bytes25: "bytes25",
751 Bytes26: "bytes26",
752 Bytes27: "bytes27",
753 Bytes28: "bytes28",
754 Bytes29: "bytes29",
755 Bytes30: "bytes30",
756 Bytes31: "bytes31",
757 Bytes32: "bytes32",
758 String: "string",
759 Address: "address",
760 Bool: "bool",
761 Fixed: "fixed",
762 UFixed: "ufixed",
763
764 Wei: "wei",
766 Gwei: "gwei",
767 Ether: "ether",
768 Seconds: "seconds",
769 Minutes: "minutes",
770 Hours: "hours",
771 Days: "days",
772 Weeks: "weeks",
773 Years: "years",
774
775 False: "false",
777 True: "true",
778
779 After: "after",
781 Alias: "alias",
782 Apply: "apply",
783 Auto: "auto",
784 Byte: "byte",
785 Case: "case",
786 CopyOf: "copyof",
787 Default: "default",
788 Define: "define",
789 Final: "final",
790 Implements: "implements",
791 In: "in",
792 Inline: "inline",
793 Let: "let",
794 Macro: "macro",
795 Match: "match",
796 Mutable: "mutable",
797 NullLiteral: "null",
798 Of: "of",
799 Partial: "partial",
800 Promise: "promise",
801 Reference: "reference",
802 Relocatable: "relocatable",
803 Sealed: "sealed",
804 Sizeof: "sizeof",
805 Static: "static",
806 Supports: "supports",
807 Switch: "switch",
808 Typedef: "typedef",
809 TypeOf: "typeof",
810 Var: "var",
811
812 Leave: "leave",
817 Revert: "revert",
818
819 Add: "add",
825 Addmod: "addmod",
826 And: "and",
827 Balance: "balance",
828 Basefee: "basefee",
829 Blobbasefee: "blobbasefee",
830 Blobhash: "blobhash",
831 Blockhash: "blockhash",
832 Call: "call",
833 Callcode: "callcode",
834 Calldatacopy: "calldatacopy",
835 Calldataload: "calldataload",
836 Calldatasize: "calldatasize",
837 Caller: "caller",
838 Callvalue: "callvalue",
839 Chainid: "chainid",
840 Coinbase: "coinbase",
841 Create: "create",
842 Create2: "create2",
843 Delegatecall: "delegatecall",
844 Difficulty: "difficulty",
845 Div: "div",
846 Eq: "eq",
847 Exp: "exp",
848 Extcodecopy: "extcodecopy",
849 Extcodehash: "extcodehash",
850 Extcodesize: "extcodesize",
851 Gas: "gas",
852 Gaslimit: "gaslimit",
853 Gasprice: "gasprice",
854 Gt: "gt",
855 Invalid: "invalid",
856 Iszero: "iszero",
857 Keccak256: "keccak256",
858 Log0: "log0",
859 Log1: "log1",
860 Log2: "log2",
861 Log3: "log3",
862 Log4: "log4",
863 Lt: "lt",
864 Mcopy: "mcopy",
865 Mload: "mload",
866 Mod: "mod",
867 Msize: "msize",
868 Mstore: "mstore",
869 Mstore8: "mstore8",
870 Mul: "mul",
871 Mulmod: "mulmod",
872 Not: "not",
873 Number: "number",
874 Or: "or",
875 Origin: "origin",
876 Pop: "pop",
877 Prevrandao: "prevrandao",
878 Returndatacopy: "returndatacopy",
879 Returndatasize: "returndatasize",
880 Sar: "sar",
881 Sdiv: "sdiv",
882 Selfbalance: "selfbalance",
883 Selfdestruct: "selfdestruct",
884 Sgt: "sgt",
885 Shl: "shl",
886 Shr: "shr",
887 Signextend: "signextend",
888 Sload: "sload",
889 Slt: "slt",
890 Smod: "smod",
891 Sstore: "sstore",
892 Staticcall: "staticcall",
893 Stop: "stop",
894 Sub: "sub",
895 Timestamp: "timestamp",
896 Tload: "tload",
897 Tstore: "tstore",
898 Xor: "xor",
899
900 Class: "class",
902 Instantiation: "instantiation",
903 Integer: "Integer",
904 Itself: "itself",
905 StaticAssert: "static_assert",
906 Builtin: "__builtin",
907 ForAll: "forall",
908 }
909
910 Symbols {
929 X,
930 __tmp_struct,
931 abi,
932 abicoder,
933 assert,
934 at,
935 block,
936 code,
937 codehash,
938 concat,
939 creationCode,
940 data,
941 decode,
942 ecrecover,
943 encode,
944 encodeCall,
945 encodePacked,
946 encodeWithSelector,
947 encodeWithSignature,
948 error,
949 experimental,
950 from,
951 gasleft,
952 global,
953 interfaceId,
954 layout,
955 length,
956 max,
957 memory_dash_safe: "memory-safe",
958 min,
959 msg,
960 name,
961 object,
962 push,
963 require,
964 ripemd160,
965 runtimeCode,
966 selector,
967 send,
968 sender,
969 sha256,
970 sig,
971 solidity,
972 super_: "super",
973 this,
974 transfer,
975 transient,
976 tx,
977 underscore: "_",
978 unwrap,
979 value,
980 wrap,
981 x,
982 }
983}
984
985#[cfg(test)]
986mod tests {
987 use super::*;
988
989 #[test]
990 fn interner_tests() {
991 let i = Interner::prefill(&[]);
992 assert_eq!(i.intern("dog"), Symbol::new(0));
994 assert_eq!(i.intern("dog"), Symbol::new(0));
996 assert_eq!(i.intern("cat"), Symbol::new(1));
998 assert_eq!(i.intern("cat"), Symbol::new(1));
999 assert_eq!(i.intern("dog"), Symbol::new(0));
1001 }
1002
1003 #[test]
1004 fn defaults() {
1005 assert_eq!(Symbol::DUMMY, Symbol::new(0));
1006 assert_eq!(Symbol::DUMMY, Symbol::default());
1007 assert_eq!(Ident::DUMMY, Ident::new(Symbol::DUMMY, Span::DUMMY));
1008 assert_eq!(Ident::DUMMY, Ident::default());
1009
1010 crate::enter(|| {
1011 assert_eq!(Symbol::DUMMY.as_str(), "");
1012 assert_eq!(Symbol::DUMMY.to_string(), "");
1013 assert_eq!(Ident::DUMMY.as_str(), "");
1014 assert_eq!(Ident::DUMMY.to_string(), "");
1015 });
1016 }
1017}