570 lines · cpp
1// RUN: %clang_cc1 -fno-rtti -emit-llvm -fdump-vtable-layouts %s -o %t.ll -triple=i386-pc-win32 > %t2// RUN: FileCheck %s < %t3// RUN: FileCheck --check-prefix=MANGLING %s < %t.ll4 5// For now, just make sure x86_64 doesn't crash.6// RUN: %clang_cc1 -fno-rtti -emit-llvm-only -fdump-vtable-layouts %s -triple=x86_64-pc-win32 > /dev/null7 8struct V1 {9 virtual void f();10 virtual ~V1();11};12 13struct V2 {14 virtual void f();15 virtual ~V2();16 int v;17};18 19struct Z {20 virtual void g();21 virtual ~Z();22 int x;23};24 25struct V3 : Z, V2 {26};27 28struct V4 : Z, V1, V2 {29 int y;30};31 32void use_somewhere_else(void*);33 34namespace simple {35// In case of a single-layer virtual inheritance, the "this" adjustment for a36// virtual method is done statically:37// struct A {38// virtual void f(); // Expects "(A*)this" in ECX39// };40// struct B : virtual A {41// virtual void f(); // Expects "(char*)(B*)this + 12" in ECX42// virtual ~B(); // Might call f()43// };44//45// If a class overrides a virtual function of its base and has a non-trivial46// ctor/dtor that call(s) the virtual function (or may escape "this" to some47// code that might call it), a virtual adjustment might be needed in case the48// current class layout and the most derived class layout are different.49// This is done using vtordisp thunks.50//51// A simple vtordisp{x,y} thunk for Method@Class is something like:52// sub ecx, [ecx+x] // apply the vtordisp adjustment53// sub ecx, y // apply the subobject adjustment, if needed.54// jmp Method@Class55 56struct A : virtual V1 {57 // CHECK-LABEL: VFTable for 'V1' in 'simple::A' (2 entries).58 // CHECK-NEXT: 0 | void simple::A::f()59 // CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]60 // CHECK-NEXT: 1 | simple::A::~A() [scalar deleting]61 // CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]62 63 // CHECK-LABEL: Thunks for 'simple::A::~A()' (1 entry).64 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual]65 66 // CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry).67 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual]68 69 virtual void f();70 // MANGLING-DAG: @"?f@A@simple@@$4PPPPPPPM@A@AEXXZ"71 72 virtual ~A();73 // MANGLING-DAG: @"??_EA@simple@@$4PPPPPPPM@A@AEPAXI@Z"74};75 76A a;77void use(A *obj) { obj->f(); }78 79struct B : virtual V3 {80 // CHECK-LABEL: VFTable for 'Z' in 'V3' in 'simple::B' (2 entries).81 // CHECK-NEXT: 0 | void Z::g()82 // CHECK-NEXT: 1 | simple::B::~B() [scalar deleting]83 // CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]84 85 // CHECK-LABEL: Thunks for 'simple::B::~B()' (1 entry).86 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual]87 88 // CHECK-LABEL: VFTable for 'V2' in 'V3' in 'simple::B' (2 entries).89 // CHECK-NEXT: 0 | void simple::B::f()90 // CHECK-NEXT: [this adjustment: vtordisp at -12, 0 non-virtual]91 // CHECK-NEXT: 1 | simple::B::~B() [scalar deleting]92 // CHECK-NEXT: [this adjustment: vtordisp at -12, -8 non-virtual]93 94 // CHECK-LABEL: Thunks for 'simple::B::~B()' (1 entry).95 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -12, -8 non-virtual]96 97 // CHECK-LABEL: Thunks for 'void simple::B::f()' (1 entry).98 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -12, 0 non-virtual]99 100 // FIXME: The vtordisp thunk should only get emitted for a constructor101 // if "this" leaves scope.102 B() { use_somewhere_else(this); }103 104 virtual void f();105 // MANGLING-DAG: @"?f@B@simple@@$4PPPPPPPE@A@AEXXZ"106 107 // Has an implicit destructor.108 // MANGLING-DAG: @"??_EB@simple@@$4PPPPPPPE@7AEPAXI@Z"109 // MANGLING-DAG: @"??_EB@simple@@$4PPPPPPPM@A@AEPAXI@Z"110};111 112B b;113void use(B *obj) { obj->f(); }114 115struct C : virtual V4 {116 // CHECK-LABEL: VFTable for 'Z' in 'V4' in 'simple::C' (2 entries).117 // CHECK-NEXT: 0 | void Z::g()118 // CHECK-NEXT: 1 | simple::C::~C() [scalar deleting]119 // CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]120 121 // CHECK-LABEL: Thunks for 'simple::C::~C()' (1 entry).122 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual]123 124 // CHECK-LABEL: VFTable for 'V1' in 'V4' in 'simple::C' (2 entries).125 // CHECK-NEXT: 0 | void simple::C::f()126 // CHECK-NEXT: [this adjustment: vtordisp at -12, 0 non-virtual]127 // CHECK-NEXT: 1 | simple::C::~C() [scalar deleting]128 // CHECK-NEXT: [this adjustment: vtordisp at -12, -8 non-virtual]129 130 // CHECK-LABEL: Thunks for 'simple::C::~C()' (1 entry).131 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -12, -8 non-virtual]132 133 // CHECK-LABEL: Thunks for 'void simple::C::f()' (1 entry).134 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -12, 0 non-virtual]135 136 // CHECK-LABEL: VFTable for 'V2' in 'V4' in 'simple::C' (2 entries).137 // CHECK-NEXT: 0 | void simple::C::f()138 // CHECK-NEXT: [this adjustment: vtordisp at -16, -4 non-virtual]139 // CHECK-NEXT: 1 | simple::C::~C() [scalar deleting]140 // CHECK-NEXT: [this adjustment: vtordisp at -16, -12 non-virtual]141 142 // CHECK-LABEL: Thunks for 'simple::C::~C()' (1 entry).143 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -16, -12 non-virtual]144 145 // CHECK-LABEL: Thunks for 'void simple::C::f()' (1 entry).146 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -16, -4 non-virtual]147 148 int x;149 virtual void f();150 // MANGLING-DAG: @"?f@C@simple@@$4PPPPPPPA@3AEXXZ"151 // MANGLING-DAG: @"?f@C@simple@@$4PPPPPPPE@A@AEXXZ"152 virtual ~C();153 // MANGLING-DAG: @"??_EC@simple@@$4PPPPPPPA@M@AEPAXI@Z"154 // MANGLING-DAG: @"??_EC@simple@@$4PPPPPPPE@7AEPAXI@Z"155 // MANGLING-DAG: @"??_EC@simple@@$4PPPPPPPM@A@AEPAXI@Z"156};157 158C c;159void use(C *obj) { obj->f(); }160 161class D : B {162 // CHECK-LABEL: VFTable for 'V2' in 'V3' in 'simple::B' in 'simple::D' (2 entries).163 // CHECK-NEXT: 0 | void simple::B::f()164 // CHECK-NEXT: [this adjustment: vtordisp at -12, -4 non-virtual]165 // CHECK-NEXT: 1 | simple::D::~D() [scalar deleting]166 // CHECK-NEXT: [this adjustment: vtordisp at -12, -8 non-virtual]167 D();168 int z;169 170 // MANGLING-DAG: @"?f@B@simple@@$4PPPPPPPE@3AEXXZ"171};172 173D::D() {}174 175struct E : V3 {176 virtual void f();177};178 179struct F : virtual E {180 // CHECK-LABEL: VFTable for 'Z' in 'V3' in 'simple::E' in 'simple::F' (2 entries).181 // CHECK-NEXT: 0 | void simple::F::g()182 // CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]183 // CHECK-NEXT: 1 | simple::F::~F() [scalar deleting]184 // CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]185 186 // CHECK-LABEL: VFTable for 'V2' in 'V3' in 'simple::E' in 'simple::F' (2 entries).187 // CHECK-NEXT: 0 | void simple::E::f()188 // CHECK-NEXT: 1 | simple::F::~F() [scalar deleting]189 // CHECK-NEXT: [this adjustment: vtordisp at -12, -8 non-virtual]190 191 F();192 virtual void g(); // Force a vtordisp.193 int f;194 195 // MANGLING-DAG: @"?g@F@simple@@$4PPPPPPPM@A@AEXXZ"{{.*}}??_EF@simple@@$4PPPPPPPM@A@AEPAXI@Z196 // MANGLING-DAG: ?f@E@simple@@UAEXXZ{{.*}}??_EF@simple@@$4PPPPPPPE@7AEPAXI@Z197};198 199F::F() {}200 201struct G : F {202 // CHECK-LABEL: VFTable for 'Z' in 'V3' in 'simple::E' in 'simple::F' in 'simple::G' (2 entries).203 // CHECK-NEXT: 0 | void simple::F::g()204 // CHECK-NEXT: [this adjustment: vtordisp at -4, -4 non-virtual]205 // CHECK-NEXT: 1 | simple::G::~G() [scalar deleting]206 // CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]207 208 // CHECK-LABEL: VFTable for 'V2' in 'V3' in 'simple::E' in 'simple::F' in 'simple::G' (2 entries).209 // CHECK-NEXT: 0 | void simple::E::f()210 // CHECK-NEXT: 1 | simple::G::~G() [scalar deleting]211 // CHECK-NEXT: [this adjustment: vtordisp at -12, -8 non-virtual]212 213 G();214 int g;215 216 // MANGLING-DAG: @"?g@F@simple@@$4PPPPPPPM@3AEXXZ"{{.*}}@"??_EG@simple@@$4PPPPPPPM@A@AEPAXI@Z"217 // MANGLING-DAG: @"?f@E@simple@@UAEXXZ"{{.*}}@"??_EG@simple@@$4PPPPPPPE@7AEPAXI@Z"218};219 220G::G() {}221}222 223namespace extended {224// If a virtual function requires vtordisp adjustment and the final overrider225// is defined in another virtual base of the most derived class,226// we need to know two vbase offsets.227// In this case, we should use the extended form of vtordisp thunks, called228// vtordispex thunks.229//230// vtordispex{x,y,z,w} thunk for Method@Class is something like:231// sub ecx, [ecx+z] // apply the vtordisp adjustment232// sub ecx, x // jump to the vbptr of the most derived class233// mov eax, [ecx] // load the vbtable address234// add ecx, [eax+y] // lookup the final overrider's vbase offset235// add ecx, w // apphy the subobject offset if needed236// jmp Method@Class237 238struct A : virtual simple::A {239 // CHECK-LABEL: VFTable for 'V1' in 'simple::A' in 'extended::A' (2 entries).240 // CHECK-NEXT: 0 | void simple::A::f()241 // CHECK-NEXT: [this adjustment: vtordisp at -4, vbptr at 8 to the left,242 // CHECK-NEXT: vboffset at 8 in the vbtable, 8 non-virtual]243 // CHECK-NEXT: 1 | extended::A::~A() [scalar deleting]244 // CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]245 246 // CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry).247 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left,248 // CHECK-NEXT: vboffset at 8 in the vbtable, 8 non-virtual]249 250 // `vtordispex{8,8,4294967292,8}'251 // MANGLING-DAG: @"?f@A@simple@@$R477PPPPPPPM@7AEXXZ"252 253 virtual ~A();254 // vtordisp{4294967292,0}255 // MANGLING-DAG: @"??_EA@extended@@$4PPPPPPPM@A@AEPAXI@Z"256};257 258A a;259void use(A *obj) { delete obj; }260 261struct B : virtual simple::A {262 // This class has an implicit dtor. Vdtors don't require vtordispex thunks263 // as the most derived class always has an implicit dtor,264 // which is a final overrider.265 266 // CHECK-LABEL: VFTable for 'V1' in 'simple::A' in 'extended::B' (2 entries).267 // ...268 // CHECK: 1 | extended::B::~B() [scalar deleting]269 // CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]270 271 // CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry).272 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left,273 // CHECK-NEXT: vboffset at 8 in the vbtable, 8 non-virtual]274 275 // vtordisp{4294967292,0}276 // MANGLING-DAG: @"??_EB@extended@@$4PPPPPPPM@A@AEPAXI@Z"277};278 279B b;280void use(B *obj) { delete obj; }281 282struct C : virtual simple::A {283 // CHECK-LABEL: VFTable for 'V1' in 'simple::A' in 'extended::C' (2 entries).284 // CHECK-NEXT: 0 | void simple::A::f()285 // CHECK-NEXT: [this adjustment: vtordisp at -4, vbptr at 12 to the left,286 // CHECK-NEXT: vboffset at 8 in the vbtable, 8 non-virtual]287 288 // CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry).289 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 12 to the left,290 // CHECK-NEXT: vboffset at 8 in the vbtable, 8 non-virtual]291 292 // `vtordispex{12,8,4294967292,8}'293 // MANGLING-DAG: @"?f@A@simple@@$R4M@7PPPPPPPM@7AEXXZ"294 int x;295 virtual ~C();296 // MANGLING-DAG: @"??_EC@extended@@$4PPPPPPPM@A@AEPAXI@Z"297};298 299C c;300void use(C *obj) { delete obj; }301 302struct D : virtual V2 {303 virtual void f();304 virtual ~D();305 int x;306};307 308struct E : virtual D {309 // CHECK-LABEL: VFTable for 'V2' in 'extended::D' in 'extended::E' (2 entries).310 // CHECK-NEXT: 0 | void extended::D::f()311 // CHECK-NEXT: [this adjustment: vtordisp at -4, vbptr at 8 to the left,312 // CHECK-NEXT: vboffset at 8 in the vbtable, 12 non-virtual]313 314 // CHECK-LABEL: Thunks for 'void extended::D::f()' (1 entry).315 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left,316 // CHECK-NEXT: vboffset at 8 in the vbtable, 12 non-virtual]317 318 // `vtordispex{8,8,4294967292,12}'319 // MANGLING-DAG: @"?f@D@extended@@$R477PPPPPPPM@M@AEXXZ"320 321 virtual ~E();322 // MANGLING-DAG: @"??_EE@extended@@$4PPPPPPPM@A@AEPAXI@Z"323};324 325E e;326void use(E *obj) { delete obj; } 327 328struct F : virtual Z, virtual D {329 // CHECK-LABEL: VFTable for 'V2' in 'extended::D' in 'extended::F' (2 entries).330 // CHECK-NEXT: 0 | void extended::D::f()331 // CHECK-NEXT: [this adjustment: vtordisp at -4, vbptr at 20 to the left,332 // CHECK-NEXT: vboffset at 12 in the vbtable, 12 non-virtual]333 334 // CHECK-LABEL: Thunks for 'void extended::D::f()' (1 entry).335 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 20 to the left,336 // CHECK-NEXT: vboffset at 12 in the vbtable, 12 non-virtual]337 338 // `vtordispex{20,12,4294967292,12}'339 // MANGLING-DAG: @"?f@D@extended@@$R4BE@M@PPPPPPPM@M@AEXXZ"340 int x;341 virtual ~F();342 // MANGLING-DAG: @"??_EF@extended@@$4PPPPPPPM@M@AEPAXI@Z"343};344 345F f;346void use(F *obj) { delete obj; }347 348struct G : virtual simple::A {349 // CHECK-LABEL: VFTable for 'extended::G' (1 entry).350 // CHECK-NEXT: 0 | void extended::G::g()351 352 // CHECK-LABEL: VFTable for 'V1' in 'simple::A' in 'extended::G' (2 entries).353 // CHECK-NEXT: 0 | void simple::A::f()354 // CHECK-NEXT: [this adjustment: vtordisp at -4, vbptr at 8 to the left,355 // CHECK-NEXT: vboffset at 8 in the vbtable, 8 non-virtual]356 // CHECK-NEXT: 1 | extended::G::~G() [scalar deleting]357 // CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]358 359 // CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry).360 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left,361 // CHECK-NEXT: vboffset at 8 in the vbtable, 8 non-virtual]362 363 // Emits a G's own vfptr, thus moving the vbptr in the layout.364 virtual void g();365 366 virtual ~G();367 // vtordisp{4294967292,0}368 // MANGLING-DAG: @"??_EG@extended@@$4PPPPPPPM@A@AEPAXI@Z"369};370 371G g;372void use(G *obj) { obj->g(); }373 374struct H : Z, A {375 // CHECK-LABEL: VFTable for 'Z' in 'extended::H' (2 entries).376 // CHECK-NEXT: 0 | void Z::g()377 // CHECK-NEXT: 1 | extended::H::~H() [scalar deleting]378 379 // CHECK-LABEL: VFTable for 'V1' in 'simple::A' in 'extended::A' in 'extended::H' (2 entries).380 // CHECK-NEXT: 0 | void simple::A::f()381 // CHECK-NEXT: [this adjustment: vtordisp at -4, vbptr at 8 to the left,382 // CHECK-NEXT: vboffset at 8 in the vbtable, 8 non-virtual]383 384 // CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry).385 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left,386 // CHECK-NEXT: vboffset at 8 in the vbtable, 8 non-virtual]387 388 // MANGLING-DAG: @"?f@A@simple@@$R477PPPPPPPM@7AEXXZ"389 // MANGLING-DAG: @"??_EH@extended@@$4PPPPPPPM@BA@AEPAXI@Z"390};391 392H h;393void use(H *obj) { delete obj; }394}395 396namespace pr17738 {397// These classes should have vtordispex thunks but MSVS CL miscompiles them.398// Just do the right thing.399 400struct A : virtual simple::B {401 // CHECK-LABEL: VFTable for 'V2' in 'V3' in 'simple::B' in 'pr17738::A' (2 entries).402 // CHECK-NEXT: 0 | void simple::B::f()403 // CHECK-NEXT: [this adjustment: vtordisp at -12, vbptr at 20 to the left,404 // CHECK-NEXT: vboffset at 8 in the vbtable, 16 non-virtual]405 406 // CHECK-LABEL: Thunks for 'void simple::B::f()' (1 entry).407 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -12, vbptr at 20 to the left,408 // CHECK-NEXT: vboffset at 8 in the vbtable, 16 non-virtual]409 410 // MANGLING-DAG: @"?f@B@simple@@$R4BE@7PPPPPPPE@BA@AEXXZ"411 int a;412 virtual ~A();413};414 415A a;416void use(A *obj) { delete obj; }417}418 419namespace pr19408 {420// In this test, the vptr used to vcall D::f() is located in the A vbase.421// The offset of A in different in C and D, so the D vtordisp thunk should422// adjust "this" so C::f gets the right value.423struct A {424 A();425 virtual void f();426 int a;427};428 429struct B : virtual A {430 B();431 int b;432};433 434struct C : B {435 C();436 virtual void f();437 int c;438};439 440struct D : C {441 // CHECK-LABEL: VFTable for 'pr19408::A' in 'pr19408::B' in 'pr19408::C' in 'pr19408::D' (1 entry).442 // CHECK-NEXT: 0 | void pr19408::C::f()443 // CHECK-NEXT: [this adjustment: vtordisp at -4, -4 non-virtual]444 445 // MANGLING-DAG: @"?f@C@pr19408@@$4PPPPPPPM@3AEXXZ"446 D();447 int d;448};449 450D::D() {}451}452 453namespace access {454struct A {455 virtual ~A();456protected:457 virtual void prot();458private:459 virtual void priv();460};461 462struct B : virtual A {463 virtual ~B();464protected:465 virtual void prot();466 // MANGLING-DAG: @"?prot@B@access@@$2PPPPPPPM@A@AEXXZ"467private:468 virtual void priv();469 // MANGLING-DAG: @"?priv@B@access@@$0PPPPPPPM@A@AEXXZ"470};471 472B b;473 474struct C : virtual B {475 virtual ~C();476 477 // MANGLING-DAG: @"?prot@B@access@@$R277PPPPPPPM@7AEXXZ"478 // MANGLING-DAG: @"?priv@B@access@@$R077PPPPPPPM@7AEXXZ"479};480 481C c;482}483 484namespace pr19505 {485struct A {486 virtual void f();487 virtual void z();488};489 490struct B : A {491 virtual void f();492};493 494struct C : A, B {495 virtual void g();496};497 498struct X : B, virtual C {499 X() {}500 virtual void g();501 502 // CHECK-LABEL: VFTable for 'pr19505::A' in 'pr19505::B' in 'pr19505::C' in 'pr19505::X' (2 entries).503 // CHECK-NEXT: 0 | void pr19505::B::f()504 // CHECK-NEXT: 1 | void pr19505::A::z()505 506 // MANGLING-DAG: @"??_7X@pr19505@@6BB@1@@" = {{.*}}@"?f@B@pr19505@@UAEXXZ"507} x;508 509void build_vftable(X *obj) { obj->g(); }510}511 512namespace pr19506 {513struct A {514 virtual void f();515 virtual void g();516};517 518struct B : A {519 virtual void f();520};521 522struct C : B {};523 524struct X : C, virtual B {525 virtual void g();526 X() {}527 528 // CHECK-LABEL: VFTable for 'pr19506::A' in 'pr19506::B' in 'pr19506::X' (2 entries).529 // CHECK-NEXT: 0 | void pr19506::B::f()530 // CHECK-NEXT: 1 | void pr19506::X::g()531 // CHECK-NEXT: [this adjustment: vtordisp at -4, -12 non-virtual]532 533 // MANGLING-DAG: @"??_7X@pr19506@@6BB@1@@" = {{.*}}@"?f@B@pr19506@@UAEXXZ"534} x;535 536void build_vftable(X *obj) { obj->g(); }537}538 539namespace pr19519 {540// VS2013 CL miscompiles this, just make sure we don't regress.541 542struct A {543 virtual void f();544 virtual void g();545};546 547struct B : virtual A {548 virtual void f();549 B();550};551 552struct C : virtual A {553 virtual void g();554};555 556struct X : B, C {557 X();558 559 // CHECK-LABEL: VFTable for 'pr19519::A' in 'pr19519::B' in 'pr19519::X' (2 entries).560 // CHECK-NEXT: 0 | void pr19519::B::f()561 // CHECK-NEXT: [this adjustment: vtordisp at -4, -4 non-virtual]562 // CHECK-NEXT: 1 | void pr19519::C::g()563 // CHECK-NEXT: [this adjustment: vtordisp at -4, -4 non-virtual]564 565 // MANGLING-DAG: @"??_7X@pr19519@@6B@" = {{.*}}@"?g@C@pr19519@@$4PPPPPPPM@3AEXXZ"566};567 568X::X() {}569}570