]>
Commit | Line | Data |
---|---|---|
cc61c64b XL |
1 | //! This module contains `HashStable` implementations for various data types |
2 | //! from libsyntax in no particular order. | |
3 | ||
9fa01778 | 4 | use crate::ich::StableHashingContext; |
cc61c64b XL |
5 | |
6 | use std::hash as std_hash; | |
7 | use std::mem; | |
8 | ||
9 | use syntax::ast; | |
0531ce1d | 10 | use syntax::feature_gate; |
cc61c64b | 11 | use syntax::parse::token; |
83c7162d | 12 | use syntax::symbol::{InternedString, LocalInternedString}; |
cc61c64b | 13 | use syntax::tokenstream; |
b7449926 | 14 | use syntax_pos::SourceFile; |
7cac9316 | 15 | |
9fa01778 | 16 | use crate::hir::def_id::{DefId, CrateNum, CRATE_DEF_INDEX}; |
cc61c64b | 17 | |
b7449926 | 18 | use smallvec::SmallVec; |
ea8adc8c XL |
19 | use rustc_data_structures::stable_hasher::{HashStable, ToStableHashKey, |
20 | StableHasher, StableHasherResult}; | |
cc61c64b | 21 | |
0531ce1d | 22 | impl<'a> HashStable<StableHashingContext<'a>> for InternedString { |
cc61c64b XL |
23 | #[inline] |
24 | fn hash_stable<W: StableHasherResult>(&self, | |
0531ce1d | 25 | hcx: &mut StableHashingContext<'a>, |
cc61c64b | 26 | hasher: &mut StableHasher<W>) { |
83c7162d | 27 | self.with(|s| s.hash_stable(hcx, hasher)) |
cc61c64b XL |
28 | } |
29 | } | |
30 | ||
0531ce1d | 31 | impl<'a> ToStableHashKey<StableHashingContext<'a>> for InternedString { |
ea8adc8c XL |
32 | type KeyType = InternedString; |
33 | ||
34 | #[inline] | |
35 | fn to_stable_hash_key(&self, | |
0531ce1d | 36 | _: &StableHashingContext<'a>) |
ea8adc8c XL |
37 | -> InternedString { |
38 | self.clone() | |
39 | } | |
40 | } | |
41 | ||
83c7162d XL |
42 | impl<'a> HashStable<StableHashingContext<'a>> for LocalInternedString { |
43 | #[inline] | |
44 | fn hash_stable<W: StableHasherResult>(&self, | |
45 | hcx: &mut StableHashingContext<'a>, | |
46 | hasher: &mut StableHasher<W>) { | |
47 | let s: &str = &**self; | |
48 | s.hash_stable(hcx, hasher); | |
49 | } | |
50 | } | |
51 | ||
52 | impl<'a> ToStableHashKey<StableHashingContext<'a>> for LocalInternedString { | |
53 | type KeyType = LocalInternedString; | |
54 | ||
55 | #[inline] | |
56 | fn to_stable_hash_key(&self, | |
57 | _: &StableHashingContext<'a>) | |
58 | -> LocalInternedString { | |
59 | self.clone() | |
60 | } | |
61 | } | |
62 | ||
0531ce1d | 63 | impl<'a> HashStable<StableHashingContext<'a>> for ast::Name { |
cc61c64b XL |
64 | #[inline] |
65 | fn hash_stable<W: StableHasherResult>(&self, | |
0531ce1d | 66 | hcx: &mut StableHashingContext<'a>, |
cc61c64b XL |
67 | hasher: &mut StableHasher<W>) { |
68 | self.as_str().hash_stable(hcx, hasher); | |
69 | } | |
70 | } | |
71 | ||
0531ce1d | 72 | impl<'a> ToStableHashKey<StableHashingContext<'a>> for ast::Name { |
ea8adc8c XL |
73 | type KeyType = InternedString; |
74 | ||
75 | #[inline] | |
76 | fn to_stable_hash_key(&self, | |
0531ce1d | 77 | _: &StableHashingContext<'a>) |
ea8adc8c | 78 | -> InternedString { |
83c7162d | 79 | self.as_interned_str() |
ea8adc8c XL |
80 | } |
81 | } | |
82 | ||
cc61c64b XL |
83 | impl_stable_hash_for!(enum ::syntax::ast::AsmDialect { |
84 | Att, | |
85 | Intel | |
86 | }); | |
87 | ||
88 | impl_stable_hash_for!(enum ::syntax::ext::base::MacroKind { | |
89 | Bang, | |
90 | Attr, | |
8faf50e0 XL |
91 | Derive, |
92 | ProcMacroStub, | |
cc61c64b XL |
93 | }); |
94 | ||
95 | ||
83c7162d | 96 | impl_stable_hash_for!(enum ::rustc_target::spec::abi::Abi { |
cc61c64b XL |
97 | Cdecl, |
98 | Stdcall, | |
99 | Fastcall, | |
100 | Vectorcall, | |
7cac9316 | 101 | Thiscall, |
cc61c64b XL |
102 | Aapcs, |
103 | Win64, | |
104 | SysV64, | |
105 | PtxKernel, | |
106 | Msp430Interrupt, | |
107 | X86Interrupt, | |
8faf50e0 | 108 | AmdGpuKernel, |
cc61c64b XL |
109 | Rust, |
110 | C, | |
111 | System, | |
112 | RustIntrinsic, | |
113 | RustCall, | |
114 | PlatformIntrinsic, | |
115 | Unadjusted | |
116 | }); | |
117 | ||
118 | impl_stable_hash_for!(struct ::syntax::attr::Deprecation { since, note }); | |
ea8adc8c XL |
119 | impl_stable_hash_for!(struct ::syntax::attr::Stability { |
120 | level, | |
121 | feature, | |
122 | rustc_depr, | |
0bf4aa26 | 123 | promotable, |
b7449926 | 124 | const_stability |
ea8adc8c | 125 | }); |
cc61c64b | 126 | |
0731742a XL |
127 | impl_stable_hash_for!(enum ::syntax::edition::Edition { |
128 | Edition2015, | |
129 | Edition2018, | |
130 | }); | |
94b46f34 | 131 | |
0531ce1d | 132 | impl<'a> HashStable<StableHashingContext<'a>> |
041b39d2 | 133 | for ::syntax::attr::StabilityLevel { |
cc61c64b | 134 | fn hash_stable<W: StableHasherResult>(&self, |
0531ce1d | 135 | hcx: &mut StableHashingContext<'a>, |
cc61c64b XL |
136 | hasher: &mut StableHasher<W>) { |
137 | mem::discriminant(self).hash_stable(hcx, hasher); | |
138 | match *self { | |
139 | ::syntax::attr::StabilityLevel::Unstable { ref reason, ref issue } => { | |
140 | reason.hash_stable(hcx, hasher); | |
141 | issue.hash_stable(hcx, hasher); | |
142 | } | |
143 | ::syntax::attr::StabilityLevel::Stable { ref since } => { | |
144 | since.hash_stable(hcx, hasher); | |
145 | } | |
146 | } | |
147 | } | |
148 | } | |
149 | ||
9fa01778 | 150 | impl_stable_hash_for!(struct ::syntax::attr::RustcDeprecation { since, reason, suggestion }); |
cc61c64b XL |
151 | |
152 | ||
153 | impl_stable_hash_for!(enum ::syntax::attr::IntType { | |
154 | SignedInt(int_ty), | |
155 | UnsignedInt(uint_ty) | |
156 | }); | |
157 | ||
158 | impl_stable_hash_for!(enum ::syntax::ast::LitIntType { | |
159 | Signed(int_ty), | |
160 | Unsigned(int_ty), | |
161 | Unsuffixed | |
162 | }); | |
163 | ||
164 | impl_stable_hash_for_spanned!(::syntax::ast::LitKind); | |
165 | impl_stable_hash_for!(enum ::syntax::ast::LitKind { | |
166 | Str(value, style), | |
9fa01778 | 167 | Err(value), |
cc61c64b XL |
168 | ByteStr(value), |
169 | Byte(value), | |
170 | Char(value), | |
171 | Int(value, lit_int_type), | |
172 | Float(value, float_ty), | |
173 | FloatUnsuffixed(value), | |
174 | Bool(value) | |
175 | }); | |
176 | ||
2c00a5a8 XL |
177 | impl_stable_hash_for!(enum ::syntax::ast::IntTy { Isize, I8, I16, I32, I64, I128 }); |
178 | impl_stable_hash_for!(enum ::syntax::ast::UintTy { Usize, U8, U16, U32, U64, U128 }); | |
cc61c64b XL |
179 | impl_stable_hash_for!(enum ::syntax::ast::FloatTy { F32, F64 }); |
180 | impl_stable_hash_for!(enum ::syntax::ast::Unsafety { Unsafe, Normal }); | |
181 | impl_stable_hash_for!(enum ::syntax::ast::Constness { Const, NotConst }); | |
182 | impl_stable_hash_for!(enum ::syntax::ast::Defaultness { Default, Final }); | |
83c7162d | 183 | impl_stable_hash_for!(struct ::syntax::ast::Lifetime { id, ident }); |
cc61c64b XL |
184 | impl_stable_hash_for!(enum ::syntax::ast::StrStyle { Cooked, Raw(pounds) }); |
185 | impl_stable_hash_for!(enum ::syntax::ast::AttrStyle { Outer, Inner }); | |
186 | ||
0531ce1d | 187 | impl<'a> HashStable<StableHashingContext<'a>> for [ast::Attribute] { |
cc61c64b | 188 | fn hash_stable<W: StableHasherResult>(&self, |
0531ce1d | 189 | hcx: &mut StableHashingContext<'a>, |
cc61c64b | 190 | hasher: &mut StableHasher<W>) { |
ea8adc8c XL |
191 | if self.len() == 0 { |
192 | self.len().hash_stable(hcx, hasher); | |
193 | return | |
194 | } | |
195 | ||
cc61c64b | 196 | // Some attributes are always ignored during hashing. |
b7449926 | 197 | let filtered: SmallVec<[&ast::Attribute; 8]> = self |
cc61c64b XL |
198 | .iter() |
199 | .filter(|attr| { | |
9fa01778 XL |
200 | !attr.is_sugared_doc && |
201 | !attr.ident().map_or(false, |ident| hcx.is_ignored_attr(ident.name)) | |
cc61c64b XL |
202 | }) |
203 | .collect(); | |
204 | ||
205 | filtered.len().hash_stable(hcx, hasher); | |
206 | for attr in filtered { | |
207 | attr.hash_stable(hcx, hasher); | |
208 | } | |
209 | } | |
210 | } | |
211 | ||
83c7162d XL |
212 | impl<'a> HashStable<StableHashingContext<'a>> for ast::Path { |
213 | fn hash_stable<W: StableHasherResult>(&self, | |
214 | hcx: &mut StableHashingContext<'a>, | |
215 | hasher: &mut StableHasher<W>) { | |
216 | self.segments.len().hash_stable(hcx, hasher); | |
217 | for segment in &self.segments { | |
218 | segment.ident.name.hash_stable(hcx, hasher); | |
219 | } | |
220 | } | |
221 | } | |
222 | ||
0531ce1d | 223 | impl<'a> HashStable<StableHashingContext<'a>> for ast::Attribute { |
cc61c64b | 224 | fn hash_stable<W: StableHasherResult>(&self, |
0531ce1d | 225 | hcx: &mut StableHashingContext<'a>, |
cc61c64b XL |
226 | hasher: &mut StableHasher<W>) { |
227 | // Make sure that these have been filtered out. | |
9fa01778 | 228 | debug_assert!(!self.ident().map_or(false, |ident| hcx.is_ignored_attr(ident.name))); |
cc61c64b XL |
229 | debug_assert!(!self.is_sugared_doc); |
230 | ||
231 | let ast::Attribute { | |
232 | id: _, | |
233 | style, | |
234 | ref path, | |
235 | ref tokens, | |
236 | is_sugared_doc: _, | |
237 | span, | |
238 | } = *self; | |
239 | ||
240 | style.hash_stable(hcx, hasher); | |
83c7162d | 241 | path.hash_stable(hcx, hasher); |
cc61c64b XL |
242 | for tt in tokens.trees() { |
243 | tt.hash_stable(hcx, hasher); | |
244 | } | |
245 | span.hash_stable(hcx, hasher); | |
246 | } | |
247 | } | |
248 | ||
0531ce1d | 249 | impl<'a> HashStable<StableHashingContext<'a>> |
041b39d2 | 250 | for tokenstream::TokenTree { |
cc61c64b | 251 | fn hash_stable<W: StableHasherResult>(&self, |
0531ce1d | 252 | hcx: &mut StableHashingContext<'a>, |
cc61c64b XL |
253 | hasher: &mut StableHasher<W>) { |
254 | mem::discriminant(self).hash_stable(hcx, hasher); | |
255 | match *self { | |
256 | tokenstream::TokenTree::Token(span, ref token) => { | |
257 | span.hash_stable(hcx, hasher); | |
ea8adc8c | 258 | hash_token(token, hcx, hasher); |
cc61c64b | 259 | } |
0731742a | 260 | tokenstream::TokenTree::Delimited(span, delim, ref tts) => { |
cc61c64b | 261 | span.hash_stable(hcx, hasher); |
0731742a | 262 | std_hash::Hash::hash(&delim, hasher); |
9fa01778 | 263 | for sub_tt in tts.trees() { |
cc61c64b XL |
264 | sub_tt.hash_stable(hcx, hasher); |
265 | } | |
266 | } | |
267 | } | |
268 | } | |
269 | } | |
270 | ||
0531ce1d | 271 | impl<'a> HashStable<StableHashingContext<'a>> |
041b39d2 | 272 | for tokenstream::TokenStream { |
cc61c64b | 273 | fn hash_stable<W: StableHasherResult>(&self, |
0531ce1d | 274 | hcx: &mut StableHashingContext<'a>, |
cc61c64b XL |
275 | hasher: &mut StableHasher<W>) { |
276 | for sub_tt in self.trees() { | |
277 | sub_tt.hash_stable(hcx, hasher); | |
278 | } | |
279 | } | |
280 | } | |
281 | ||
0531ce1d XL |
282 | fn hash_token<'a, 'gcx, W: StableHasherResult>( |
283 | token: &token::Token, | |
284 | hcx: &mut StableHashingContext<'a>, | |
285 | hasher: &mut StableHasher<W>, | |
286 | ) { | |
cc61c64b XL |
287 | mem::discriminant(token).hash_stable(hcx, hasher); |
288 | match *token { | |
289 | token::Token::Eq | | |
290 | token::Token::Lt | | |
291 | token::Token::Le | | |
292 | token::Token::EqEq | | |
293 | token::Token::Ne | | |
294 | token::Token::Ge | | |
295 | token::Token::Gt | | |
296 | token::Token::AndAnd | | |
297 | token::Token::OrOr | | |
298 | token::Token::Not | | |
299 | token::Token::Tilde | | |
300 | token::Token::At | | |
301 | token::Token::Dot | | |
302 | token::Token::DotDot | | |
303 | token::Token::DotDotDot | | |
ea8adc8c | 304 | token::Token::DotDotEq | |
cc61c64b XL |
305 | token::Token::Comma | |
306 | token::Token::Semi | | |
307 | token::Token::Colon | | |
308 | token::Token::ModSep | | |
309 | token::Token::RArrow | | |
310 | token::Token::LArrow | | |
311 | token::Token::FatArrow | | |
312 | token::Token::Pound | | |
313 | token::Token::Dollar | | |
314 | token::Token::Question | | |
94b46f34 | 315 | token::Token::SingleQuote | |
cc61c64b XL |
316 | token::Token::Whitespace | |
317 | token::Token::Comment | | |
318 | token::Token::Eof => {} | |
319 | ||
320 | token::Token::BinOp(bin_op_token) | | |
321 | token::Token::BinOpEq(bin_op_token) => { | |
322 | std_hash::Hash::hash(&bin_op_token, hasher); | |
323 | } | |
324 | ||
325 | token::Token::OpenDelim(delim_token) | | |
326 | token::Token::CloseDelim(delim_token) => { | |
327 | std_hash::Hash::hash(&delim_token, hasher); | |
328 | } | |
329 | token::Token::Literal(ref lit, ref opt_name) => { | |
330 | mem::discriminant(lit).hash_stable(hcx, hasher); | |
331 | match *lit { | |
332 | token::Lit::Byte(val) | | |
333 | token::Lit::Char(val) | | |
9fa01778 | 334 | token::Lit::Err(val) | |
cc61c64b XL |
335 | token::Lit::Integer(val) | |
336 | token::Lit::Float(val) | | |
337 | token::Lit::Str_(val) | | |
338 | token::Lit::ByteStr(val) => val.hash_stable(hcx, hasher), | |
339 | token::Lit::StrRaw(val, n) | | |
340 | token::Lit::ByteStrRaw(val, n) => { | |
341 | val.hash_stable(hcx, hasher); | |
342 | n.hash_stable(hcx, hasher); | |
343 | } | |
344 | }; | |
345 | opt_name.hash_stable(hcx, hasher); | |
346 | } | |
347 | ||
0531ce1d XL |
348 | token::Token::Ident(ident, is_raw) => { |
349 | ident.name.hash_stable(hcx, hasher); | |
350 | is_raw.hash_stable(hcx, hasher); | |
351 | } | |
041b39d2 | 352 | token::Token::Lifetime(ident) => ident.name.hash_stable(hcx, hasher), |
cc61c64b | 353 | |
ea8adc8c XL |
354 | token::Token::Interpolated(_) => { |
355 | bug!("interpolated tokens should not be present in the HIR") | |
cc61c64b XL |
356 | } |
357 | ||
358 | token::Token::DocComment(val) | | |
359 | token::Token::Shebang(val) => val.hash_stable(hcx, hasher), | |
360 | } | |
361 | } | |
7cac9316 XL |
362 | |
363 | impl_stable_hash_for_spanned!(::syntax::ast::NestedMetaItemKind); | |
364 | ||
365 | impl_stable_hash_for!(enum ::syntax::ast::NestedMetaItemKind { | |
366 | MetaItem(meta_item), | |
367 | Literal(lit) | |
368 | }); | |
369 | ||
370 | impl_stable_hash_for!(struct ::syntax::ast::MetaItem { | |
83c7162d | 371 | ident, |
7cac9316 XL |
372 | node, |
373 | span | |
374 | }); | |
375 | ||
376 | impl_stable_hash_for!(enum ::syntax::ast::MetaItemKind { | |
377 | Word, | |
378 | List(nested_items), | |
379 | NameValue(lit) | |
380 | }); | |
381 | ||
ff7c6d11 XL |
382 | impl_stable_hash_for!(struct ::syntax_pos::hygiene::ExpnInfo { |
383 | call_site, | |
8faf50e0 | 384 | def_site, |
ff7c6d11 XL |
385 | format, |
386 | allow_internal_unstable, | |
387 | allow_internal_unsafe, | |
8faf50e0 XL |
388 | local_inner_macros, |
389 | edition | |
ff7c6d11 XL |
390 | }); |
391 | ||
392 | impl_stable_hash_for!(enum ::syntax_pos::hygiene::ExpnFormat { | |
393 | MacroAttribute(sym), | |
394 | MacroBang(sym), | |
395 | CompilerDesugaring(kind) | |
396 | }); | |
397 | ||
398 | impl_stable_hash_for!(enum ::syntax_pos::hygiene::CompilerDesugaringKind { | |
8faf50e0 | 399 | Async, |
83c7162d | 400 | QuestionMark, |
94b46f34 | 401 | ExistentialReturnType, |
8faf50e0 | 402 | ForLoop, |
b7449926 | 403 | TryBlock |
ff7c6d11 XL |
404 | }); |
405 | ||
406 | impl_stable_hash_for!(enum ::syntax_pos::FileName { | |
407 | Real(pb), | |
408 | Macros(s), | |
0731742a XL |
409 | QuoteExpansion(s), |
410 | Anon(s), | |
411 | MacroExpansion(s), | |
412 | ProcMacroSourceCode(s), | |
413 | CliCrateAttr(s), | |
414 | CfgSpec(s), | |
415 | Custom(s), | |
416 | DocTest(pb, line), | |
ff7c6d11 XL |
417 | }); |
418 | ||
b7449926 | 419 | impl<'a> HashStable<StableHashingContext<'a>> for SourceFile { |
7cac9316 | 420 | fn hash_stable<W: StableHasherResult>(&self, |
0531ce1d | 421 | hcx: &mut StableHashingContext<'a>, |
7cac9316 | 422 | hasher: &mut StableHasher<W>) { |
b7449926 | 423 | let SourceFile { |
ff7c6d11 XL |
424 | name: _, // We hash the smaller name_hash instead of this |
425 | name_hash, | |
7cac9316 | 426 | name_was_remapped, |
ea8adc8c | 427 | unmapped_path: _, |
7cac9316 XL |
428 | crate_of_origin, |
429 | // Do not hash the source as it is not encoded | |
430 | src: _, | |
041b39d2 XL |
431 | src_hash, |
432 | external_src: _, | |
7cac9316 XL |
433 | start_pos, |
434 | end_pos: _, | |
435 | ref lines, | |
436 | ref multibyte_chars, | |
abe05a73 | 437 | ref non_narrow_chars, |
7cac9316 XL |
438 | } = *self; |
439 | ||
ff7c6d11 | 440 | (name_hash as u64).hash_stable(hcx, hasher); |
7cac9316 XL |
441 | name_was_remapped.hash_stable(hcx, hasher); |
442 | ||
443 | DefId { | |
444 | krate: CrateNum::from_u32(crate_of_origin), | |
445 | index: CRATE_DEF_INDEX, | |
446 | }.hash_stable(hcx, hasher); | |
447 | ||
041b39d2 XL |
448 | src_hash.hash_stable(hcx, hasher); |
449 | ||
b7449926 | 450 | // We only hash the relative position within this source_file |
8faf50e0 XL |
451 | lines.len().hash_stable(hcx, hasher); |
452 | for &line in lines.iter() { | |
453 | stable_byte_pos(line, start_pos).hash_stable(hcx, hasher); | |
454 | } | |
7cac9316 | 455 | |
b7449926 | 456 | // We only hash the relative position within this source_file |
8faf50e0 XL |
457 | multibyte_chars.len().hash_stable(hcx, hasher); |
458 | for &char_pos in multibyte_chars.iter() { | |
459 | stable_multibyte_char(char_pos, start_pos).hash_stable(hcx, hasher); | |
460 | } | |
abe05a73 | 461 | |
8faf50e0 XL |
462 | non_narrow_chars.len().hash_stable(hcx, hasher); |
463 | for &char_pos in non_narrow_chars.iter() { | |
464 | stable_non_narrow_char(char_pos, start_pos).hash_stable(hcx, hasher); | |
465 | } | |
7cac9316 XL |
466 | } |
467 | } | |
468 | ||
469 | fn stable_byte_pos(pos: ::syntax_pos::BytePos, | |
b7449926 | 470 | source_file_start: ::syntax_pos::BytePos) |
7cac9316 | 471 | -> u32 { |
b7449926 | 472 | pos.0 - source_file_start.0 |
7cac9316 XL |
473 | } |
474 | ||
475 | fn stable_multibyte_char(mbc: ::syntax_pos::MultiByteChar, | |
b7449926 | 476 | source_file_start: ::syntax_pos::BytePos) |
7cac9316 XL |
477 | -> (u32, u32) { |
478 | let ::syntax_pos::MultiByteChar { | |
479 | pos, | |
480 | bytes, | |
481 | } = mbc; | |
482 | ||
b7449926 | 483 | (pos.0 - source_file_start.0, bytes as u32) |
7cac9316 | 484 | } |
abe05a73 XL |
485 | |
486 | fn stable_non_narrow_char(swc: ::syntax_pos::NonNarrowChar, | |
b7449926 | 487 | source_file_start: ::syntax_pos::BytePos) |
abe05a73 XL |
488 | -> (u32, u32) { |
489 | let pos = swc.pos(); | |
490 | let width = swc.width(); | |
491 | ||
b7449926 | 492 | (pos.0 - source_file_start.0, width as u32) |
abe05a73 | 493 | } |
0531ce1d XL |
494 | |
495 | ||
496 | ||
497 | impl<'gcx> HashStable<StableHashingContext<'gcx>> for feature_gate::Features { | |
498 | fn hash_stable<W: StableHasherResult>(&self, | |
499 | hcx: &mut StableHashingContext<'gcx>, | |
500 | hasher: &mut StableHasher<W>) { | |
501 | // Unfortunately we cannot exhaustively list fields here, since the | |
502 | // struct is macro generated. | |
b7449926 | 503 | self.declared_lang_features.hash_stable(hcx, hasher); |
0531ce1d XL |
504 | self.declared_lib_features.hash_stable(hcx, hasher); |
505 | ||
506 | self.walk_feature_fields(|feature_name, value| { | |
507 | feature_name.hash_stable(hcx, hasher); | |
508 | value.hash_stable(hcx, hasher); | |
509 | }); | |
510 | } | |
511 | } |