[mcs] Moves using static lookup at the end of simple name lookup rules. Fixes #55348
[mono.git] / mcs / mcs / context.cs
1 //
2 // context.cs: Various compiler contexts.
3 //
4 // Author:
5 //   Marek Safar (marek.safar@gmail.com)
6 //   Miguel de Icaza (miguel@ximian.com)
7 //
8 // Copyright 2001, 2002, 2003 Ximian, Inc.
9 // Copyright 2004-2009 Novell, Inc.
10 // Copyright 2011 Xamarin Inc.
11 //
12
13 using System;
14 using System.Collections.Generic;
15 using System.IO;
16 using System.Security.Cryptography;
17 using System.Diagnostics;
18
19 namespace Mono.CSharp
20 {
21         public enum LookupMode
22         {
23                 Normal = 0,
24                 Probing = 1,
25                 IgnoreAccessibility = 2,
26                 IgnoreStaticUsing = 1 << 10
27         }
28
29         //
30         // Implemented by elements which can act as independent contexts
31         // during resolve phase. Used mostly for lookups.
32         //
33         public interface IMemberContext : IModuleContext
34         {
35                 //
36                 // A scope type context, it can be inflated for generic types
37                 //
38                 TypeSpec CurrentType { get; }
39
40                 //
41                 // A scope type parameters either VAR or MVAR
42                 //
43                 TypeParameters CurrentTypeParameters { get; }
44
45                 //
46                 // A member definition of the context. For partial types definition use
47                 // CurrentTypeDefinition.PartialContainer otherwise the context is local
48                 //
49                 // TODO: Obsolete it in this context, dynamic context cannot guarantee sensible value
50                 //
51                 MemberCore CurrentMemberDefinition { get; }
52
53                 bool IsObsolete { get; }
54                 bool IsUnsafe { get; }
55                 bool IsStatic { get; }
56
57                 string GetSignatureForError ();
58
59                 ExtensionMethodCandidates LookupExtensionMethod (string name, int arity);
60                 FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc);
61                 FullNamedExpression LookupNamespaceAlias (string name);
62         }
63
64         public interface IModuleContext
65         {
66                 ModuleContainer Module { get; }
67         }
68
69         //
70         // Block or statement resolving context
71         //
72         public class BlockContext : ResolveContext
73         {
74                 readonly TypeSpec return_type;
75
76                 //
77                 // Tracks the last offset used by VariableInfo
78                 //
79                 public int AssignmentInfoOffset;
80
81                 public BlockContext (IMemberContext mc, ExplicitBlock block, TypeSpec returnType)
82                         : base (mc)
83                 {
84                         if (returnType == null)
85                                 throw new ArgumentNullException ("returnType");
86
87                         this.return_type = returnType;
88
89                         // TODO: check for null value
90                         CurrentBlock = block;
91                 }
92
93                 public BlockContext (ResolveContext rc, ExplicitBlock block, TypeSpec returnType)
94                         : this (rc.MemberContext, block, returnType)
95                 {
96                         if (rc.IsUnsafe)
97                                 flags |= ResolveContext.Options.UnsafeScope;
98
99                         if (rc.HasSet (ResolveContext.Options.CheckedScope))
100                                 flags |= ResolveContext.Options.CheckedScope;
101
102                         if (!rc.ConstantCheckState)
103                                 flags &= ~Options.ConstantCheckState;
104
105                         if (rc.IsInProbingMode)
106                                 flags |= ResolveContext.Options.ProbingMode;
107
108                         if (rc.HasSet (ResolveContext.Options.FieldInitializerScope))
109                                 flags |= ResolveContext.Options.FieldInitializerScope;
110
111                         if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
112                                 flags |= ResolveContext.Options.ExpressionTreeConversion;
113
114                         if (rc.HasSet (ResolveContext.Options.BaseInitializer))
115                                 flags |= ResolveContext.Options.BaseInitializer;
116                 }
117
118                 public ExceptionStatement CurrentTryBlock { get; set; }
119
120                 public TryCatch CurrentTryCatch { get; set; }
121
122                 public LoopStatement EnclosingLoop { get; set; }
123
124                 public LoopStatement EnclosingLoopOrSwitch { get; set; }
125
126                 public Switch Switch { get; set; }
127
128                 public TypeSpec ReturnType {
129                         get { return return_type; }
130                 }
131         }
132
133         //
134         // Expression resolving context
135         //
136         public class ResolveContext : IMemberContext
137         {
138                 [Flags]
139                 public enum Options
140                 {
141                         /// <summary>
142                         ///   This flag tracks the `checked' state of the compilation,
143                         ///   it controls whether we should generate code that does overflow
144                         ///   checking, or if we generate code that ignores overflows.
145                         ///
146                         ///   The default setting comes from the command line option to generate
147                         ///   checked or unchecked code plus any source code changes using the
148                         ///   checked/unchecked statements or expressions.   Contrast this with
149                         ///   the ConstantCheckState flag.
150                         /// </summary>
151                         CheckedScope = 1 << 0,
152
153                         /// <summary>
154                         ///   The constant check state is always set to `true' and cant be changed
155                         ///   from the command line.  The source code can change this setting with
156                         ///   the `checked' and `unchecked' statements and expressions. 
157                         /// </summary>
158                         ConstantCheckState = 1 << 1,
159
160                         AllCheckStateFlags = CheckedScope | ConstantCheckState,
161
162                         //
163                         // unsafe { ... } scope
164                         //
165                         UnsafeScope = 1 << 2,
166                         CatchScope = 1 << 3,
167                         FinallyScope = 1 << 4,
168                         FieldInitializerScope = 1 << 5,
169                         CompoundAssignmentScope = 1 << 6,
170                         FixedInitializerScope = 1 << 7,
171                         BaseInitializer = 1 << 8,
172
173                         //
174                         // Inside an enum definition, we do not resolve enumeration values
175                         // to their enumerations, but rather to the underlying type/value
176                         // This is so EnumVal + EnumValB can be evaluated.
177                         //
178                         // There is no "E operator + (E x, E y)", so during an enum evaluation
179                         // we relax the rules
180                         //
181                         EnumScope = 1 << 9,
182
183                         ConstantScope = 1 << 10,
184
185                         ConstructorScope = 1 << 11,
186
187                         UsingInitializerScope = 1 << 12,
188
189                         LockScope = 1 << 13,
190
191                         TryScope = 1 << 14,
192
193                         TryWithCatchScope = 1 << 15,
194
195                         DontSetConditionalAccessReceiver = 1 << 16,
196
197                         NameOfScope = 1 << 17,
198
199                         ///
200                         /// Indicates the current context is in probing mode, no errors are reported. 
201                         ///
202                         ProbingMode = 1 << 22,
203
204                         //
205                         // Return and ContextualReturn statements will set the ReturnType
206                         // value based on the expression types of each return statement
207                         // instead of the method return type which is initially null.
208                         //
209                         InferReturnType = 1 << 23,
210
211                         OmitDebuggingInfo = 1 << 24,
212
213                         ExpressionTreeConversion = 1 << 25,
214
215                         InvokeSpecialName = 1 << 26
216                 }
217
218                 // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
219                 // it's public so that we can use a struct at the callsite
220                 public struct FlagsHandle : IDisposable
221                 {
222                         readonly ResolveContext ec;
223                         readonly Options invmask, oldval;
224
225                         public FlagsHandle (ResolveContext ec, Options flagsToSet)
226                                 : this (ec, flagsToSet, flagsToSet)
227                         {
228                         }
229
230                         internal FlagsHandle (ResolveContext ec, Options mask, Options val)
231                         {
232                                 this.ec = ec;
233                                 invmask = ~mask;
234                                 oldval = ec.flags & mask;
235                                 ec.flags = (ec.flags & invmask) | (val & mask);
236
237 //                              if ((mask & Options.ProbingMode) != 0)
238 //                                      ec.Report.DisableReporting ();
239                         }
240
241                         public void Dispose ()
242                         {
243 //                              if ((invmask & Options.ProbingMode) == 0)
244 //                                      ec.Report.EnableReporting ();
245
246                                 ec.flags = (ec.flags & invmask) | oldval;
247                         }
248                 }
249
250                 protected Options flags;
251
252                 //
253                 // Whether we are inside an anonymous method.
254                 //
255                 public AnonymousExpression CurrentAnonymousMethod;
256
257                 //
258                 // Holds a varible used during collection or object initialization.
259                 //
260                 public Expression CurrentInitializerVariable;
261
262                 public Block CurrentBlock;
263
264                 public readonly IMemberContext MemberContext;
265
266                 public ResolveContext (IMemberContext mc)
267                 {
268                         if (mc == null)
269                                 throw new ArgumentNullException ();
270
271                         MemberContext = mc;
272
273                         //
274                         // The default setting comes from the command line option
275                         //
276                         if (mc.Module.Compiler.Settings.Checked)
277                                 flags |= Options.CheckedScope;
278
279                         //
280                         // The constant check state is always set to true
281                         //
282                         flags |= Options.ConstantCheckState;
283                 }
284
285                 public ResolveContext (IMemberContext mc, Options options)
286                         : this (mc)
287                 {
288                         flags |= options;
289                 }
290
291                 #region Properties
292
293                 public BuiltinTypes BuiltinTypes {
294                         get {
295                                 return MemberContext.Module.Compiler.BuiltinTypes;
296                         }
297                 }
298
299                 public virtual ExplicitBlock ConstructorBlock {
300                         get {
301                                 return CurrentBlock.Explicit;
302                         }
303                 }
304
305                 //
306                 // The current iterator
307                 //
308                 public Iterator CurrentIterator {
309                         get { return CurrentAnonymousMethod as Iterator; }
310                 }
311
312                 public TypeSpec CurrentType {
313                         get { return MemberContext.CurrentType; }
314                 }
315
316                 public TypeParameters CurrentTypeParameters {
317                         get { return MemberContext.CurrentTypeParameters; }
318                 }
319
320                 public MemberCore CurrentMemberDefinition {
321                         get { return MemberContext.CurrentMemberDefinition; }
322                 }
323
324                 public bool ConstantCheckState {
325                         get { return (flags & Options.ConstantCheckState) != 0; }
326                 }
327
328                 public bool IsInProbingMode {
329                         get {
330                                 return (flags & Options.ProbingMode) != 0;
331                         }
332                 }
333
334                 public bool IsObsolete {
335                         get {
336                                 // Disables obsolete checks when probing is on
337                                 return MemberContext.IsObsolete;
338                         }
339                 }
340
341                 public bool IsStatic {
342                         get {
343                                 return MemberContext.IsStatic;
344                         }
345                 }
346
347                 public bool IsUnsafe {
348                         get {
349                                 return HasSet (Options.UnsafeScope) || MemberContext.IsUnsafe;
350                         }
351                 }
352
353                 public bool IsRuntimeBinder {
354                         get {
355                                 return Module.Compiler.IsRuntimeBinder;
356                         }
357                 }
358
359                 public bool IsVariableCapturingRequired {
360                         get {
361                                 return !IsInProbingMode;
362                         }
363                 }
364
365                 public ModuleContainer Module {
366                         get {
367                                 return MemberContext.Module;
368                         }
369                 }
370
371                 public Report Report {
372                         get {
373                                 return Module.Compiler.Report;
374                         }
375                 }
376
377                 #endregion
378
379                 public bool MustCaptureVariable (INamedBlockVariable local)
380                 {
381                         if (CurrentAnonymousMethod == null)
382                                 return false;
383
384                         //
385                         // Capture only if this or any of child blocks contain yield
386                         // or it's a parameter
387                         //
388                         if (CurrentAnonymousMethod.IsIterator)
389                                 return local.IsParameter || local.Block.Explicit.HasYield;
390
391                         //
392                         // Capture only if this or any of child blocks contain await
393                         // or it's a parameter or we need to access variable from 
394                         // different parameter block
395                         //
396                         if (CurrentAnonymousMethod is AsyncInitializer)
397                                 return local.IsParameter || local.Block.Explicit.HasAwait || CurrentBlock.Explicit.HasAwait ||
398                                         local.Block.ParametersBlock != CurrentBlock.ParametersBlock.Original;
399
400                         return local.Block.ParametersBlock != CurrentBlock.ParametersBlock.Original;
401                 }
402
403                 public bool HasSet (Options options)
404                 {
405                         return (this.flags & options) == options;
406                 }
407
408                 public bool HasAny (Options options)
409                 {
410                         return (this.flags & options) != 0;
411                 }
412
413
414                 // Temporarily set all the given flags to the given value.  Should be used in an 'using' statement
415                 public FlagsHandle Set (Options options)
416                 {
417                         return new FlagsHandle (this, options);
418                 }
419
420                 public FlagsHandle With (Options options, bool enable)
421                 {
422                         return new FlagsHandle (this, options, enable ? options : 0);
423                 }
424
425                 #region IMemberContext Members
426
427                 public string GetSignatureForError ()
428                 {
429                         return MemberContext.GetSignatureForError ();
430                 }
431
432                 public ExtensionMethodCandidates LookupExtensionMethod (string name, int arity)
433                 {
434                         return MemberContext.LookupExtensionMethod (name, arity);
435                 }
436
437                 public FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc)
438                 {
439                         return MemberContext.LookupNamespaceOrType (name, arity, mode, loc);
440                 }
441
442                 public FullNamedExpression LookupNamespaceAlias (string name)
443                 {
444                         return MemberContext.LookupNamespaceAlias (name);
445                 }
446
447                 #endregion
448         }
449
450         public class FlowAnalysisContext
451         {
452                 readonly CompilerContext ctx;
453
454                 public FlowAnalysisContext (CompilerContext ctx, ParametersBlock parametersBlock, int definiteAssignmentLength)
455                 {
456                         this.ctx = ctx;
457                         this.ParametersBlock = parametersBlock;
458
459                         DefiniteAssignment = definiteAssignmentLength == 0 ?
460                                 DefiniteAssignmentBitSet.Empty :
461                                 new DefiniteAssignmentBitSet (definiteAssignmentLength);
462                 }
463
464                 public DefiniteAssignmentBitSet DefiniteAssignment { get; set; }
465
466                 public DefiniteAssignmentBitSet DefiniteAssignmentOnTrue { get; set; }
467
468                 public DefiniteAssignmentBitSet DefiniteAssignmentOnFalse { get; set; }
469
470                 Dictionary<Statement, List<DefiniteAssignmentBitSet>> LabelStack { get; set; }
471
472                 public ParametersBlock ParametersBlock { get; set; }
473
474                 public Report Report {
475                         get {
476                                 return ctx.Report;
477                         }
478                 }
479
480                 public DefiniteAssignmentBitSet SwitchInitialDefinitiveAssignment { get; set; }
481
482                 public TryFinally TryFinally { get; set; }
483
484                 public bool UnreachableReported { get; set; }
485
486                 public bool AddReachedLabel (Statement label)
487                 {
488                         List<DefiniteAssignmentBitSet> das;
489                         if (LabelStack == null) {
490                                 LabelStack = new Dictionary<Statement, List<DefiniteAssignmentBitSet>> ();
491                                 das = null;
492                         } else {
493                                 LabelStack.TryGetValue (label, out das);
494                         }
495
496                         if (das == null) {
497                                 das = new List<DefiniteAssignmentBitSet> ();
498                                 das.Add (new DefiniteAssignmentBitSet (DefiniteAssignment));
499                                 LabelStack.Add (label, das);
500                                 return false;
501                         }
502
503                         foreach (var existing in das) {
504                                 if (DefiniteAssignmentBitSet.IsIncluded (existing, DefiniteAssignment))
505                                         return true;
506                         }
507
508                         if (DefiniteAssignment == DefiniteAssignmentBitSet.Empty)
509                                 das.Add (DefiniteAssignment);
510                         else
511                                 das.Add (new DefiniteAssignmentBitSet (DefiniteAssignment));
512
513                         return false;
514                 }
515
516                 public DefiniteAssignmentBitSet BranchDefiniteAssignment ()
517                 {
518                         return BranchDefiniteAssignment (DefiniteAssignment);
519                 }
520
521                 public DefiniteAssignmentBitSet BranchDefiniteAssignment (DefiniteAssignmentBitSet da)
522                 {
523                         if (da != DefiniteAssignmentBitSet.Empty) {
524                                 DefiniteAssignment = new DefiniteAssignmentBitSet (da);
525                         }
526
527                         return da;
528                 }
529
530                 public Dictionary<Statement, List<DefiniteAssignmentBitSet>> CopyLabelStack ()
531                 {
532                         if (LabelStack == null)
533                                 return null;
534
535                         var dest = new Dictionary<Statement, List<DefiniteAssignmentBitSet>> ();
536                         foreach (var entry in LabelStack) {
537                                 dest.Add (entry.Key, new List<DefiniteAssignmentBitSet> (entry.Value));
538                         }
539
540                         return dest;
541                 }
542
543                 public bool IsDefinitelyAssigned (VariableInfo variable)
544                 {
545                         return variable.IsAssigned (DefiniteAssignment);
546                 }
547
548                 public bool IsStructFieldDefinitelyAssigned (VariableInfo variable, string name)
549                 {
550                         return variable.IsStructFieldAssigned (DefiniteAssignment, name);
551                 }
552
553                 public void SetLabelStack (Dictionary<Statement, List<DefiniteAssignmentBitSet>> labelStack)
554                 {
555                         LabelStack = labelStack;
556                 }
557
558                 public void SetVariableAssigned (VariableInfo variable, bool generatedAssignment = false)
559                 {
560                         variable.SetAssigned (DefiniteAssignment, generatedAssignment);
561                 }
562
563                 public void SetStructFieldAssigned (VariableInfo variable, string name)
564                 {
565                         variable.SetStructFieldAssigned (DefiniteAssignment, name);
566                 }
567         }
568
569
570         //
571         // This class is used during the Statement.Clone operation
572         // to remap objects that have been cloned.
573         //
574         // Since blocks are cloned by Block.Clone, we need a way for
575         // expressions that must reference the block to be cloned
576         // pointing to the new cloned block.
577         //
578         public class CloneContext
579         {
580                 Dictionary<Block, Block> block_map = new Dictionary<Block, Block> ();
581
582                 public void AddBlockMap (Block from, Block to)
583                 {
584                         block_map.Add (from, to);
585                 }
586
587                 public Block LookupBlock (Block from)
588                 {
589                         Block result;
590                         if (!block_map.TryGetValue (from, out result)) {
591                                 result = (Block) from.Clone (this);
592                         }
593
594                         return result;
595                 }
596
597                 ///
598                 /// Remaps block to cloned copy if one exists.
599                 ///
600                 public Block RemapBlockCopy (Block from)
601                 {
602                         Block mapped_to;
603                         if (!block_map.TryGetValue (from, out mapped_to))
604                                 return from;
605
606                         return mapped_to;
607                 }
608         }
609
610         //
611         // Main compiler context
612         //
613         public class CompilerContext
614         {
615                 static readonly TimeReporter DisabledTimeReporter = new TimeReporter (false);
616
617                 readonly Report report;
618                 readonly BuiltinTypes builtin_types;
619                 readonly CompilerSettings settings;
620
621                 Dictionary<string, SourceFile> all_source_files;
622
623                 public CompilerContext (CompilerSettings settings, ReportPrinter reportPrinter)
624                 {
625                         this.settings = settings;
626                         this.report = new Report (this, reportPrinter);
627                         this.builtin_types = new BuiltinTypes ();
628                         this.TimeReporter = DisabledTimeReporter;
629                 }
630
631                 #region Properties
632
633                 public BuiltinTypes BuiltinTypes {
634                         get {
635                                 return builtin_types;
636                         }
637                 }
638
639                 // Used for special handling of runtime dynamic context mostly
640                 // by error reporting but also by member accessibility checks
641                 public bool IsRuntimeBinder {
642                         get; set;
643                 }
644
645                 public Report Report {
646                         get {
647                                 return report;
648                         }
649                 }
650
651                 public CompilerSettings Settings {
652                         get {
653                                 return settings;
654                         }
655                 }
656
657                 public List<SourceFile> SourceFiles {
658                         get {
659                                 return settings.SourceFiles;
660                         }
661                 }
662
663                 internal TimeReporter TimeReporter {
664                         get; set;
665                 }
666
667                 #endregion
668
669                 //
670                 // This is used when we encounter a #line preprocessing directive during parsing
671                 // to register additional source file names
672                 //
673                 public SourceFile LookupFile (CompilationSourceFile comp_unit, string name)
674                 {
675                         if (all_source_files == null) {
676                                 all_source_files = new Dictionary<string, SourceFile> ();
677                                 foreach (var source in SourceFiles)
678                                         all_source_files[source.OriginalFullPathName] = source;
679                         }
680
681                         string path;
682                         if (!Path.IsPathRooted (name)) {
683                                 var loc = comp_unit.SourceFile;
684                                 string root = Path.GetDirectoryName (loc.OriginalFullPathName);
685                                 path = Path.GetFullPath (Path.Combine (root, name));
686                                 var dir = Path.GetDirectoryName (loc.Name);
687                                 if (!string.IsNullOrEmpty (dir))
688                                         name = Path.Combine (dir, name);
689                         } else
690                                 path = name;
691
692                         SourceFile retval;
693                         if (all_source_files.TryGetValue (path, out retval))
694                                 return retval;
695
696                         retval = new SourceFile (name, path, all_source_files.Count + 1);
697                         Location.AddFile (retval);
698                         all_source_files.Add (path, retval);
699                         return retval;
700                 }
701         }
702
703         //
704         // Generic code emitter context
705         //
706         public class BuilderContext
707         {
708                 [Flags]
709                 public enum Options
710                 {
711                         /// <summary>
712                         ///   This flag tracks the `checked' state of the compilation,
713                         ///   it controls whether we should generate code that does overflow
714                         ///   checking, or if we generate code that ignores overflows.
715                         ///
716                         ///   The default setting comes from the command line option to generate
717                         ///   checked or unchecked code plus any source code changes using the
718                         ///   checked/unchecked statements or expressions.   Contrast this with
719                         ///   the ConstantCheckState flag.
720                         /// </summary>
721                         CheckedScope = 1 << 0,
722
723                         AccurateDebugInfo = 1 << 1,
724
725                         OmitDebugInfo = 1 << 2,
726
727                         ConstructorScope = 1 << 3,
728
729                         AsyncBody = 1 << 4,
730                 }
731
732                 // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
733                 // it's public so that we can use a struct at the callsite
734                 public struct FlagsHandle : IDisposable
735                 {
736                         readonly BuilderContext ec;
737                         readonly Options invmask, oldval;
738
739                         public FlagsHandle (BuilderContext ec, Options flagsToSet)
740                                 : this (ec, flagsToSet, flagsToSet)
741                         {
742                         }
743
744                         internal FlagsHandle (BuilderContext ec, Options mask, Options val)
745                         {
746                                 this.ec = ec;
747                                 invmask = ~mask;
748                                 oldval = ec.flags & mask;
749                                 ec.flags = (ec.flags & invmask) | (val & mask);
750                         }
751
752                         public void Dispose ()
753                         {
754                                 ec.flags = (ec.flags & invmask) | oldval;
755                         }
756                 }
757
758                 protected Options flags;
759
760                 public bool HasSet (Options options)
761                 {
762                         return (this.flags & options) == options;
763                 }
764
765                 // Temporarily set all the given flags to the given value.  Should be used in an 'using' statement
766                 public FlagsHandle With (Options options, bool enable)
767                 {
768                         return new FlagsHandle (this, options, enable ? options : 0);
769                 }
770         }
771
772         //
773         // Parser session objects. We could recreate all these objects for each parser
774         // instance but the best parser performance the session object can be reused
775         //
776         public class ParserSession
777         {
778                 MD5 md5;
779
780                 public readonly char[] StreamReaderBuffer = new char[SeekableStreamReader.DefaultReadAheadSize * 2];
781                 public readonly Dictionary<char[], string>[] Identifiers = new Dictionary<char[], string>[Tokenizer.MaxIdentifierLength + 1];
782                 public readonly List<Parameter> ParametersStack = new List<Parameter> (4);
783                 public readonly char[] IDBuilder = new char[Tokenizer.MaxIdentifierLength];
784                 public readonly char[] NumberBuilder = new char[Tokenizer.MaxNumberLength];
785
786                 public LocationsBag LocationsBag { get; set; }
787                 public bool UseJayGlobalArrays { get; set; }
788                 public LocatedToken[] LocatedTokens { get; set; }
789
790                 public MD5 GetChecksumAlgorithm ()
791                 {
792                         return md5 ?? (md5 = MD5.Create ());
793                 }
794         }
795 }