]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_symbol_mangling/src/v0.rs
Rollup merge of #80850 - m-ou-se:rustc-builtin-macro-name, r=petrochenkov
[rust.git] / compiler / rustc_symbol_mangling / src / v0.rs
1 use rustc_ast::{FloatTy, IntTy, UintTy};
2 use rustc_data_structures::base_n;
3 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
4 use rustc_hir as hir;
5 use rustc_hir::def_id::{CrateNum, DefId};
6 use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
7 use rustc_middle::ty::print::{Print, Printer};
8 use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst};
9 use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable};
10 use rustc_target::spec::abi::Abi;
11
12 use std::fmt::Write;
13 use std::ops::Range;
14
15 pub(super) fn mangle(
16     tcx: TyCtxt<'tcx>,
17     instance: Instance<'tcx>,
18     instantiating_crate: Option<CrateNum>,
19 ) -> String {
20     let def_id = instance.def_id();
21     // FIXME(eddyb) this should ideally not be needed.
22     let substs = tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), instance.substs);
23
24     let prefix = "_R";
25     let mut cx = SymbolMangler {
26         tcx,
27         compress: Some(Box::new(CompressionCaches {
28             start_offset: prefix.len(),
29
30             paths: FxHashMap::default(),
31             types: FxHashMap::default(),
32             consts: FxHashMap::default(),
33         })),
34         binders: vec![],
35         out: String::from(prefix),
36     };
37
38     // Append `::{shim:...#0}` to shims that can coexist with a non-shim instance.
39     let shim_kind = match instance.def {
40         ty::InstanceDef::VtableShim(_) => Some("vtable"),
41         ty::InstanceDef::ReifyShim(_) => Some("reify"),
42
43         _ => None,
44     };
45
46     cx = if let Some(shim_kind) = shim_kind {
47         cx.path_append_ns(|cx| cx.print_def_path(def_id, substs), 'S', 0, shim_kind).unwrap()
48     } else {
49         cx.print_def_path(def_id, substs).unwrap()
50     };
51     if let Some(instantiating_crate) = instantiating_crate {
52         cx = cx.print_def_path(instantiating_crate.as_def_id(), &[]).unwrap();
53     }
54     cx.out
55 }
56
57 struct CompressionCaches<'tcx> {
58     // The length of the prefix in `out` (e.g. 2 for `_R`).
59     start_offset: usize,
60
61     // The values are start positions in `out`, in bytes.
62     paths: FxHashMap<(DefId, &'tcx [GenericArg<'tcx>]), usize>,
63     types: FxHashMap<Ty<'tcx>, usize>,
64     consts: FxHashMap<&'tcx ty::Const<'tcx>, usize>,
65 }
66
67 struct BinderLevel {
68     /// The range of distances from the root of what's
69     /// being printed, to the lifetimes in a binder.
70     /// Specifically, a `BrAnon(i)` lifetime has depth
71     /// `lifetime_depths.start + i`, going away from the
72     /// the root and towards its use site, as `i` increases.
73     /// This is used to flatten rustc's pairing of `BrAnon`
74     /// (intra-binder disambiguation) with a `DebruijnIndex`
75     /// (binder addressing), to "true" de Bruijn indices,
76     /// by subtracting the depth of a certain lifetime, from
77     /// the innermost depth at its use site.
78     lifetime_depths: Range<u32>,
79 }
80
81 struct SymbolMangler<'tcx> {
82     tcx: TyCtxt<'tcx>,
83     compress: Option<Box<CompressionCaches<'tcx>>>,
84     binders: Vec<BinderLevel>,
85     out: String,
86 }
87
88 impl SymbolMangler<'tcx> {
89     fn push(&mut self, s: &str) {
90         self.out.push_str(s);
91     }
92
93     /// Push a `_`-terminated base 62 integer, using the format
94     /// specified in the RFC as `<base-62-number>`, that is:
95     /// * `x = 0` is encoded as just the `"_"` terminator
96     /// * `x > 0` is encoded as `x - 1` in base 62, followed by `"_"`,
97     ///   e.g. `1` becomes `"0_"`, `62` becomes `"Z_"`, etc.
98     fn push_integer_62(&mut self, x: u64) {
99         if let Some(x) = x.checked_sub(1) {
100             base_n::push_str(x as u128, 62, &mut self.out);
101         }
102         self.push("_");
103     }
104
105     /// Push a `tag`-prefixed base 62 integer, when larger than `0`, that is:
106     /// * `x = 0` is encoded as `""` (nothing)
107     /// * `x > 0` is encoded as the `tag` followed by `push_integer_62(x - 1)`
108     ///   e.g. `1` becomes `tag + "_"`, `2` becomes `tag + "0_"`, etc.
109     fn push_opt_integer_62(&mut self, tag: &str, x: u64) {
110         if let Some(x) = x.checked_sub(1) {
111             self.push(tag);
112             self.push_integer_62(x);
113         }
114     }
115
116     fn push_disambiguator(&mut self, dis: u64) {
117         self.push_opt_integer_62("s", dis);
118     }
119
120     fn push_ident(&mut self, ident: &str) {
121         let mut use_punycode = false;
122         for b in ident.bytes() {
123             match b {
124                 b'_' | b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' => {}
125                 0x80..=0xff => use_punycode = true,
126                 _ => bug!("symbol_names: bad byte {} in ident {:?}", b, ident),
127             }
128         }
129
130         let punycode_string;
131         let ident = if use_punycode {
132             self.push("u");
133
134             // FIXME(eddyb) we should probably roll our own punycode implementation.
135             let mut punycode_bytes = match punycode::encode(ident) {
136                 Ok(s) => s.into_bytes(),
137                 Err(()) => bug!("symbol_names: punycode encoding failed for ident {:?}", ident),
138             };
139
140             // Replace `-` with `_`.
141             if let Some(c) = punycode_bytes.iter_mut().rfind(|&&mut c| c == b'-') {
142                 *c = b'_';
143             }
144
145             // FIXME(eddyb) avoid rechecking UTF-8 validity.
146             punycode_string = String::from_utf8(punycode_bytes).unwrap();
147             &punycode_string
148         } else {
149             ident
150         };
151
152         let _ = write!(self.out, "{}", ident.len());
153
154         // Write a separating `_` if necessary (leading digit or `_`).
155         if let Some('_' | '0'..='9') = ident.chars().next() {
156             self.push("_");
157         }
158
159         self.push(ident);
160     }
161
162     fn path_append_ns(
163         mut self,
164         print_prefix: impl FnOnce(Self) -> Result<Self, !>,
165         ns: char,
166         disambiguator: u64,
167         name: &str,
168     ) -> Result<Self, !> {
169         self.push("N");
170         self.out.push(ns);
171         self = print_prefix(self)?;
172         self.push_disambiguator(disambiguator as u64);
173         self.push_ident(name);
174         Ok(self)
175     }
176
177     fn print_backref(mut self, i: usize) -> Result<Self, !> {
178         self.push("B");
179         self.push_integer_62((i - self.compress.as_ref().unwrap().start_offset) as u64);
180         Ok(self)
181     }
182
183     fn in_binder<T>(
184         mut self,
185         value: &ty::Binder<T>,
186         print_value: impl FnOnce(Self, &T) -> Result<Self, !>,
187     ) -> Result<Self, !>
188     where
189         T: TypeFoldable<'tcx>,
190     {
191         let regions = if value.has_late_bound_regions() {
192             self.tcx.collect_referenced_late_bound_regions(value)
193         } else {
194             FxHashSet::default()
195         };
196
197         let mut lifetime_depths =
198             self.binders.last().map(|b| b.lifetime_depths.end).map_or(0..0, |i| i..i);
199
200         let lifetimes = regions
201             .into_iter()
202             .map(|br| match br {
203                 ty::BrAnon(i) => i,
204                 _ => bug!("symbol_names: non-anonymized region `{:?}` in `{:?}`", br, value),
205             })
206             .max()
207             .map_or(0, |max| max + 1);
208
209         self.push_opt_integer_62("G", lifetimes as u64);
210         lifetime_depths.end += lifetimes;
211
212         self.binders.push(BinderLevel { lifetime_depths });
213         self = print_value(self, value.as_ref().skip_binder())?;
214         self.binders.pop();
215
216         Ok(self)
217     }
218 }
219
220 impl Printer<'tcx> for SymbolMangler<'tcx> {
221     type Error = !;
222
223     type Path = Self;
224     type Region = Self;
225     type Type = Self;
226     type DynExistential = Self;
227     type Const = Self;
228
229     fn tcx(&self) -> TyCtxt<'tcx> {
230         self.tcx
231     }
232
233     fn print_def_path(
234         mut self,
235         def_id: DefId,
236         substs: &'tcx [GenericArg<'tcx>],
237     ) -> Result<Self::Path, Self::Error> {
238         if let Some(&i) = self.compress.as_ref().and_then(|c| c.paths.get(&(def_id, substs))) {
239             return self.print_backref(i);
240         }
241         let start = self.out.len();
242
243         self = self.default_print_def_path(def_id, substs)?;
244
245         // Only cache paths that do not refer to an enclosing
246         // binder (which would change depending on context).
247         if !substs.iter().any(|k| k.has_escaping_bound_vars()) {
248             if let Some(c) = &mut self.compress {
249                 c.paths.insert((def_id, substs), start);
250             }
251         }
252         Ok(self)
253     }
254
255     fn print_impl_path(
256         mut self,
257         impl_def_id: DefId,
258         substs: &'tcx [GenericArg<'tcx>],
259         mut self_ty: Ty<'tcx>,
260         mut impl_trait_ref: Option<ty::TraitRef<'tcx>>,
261     ) -> Result<Self::Path, Self::Error> {
262         let key = self.tcx.def_key(impl_def_id);
263         let parent_def_id = DefId { index: key.parent.unwrap(), ..impl_def_id };
264
265         let mut param_env = self.tcx.param_env_reveal_all_normalized(impl_def_id);
266         if !substs.is_empty() {
267             param_env = param_env.subst(self.tcx, substs);
268         }
269
270         match &mut impl_trait_ref {
271             Some(impl_trait_ref) => {
272                 assert_eq!(impl_trait_ref.self_ty(), self_ty);
273                 *impl_trait_ref = self.tcx.normalize_erasing_regions(param_env, *impl_trait_ref);
274                 self_ty = impl_trait_ref.self_ty();
275             }
276             None => {
277                 self_ty = self.tcx.normalize_erasing_regions(param_env, self_ty);
278             }
279         }
280
281         self.push(match impl_trait_ref {
282             Some(_) => "X",
283             None => "M",
284         });
285
286         // Encode impl generic params if the substitutions contain parameters (implying
287         // polymorphization is enabled) and this isn't an inherent impl.
288         if impl_trait_ref.is_some() && substs.iter().any(|a| a.has_param_types_or_consts()) {
289             self = self.path_generic_args(
290                 |this| {
291                     this.path_append_ns(
292                         |cx| cx.print_def_path(parent_def_id, &[]),
293                         'I',
294                         key.disambiguated_data.disambiguator as u64,
295                         "",
296                     )
297                 },
298                 substs,
299             )?;
300         } else {
301             self.push_disambiguator(key.disambiguated_data.disambiguator as u64);
302             self = self.print_def_path(parent_def_id, &[])?;
303         }
304
305         self = self_ty.print(self)?;
306
307         if let Some(trait_ref) = impl_trait_ref {
308             self = self.print_def_path(trait_ref.def_id, trait_ref.substs)?;
309         }
310
311         Ok(self)
312     }
313
314     fn print_region(mut self, region: ty::Region<'_>) -> Result<Self::Region, Self::Error> {
315         let i = match *region {
316             // Erased lifetimes use the index 0, for a
317             // shorter mangling of `L_`.
318             ty::ReErased => 0,
319
320             // Late-bound lifetimes use indices starting at 1,
321             // see `BinderLevel` for more details.
322             ty::ReLateBound(debruijn, ty::BoundRegion { kind: ty::BrAnon(i) }) => {
323                 let binder = &self.binders[self.binders.len() - 1 - debruijn.index()];
324                 let depth = binder.lifetime_depths.start + i;
325
326                 1 + (self.binders.last().unwrap().lifetime_depths.end - 1 - depth)
327             }
328
329             _ => bug!("symbol_names: non-erased region `{:?}`", region),
330         };
331         self.push("L");
332         self.push_integer_62(i as u64);
333         Ok(self)
334     }
335
336     fn print_type(mut self, ty: Ty<'tcx>) -> Result<Self::Type, Self::Error> {
337         // Basic types, never cached (single-character).
338         let basic_type = match ty.kind() {
339             ty::Bool => "b",
340             ty::Char => "c",
341             ty::Str => "e",
342             ty::Tuple(_) if ty.is_unit() => "u",
343             ty::Int(IntTy::I8) => "a",
344             ty::Int(IntTy::I16) => "s",
345             ty::Int(IntTy::I32) => "l",
346             ty::Int(IntTy::I64) => "x",
347             ty::Int(IntTy::I128) => "n",
348             ty::Int(IntTy::Isize) => "i",
349             ty::Uint(UintTy::U8) => "h",
350             ty::Uint(UintTy::U16) => "t",
351             ty::Uint(UintTy::U32) => "m",
352             ty::Uint(UintTy::U64) => "y",
353             ty::Uint(UintTy::U128) => "o",
354             ty::Uint(UintTy::Usize) => "j",
355             ty::Float(FloatTy::F32) => "f",
356             ty::Float(FloatTy::F64) => "d",
357             ty::Never => "z",
358
359             // Placeholders (should be demangled as `_`).
360             ty::Param(_) | ty::Bound(..) | ty::Placeholder(_) | ty::Infer(_) | ty::Error(_) => "p",
361
362             _ => "",
363         };
364         if !basic_type.is_empty() {
365             self.push(basic_type);
366             return Ok(self);
367         }
368
369         if let Some(&i) = self.compress.as_ref().and_then(|c| c.types.get(&ty)) {
370             return self.print_backref(i);
371         }
372         let start = self.out.len();
373
374         match *ty.kind() {
375             // Basic types, handled above.
376             ty::Bool | ty::Char | ty::Str | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Never => {
377                 unreachable!()
378             }
379             ty::Tuple(_) if ty.is_unit() => unreachable!(),
380
381             // Placeholders, also handled as part of basic types.
382             ty::Param(_) | ty::Bound(..) | ty::Placeholder(_) | ty::Infer(_) | ty::Error(_) => {
383                 unreachable!()
384             }
385
386             ty::Ref(r, ty, mutbl) => {
387                 self.push(match mutbl {
388                     hir::Mutability::Not => "R",
389                     hir::Mutability::Mut => "Q",
390                 });
391                 if *r != ty::ReErased {
392                     self = r.print(self)?;
393                 }
394                 self = ty.print(self)?;
395             }
396
397             ty::RawPtr(mt) => {
398                 self.push(match mt.mutbl {
399                     hir::Mutability::Not => "P",
400                     hir::Mutability::Mut => "O",
401                 });
402                 self = mt.ty.print(self)?;
403             }
404
405             ty::Array(ty, len) => {
406                 self.push("A");
407                 self = ty.print(self)?;
408                 self = self.print_const(len)?;
409             }
410             ty::Slice(ty) => {
411                 self.push("S");
412                 self = ty.print(self)?;
413             }
414
415             ty::Tuple(tys) => {
416                 self.push("T");
417                 for ty in tys.iter().map(|k| k.expect_ty()) {
418                     self = ty.print(self)?;
419                 }
420                 self.push("E");
421             }
422
423             // Mangle all nominal types as paths.
424             ty::Adt(&ty::AdtDef { did: def_id, .. }, substs)
425             | ty::FnDef(def_id, substs)
426             | ty::Opaque(def_id, substs)
427             | ty::Projection(ty::ProjectionTy { item_def_id: def_id, substs })
428             | ty::Closure(def_id, substs)
429             | ty::Generator(def_id, substs, _) => {
430                 self = self.print_def_path(def_id, substs)?;
431             }
432             ty::Foreign(def_id) => {
433                 self = self.print_def_path(def_id, &[])?;
434             }
435
436             ty::FnPtr(sig) => {
437                 self.push("F");
438                 self = self.in_binder(&sig, |mut cx, sig| {
439                     if sig.unsafety == hir::Unsafety::Unsafe {
440                         cx.push("U");
441                     }
442                     match sig.abi {
443                         Abi::Rust => {}
444                         Abi::C => cx.push("KC"),
445                         abi => {
446                             cx.push("K");
447                             let name = abi.name();
448                             if name.contains('-') {
449                                 cx.push_ident(&name.replace('-', "_"));
450                             } else {
451                                 cx.push_ident(name);
452                             }
453                         }
454                     }
455                     for &ty in sig.inputs() {
456                         cx = ty.print(cx)?;
457                     }
458                     if sig.c_variadic {
459                         cx.push("v");
460                     }
461                     cx.push("E");
462                     sig.output().print(cx)
463                 })?;
464             }
465
466             ty::Dynamic(predicates, r) => {
467                 self.push("D");
468                 self = self.print_dyn_existential(predicates)?;
469                 self = r.print(self)?;
470             }
471
472             ty::GeneratorWitness(_) => bug!("symbol_names: unexpected `GeneratorWitness`"),
473         }
474
475         // Only cache types that do not refer to an enclosing
476         // binder (which would change depending on context).
477         if !ty.has_escaping_bound_vars() {
478             if let Some(c) = &mut self.compress {
479                 c.types.insert(ty, start);
480             }
481         }
482         Ok(self)
483     }
484
485     fn print_dyn_existential(
486         mut self,
487         predicates: &'tcx ty::List<ty::Binder<ty::ExistentialPredicate<'tcx>>>,
488     ) -> Result<Self::DynExistential, Self::Error> {
489         for predicate in predicates {
490             self = self.in_binder(&predicate, |mut cx, predicate| {
491                 match predicate {
492                     ty::ExistentialPredicate::Trait(trait_ref) => {
493                         // Use a type that can't appear in defaults of type parameters.
494                         let dummy_self = cx.tcx.mk_ty_infer(ty::FreshTy(0));
495                         let trait_ref = trait_ref.with_self_ty(cx.tcx, dummy_self);
496                         cx = cx.print_def_path(trait_ref.def_id, trait_ref.substs)?;
497                     }
498                     ty::ExistentialPredicate::Projection(projection) => {
499                         let name = cx.tcx.associated_item(projection.item_def_id).ident;
500                         cx.push("p");
501                         cx.push_ident(&name.as_str());
502                         cx = projection.ty.print(cx)?;
503                     }
504                     ty::ExistentialPredicate::AutoTrait(def_id) => {
505                         cx = cx.print_def_path(*def_id, &[])?;
506                     }
507                 }
508                 Ok(cx)
509             })?;
510         }
511         self.push("E");
512         Ok(self)
513     }
514
515     fn print_const(mut self, ct: &'tcx ty::Const<'tcx>) -> Result<Self::Const, Self::Error> {
516         if let Some(&i) = self.compress.as_ref().and_then(|c| c.consts.get(&ct)) {
517             return self.print_backref(i);
518         }
519         let start = self.out.len();
520
521         let mut neg = false;
522         let val = match ct.ty.kind() {
523             ty::Uint(_) | ty::Bool | ty::Char => {
524                 ct.try_eval_bits(self.tcx, ty::ParamEnv::reveal_all(), ct.ty)
525             }
526             ty::Int(_) => {
527                 let param_env = ty::ParamEnv::reveal_all();
528                 ct.try_eval_bits(self.tcx, param_env, ct.ty).and_then(|b| {
529                     let sz = self.tcx.layout_of(param_env.and(ct.ty)).ok()?.size;
530                     let val = sz.sign_extend(b) as i128;
531                     if val < 0 {
532                         neg = true;
533                     }
534                     Some(val.wrapping_abs() as u128)
535                 })
536             }
537             _ => {
538                 bug!("symbol_names: unsupported constant of type `{}` ({:?})", ct.ty, ct);
539             }
540         };
541
542         if let Some(bits) = val {
543             // We only print the type if the const can be evaluated.
544             self = ct.ty.print(self)?;
545             let _ = write!(self.out, "{}{:x}_", if neg { "n" } else { "" }, bits);
546         } else {
547             // NOTE(eddyb) despite having the path, we need to
548             // encode a placeholder, as the path could refer
549             // back to e.g. an `impl` using the constant.
550             self.push("p");
551         }
552
553         // Only cache consts that do not refer to an enclosing
554         // binder (which would change depending on context).
555         if !ct.has_escaping_bound_vars() {
556             if let Some(c) = &mut self.compress {
557                 c.consts.insert(ct, start);
558             }
559         }
560         Ok(self)
561     }
562
563     fn path_crate(mut self, cnum: CrateNum) -> Result<Self::Path, Self::Error> {
564         self.push("C");
565         let fingerprint = self.tcx.crate_disambiguator(cnum).to_fingerprint();
566         self.push_disambiguator(fingerprint.to_smaller_hash());
567         let name = self.tcx.original_crate_name(cnum).as_str();
568         self.push_ident(&name);
569         Ok(self)
570     }
571
572     fn path_qualified(
573         mut self,
574         self_ty: Ty<'tcx>,
575         trait_ref: Option<ty::TraitRef<'tcx>>,
576     ) -> Result<Self::Path, Self::Error> {
577         assert!(trait_ref.is_some());
578         let trait_ref = trait_ref.unwrap();
579
580         self.push("Y");
581         self = self_ty.print(self)?;
582         self.print_def_path(trait_ref.def_id, trait_ref.substs)
583     }
584
585     fn path_append_impl(
586         self,
587         _: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
588         _: &DisambiguatedDefPathData,
589         _: Ty<'tcx>,
590         _: Option<ty::TraitRef<'tcx>>,
591     ) -> Result<Self::Path, Self::Error> {
592         // Inlined into `print_impl_path`
593         unreachable!()
594     }
595
596     fn path_append(
597         self,
598         print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
599         disambiguated_data: &DisambiguatedDefPathData,
600     ) -> Result<Self::Path, Self::Error> {
601         let ns = match disambiguated_data.data {
602             // Uppercase categories are more stable than lowercase ones.
603             DefPathData::TypeNs(_) => 't',
604             DefPathData::ValueNs(_) => 'v',
605             DefPathData::ClosureExpr => 'C',
606             DefPathData::Ctor => 'c',
607             DefPathData::AnonConst => 'k',
608             DefPathData::ImplTrait => 'i',
609
610             // These should never show up as `path_append` arguments.
611             DefPathData::CrateRoot
612             | DefPathData::Misc
613             | DefPathData::Impl
614             | DefPathData::MacroNs(_)
615             | DefPathData::LifetimeNs(_) => {
616                 bug!("symbol_names: unexpected DefPathData: {:?}", disambiguated_data.data)
617             }
618         };
619
620         let name = disambiguated_data.data.get_opt_name().map(|s| s.as_str());
621
622         self.path_append_ns(
623             print_prefix,
624             ns,
625             disambiguated_data.disambiguator as u64,
626             name.as_ref().map_or("", |s| &s[..]),
627         )
628     }
629
630     fn path_generic_args(
631         mut self,
632         print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
633         args: &[GenericArg<'tcx>],
634     ) -> Result<Self::Path, Self::Error> {
635         // Don't print any regions if they're all erased.
636         let print_regions = args.iter().any(|arg| match arg.unpack() {
637             GenericArgKind::Lifetime(r) => *r != ty::ReErased,
638             _ => false,
639         });
640         let args = args.iter().cloned().filter(|arg| match arg.unpack() {
641             GenericArgKind::Lifetime(_) => print_regions,
642             _ => true,
643         });
644
645         if args.clone().next().is_none() {
646             return print_prefix(self);
647         }
648
649         self.push("I");
650         self = print_prefix(self)?;
651         for arg in args {
652             match arg.unpack() {
653                 GenericArgKind::Lifetime(lt) => {
654                     self = lt.print(self)?;
655                 }
656                 GenericArgKind::Type(ty) => {
657                     self = ty.print(self)?;
658                 }
659                 GenericArgKind::Const(c) => {
660                     self.push("K");
661                     self = c.print(self)?;
662                 }
663             }
664         }
665         self.push("E");
666
667         Ok(self)
668     }
669 }