1 //===- MicrosoftDemangle.cpp ----------------------------------------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is dual licensed under the MIT and the University of Illinois Open
6 // Source Licenses. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file defines a demangler for MSVC-style mangled symbols.
12 //===----------------------------------------------------------------------===//
14 #include "MicrosoftDemangleNodes.h"
15 #include "llvm/Demangle/Compiler.h"
16 #include "llvm/Demangle/Utility.h"
20 using namespace ms_demangle
;
22 #define OUTPUT_ENUM_CLASS_VALUE(Enum, Value, Desc) \
27 // Writes a space if the last token does not end with a punctuation.
28 static void outputSpaceIfNecessary(OutputStream
&OS
) {
33 if (std::isalnum(C
) || C
== '>')
37 static bool outputSingleQualifier(OutputStream
&OS
, Qualifiers Q
) {
54 static bool outputQualifierIfPresent(OutputStream
&OS
, Qualifiers Q
,
55 Qualifiers Mask
, bool NeedSpace
) {
62 outputSingleQualifier(OS
, Mask
);
66 static void outputQualifiers(OutputStream
&OS
, Qualifiers Q
, bool SpaceBefore
,
71 size_t Pos1
= OS
.getCurrentPosition();
72 SpaceBefore
= outputQualifierIfPresent(OS
, Q
, Q_Const
, SpaceBefore
);
73 SpaceBefore
= outputQualifierIfPresent(OS
, Q
, Q_Volatile
, SpaceBefore
);
74 SpaceBefore
= outputQualifierIfPresent(OS
, Q
, Q_Restrict
, SpaceBefore
);
75 size_t Pos2
= OS
.getCurrentPosition();
76 if (SpaceAfter
&& Pos2
> Pos1
)
80 static void outputCallingConvention(OutputStream
&OS
, CallingConv CC
) {
81 outputSpaceIfNecessary(OS
);
84 case CallingConv::Cdecl
:
87 case CallingConv::Fastcall
:
90 case CallingConv::Pascal
:
93 case CallingConv::Regcall
:
96 case CallingConv::Stdcall
:
99 case CallingConv::Thiscall
:
102 case CallingConv::Eabi
:
105 case CallingConv::Vectorcall
:
106 OS
<< "__vectorcall";
108 case CallingConv::Clrcall
:
116 void TypeNode::outputQuals(bool SpaceBefore
, bool SpaceAfter
) const {}
118 void PrimitiveTypeNode::outputPre(OutputStream
&OS
, OutputFlags Flags
) const {
120 OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind
, Void
, "void");
121 OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind
, Bool
, "bool");
122 OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind
, Char
, "char");
123 OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind
, Schar
, "signed char");
124 OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind
, Uchar
, "unsigned char");
125 OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind
, Char16
, "char16_t");
126 OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind
, Char32
, "char32_t");
127 OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind
, Short
, "short");
128 OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind
, Ushort
, "unsigned short");
129 OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind
, Int
, "int");
130 OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind
, Uint
, "unsigned int");
131 OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind
, Long
, "long");
132 OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind
, Ulong
, "unsigned long");
133 OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind
, Int64
, "__int64");
134 OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind
, Uint64
, "unsigned __int64");
135 OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind
, Wchar
, "wchar_t");
136 OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind
, Float
, "float");
137 OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind
, Double
, "double");
138 OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind
, Ldouble
, "long double");
139 OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind
, Nullptr
, "std::nullptr_t");
141 outputQualifiers(OS
, Quals
, true, false);
144 void NodeArrayNode::output(OutputStream
&OS
, OutputFlags Flags
) const {
145 output(OS
, Flags
, ", ");
148 void NodeArrayNode::output(OutputStream
&OS
, OutputFlags Flags
,
149 StringView Separator
) const {
153 Nodes
[0]->output(OS
, Flags
);
154 for (size_t I
= 1; I
< Count
; ++I
) {
156 Nodes
[I
]->output(OS
, Flags
);
160 void EncodedStringLiteralNode::output(OutputStream
&OS
,
161 OutputFlags Flags
) const {
163 case CharKind::Wchar
:
164 OS
<< "const wchar_t * {L\"";
167 OS
<< "const char * {\"";
169 case CharKind::Char16
:
170 OS
<< "const char16_t * {u\"";
172 case CharKind::Char32
:
173 OS
<< "const char32_t * {U\"";
176 OS
<< DecodedString
<< "\"";
182 void IntegerLiteralNode::output(OutputStream
&OS
, OutputFlags Flags
) const {
188 void TemplateParameterReferenceNode::output(OutputStream
&OS
,
189 OutputFlags Flags
) const {
190 if (ThunkOffsetCount
> 0)
192 else if (Affinity
== PointerAffinity::Pointer
)
196 Symbol
->output(OS
, Flags
);
197 if (ThunkOffsetCount
> 0)
201 if (ThunkOffsetCount
> 0)
202 OS
<< ThunkOffsets
[0];
203 for (int I
= 1; I
< ThunkOffsetCount
; ++I
) {
204 OS
<< ", " << ThunkOffsets
[I
];
206 if (ThunkOffsetCount
> 0)
210 void IdentifierNode::outputTemplateParameters(OutputStream
&OS
,
211 OutputFlags Flags
) const {
215 TemplateParams
->output(OS
, Flags
);
219 void DynamicStructorIdentifierNode::output(OutputStream
&OS
,
220 OutputFlags Flags
) const {
222 OS
<< "`dynamic atexit destructor for ";
224 OS
<< "`dynamic initializer for ";
228 Variable
->output(OS
, Flags
);
232 Name
->output(OS
, Flags
);
237 void NamedIdentifierNode::output(OutputStream
&OS
, OutputFlags Flags
) const {
239 outputTemplateParameters(OS
, Flags
);
242 void IntrinsicFunctionIdentifierNode::output(OutputStream
&OS
,
243 OutputFlags Flags
) const {
245 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, New
, "operator new");
246 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, Delete
, "operator delete");
247 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, Assign
, "operator=");
248 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, RightShift
, "operator>>");
249 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, LeftShift
, "operator<<");
250 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, LogicalNot
, "operator!");
251 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, Equals
, "operator==");
252 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, NotEquals
, "operator!=");
253 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, ArraySubscript
,
255 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, Pointer
, "operator->");
256 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, Increment
, "operator++");
257 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, Decrement
, "operator--");
258 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, Minus
, "operator-");
259 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, Plus
, "operator+");
260 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, Dereference
, "operator*");
261 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, BitwiseAnd
, "operator&");
262 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, MemberPointer
,
264 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, Divide
, "operator/");
265 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, Modulus
, "operator%");
266 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, LessThan
, "operator<");
267 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, LessThanEqual
, "operator<=");
268 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, GreaterThan
, "operator>");
269 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, GreaterThanEqual
,
271 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, Comma
, "operator,");
272 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, Parens
, "operator()");
273 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, BitwiseNot
, "operator~");
274 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, BitwiseXor
, "operator^");
275 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, BitwiseOr
, "operator|");
276 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, LogicalAnd
, "operator&&");
277 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, LogicalOr
, "operator||");
278 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, TimesEqual
, "operator*=");
279 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, PlusEqual
, "operator+=");
280 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, MinusEqual
, "operator-=");
281 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, DivEqual
, "operator/=");
282 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, ModEqual
, "operator%=");
283 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, RshEqual
, "operator>>=");
284 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, LshEqual
, "operator<<=");
285 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, BitwiseAndEqual
,
287 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, BitwiseOrEqual
,
289 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, BitwiseXorEqual
,
291 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, VbaseDtor
, "`vbase dtor'");
292 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, VecDelDtor
,
293 "`vector deleting dtor'");
294 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, DefaultCtorClosure
,
295 "`default ctor closure'");
296 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, ScalarDelDtor
,
297 "`scalar deleting dtor'");
298 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, VecCtorIter
,
299 "`vector ctor iterator'");
300 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, VecDtorIter
,
301 "`vector dtor iterator'");
302 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, VecVbaseCtorIter
,
303 "`vector vbase ctor iterator'");
304 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, VdispMap
,
305 "`virtual displacement map'");
306 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, EHVecCtorIter
,
307 "`eh vector ctor iterator'");
308 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, EHVecDtorIter
,
309 "`eh vector dtor iterator'");
310 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, EHVecVbaseCtorIter
,
311 "`eh vector vbase ctor iterator'");
312 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, CopyCtorClosure
,
313 "`copy ctor closure'");
314 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, LocalVftableCtorClosure
,
315 "`local vftable ctor closure'");
316 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, ArrayNew
, "operator new[]");
317 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, ArrayDelete
,
318 "operator delete[]");
319 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, ManVectorCtorIter
,
320 "`managed vector ctor iterator'");
321 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, ManVectorDtorIter
,
322 "`managed vector dtor iterator'");
323 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, EHVectorCopyCtorIter
,
324 "`EH vector copy ctor iterator'");
325 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, EHVectorVbaseCopyCtorIter
,
326 "`EH vector vbase copy ctor iterator'");
327 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, VectorCopyCtorIter
,
328 "`vector copy ctor iterator'");
329 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, VectorVbaseCopyCtorIter
,
330 "`vector vbase copy constructor iterator'");
331 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, ManVectorVbaseCopyCtorIter
,
332 "`managed vector vbase copy constructor iterator'");
333 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, CoAwait
, "co_await");
334 OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind
, Spaceship
, "operator <=>");
335 case IntrinsicFunctionKind::MaxIntrinsic
:
336 case IntrinsicFunctionKind::None
:
339 outputTemplateParameters(OS
, Flags
);
342 void LocalStaticGuardIdentifierNode::output(OutputStream
&OS
,
343 OutputFlags Flags
) const {
344 OS
<< "`local static guard'";
346 OS
<< "{" << ScopeIndex
<< "}";
349 void ConversionOperatorIdentifierNode::output(OutputStream
&OS
,
350 OutputFlags Flags
) const {
352 outputTemplateParameters(OS
, Flags
);
354 TargetType
->output(OS
, Flags
);
357 void StructorIdentifierNode::output(OutputStream
&OS
, OutputFlags Flags
) const {
360 Class
->output(OS
, Flags
);
361 outputTemplateParameters(OS
, Flags
);
364 void LiteralOperatorIdentifierNode::output(OutputStream
&OS
,
365 OutputFlags Flags
) const {
366 OS
<< "operator \"\"" << Name
;
367 outputTemplateParameters(OS
, Flags
);
370 void FunctionSignatureNode::outputPre(OutputStream
&OS
,
371 OutputFlags Flags
) const {
372 if (!(FunctionClass
& FC_Global
)) {
373 if (FunctionClass
& FC_Static
)
376 if (FunctionClass
& FC_ExternC
)
377 OS
<< "extern \"C\" ";
379 if (FunctionClass
& FC_Virtual
)
383 ReturnType
->outputPre(OS
, Flags
);
387 if (!(Flags
& OF_NoCallingConvention
))
388 outputCallingConvention(OS
, CallConvention
);
391 void FunctionSignatureNode::outputPost(OutputStream
&OS
,
392 OutputFlags Flags
) const {
393 if (!(FunctionClass
& FC_NoParameterList
)) {
396 Params
->output(OS
, Flags
);
404 if (Quals
& Q_Volatile
)
406 if (Quals
& Q_Restrict
)
408 if (Quals
& Q_Unaligned
)
409 OS
<< " __unaligned";
411 if (RefQualifier
== FunctionRefQualifier::Reference
)
413 else if (RefQualifier
== FunctionRefQualifier::RValueReference
)
417 ReturnType
->outputPost(OS
, Flags
);
420 void ThunkSignatureNode::outputPre(OutputStream
&OS
, OutputFlags Flags
) const {
423 FunctionSignatureNode::outputPre(OS
, Flags
);
426 void ThunkSignatureNode::outputPost(OutputStream
&OS
, OutputFlags Flags
) const {
427 if (FunctionClass
& FC_StaticThisAdjust
) {
428 OS
<< "`adjustor{" << ThisAdjust
.StaticOffset
<< "}'";
429 } else if (FunctionClass
& FC_VirtualThisAdjust
) {
430 if (FunctionClass
& FC_VirtualThisAdjustEx
) {
431 OS
<< "`vtordispex{" << ThisAdjust
.VBPtrOffset
<< ", "
432 << ThisAdjust
.VBOffsetOffset
<< ", " << ThisAdjust
.VtordispOffset
433 << ", " << ThisAdjust
.StaticOffset
<< "}'";
435 OS
<< "`vtordisp{" << ThisAdjust
.VtordispOffset
<< ", "
436 << ThisAdjust
.StaticOffset
<< "}'";
440 FunctionSignatureNode::outputPost(OS
, Flags
);
443 void PointerTypeNode::outputPre(OutputStream
&OS
, OutputFlags Flags
) const {
444 if (Pointee
->kind() == NodeKind::FunctionSignature
) {
445 // If this is a pointer to a function, don't output the calling convention.
446 // It needs to go inside the parentheses.
447 const FunctionSignatureNode
*Sig
=
448 static_cast<const FunctionSignatureNode
*>(Pointee
);
449 Sig
->outputPre(OS
, OF_NoCallingConvention
);
451 Pointee
->outputPre(OS
, Flags
);
453 outputSpaceIfNecessary(OS
);
455 if (Quals
& Q_Unaligned
)
456 OS
<< "__unaligned ";
458 if (Pointee
->kind() == NodeKind::ArrayType
) {
460 } else if (Pointee
->kind() == NodeKind::FunctionSignature
) {
462 const FunctionSignatureNode
*Sig
=
463 static_cast<const FunctionSignatureNode
*>(Pointee
);
464 outputCallingConvention(OS
, Sig
->CallConvention
);
469 ClassParent
->output(OS
, Flags
);
474 case PointerAffinity::Pointer
:
477 case PointerAffinity::Reference
:
480 case PointerAffinity::RValueReference
:
486 outputQualifiers(OS
, Quals
, false, false);
489 void PointerTypeNode::outputPost(OutputStream
&OS
, OutputFlags Flags
) const {
490 if (Pointee
->kind() == NodeKind::ArrayType
||
491 Pointee
->kind() == NodeKind::FunctionSignature
)
494 Pointee
->outputPost(OS
, Flags
);
497 void TagTypeNode::outputPre(OutputStream
&OS
, OutputFlags Flags
) const {
499 OUTPUT_ENUM_CLASS_VALUE(TagKind
, Class
, "class");
500 OUTPUT_ENUM_CLASS_VALUE(TagKind
, Struct
, "struct");
501 OUTPUT_ENUM_CLASS_VALUE(TagKind
, Union
, "union");
502 OUTPUT_ENUM_CLASS_VALUE(TagKind
, Enum
, "enum");
505 QualifiedName
->output(OS
, Flags
);
506 outputQualifiers(OS
, Quals
, true, false);
509 void TagTypeNode::outputPost(OutputStream
&OS
, OutputFlags Flags
) const {}
511 void ArrayTypeNode::outputPre(OutputStream
&OS
, OutputFlags Flags
) const {
512 ElementType
->outputPre(OS
, Flags
);
513 outputQualifiers(OS
, Quals
, true, false);
516 void ArrayTypeNode::outputOneDimension(OutputStream
&OS
, OutputFlags Flags
,
518 assert(N
->kind() == NodeKind::IntegerLiteral
);
519 IntegerLiteralNode
*ILN
= static_cast<IntegerLiteralNode
*>(N
);
521 ILN
->output(OS
, Flags
);
524 void ArrayTypeNode::outputDimensionsImpl(OutputStream
&OS
,
525 OutputFlags Flags
) const {
526 if (Dimensions
->Count
== 0)
529 outputOneDimension(OS
, Flags
, Dimensions
->Nodes
[0]);
530 for (size_t I
= 1; I
< Dimensions
->Count
; ++I
) {
532 outputOneDimension(OS
, Flags
, Dimensions
->Nodes
[I
]);
536 void ArrayTypeNode::outputPost(OutputStream
&OS
, OutputFlags Flags
) const {
538 outputDimensionsImpl(OS
, Flags
);
541 ElementType
->outputPost(OS
, Flags
);
544 void SymbolNode::output(OutputStream
&OS
, OutputFlags Flags
) const {
545 Name
->output(OS
, Flags
);
548 void FunctionSymbolNode::output(OutputStream
&OS
, OutputFlags Flags
) const {
549 Signature
->outputPre(OS
, Flags
);
550 outputSpaceIfNecessary(OS
);
551 Name
->output(OS
, Flags
);
552 Signature
->outputPost(OS
, Flags
);
555 void VariableSymbolNode::output(OutputStream
&OS
, OutputFlags Flags
) const {
557 case StorageClass::PrivateStatic
:
558 case StorageClass::PublicStatic
:
559 case StorageClass::ProtectedStatic
:
566 Type
->outputPre(OS
, Flags
);
567 outputSpaceIfNecessary(OS
);
569 Name
->output(OS
, Flags
);
571 Type
->outputPost(OS
, Flags
);
574 void CustomTypeNode::outputPre(OutputStream
&OS
, OutputFlags Flags
) const {
575 Identifier
->output(OS
, Flags
);
577 void CustomTypeNode::outputPost(OutputStream
&OS
, OutputFlags Flags
) const {}
579 void QualifiedNameNode::output(OutputStream
&OS
, OutputFlags Flags
) const {
580 Components
->output(OS
, Flags
, "::");
583 void RttiBaseClassDescriptorNode::output(OutputStream
&OS
,
584 OutputFlags Flags
) const {
585 OS
<< "`RTTI Base Class Descriptor at (";
586 OS
<< NVOffset
<< ", " << VBPtrOffset
<< ", " << VBTableOffset
<< ", "
591 void LocalStaticGuardVariableNode::output(OutputStream
&OS
,
592 OutputFlags Flags
) const {
593 Name
->output(OS
, Flags
);
596 void VcallThunkIdentifierNode::output(OutputStream
&OS
,
597 OutputFlags Flags
) const {
598 OS
<< "`vcall'{" << OffsetInVTable
<< ", {flat}}";
601 void SpecialTableSymbolNode::output(OutputStream
&OS
, OutputFlags Flags
) const {
602 outputQualifiers(OS
, Quals
, false, true);
603 Name
->output(OS
, Flags
);
606 TargetName
->output(OS
, Flags
);