--- /dev/null
+QT LICENSE AGREEMENT
+Agreement version 4.4.1
+
+This Qt License Agreement ("Agreement") is a legal agreement for the licensing
+of Licensed Software (as defined below) between The Qt Company (as defined
+below) and the Licensee who has accepted the terms of this Agreement by signing
+this Agreement or by downloading or using the Licensed Software or in any other
+appropriate means.
+
+Capitalized terms used herein are defined in Section 1.
+
+WHEREAS:
+ (A) Licensee wishes to use the Licensed Software for the purpose of
+ developing and distributing Applications and/or Devices (each as defined
+ below);
+ (B) The Qt Company is willing to grant the Licensee a right to use Licensed
+ Software for such a purpose pursuant to term and conditions of this
+ Agreement; and
+ (C) Parties wish to enable that their respective Affiliates also can sell
+ and purchase licenses to serve Licensee Affiliates' needs to use Licensed
+ Software pursuant to terms of the Agreement. Any such license purchases by
+ Licensee Affiliates from The Qt Company or its Affiliates will create
+ contractual relationship directly between the relevant The Qt Company and
+ the respective ordering Licensee Affiliate "Acceding Agreement").
+ Accordingly, Licensee shall not be a party to any such Acceding Agreement,
+ and no rights or obligations are created to the Licensee thereunder but all
+ rights and obligations under such Acceding Agreement are vested and borne
+ solely by the ordering Licensee Affiliate and the relevant The Qt Company
+ as a contracting parties under such Acceding Agreement.
+
+NOW, THEREFORE, THE PARTIES HEREBY AGREE AS FOLLOWS:
+
+1. DEFINITIONS
+
+"Affiliate" of a Party shall mean an entity
+ (i) which is directly or indirectly controlling such Party;
+ (ii) which is under the same direct or indirect ownership or control as
+ such Party; or
+ (iii) which is directly or indirectly owned or controlled by such Party.
+For these purposes, an entity shall be treated as being controlled by another
+if that other entity has fifty percent (50 %) or more of the votes in such
+entity, is able to direct its affairs and/or to control the composition of its
+board of directors or equivalent body.
+
+"Add-on Products" shall mean The Qt Company's specific add-on software products
+which are not licensed as part of The Qt Company's standard product offering,
+but shall be included into the scope of Licensed Software only if so
+specifically agreed between the Parties.
+
+"Agreement Term" shall mean the validity period of this Agreement, as set forth
+in Section 12.
+
+"Applications" shall mean software products created using the Licensed
+Software, which include the Redistributables, or part thereof.
+
+"Contractor(s)" shall mean third party consultants, distributors and
+contractors performing services to the Licensee under applicable contractual
+arrangement.
+
+"Customer(s)" shall mean Licensee's customers to whom Licensee, directly or
+indirectly, distributes copies of the Redistributables as integrated or
+incorporated into Applications or Devices.
+
+"Data Protection Legislation" shall mean the General Data Protection Regulation
+(EU 2016/679) (GDPR) and any national implementing laws, regulations and
+secondary legislation, as may be amended or updated from time to time, as well
+as any other data protection laws or regulations applicable in relevant
+territory.
+
+"Deployment Platforms" shall mean target operating systems and/or hardware
+specified in the License Certificate, on which the Redistributables can be
+distributed pursuant to the terms and conditions of this Agreement.
+
+"Designated User(s)" shall mean the employee(s) of Licensee or Licensee's
+Affiliates acting within the scope of their employment or Licensee's
+Contractors acting within the scope of their services on behalf of Licensee.
+
+"Development License" shall mean the license needed by the Licensee for each
+Designated User to use the Licensed Software under the license grant described
+in Section 3.1 of this Agreement. Development Licenses are available per
+respective Licensed Software products, each product having its designated scope
+and purpose of use.
+
+"Development License Term" shall mean the agreed validity period of the
+Development License or QA Tools license during which time the relevant Licensed
+Software product can be used pursuant to this Agreement. Agreed Development
+License Term, as ordered and paid for by the Licensee, shall be memorialized in
+the applicable License Certificate.
+
+"Development Platforms" shall mean those host operating systems specified in
+the License Certificate, in which the Licensed Software can be used under the
+Development License.
+
+"Devices" shall mean
+ (1) hardware devices or products that
+ i. are manufactured and/or distributed by the Licensee, its Affiliates,
+ Contractors or Customers, and
+ ii. incorporate, integrate or link to Applications such that
+ substantial functionality of such unit, when used by an End User,
+ is provided by Application(s) or otherwise depends on the Licensed
+ Software, regardless of whether the Application is developed by
+ Licensee or its Contractors; or
+ (2) Applications designed for the hardware devices specified in item (1).
+
+ Devices covered by this Agreement shall be specified in Appendix 2 or in a
+ quote.
+
+"Distribution License(s)" shall mean a royalty-bearing license required for any
+kind of sale, trade, exchange, loan, lease, rental or other distribution by or
+on behalf of Licensee to a third party of Redistributables in connection with
+Devices pursuant to license grant described in Section 3.3 of this Agreement.
+Distribution Licensed are sold separately for each type of Device respectively
+and cannot be used for any type of Devices at Licensee's discretion.
+
+"Distribution License Packs" shall mean set of prepaid Distribution Licenses
+for distribution of Redistributables, as defined in The Qt Company's standard
+price list, quote, Purchase Order confirmation or in an Appendix 2 hereto, as
+the case may be.
+
+"End User" shall mean the final end user of the Application or a Device.
+
+"Evaluation License Term" shall mean a time period specified in the License
+Certificate for the Licensee to use the relevant Licensed Software for
+evaluation purposes according to Section 3.6 herein.
+
+"Intellectual Property Rights" shall mean patents (including utility models),
+design patents, and designs (whether or not capable of registration), chip
+topography rights and other like protection, copyrights, trademarks, service
+marks, trade names, logos or other words or symbols and any other form of
+statutory protection of any kind and applications for any of the foregoing as
+well as any trade secrets.
+
+"License Certificate" shall mean a certificate generated by The Qt Company for
+each Designated User respectively upon them downloading the Licensed Software,
+which will be available under respective Designated User's Qt Account at
+account.qt.io. License Certificates will specify relevant information
+pertaining the Licensed Software purchased by Licensee and Designated User's
+license to the Licensed Software.
+
+"License Fee" shall mean the fee charged to the Licensee for rights granted
+under the terms of this Agreement.
+
+"Licensed Software" shall mean specified product of commercially licensed
+version of Qt Software and/or QA Tools defined in Appendix 1 and/or Appendix 3,
+which Licensee has purchased and which is provided to Licensee under the terms
+of this Agreement. Licensed Software shall include corresponding online or
+electronic documentation, associated media and printed materials, including the
+source code (where applicable), example programs and the documentation.
+Licensed Software does not include Third Party Software (as defined in Section
+4) or Open Source Qt. The Qt Company may, in the course of its development
+activities, at its free and absolute discretion and without any obligation to
+send or publish any notifications to the Licensee or in general, make changes,
+additions or deletions in the components and functionalities of the Licensed
+Software, provided that no such changes, additions or deletions will affect
+the already released version of the Licensed Software, but only upcoming
+version(s).
+
+"Licensee" shall mean the individual or legal entity that is party to this
+Agreement.
+
+"Licensee's Records" shall mean books and records that contain information
+bearing on Licensee's compliance with this Agreement, Licensee's use of Open
+Source Qt and/or the payments due to The Qt Company under this Agreement,
+including, but not limited to user information, assembly logs, sales records
+and distribution records.
+
+"Modified Software" shall have the meaning as set forth in Section 2.3.
+
+"Online Services" shall mean any services or access to systems made available
+by The Qt Company to the Licensee over the Internet relating to the Licensed
+Software or for the purpose of use by the Licensee of the Licensed Software or
+Support. Use of any such Online Services is discretionary for the Licensee and
+some of them may be subject to additional fees.
+
+"Open Source Qt" shall mean Qt Software available under the terms of the GNU
+Lesser General Public License, version 2.1 or later ("LGPL") or the GNU General
+Public License, version 2.0 or later ("GPL"). For clarity, Open Source Qt shall
+not be provided, governed or used under this Agreement.
+
+"Party" or "Parties" shall mean Licensee and/or The Qt Company.
+
+"Permitted Software" shall mean (i) third party open source software products
+that are generally available for public in source code form and free of any
+charge under any of the licenses approved by Open Source Initiative as listed
+on https://opensource.org/licenses, which may include parts of Open Source Qt
+or be developed using Open Source Qt; and (ii) software The Qt Company has made
+available via its Qt Marketplace online distribution channel.
+
+"Pre-Release Code" shall have the meaning as set forth in Section 4.
+
+"Prohibited Combination" shall mean any effort to use, combine, incorporate,
+link or integrate Licensed Software with any software created with or
+incorporating Open Source Qt, or use Licensed Software for creation of any such
+software.
+
+"Purchase Order" shall have the meaning as set forth in Section 10.2.
+
+"QA Tools" shall mean software libraries and tools as defined in Appendix 1
+depending on which product(s) the Licensee has purchased under the Agreement.
+
+"Qt Software" shall mean the software libraries and tools of The Qt Company,
+which The Qt Company makes available under commercial and/or open source
+licenses.
+
+"Redistributables" shall mean the portions of the Licensed Software set forth
+in Appendix 1 that may be distributed pursuant to the terms of this Agreement
+in object code form only, including any relevant documentation. Where relevant,
+any reference to Licensed Software in this Agreement shall include and refer
+also to Redistributables.
+
+"Renewal Term" shall mean an extension of previous Development License Term as
+agreed between the Parties.
+
+"Submitted Modified Software" shall have the meaning as set forth in Section
+2.3.
+
+"Support" shall mean standard developer support that is provided by The Qt
+Company to assist Designated Users in using the Licensed Software in accordance
+with this Agreement and the Support Terms.
+
+"Support Terms" shall mean The Qt Company's standard support terms specified in
+Appendix 9 hereto.
+
+"Taxes" shall have the meaning set forth in Section 10.5.
+
+"The Qt Company" shall mean:
+ (i) in the event Licensee is an individual residing in the United States or
+ a legal entity incorporated in the United States or having its
+ headquarters in the United States, The Qt Company Inc., a Delaware
+ corporation with its office at 3031 Tisch Way, 110 Plaza West,
+ San Jose, CA 95128, USA.; or
+ (ii) in the event the Licensee is an individual residing outside of the
+ United States or a legal entity incorporated outside of the United
+ States or having its registered office outside of the United States,
+ The Qt Company Ltd., a Finnish company with its registered office at
+ Miestentie 7, 02150 Espoo, Finland.
+
+"Third-Party Software" shall have the meaning set forth in Section 4.
+
+"Updates" shall mean a release or version of the Licensed Software containing
+bug fixes, error corrections and other changes that are generally made
+available to users of the Licensed Software that have contracted for Support.
+Updates are generally depicted as a change to the digits following the decimal
+in the Licensed Software version number. The Qt Company shall make Updates
+available to the Licensee under the Support. Updates shall be considered as
+part of the Licensed Software hereunder.
+
+"Upgrades" shall mean a release or version of the Licensed Software containing
+enhancements and new features and are generally depicted as a change to the
+first digit of the Licensed Software version number. In the event Upgrades are
+provided to the Licensee under this Agreement, they shall be considered as part
+of the Licensed Software hereunder.
+
+2. OWNERSHIP
+
+2.1. Ownership of The Qt Company
+
+The Licensed Software is protected by copyright laws and international
+copyright treaties, as well as other intellectual property laws and treaties.
+The Licensed Software is licensed, not sold.
+
+All of The Qt Company's Intellectual Property Rights are and shall remain the
+exclusive property of The Qt Company or its licensors respectively. No rights
+to The Qt Company's Intellectual Property Rights are assigned or granted to
+Licensee under this Agreement, except when and to the extent expressly
+specified herein.
+
+2.2. Ownership of Licensee
+
+All the Licensee's Intellectual Property Rights are and shall remain the
+exclusive property of the Licensee or its licensors respectively.
+
+All Intellectual Property Rights to the Modified Software, Applications and
+Devices shall remain with the Licensee and no rights thereto shall be granted
+by the Licensee to The Qt Company under this Agreement (except as set forth in
+Section 2.3 below).
+
+2.3. Modified Software
+
+Licensee may create bug-fixes, error corrections, patches or modifications to
+the Licensed Software ("Modified Software"). Such Modified Software may break
+the source or binary compatibility with the Licensed Software (including
+without limitation through changing the application programming interfaces
+("API") or by adding, changing or deleting any variable, method, or class
+signature in the Licensed Software and/or any inter-process protocols,
+services or standards in the Licensed Software libraries). To the extent that
+Licensee's Modified Software so breaks source or binary compatibility with the
+Licensed Software, Licensee acknowledges that The Qt Company's ability to
+provide Support may be prevented or limited and Licensee's ability to make use
+of Updates may be restricted.
+
+Licensee may, at its sole and absolute discretion, choose to submit Modified
+Software to The Qt Company ("Submitted Modified Software") in connection with
+Licensee's Support request, service request or otherwise. In the event
+Licensee does so, then, Licensee hereby grants The Qt Company a sublicensable,
+assignable, irrevocable, perpetual, worldwide, non-exclusive, royalty-free and
+fully paid-up license, under all of Licensee's Intellectual Property Rights, to
+reproduce, adapt, translate, modify, and prepare derivative works of, publicly
+display, publicly perform, sublicense, make available and distribute such
+Submitted Modified Software as The Qt Company sees fit at its free and absolute
+discretion.
+
+3. LICENSES GRANTED
+
+3.1. Development with Licensed Software
+
+Subject to the terms of this Agreement, The Qt Company grants to Licensee a
+worldwide, non-exclusive, non-transferable license, valid for each Development
+License Term, to use, modify and copy the Licensed Software by Designated
+Users on the Development Platforms for the sole purposes of designing,
+developing, demonstrating and testing Application(s) and/or Devices, and to
+provide thereto related support and other related services to Customers. Each
+Application and/or Device can only include, incorporate or integrate
+contributions by such Designated Users who are duly licensed for the applicable
+Development Platform(s) and Deployment Platform(s) (i.e have a valid license
+for the appropriate Licensed Software product).
+
+Licensee may install copies of the Licensed Software on five (5) computers per
+Designated User, provided that only the Designated Users who have a valid
+Development License may use the Licensed Software.
+
+Licensee may at any time designate another Designated User to replace a
+then-current Designated User by notifying The Qt Company in writing, where such
+replacement is due to termination of employment, change of job duties, long
+time absence or other such permanent reason affecting Designated User's need
+for Licensed Software.
+
+Upon expiry of the initially agreed Development License Term, the respective
+Development License Term shall be automatically extended to one or more Renewal
+Term(s), unless and until either Party notifies the other Party in writing, or
+any other method acceptable to The Qt Company (it being specifically
+acknowledged and understood that verbal notification is explicitly deemed
+inadequate in all circumstances), that it does not wish to continue the
+Development License Term, such notification to be provided to the other Party
+no less than thirty (30) days before expiry of the respective Development
+License Term. The Qt Company shall, in good time before the due date for the
+above notification, remind the Licensee on the coming Renewal Term. Unless
+otherwise agreed between the Parties, Renewal Term shall be 12 months.
+
+Any such Renewal Term shall be subject to License Fees agreed between the
+Parties or, if no advance agreement exists, subject to The Qt Company's
+standard list pricing applicable at the commencement date of any such
+Renewal Term.
+
+The Qt Company may either request the Licensee to place a purchase order
+corresponding to a quote by The Qt Company, or use Licensee's stored Credit
+Card information in the Qt Account to automatically charge the Licensee for the
+relevant Renewal Term.
+
+3.2. Distribution of Applications
+
+Subject to the terms of this Agreement, The Qt Company grants to Licensee a
+worldwide, non-exclusive, non-transferable, revocable (for cause pursuant to
+this Agreement), right and license, valid for the Agreement Term, to
+ (i) distribute, by itself or through its Contractors, Redistributables as
+ installed, incorporated or integrated into Applications for execution
+ on the Deployment Platforms, and
+ (ii) grant perpetual and irrevocable sublicenses to Redistributables, as
+ distributed hereunder, for Customers solely to the extent necessary in
+ order for the Customers to use the Applications for their respective
+ intended purposes.
+
+Right to distribute the Redistributables as part of an Application as provided
+herein is not royalty-bearing but is conditional upon the Application having
+been created, updated and maintained under a valid and duly paid Development
+Licenses.
+
+3.3. Distribution of Devices
+
+Subject to the terms of this Agreement, The Qt Company grants to Licensee a
+worldwide, non-exclusive, non-transferable, revocable (for cause pursuant to
+this Agreement), right and license, valid for the Agreement Term, to
+ (i) distribute, by itself or through one or more tiers of Contractors,
+ Redistributables as installed, incorporated or integrated, or intended
+ to be installed, incorporated or integrated into Devices for execution
+ on the Deployment Platforms, and
+ (ii) grant perpetual and irrevocable sublicenses to Redistributables, as
+ distributed hereunder, for Customers solely to the extent necessary in
+ order for the Customers to use the Devices for their respective
+ intended purposes.
+
+Right to distribute the Devices as provided herein is conditional upon
+ (i) the Devices having been created, updated and maintained under a valid
+ and duly paid Development Licenses, and
+ (ii) the Licensee having acquired corresponding Distribution Licenses at
+ the time of distribution of any Devices to Customers.
+
+3.4. Further Requirements
+
+The licenses granted above in this Section 3 by The Qt Company to Licensee are
+conditional and subject to Licensee's compliance with the following terms:
+ (i) Licensee acknowledges that The Qt Company has separate products of
+ Licensed Software for the purpose of Applications and Devices
+ respectively, where development and distribution of Devices is only
+ allowed using the correct designated product. Licensee shall make sure
+ and bear the burden of proof that Licensee is using a correct product
+ of Licensed Software entitling Licensee to development and distribution
+ of Devices;
+ (ii) Licensee shall not remove or alter any copyright, trademark or other
+ proprietary rights notice(s) contained in any portion of the Licensed
+ Software;
+ (iii) Applications must add primary and substantial functionality to the
+ Licensed Software so as not to compete with the Licensed Software;
+ (iv) Applications may not pass on functionality which in any way makes it
+ possible for others to create software with the Licensed Software;
+ provided however that Licensee may use the Licensed Software's
+ scripting and QML ("Qt Quick") functionality solely in order to enable
+ scripting, themes and styles that augment the functionality and
+ appearance of the Application(s) without adding primary and substantial
+ functionality to the Application(s);
+ (v) Licensee shall not use Licensed Software in any manner or for any
+ purpose that infringes, misappropriates or otherwise violates any
+ Intellectual property or right of any third party, or that violates any
+ applicable law;
+ (vi) Licensee shall not use The Qt Company's or any of its suppliers'
+ names, logos, or trademarks to market Applications, except that
+ Licensee may use "Built with Qt" logo to indicate that Application(s)
+ or Device(s) was developed using the Licensed Software;
+ (vii) Licensee shall not distribute, sublicense or disclose source code of
+ Licensed Software to any third party (provided however that Licensee
+ may appoint employee(s) of Contractors and Affiliates as Designated
+ Users to use Licensed Software pursuant to this Agreement). Such right
+ may be available for the Licensee subject to a separate software
+ development kit ("SDK") license agreement to be concluded with The Qt
+ Company;
+ (viii) Licensee shall not grant the Customers a right to (a) make copies of
+ the Redistributables except when and to the extent required to use the
+ Applications and/or Devices for their intended purpose, (b) modify the
+ Redistributables or create derivative works thereof, (c) decompile,
+ disassemble or otherwise reverse engineer Redistributables, or (d)
+ redistribute any copy or portion of the Redistributables to any third
+ party, except as part of the onward sale of the Application or Device
+ on which the Redistributables are installed;
+ (ix) Licensee shall not and shall cause that its Affiliates or Contractors
+ shall not use Licensed Software in any Prohibited Combination, unless
+ Licensee has received an advance written permission from The Qt Company
+ to do so. Absent such written permission, any and all distribution by
+ the Licensee during the Agreement Term of a hardware device or product
+ a) which incorporate or integrate any part of Licensed Software or Open
+ Source Qt; or b) where substantial functionality is provided by
+ software built with Licensed Software or Open Source Qt or otherwise
+ depends on the Licensed Software or Open Source Qt, shall be considered
+ to be Device distribution under this Agreement and shall be dependent
+ on Licensee's compliance thereof (including but not limited to
+ obligation to pay applicable License Fees for such distribution).
+ Notwithstanding what is provided above in this sub-section (ix),
+ Licensee is entitled to use and combine Licensed Software with any
+ Permitted Software;
+ (x) Licensee shall cause all of its Affiliates, Contractors and Customers
+ entitled to make use of the licenses granted under this Agreement, to
+ be contractually bound to comply with the relevant terms of this
+ Agreement and not to use the Licensed Software beyond the terms hereof
+ and for any purposes other than operating within the scope of their
+ services for Licensee. Licensee shall be responsible for any and all
+ actions and omissions of its Affiliates and Contractors relating to the
+ Licensed Software and use thereof (including but not limited to payment
+ of all applicable License Fees);
+ (xi) Except when and to the extent explicitly provided in this Section 3,
+ Licensee shall not transfer, publish, disclose, display or otherwise
+ make available the Licensed Software; and
+ (xii) Licensee shall not attempt or enlist a third party to conduct or
+ attempt to conduct any of the above.
+
+Above terms shall not be applicable if and to the extent they conflict with any
+mandatory provisions of any applicable laws.
+
+Any use of Licensed Software beyond the provisions of this Agreement is
+strictly prohibited and requires an additional license from The Qt Company.
+
+3.5 QA Tools License
+
+Subject to the terms of this Agreement, The Qt Company grants to Licensee a
+worldwide, non-exclusive, non-transferable license, valid for the Development
+License Term, to use the QA Tools for Licensee's internal business purposes in
+the manner provided below and in Appendix 1 hereto.
+
+Licensee may modify the QA Tools except for altering or removing any details of
+ownership, copyright, trademark or other property right connected with the QA
+Tools.
+
+Licensee shall not distribute the QA Tools or any part thereof, modified or
+unmodified, separately or as part of any software package, Application or
+Device.
+
+Upon expiry of the initially agreed Development License Term, the respective
+Development License Term shall be automatically extended to one or more Renewal
+Term(s), unless and until either Party notifies the other Party in writing, or
+any other method acceptable to The Qt Company (it being specifically
+acknowledged and understood that verbal notification is explicitly deemed
+inadequate in all circumstances), that it does not wish to continue the
+Development License Term, such notification to be provided to the other Party
+no less than thirty (30) days before expiry of the respective Development
+License Term. The Qt Company shall, in good time before the due date for the
+above notification, remind the Licensee on the coming Renewal Term. Unless
+otherwise agreed between the Parties, Renewal Term shall be 12 months.
+
+Any such Renewal Term shall be subject to License Fees agreed between the
+Parties or, if no advance agreement exists, subject to The Qt Company's
+standard list pricing applicable at the commencement date of any such
+Renewal Term.
+
+3.6 Evaluation License
+
+Subject to the terms of this Agreement, The Qt Company grants to Licensee a
+worldwide, non-exclusive, non-transferable license, valid for the Evaluation
+License Term to use the Licensed Software solely for the Licensee's internal
+use to evaluate and determine whether the Licensed Software meets Licensee's
+business requirements, specifically excluding any commercial use of the
+Licensed Software or any derived work thereof.
+
+Upon the expiry of the Evaluation License Term, Licensee must either
+discontinue use of the relevant Licensed Software or acquire a commercial
+Development License or QA Tools License specified herein.
+
+4. THIRD-PARTY SOFTWARE
+
+The Licensed Software may provide links or access to third party libraries or
+code (collectively "Third-Party Software") to implement various functions.
+Third-Party Software does not, however, comprise part of the Licensed Software,
+but is provided to Licensee complimentary and use thereof is discretionary for
+the Licensee. Third-Party Software will be listed in the ".../src/3rdparty"
+source tree delivered with the Licensed Software or documented in the Licensed
+Software, as such may be amended from time to time. Licensee acknowledges that
+use or distribution of Third-Party Software is in all respects subject to
+applicable license terms of applicable third-party right holders.
+
+5. PRE-RELEASE CODE
+
+The Licensed Software may contain pre-release code and functionality, or sample
+code marked or otherwise stated with appropriate designation such as
+"Technology Preview", "Alpha", "Beta", "Sample", "Example" etc.
+("Pre-Release Code").
+
+Such Pre-Release Code may be present complimentary for the Licensee, in order
+to provide experimental support or information for new platforms or
+preliminary versions of one or more new functionalities or for other similar
+reasons. The Pre-Release Code may not be at the level of performance and
+compatibility of a final, generally available, product offering. The
+Pre-Release Code may not operate correctly, may contain errors and may be
+substantially modified by The Qt Company prior to the first commercial
+product release, if any. The Qt Company is under no obligation to make
+Pre-Release Code commercially available, or provide any Support or Updates
+relating thereto. The Qt Company assumes no liability whatsoever regarding
+any Pre-Release Code, but any use thereof is exclusively at Licensee's own risk
+and expense.
+
+For clarity, unless Licensed Software specifies different license terms for the
+respective Pre-Release Code, the Licensee is entitled to use such pre-release
+code pursuant to Section 3, just like other Licensed Software.
+
+6. LIMITED WARRANTY AND WARRANTY DISCLAIMER
+
+The Qt Company hereby represents and warrants that (i) it has the power and
+authority to grant the rights and licenses granted to Licensee under this
+Agreement, and (ii) Licensed Software will operate materially in accordance
+with its specifications.
+
+Except as set forth above, the Licensed Software is licensed to Licensee "as
+is" and Licensee's exclusive remedy and The Qt Company's entire liability for
+errors in the Licensed Software shall be limited, at The Qt Company's option,
+to correction of the error, replacement of the Licensed Software or return of
+the applicable fees paid for the defective Licensed Software for the time
+period during which the License is not able to utilize the Licensed Software
+under the terms of this Agreement.
+
+TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE QT COMPANY ON BEHALF OF
+ITSELF AND ITS LICENSORS, SUPPLIERS AND AFFILIATES, DISCLAIMS ALL OTHER
+WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, ANY IMPLIED
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
+NON-INFRINGEMENT WITH REGARD TO THE LICENSED SOFTWARE. THE QT COMPANY DOES NOT
+WARRANT THAT THE LICENSED SOFTWARE WILL SATISFY LICENSEE'S REQUIREMENTS OR THAT
+IT WILL OPERATE WITHOUT DEFECT OR ERROR OR THAT THE OPERATION THEREOF WILL BE
+UNINTERRUPTED.
+
+7. LIMITATION OF LIABILITY
+
+EXCEPT FOR (I) CASES OF GROSS NEGLIGENCE OR INTENTIONAL MISCONDUCT, AND (II)
+BREACH OF CONFIDENTIALITY, AND TO THE EXTENT PERMITTED BY APPLICABLE LAW, IN NO
+EVENT SHALL EITHER PARTY BE LIABLE TO THE OTHER PARTY FOR ANY LOSS OF PROFIT,
+LOSS OF DATA, LOSS OF BUSINESS OR GOODWILL OR ANY OTHER INDIRECT, SPECIAL,
+CONSEQUENTIAL, INCIDENTAL OR PUNITIVE COST, DAMAGES OR EXPENSE OF ANY KIND,
+HOWSOEVER ARISING UNDER OR IN CONNECTION WITH THIS AGREEMENT.
+
+EXCEPT FOR (I) CASES OF GROSS NEGLIGENCE OR INTENTIONAL MISCONDUCT, AND (II)
+BREACH OF CONFIDENTIALITY, AND TO THE EXTENT PERMITTED BY APPLICABLE LAW, IN NO
+EVENT SHALL EITHER PARTY'S TOTAL AGGREGATE LIABILITY UNDER THIS AGREEMENT
+EXCEED THE AGGREGATE LICENSE FEES PAID OR PAYABLE TO THE QT COMPANY BY LICENSEE
+DURING THE DEVELOPMENT LICENSE TERM DURING WHICH THE EVENT RESULTING IN SUCH
+LIABILITY OCCURRED.
+
+THE PROVISIONS OF THIS SECTION 7 ALLOCATE THE RISKS UNDER THIS AGREEMENT
+BETWEEN THE QT COMPANY AND LICENSEE AND THE PARTIES HAVE RELIED UPON THE
+LIMITATIONS SET FORTH HEREIN IN DETERMINING WHETHER TO ENTER INTO THIS
+AGREEMENT.
+
+NOTWITHSTANDING ANYTHING TO THE CONTRARY IN THIS AGREEMENT, LICENSEE SHALL
+ALWAYS BE LIABLE TO PAY THE APPLICABLE LICENSE FEES CORRESPONDING TO ITS
+ACTUAL USE OF LICENSED SOFTWARE.
+
+8. SUPPORT, UPDATES AND ONLINE SERVICES
+
+Upon due payment of the agreed License Fees the Licensee will be eligible to
+receive Support and Updates and to use the Online Services during the agreed
+Development License Term or other agreed fixed time period. Support is
+provided according to agreed support level and subject to applicable
+requirements and restrictions, as specified in the Support Terms.
+
+Unless otherwise decided by The Qt Company at its free and absolute discretion,
+Upgrades will not be included in the Support but may be available subject to
+additional fees.
+
+From time to time The Qt Company may change the Support Terms, provided that
+during the respective ongoing Support period the level of Support may not be
+reduced without the consent of the Licensee.
+
+Unless otherwise agreed, The Qt Company shall not be responsible for providing
+any service or support to Customers.
+
+9. CONFIDENTIALITY
+
+Each Party acknowledges that during the Agreement Term each Party may receive
+information about the other Party's business, business methods, business plans,
+customers, business relations, technology, and other information, including the
+terms of this Agreement, that is confidential and of great value to the other
+Party, and the value of which would be significantly reduced if disclosed to
+third parties ("Confidential Information"). Accordingly, when a Party (the
+"Receiving Party") receives Confidential Information from the other Party (the
+"Disclosing Party"), the Receiving Party shall only disclose such information
+to employees and Contractors on a need to know basis, and shall cause its
+employees and employees of its Affiliates to: (i) maintain any and all
+Confidential Information in confidence; (ii) not disclose the Confidential
+Information to a third party without the Disclosing Party's prior written
+approval; and (iii) not, directly or indirectly, use the Confidential
+Information for any purpose other than for exercising its rights and
+fulfilling its responsibilities pursuant to this Agreement. Each Party shall
+take reasonable measures to protect the Confidential Information of the other
+Party, which measures shall not be less than the measures taken by such Party
+to protect its own confidential and proprietary information.
+
+Obligation of confidentiality shall not apply to information that (i) is or
+becomes generally known to the public through no act or omission of the
+Receiving Party; (ii) was in the Receiving Party's lawful possession prior to
+the disclosure hereunder and was not subject to limitations on disclosure or
+use; (iii) is developed independently by employees or Contractors of the
+Receiving Party or other persons working for the Receiving Party who have not
+had access to the Confidential Information of the Disclosing Party, as proven
+by the written records of the Receiving Party; (iv) is lawfully disclosed to
+the Receiving Party without restrictions, by a third party not under an
+obligation of confidentiality; or (v) the Receiving Party is legally compelled
+to disclose, in which case the Receiving Party shall notify the Disclosing
+Party of such compelled disclosure and assert the privileged and confidential
+nature of the information and cooperate fully with the Disclosing Party to
+limit the scope of disclosure and the dissemination of disclosed Confidential
+Information to the minimum extent necessary.
+
+The obligations under this Section 9 shall continue to remain in force for a
+period of five (5) years after the last disclosure, and, with respect to trade
+secrets, for so long as such trade secrets are protected under applicable trade
+secret laws.
+
+10. FEES, DELIVERY AND PAYMENT
+
+10.1. License Fees
+
+License Fees are described in The Qt Company's standard price list, quote or
+Purchase Order confirmation or in an Appendix 2 hereto, as the case may be.
+
+Unless otherwise expressly provided in this Agreement, the License Fees shall
+not be refunded or claimed as a credit in any event or for any reason
+whatsoever.
+
+10.2. Ordering Licenses
+
+Licensee may purchase Development Licenses, Distribution Licenses and QA Tools
+Licenses pursuant to agreed pricing terms or, if no specific pricing terms have
+been agreed upon, at The Qt Company's standard pricing terms applicable at the
+time of purchase.
+
+Unless expressly otherwise agreed, any price or other term quoted to the
+Licensee or specified herein shall only be valid for the thirty (30) days from
+the effective date of this Agreement, Appendix 2 or the date of the quote, as
+applicable.
+
+Licensee shall submit all purchase orders for Development Licenses and
+Distribution Licenses to The Qt Company by email or any other method acceptable
+to The Qt Company (each such order is referred to herein as a "Purchase Order")
+for confirmation, whereupon the Purchase Order shall become binding between the
+Parties.
+
+Licensee acknowledges and agrees that all Purchase Orders for Licensed Software
+the Licensee makes during the Agreement Term shall be governed exclusively
+under the terms of this Agreement.
+
+10.3. Distribution License Packs
+
+Unless otherwise agreed, Distribution Licenses shall be purchased by way of
+Distribution License Packs.
+
+Upon due payment of the ordered Distribution License Pack(s), the Licensee will
+have an account of Distribution Licenses available for distributing the
+Redistributables in accordance with this Agreement.
+
+Each time Licensee distributes a copy of Redistributables, then one
+Distribution License is used, and Licensee's account of available Distribution
+Licenses is decreased accordingly.
+
+Licensee may distribute copies of the Redistributables so long as Licensee has
+Distribution Licenses remaining on its account.
+
+10.4. Payment Terms
+
+License Fees and any other charges under this Agreement shall be paid by
+Licensee no later than thirty (30) days from the date of the applicable invoice
+from The Qt Company.
+
+The Qt Company will submit an invoice to Licensee after the date of this
+Agreement and/or after The Qt Company receives a Purchase Order from Licensee.
+
+A late payment charge of the lower of (a) one percent per month; or (b) the
+interest rate stipulated by applicable law, shall be charged on any unpaid
+balances that remain past due and which have not been disputed by the Licensee
+in good faith.
+
+10.5. Taxes
+
+All License Fees and other charges payable hereunder are gross amounts but
+exclusive of any value added tax, use tax, sales tax, withholding tax and other
+taxes, duties or tariffs ("Taxes") levied directly for the sale, delivery or
+use of Licensed Software hereunder pursuant to any applicable law. Such
+applicable Taxes shall be paid by Licensee to The Qt Company, or, where
+applicable, in lieu of payment of such Taxes to The Qt Company, Licensee shall
+provide an exemption certificate to The Qt Company and any applicable
+authority.
+
+11. RECORD-KEEPING AND REPORTING OBLIGATIONS; AUDIT RIGHTS
+
+11.1. Licensee's Record-keeping
+
+Licensee shall at all times during the Agreement Term and for a period of two
+(2) years thereafter maintain Licensee's Records in an accurate and up-to-date
+form. Licensee's Records shall be adequate to reasonably enable The Qt Company
+to determine Licensee's compliance with the provisions of this Agreement. The
+records shall conform to general good accounting practices.
+
+Licensee shall, within thirty (30) days from receiving The Qt Company's request
+to that effect, deliver to The Qt Company a report based on Licensee's Records,
+such report to contain information, in sufficient detail, on (i) number and
+identity of users working with Licensed Software or Open Source Qt, (ii) copies
+of Redistributables distributed by Licensee during the most recent calendar
+quarter and/or any other term specified by The Qt Company, , and (iii) any
+other information pertaining to Licensee's compliance with the terms of this
+Agreement (like e.g. information on products and/or projects relating to use of
+Distribution Licenses), as The Qt Company may reasonably require from time to
+time.
+
+11.2. The Qt Company's Audit Rights
+
+The Qt Company or an independent auditor acting on behalf of The Qt Company's,
+may, upon at least thirty (30) days' prior written notice and at its expense,
+audit Licensee with respect to the Licensee's use of the Licensed Software, but
+not more frequently than once during each 6-month period. Such audit may be
+conducted by mail, electronic means or through an in-person visit to Licensee's
+place of business. Any possible in-person audit shall be conducted during
+regular business hours at Licensee's facilities and shall not unreasonably
+interfere with Licensee's business activities and shall be limited in scope to
+verify Licensee's compliance with the terms of this Agreement. The Qt Company
+or the independent auditor acting on behalf of The Qt Company shall be entitled
+to inspect Licensee's Records and conduct necessary interviews of Licensee's
+relevant employees and Contractors. All such Licensee's Records and use thereof
+shall be subject to an obligation of confidentiality under this Agreement.
+
+If an audit reveals that Licensee is using the Licensed Software beyond scope
+of the licenses Licensee has paid for, Licensee shall pay to The Qt Company any
+amounts owed for such unauthorized use within 30 days from receipt of the
+corresponding invoice from The Qt Company.
+
+In addition, in the event the audit reveals a material violation of the terms
+of this Agreement (without limitation, either (i) underpayment of more than 10
+% of License Fees or 10,000 euros (whichever is more) or (ii) distribution of
+products, which include or result from Prohibited Combination, shall be deemed
+a material violation for purposes of this section), then the Licensee shall
+pay The Qt Company's reasonable cost of conducting such audit.
+
+12. TERM AND TERMINATION
+
+12.1. Agreement Term
+
+This Agreement shall enter into force upon due acceptance by both Parties and
+remain in force until terminated pursuant to the terms of this Section 12
+("Agreement Term").
+
+12.2. Termination for breach and suspension of rights
+Either Party shall have the right to terminate this Agreement upon thirty (30)
+days prior written notice if the other Party commits a material breach of any
+obligation of this Agreement and fails to remedy such breach within such notice
+period.
+
+Instead of termination, The Qt Company shall have the right to suspend or
+withhold grants of all rights to the Licensed Software hereunder, including but
+not limited to the Development Licenses, Distribution License, and Support,
+should Licensee fail to make payment in timely fashion or otherwise violates or
+is reasonably suspected to violate its obligations or terms of this Agreement,
+and where such violation or breach is not cured within ten (10) business days
+following The Qt Company's written notice thereof.
+
+12.3. Termination for insolvency
+
+Either Party shall have the right to terminate this Agreement immediately upon
+written notice in the event that the other Party becomes insolvent, files for
+any form of bankruptcy, makes any assignment for the benefit of creditors, has
+a receiver, administrative receiver or officer appointed over the whole or a
+substantial part of its assets, ceases to conduct business, or an act
+equivalent to any of the above occurs under the laws of the jurisdiction of the
+other Party.
+
+12.4. Parties' Rights and Duties upon Termination
+
+Upon expiry or termination of the Agreement, Licensee shall cease and shall
+cause all Designated Users (including those of its Affiliates' and
+Contractors') to cease using the Licensed Software under this Agreement. For
+clarity, a Development License of a Designated User or a QA Tools License, and
+all rights relating thereto, shall always terminate at the expiry of the
+respective Development License Term, even if the Agreement continues to remain
+in force.
+
+Upon such termination the Licensee shall destroy or return to The Qt Company
+all copies of the Licensed Software and all related materials and will certify
+the same by Licensee's duly authorized officer to The Qt Company upon its
+request, provided however that Licensee may retain and exploit such copies of
+the Licensed Software as it may reasonably require in providing continued
+support to Customers.
+
+Except when this Agreement is terminated by The Qt Company due to Licensee's
+material breach as set forth in Section 12.2, the Licensee may continue
+distribution of Applications and Devices under the terms of this Agreement
+despite the termination of this Agreement. In such event the terms hereof will
+continue to be applicable and govern any such distribution of Applications and
+Devices beyond the expiry or termination of this Agreement. In case of
+termination by The Qt Company due to Licensee's material breach, Licensee must
+cease any distribution of Applications and Devices at the date of termination
+of this Agreement.
+
+Expiry or termination of this Agreement for any reason whatsoever shall not
+relieve Licensee of its obligation to pay any License Fees accrued or payable
+to The Qt Company prior to the effective date of termination, and Licensee pay
+to The Qt Company all such fees within 30 days from the effective date of
+termination of this Agreement.
+
+Termination of this Agreement shall not affect any rights of Customers to
+continue use of Applications and Devices (and therein incorporated
+Redistributables).
+
+12.5. Extension of Rights under Special Circumstances
+
+In the event of The Qt Company choosing not to renew the Development License(s)
+or QA Tools Licenses, as set forth in Section 3.1 and 3.5 respectively, and
+where such decision of non-renewal is not due to any ongoing breach or alleged
+breach (as reasonably determined by The Qt Company) by Licensee of the terms of
+this Agreement or any applicable license terms of Open Source Qt, then all
+valid and affected Development Licenses and QA Tools licenses possessed by the
+Licensee at such date shall be extended to be valid in perpetuity under the
+terms of this Agreement and Licensee is entitled to purchase additional
+licenses as set forth in Section 10.2.
+
+In the event The Qt Company is declared bankrupt under a final, non-cancellable
+decision by relevant court of law, and this Agreement is not, at the date of
+expiry of the Development License(s) or QA Tools Licenses, assigned to party,
+who has assumed The Qt Company's position as a legitimate licensor of Licensed
+Software under this Agreement, then all valid Development Licenses and QA Tools
+Licenses possessed by the Licensee at such date of expiry, and which the
+Licensee has not notified for expiry, shall be extended to be valid in
+perpetuity under the terms of this Agreement.
+
+For clarity, in case of an extension under this Section 12.5, any such
+extension shall not apply to The Qt Company's Support obligations, but Support
+shall be provided only up until the end of the respective fixed Development
+License Term regardless of the extension of relevant Development License or QA
+Tools License, unless otherwise agreed between the Parties.
+
+13. GOVERNING LAW AND LEGAL VENUE
+
+In the event this Agreement is in the name of The Qt Company Inc., a Delaware
+Corporation, then:
+ (i) this Agreement shall be construed and interpreted in accordance with
+ the laws of the State of California, USA, excluding its choice of law
+ provisions;
+ (ii) the United Nations Convention on Contracts for the International Sale
+ of Goods will not apply to this Agreement; and
+ (iii) any dispute, claim or controversy arising out of or relating to this
+ Agreement or the breach, termination, enforcement, interpretation or
+ validity thereof, including the determination of the scope or
+ applicability of this Agreement to arbitrate, shall be determined by
+ arbitration in San Francisco, USA, before one arbitrator. The
+ arbitration shall be administered by JAMS pursuant to JAMS' Streamlined
+ Arbitration Rules and Procedures. Judgment on the Award may be entered
+ in any court having jurisdiction. This Section shall not preclude
+ parties from seeking provisional remedies in aid of arbitration from a
+ court of appropriate jurisdiction.
+
+In the event this Agreement is in the name of The Qt Company Ltd., a Finnish
+Company, then:
+ (i) this Agreement shall be construed and interpreted in accordance with
+ the laws of Finland, excluding its choice of law provisions;
+ (ii) the United Nations Convention on Contracts for the International Sale
+ of Goods will not apply to this Agreement; and
+ (iii) any disputes, controversy or claim arising out of or relating to this
+ Agreement, or the breach, termination or validity thereof shall be
+ finally settled by arbitration in accordance with the Arbitration Rules
+ of International Chamber of Commerce. The arbitration tribunal shall
+ consist of one (1), or if either Party so requires, of three (3),
+ arbitrators. The award shall be final and binding and enforceable in
+ any court of competent jurisdiction. The arbitration shall be held in
+ Helsinki, Finland and the process shall be conducted in the English
+ language. This Section shall not preclude parties from seeking
+ provisional remedies in aid of arbitration from a court of appropriate
+ jurisdiction.
+
+14. GENERAL PROVISIONS
+
+14.1. No Assignment
+
+Except in the case of a merger or sale of substantially all of its corporate
+assets, Licensee shall not be entitled to assign or transfer all or any of its
+rights, benefits and obligations under this Agreement without the prior written
+consent of The Qt Company, which shall not be unreasonably withheld or delayed.
+The Qt Company shall be entitled to freely assign or transfer any of its
+rights, benefits or obligations under this Agreement.
+
+14.2. No Third-Party Representations
+
+Licensee shall make no representations or warranties concerning the Licensed
+Software on behalf of The Qt Company. Any representation or warranty Licensee
+makes or purports to make on The Qt Company's behalf shall be void as to
+The Qt Company.
+
+14.3. Surviving Sections
+
+Any terms and conditions that by their nature or otherwise reasonably should
+survive termination of this Agreement shall so be deemed to survive. Such
+sections include especially the following: 1, 2, 6, 7, 9, 11, 12.4, 13 and 14.
+
+14.4. Entire Agreement
+
+This Agreement, the Appendices hereto, the License Certificate and any
+applicable quote and Purchase Order accepted by The Qt Company constitute the
+complete agreement between the Parties and supersedes all prior or
+contemporaneous discussions, representations, and proposals, written or oral,
+with respect to the subject matters discussed herein.
+
+In the event of any conflict or inconsistency between this Agreement and any
+Purchase Order, the terms of this Agreement will prevail over the terms of the
+Purchase Order with respect to such conflict or inconsistency.
+
+Parties specifically acknowledge and agree that this Agreement prevails over
+any click-to-accept or similar agreements the Designated Users may need to
+accept online upon download of the Licensed Software, as may be required by
+The Qt Company's applicable processes relating to Licensed Software.
+
+14.5. Modifications
+
+No modification of this Agreement shall be effective unless contained in a
+writing executed by an authorized representative of each Party. No term or
+condition contained in Licensee's Purchase Order ("Deviating Terms") shall
+apply unless The Qt Company has expressly agreed such Deviating Terms in
+writing. Unless and to the extent expressly agreed by The Qt Company, any such
+Deviating Terms shall be deemed void and with no legal effect. For clarity,
+delivery of the Licensed Software following the receipt of the Purchase Order
+including Deviating Terms shall not constitute acceptance of such Deviating
+Terms.
+
+14.6. Force Majeure
+
+Except for the payment obligations hereunder, neither Party shall be liable to
+the other for any delay or non-performance of its obligations hereunder in the
+event and to the extent that such delay or non-performance is due to an event
+of act of God, terrorist attack or other similar unforeseeable catastrophic
+event that prevents either Party for fulfilling its obligations under this
+Agreement and which such Party cannot avoid or circumvent ("Force Majeure
+Event"). If the Force Majeure Event results in a delay or non-performance of a
+Party for a period of three (3) months or longer, then either Party shall have
+the right to terminate this Agreement with immediate effect without any
+liability (except for the obligations of payment arising prior to the event of
+Force Majeure) towards the other Party.
+
+14.7. Notices
+
+Any notice given by one Party to the other shall be deemed properly given and
+deemed received if specifically acknowledged by the receiving Party in writing
+or when successfully delivered to the recipient by hand, fax, or special
+courier during normal business hours on a business day to the addresses
+specified for each Party on the signature page. Each communication and document
+made or delivered by one Party to the other Party pursuant to this Agreement
+shall be in the English language.
+
+14.8. Export Control
+
+Licensee acknowledges that the Redistributables, as incorporated in
+Applications or Devices, may be subject to export control restrictions under
+the applicable laws of respective countries. Licensee shall fully comply with
+all applicable export license restrictions and requirements as well as with all
+laws and regulations relating to the Redistributables and exercise of licenses
+hereunder and shall procure all necessary governmental authorizations,
+including without limitation, all necessary licenses, approvals, permissions or
+consents, where necessary for the re-exportation of the Redistributables,
+Applications and/or Devices.
+
+14.9. No Implied License
+
+There are no implied licenses or other implied rights granted under this
+Agreement, and all rights, save for those expressly granted hereunder, shall
+remain with The Qt Company and its licensors. In addition, no licenses or
+immunities are granted to the combination of the Licensed Software with any
+other software or hardware not delivered by The Qt Company under this
+Agreement.
+
+14.10. Attorney Fees
+
+The prevailing Party in any action to enforce this Agreement shall be entitled
+to recover its attorney's fees and costs in connection with such action, as to
+be ordered by the relevant dispute resolution body.
+
+14.11. Privacy
+
+Licensee acknowledges and agrees that for the purpose of this Agreement,
+The Qt Company may collect, use, transfer and disclose personal data pertaining
+to Designated Users as well as any other employees and directors of the
+Licensee and its Contractors relevant for carrying out the intent of this
+Agreement. Such personal data will be primarily collected from the relevant
+individuals but may be collected also from Licensee (e.g. in the course of
+Licensee's reporting obligations). The Parties acknowledge that as
+The Qt Company determines the purpose and means for such collection and
+processing of the applicable personal data, The Qt Company shall be regarded as
+the Data Controller under the applicable Data Protection Legislation.
+The Qt Company shall process any such personal data in accordance with its
+privacy and security policies and practices, which will comply with all
+applicable requirements of the Data Protection Legislation.
+
+14.12. Severability
+
+If any provision of this Agreement shall be adjudged by any court of competent
+jurisdiction to be unenforceable or invalid, that provision shall be limited or
+eliminated to the minimum extent necessary so that this Agreement shall
+otherwise remain in full force and effect and enforceable.
+
+14.13. Marketing Rights
+
+Parties have agreed upon Marketing Rights pursuant to Appendix 7, if any.
+
+
+
+
+APPENDICES
+The Agreement includes following Appendices 1-10, as applicable.
+- Appendix 1: Licensed Software details
+- Appendix 2: Pricing
+- Appendix 3: Add-on Software details (optional)
+- Appendix 4: Small business and startup Licenses (optional)
+- Appendix 5: Non-commercial and educational Licenses (optional)
+- Appendix 6: License Reporting (optional)
+- Appendix 7: Marketing Rights (optional)
+- Appendix 8: Intentionally left blank (optional)
+- Appendix 9: Support Terms
+- Appendix 10: Conversion from legacy Licenses to Subscription (optional)
+
+
+APPENDIX 1: LICENSED SOFTWARE
+
+The modules and/or tools that are included in the latest publicly available
+version of the respective product at the effective date of this Agreement- Qt
+for Application Development Professional (ADP), Qt for Application Development
+Enterprise (ADE), Qt for Device Creation Professional (DCP), Qt for Device
+Creation Enterprise (DCE), - are marked with "X" in the below table. The
+modules and tools are specific to each product version respectively and may
+vary from version to version. Modules and tools included in the latest publicly
+available version of the respective product at any given time are listed in
+Appendix 1 of the latest version of this Agreement available at
+www.qt.io/terms-conditions/. If a new version of Licensed Software does not
+include a module or tool present in an older version which Licensee is entitled
+to use under a valid license from The Qt Company, then Licensee will continue
+to have such right during the Term of this Agreement. In the event a new
+version of the Licensed Software adds modules or tools to any previous
+version(s), Licensee's rights will extend to cover also such additional modules
+and tools.
+
+Parts of the product that are permitted for distribution in object-code form
+only ("Redistributables") are marked with "R" in the below table.
+
++----------------------------------------------------------+
+| Modules / Tools | ADP | ADE | DCP | DCE |
++----------------------------------------------------------+
+| Active Qt | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt 3D | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt 5 Core Compatibility APIs | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Android Extras | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Bluetooth | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Canvas 3D | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Charts | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Concurrent | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Core | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Data Visualization | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt D-Bus | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt for Python | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt for WebAssembly | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Gamepad | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Graphical Effects | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt GUI | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Help | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Image Formats | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Location | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Lottie Animation | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Mac Extras | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Multimedia | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Multimedia Widgets | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Network | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Network Authorization | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt NFC | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt OpenGL | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt PDF | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Platform Headers | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Positioning | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Print Support | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Purchasing | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt QML | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Quick | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Quick 3D | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Quick Controls 1 | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Quick Controls | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Quick Dialogs | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Quick Extras | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Quick Layouts | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Quick Test | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Quick Timeline | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Quick WebGL | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Quick Widgets | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Remote Objects | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Script | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Script Tools | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt SCXML | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Sensors | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Serial Bus | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Serial Port | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Shader Tools | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Speech | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt State Machine | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt SQL | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt SVG | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Test | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt UI Tools | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Virtual Keyboard | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Wayland Compositor | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt WebChannel | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt WebEngine | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt WebSockets | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt WebView | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Widgets | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Windows Extras | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt X11 Extras | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt XML | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt XML Patterns | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Designer (Qt Widget Designer) | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Linguist | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt Assistant | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| lupdate | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| lrelease | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| lconvert | X,R | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt MQTT | | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt KNX | | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt OPC UA | | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Qt CoAP | | X,R | X,R | X,R |
++----------------------------------------------------------+
+| Boot 2 Qt stacks | | | X,R | X,R |
++----------------------------------------------------------+
+| Qt OTA | | | X,R | X,R |
++----------------------------------------------------------+
+| Device Utilities | | | X,R | X,R |
++----------------------------------------------------------+
+| Qt Debugging Bridge (QBD) Daemon | | | X,R | X,R |
++----------------------------------------------------------+
+| Qt Quick Ultralite Controls | | | X,R | X,R |
++----------------------------------------------------------+
+| Qt Quick Ultralite | | | X,R | X,R |
++----------------------------------------------------------+
+| Qt Safe Renderer (QSR) | | | | X,R |
++----------------------------------------------------------+
+| Qt Application Manager | | | | X,R |
++----------------------------------------------------------+
+| Qt Interface Framework | | | | X,R |
++----------------------------------------------------------+
+| Neptune Reference UI | | | | X,R |
++----------------------------------------------------------+
+| Qt for Android Automotive (QAA) | | | | X,R |
++----------------------------------------------------------+
+| Qt Creator | X | X | X | X |
++----------------------------------------------------------+
+| Qt Design Studio Professional | X | X | X | X |
++----------------------------------------------------------+
+| androiddeployqt | X | X | X | X |
++----------------------------------------------------------+
+| androidtestrunner | X | X | X | X |
++----------------------------------------------------------+
+| canbusutil | X | X | X | X |
++----------------------------------------------------------+
+| dumpcpp | X | X | X | X |
++----------------------------------------------------------+
+| dumpdoc | X | X | X | X |
++----------------------------------------------------------+
+| fixqt4headers.pl | X | X | X | X |
++----------------------------------------------------------+
+| idc | X | X | X | X |
++----------------------------------------------------------+
+| moc | X | X | X | X |
++----------------------------------------------------------+
+| pixeltool | X | X | X | X |
++----------------------------------------------------------+
+| qdbus | X | X | X | X |
++----------------------------------------------------------+
+| qdbuscpp2xml | X | X | X | X |
++----------------------------------------------------------+
+| qdbusviwer | X | X | X | X |
++----------------------------------------------------------+
+| qdbusxml2cpp | X | X | X | X |
++----------------------------------------------------------+
+| qdistancefieldgenerator | X | X | X | X |
++----------------------------------------------------------+
+| qdoc | X | X | X | X |
++----------------------------------------------------------+
+| qhelpgenerator | X | X | X | X |
++----------------------------------------------------------+
+| qlalr | X | X | X | X |
++----------------------------------------------------------+
+| qmake | X | X | X | X |
++----------------------------------------------------------+
+| qml | X | X | X | X |
++----------------------------------------------------------+
+| qmlcachegen | X | X | X | X |
++----------------------------------------------------------+
+| qmldom | X | X | X | X |
++----------------------------------------------------------+
+| qmleasing | X | X | X | X |
++----------------------------------------------------------+
+| qmlformat | X | X | X | X |
++----------------------------------------------------------+
+| qmllint | X | X | X | X |
++----------------------------------------------------------+
+| qmlpreview | X | X | X | X |
++----------------------------------------------------------+
+| qmlprofiler | X | X | X | X |
++----------------------------------------------------------+
+| qmlscene | X | X | X | X |
++----------------------------------------------------------+
+| qmltestrunner | X | X | X | X |
++----------------------------------------------------------+
+| qmltime | X | X | X | X |
++----------------------------------------------------------+
+| qmlviewer | X | X | X | X |
++----------------------------------------------------------+
+| qtdiag | X | X | X | X |
++----------------------------------------------------------+
+| qtpaths | X | X | X | X |
++----------------------------------------------------------+
+| qtplugininfo | X | X | X | X |
++----------------------------------------------------------+
+| qvkgen | X | X | X | X |
++----------------------------------------------------------+
+| rcc | X | X | X | X |
++----------------------------------------------------------+
+| tracegen | X | X | X | X |
++----------------------------------------------------------+
+| uic | X | X | X | X |
++----------------------------------------------------------+
+| windeployqt | X | X | X | X |
++----------------------------------------------------------+
+| Target toolchains | | | X | X |
++----------------------------------------------------------+
+| Qt Debugging Bridge Host Tools | | | X | X |
++----------------------------------------------------------+
+| qtconfig-gui | | | X | X |
++----------------------------------------------------------+
+| Qt Emulator | | | X | X |
++----------------------------------------------------------+
+| Qt Creator VxWorks plugin | | | X | X |
++----------------------------------------------------------+
+| Qt Creator plugin for Qt | | | | X |
+| Application Manager | | | | |
++----------------------------------------------------------+
+| qmlinterfacegenerator | | | | X |
++----------------------------------------------------------+
+| qmltocpp | | | | X |
++----------------------------------------------------------+
+| qulfontcompiler | | | | X |
++----------------------------------------------------------+
+| Qt Deployment Server | | | | X |
++----------------------------------------------------------+
+
+
+Rights for Application and Device use cases
+
+Following table summarizes the rights afforded by different products of the
+Licensed Software to create and distribute Applications and Devices as defined
+in this Agreement (X marks for rights):
+
++---------------------------------------------------------------+
+| | Applications | Devices |
++---------------------------------------------------------------+
+| ADP | X | |
++---------------------------------------------------------------+
+| ADE | X | |
++---------------------------------------------------------------+
+| DCP | X | X |
++---------------------------------------------------------------+
+| DCE | X | X |
++---------------------------------------------------------------+
+
+Licensed Software: Designer tools and modules
+
+The modules and/or tools that are included in the respective product - Qt for
+Design Studio Professional (DSP), Qt for Design Studio Enterprise (DSE) - are
+marked with "X" in the below table.
+
+Designer tools provides no Redistributables.
+
++---------------------------------------------+
+| | DSP | DSE |
++---------------------------------------------+
+| Qt Design Studio | X | X |
++---------------------------------------------+
+| Qt Design Bridges | | X |
++---------------------------------------------+
+| QML Live on host | X | X |
++---------------------------------------------+
+| QML Live on target | | X |
++---------------------------------------------+
+| Variant Management | | X |
++---------------------------------------------+
+| Shader creation tools | | X |
++---------------------------------------------+
+| Profiling tools | | X |
++---------------------------------------------+
+| Simulink support | | X |
++---------------------------------------------+
+
+
+Both DSP and DSE can be used to create an user interface for use cases covered
+by ADP, ADE, DCP and DCE.
+
+Licensed Software: QA Tools
+
+The modules and/or tools that are included in the respective QA Tools product
+- Squish (both Tester and execution Licenses), Coco or Test Center - are marked
+with "X" in the below table. Optional features that will need additional
+licenses are marked with "O". QA Tools include no Redistributables.
+
++---------------------------------------------------------------------+
+| | Squish | Coco | Test Center |
++---------------------------------------------------------------------+
+| Squish IDE | X | | |
++---------------------------------------------------------------------+
+| QA Tool-specific command line tools | X | X | X |
++---------------------------------------------------------------------+
+| Coverage Browser | | X | |
++---------------------------------------------------------------------+
+| HTML interface | | | X |
++---------------------------------------------------------------------+
+| Qt Support Module | X | | |
++---------------------------------------------------------------------+
+| Java support module | X | | |
++---------------------------------------------------------------------+
+| Windows support module | X | | |
++---------------------------------------------------------------------+
+| iOS support module | X | | |
++---------------------------------------------------------------------+
+| Android support module | X | | |
++---------------------------------------------------------------------+
+| Web support module | X | | |
++---------------------------------------------------------------------+
+| macOS support module | X | | |
++---------------------------------------------------------------------+
+| VNC support module | X | | |
++---------------------------------------------------------------------+
+| MCU support module | X | | |
++---------------------------------------------------------------------+
+| C and C++ language module | | X | |
++---------------------------------------------------------------------+
+| C# language module | | X | |
++---------------------------------------------------------------------+
+| QML language module | | X | |
++---------------------------------------------------------------------+
+| Tester Cross-Compilation Add-On | O | O | |
++---------------------------------------------------------------------+
+
+License capabilities for Squish
+
+License capabilities that are included in the Squish Tester and Execution
+Licenses are marked with "X" in the below table.
+
++-----------------------------------------------------------------------------+
+| | Squish Tester License | Squish Execution License |
++-----------------------------------------------------------------------------+
+| Ability to create, edit, | X | |
+| and debug test cas | | |
++-----------------------------------------------------------------------------+
+| Ability to execute test | X | X |
+| cases | | |
++-----------------------------------------------------------------------------+
+
+Install and use capabilities for QA Tools
+
+Install and use capabilities that are included in the respective QA Tools
+products are defined in the below table.
+
++-----------------------------------------------------------------------------+
+| | Squish | Squish | Coco | Test |
+| | Tester | Execution | License | Center |
+| | License | License | | License |
++-----------------------------------------------------------------------------+
+| Number of installation | Unlimited | Unlimited | Unlimited | One(1) |
+| instances per license | | | | |
++-----------------------------------------------------------------------------+
+| Number of concurrent | Limited by| Limited by | Limited by | Limited by |
+| users | number of | number of | number of | number of |
+| | Squish | Squish | Coco | Test Center |
+| | Tester | Execution | Tester | Licenses |
+| | Licenses | Licenses | Licenses | |
++-----------------------------------------------------------------------------+
+
+
+APPENDIX 2: PRICING
+
+Separate template
+
+APPENDIX 3: ADD-ON PRODUCTS TO LICENSED SOFTWARE
+
+Intentionally left blank.
+
+APPENDIX 4: SMALL BUSINESS AND STARTUP
+
+The provisions of this Appendix 4 are applicable for companies with an annual
+revenue, including funding, equivalent to maximum of 250,000 USD (in applicable
+currency) during the latest full calendar year, as evidenced by duly audited
+records of the Licensee and approved by The Qt Company ("Start-up Company").
+
+Start-up Companies are qualified for a discounted License Fee for maximum of
+four (4) Development Licenses ("Start-up Development License") unless otherwise
+agreed between the parties.
+
+Start-up Development License entitles the respective Designated User for
+Support only for Install Support as defined in Appendix 9, Support Terms.
+
+Upon expiry of the respective Development License Term, the Start-up
+Development Licenses shall be automatically extended, pursuant to Section 3.1
+of the Agreement, for a Renewal Term either as new Start-up Development
+Licenses (if the Licensee still qualifies as a Start-up Company), or as normal
+then standard list price Development Licenses (if the Licensee no longer
+qualifies as a Start-up Company).
+
+APPENDIX 5: NON-COMMERCIAL AND EDUCATIONAL USE
+
+The provisions of this Appendix 5 are applicable for non-commercial use of the
+Licensed Software by the Licensee.
+
+For the purpose of this Appendix 5, the following additional definitions
+(replacing the relevant definition of the Agreement, where applicable) shall be
+applicable:
+
+"Demo Units" shall mean (i) hardware development platform, which incorporates
+the Licensed Software along with Licensee's software and/or hardware, and (ii)
+prototype versions of Applications or Devices.
+
+"Designated User(s)" shall mean the employees and students of the Licensee.
+
+"Licensee Products" shall mean Applications and/or Devices.
+
+"Permitted Purpose" shall mean (i) Licensee's internal evaluation and testing
+of Licensed Software, (ii) building Demo Units as well as (iii) educational
+use.
+
+"Agreement Term" shall mean a period of twelve (12) months or any such other
+period as may be agreed between the Parties.
+
+For the purpose of this Appendix 5, the following changes shall be agreed with
+respect to relevant Sections of the Agreement:
+ I. Recital (A) shall be replaced in its entirety to read as follows:
+ "(A) Licensee wishes to use the Licensed Software for the Permitted
+ Purpose."
+ II. Section 3.1 shall be replaced in its entirety to read as follows:
+ "The Qt Company grants to Licensee a personal, non-exclusive,
+ non-transferable, revocable, royalty-free license, valid for the
+ Agreement Term, to use, modify and copy the Licensed Software solely
+ for the Permitted Purpose. Licensee may install copies of the Licensed
+ Software on five (5) computers per Designated User, provided that only
+ the Designated Users who have a valid Development License may use the
+ Licensed Software. Licensee may demonstrate the Demo Units, provided
+ that such demonstrations must be conducted by Licensee, and the Demo
+ Units must remain in Licensee's possession and under Licensee's control
+ at all times.
+ For clarity, this Agreement does not (i) entitle Licensee to use
+ Licensed Software to create Applications or Devices (other than
+ prototypes thereof) or (ii) carry any distribution rights to Licensee,
+ but such rights are subject to and conditional upon conclusion of a
+ separate license agreement with The Qt Company."
+ III. Sections 3.2, 3.3, 3.5, 3.6, 8 and 10 shall be deleted.
+ IV. Section 3.4 shall be replaced in its entirety to read as follows:
+ "Licensee shall not:
+ - remove or alter any copyright, trademark or other proprietary rights
+ notice contained in any portion of the Licensed Software;
+ - transfer, publish, sublicense, disclose, display or otherwise make
+ the Licensed Software available to any third party (except that
+ Licensee may demonstrate the Demo Units pursuant to Section 3.1);
+ - in any way combine, incorporate or integrate Licensed Software with,
+ or use Licensed Software for creation of, any software created with
+ or incorporating Open Source Qt; Licensee shall cause all Designated
+ Users who make use of the licenses granted under this Agreement, to
+ be contractually bound to comply with the relevant terms of this
+ Agreement and not to use the Licensed Software beyond the terms
+ hereof. Licensee shall be responsible for any and all actions and
+ omissions of its Designated Users relating to the Licensed Software
+ and use thereof. Any use of Licensed Software beyond the provisions
+ of this Agreement is strictly prohibited and requires an additional
+ license from The Qt Company."
+ V. Section 12 shall be replaced in its entirety to read as follows:
+ "This Agreement shall enter into force upon due acceptance by both
+ Parties and remain in force for the Agreement Term, unless and until
+ terminated pursuant to the terms of Section 12.
+ Upon termination of the Agreement, Licensee shall cease using the
+ Licensed Software. All other copies of Licensed Software in the
+ possession or control of Licensee must be erased or destroyed. An
+ officer of Licensee must, upon request, promptly deliver to The Qt
+ Company a written confirmation that this has occurred."
+
+Except for the modifications specified above, this Appendix carries no change
+to the terms of the Agreement which shall remain in full force.
+
+APPENDIX 6: LICENSE REPORTING
+
+Separate template
+
+APPENDIX 7: MARKETING RIGHTS
+
+This Appendix 7 has the purpose to grant visibility through The Qt Company
+marketing channels of the usage of Qt and related product and service in
+Licensee product. Following related marketing right are agreed between the Qt
+Company and the Licensee.
+
+1. LICENSEE NAME AND LICENSEE LOGO
+
+The Qt Company has the right to use Licensee name and Licensee logo in public
+channel, in respect of the value proposition that the Qt company provided to
+the Licensee.
+
+2. MARKETING CONTENT COOPERATION
+
+2.1. LICENSEE CASES
+
+The Licensee is open to collaborate on content creation for marketing and
+communication purpose. The Licensee will nominate one responsible that will be
+in charge to support The Qt company with this content creation, according to
+content format paragraph, answering technical questions or sharing professional
+picture or video of required content. The Qt Company will have the right to
+advertise this in Content Format and Channel as mentioned in paragraph 3 and 4.
+
+2.2. FINAL PRODUCT REFERRAL
+
+Licensee agree that The Qt Company could connect their software product and
+services with the Licensee device or application, that the Licensee has created
+using The Qt Company technology and competence. Licensee will provide high
+quality picture, and video of the created final product where the Qt technology
+is running into. The Qt Company will have the right to advertise this in
+Content Format and Channel as mentioned in paragraph 3 and 4.
+
+3. CONTENT FORMAT
+
+- Video
+- Written Licensee case
+- Press release
+- Social media posts
+- Emails
+- Event booth Graphics
+- Printed material
+
+4. CHANNELS
+
+- Social media
+- The Qt Company resource center and website
+- Email to the Qt company contact database
+- Events
+- Online webinars
+- Public speech
+- Public presentations
+
+APPENDIX 8: INTENTIONALLY LEFT BLANK
+
+APPENDIX 9: SUPPORT TERMS
+
+These Qt support terms and conditions ("Support Terms") set forth the legal
+framework, where under The Qt Company ("The Qt Company") provides support
+services (as herein defined) to the Licensee.
+
+1 DEFINITIONS
+
+"Application Code" shall mean a computer software program written strictly
+using the Qt programming language, by or for the Licensee, with a user
+interface, enabling the Licensee or their users to accomplish a specific task
+and display any results of the task on the display monitor or screen.
+
+"Dedicated Contact" shall mean the employee of The Qt Company who will be the
+first point of contact for all Designated Users' requests for Support.
+
+"Errors" shall mean an error, flaw, mistake, failure, or fault in Licensed
+Software that prevents it from behaving as described in the relevant
+documentation or as agreed between the Parties.
+
+"Extended Support" shall mean a continuation to the normal Support period,
+which allows Designated Users to receive selected Support (Standard Support or
+Premium Support) for a version of Licensed Software that is no longer generally
+supported by The Qt Company.
+
+"Install Support" shall mean Support that is limited to installation related
+Error(s) on Development Platforms specified as supported host platforms for
+each Qt release under doc.qt.io.
+
+"Maintenance Release" shall mean a release or version of Licensed Software
+containing bug fixes, error corrections and other changes targeted to
+maintaining and improving product stability and quality. Maintenance Releases
+are generally depicted as a change to the third digit of Licensed Software
+version number.
+
+"Platforms" shall mean both Development Platforms and Deployment Platforms.
+Supported host and target Platforms may vary from for each Qt release as
+defined under doc.qt.io.
+
+"Premium Support" shall mean an upgraded level of Support that The Qt Company
+provides pursuant to these Support Terms to Licensee if Licensee has purchased
+Premium Support instead of Standard Support. Premium Support shall always be
+purchased for all Designated User(s) in the respective development team of the
+Licensee.
+
+"Response Time" shall mean the period of time from when Licensee notifies
+TheQt Company about an Error or requests Support until The Qt Company provides
+Licensee with a response that addresses (but not necessarily resolves) the
+reported Error or provides the requested Support.
+
+"Standard Support" shall mean standard level of Support that The Qt Company
+provides pursuant to these Support Terms to Licensee.
+
+"Support" shall mean developer assistance that is provided by The Qt Company
+to assist eligible Designated Users in Licensed Software installation, usage
+and functionality problem resolution for Error(s) and Error workarounds
+pursuant to the terms of these Support Terms. Support for different products is
+available as specified in the below table ("X" marking the Support that is
+included in the license price, optional Add-on Support services are marked as
+"O"):
+
++-----------------------------------------------------------------------+
+| |ADP|ADE|DCP|DCE|DSP|DSE|Squish|Coco|Test Center|
++-----------------------------------------------------------------------+
+| Install Support | X | X | X | X | X | X | X | X | X |
++-----------------------------------------------------------------------+
+| Standard Support | | X | X | X | X | X | X | X | X |
++-----------------------------------------------------------------------+
+| Premium Support | | O | O | O | O | O | O | O | O |
++-----------------------------------------------------------------------+
+| Extended Support | | O | O | O | O | O | | | |
++-----------------------------------------------------------------------+
+| Tool Qualification Kit| | | | | | | O | O | |
++-----------------------------------------------------------------------+
+
+"Support Validity Term" shall mean the Development License Term or any other
+fixed time period agreed between the Parties during which time the Customer is
+eligible to receive Support from The Qt Company.
+
+"Tool Qualification Kit" shall mean a customized set of documents and
+validation test cases.
+
+2 SUPPORT SERVICES
+
+2.1 Support Services Provided by The Qt Company
+
+Subject to these Support Terms and during the Support Validity Term, The Qt
+Company will via its web-based support user-interface, provide Designated
+User(s) with Support for the Platforms which Customer has licensed under the
+Agreement.
+The Qt Company will make commercially reasonable efforts to solve any Errors
+reported by Designated User(s). Resolution of an Error may be provided through
+Designated User(s) themselves downloading of a later released version of the
+applicable Licensed Software product(s) or providing the Designated User with a
+temporary workaround addressing such Error.
+
+2.2 Licensee's Obligations
+
+To report an Error, the Designated User shall register the Error on The Qt
+Company's web-based support user interface located at:
+https://account.qt.io/login or at another location designated by The Qt Company.
+
+The Designated User must provide adequate information and documentation to The
+Qt Company to enable it to recreate the Error or problem for which the
+Designated User has sought assistance.
+To ensure efficient handling of Errors, the Designated User must provide the
+following information, where relevant:
+- A clear, detailed description of the problem, question or suggestion;
+- Identification of which Licensed Software product and version is affected;
+- Identification of the operating environment (e.g. operating system, hardware
+ Platform, build tools, etc.) on which the problem exists;
+- On Standard Support: A complete and compilable test case of not more than 500
+ lines of code that demonstrates the problem;
+- On Premium Support: A complete and compilable test case that demonstrates the
+ problem or access to Application Code source codes.
+
+Additional relevant content, such as screenshots, etc.
+Additional content should be included as attachments. The preferred image
+formats are JPEG and PNG. Compressed content should be included in zip or
+tar.gz archives. Executable content and documents in platform specific formats
+such as Microsoft Office' are not accepted.
+
+In order for The Qt Company to provide prompt handling of Errors, the
+Designated User shall promptly respond to any requests from The Qt Company for
+additional information.
+
+2.3 Support Limitations
+
+General limitations:
+
+Each version or release of the Licensed Software will be Supported under
+Standard Support or Premium Support only for limited time period as set forth
+in doc.qt.io. For example, regular releases of Qt Software are supported for
+one (1) year from the release date of the version x.y.0 and Long Term Support
+(LTS) Releases are supported for a period of three (3) years from the release
+date of the LTS version x.y.0.
+
+The Qt Company shall only provide Support for Designated User(s).
+
+Support is made available for the entire development teams only: It is not
+allowed to purchase Support only for some members of the development team, and
+all Designated Users of the respective development team must be eligible for
+the same level of Support.
+
+Support is not provided for snapshots, preview releases, beta releases or
+release candidates.
+
+The Qt Company shall have no obligation to provide Support for hardware or
+operating system specific problems or problems arising from improper use,
+accident, neglect or modification of Qt.
+
+Limitations with Install Support:
+
+Support limited to Error(s) regarding installation and setting up of the Qt
+development environment on host Platforms.
+
+Limitations with Standard Support:
+
+The Qt Company shall not provide Support for third-party software or problems
+caused by third-party software even if such third-party software is distributed
+together with Licensed Software product(s).
+
+The Qt Company shall only provide Support for Error(s) that are reported on and
+can be reproduced on Platforms that are officially supported for the release of
+the Licensed Software.
+
+Limitations with Premium support:
+
+The Qt Company shall not provide Support for third-party software or problems
+caused by third-party software. However, if such third-party software is
+distributed together with Licensed Software, The Qt Company will make
+commercially reasonable efforts to solve such problems.
+
+The Qt Company shall only provide Support for Error(s) that can be reproduced
+on Platforms that are officially supported for the release of the Licensed
+Software. If the Error is on a Platform that is not supported, The Qt Company
+will make commercially reasonable efforts to provide a solution on closest
+corresponding supported Platform.
+
+Premium Support is optional and purchased for an agreed bucket of hours
+("Bucket"). Hours can be used by any Designated User in the respective
+development team. To encourage continuous usage of the Support, ten percent
+(10%) of the purchased Bucket shall automatically expire (regardless of whether
+such support hours are actually used or not by the Licensee) each month after
+three (3) months from the purchase of the Premium Support.
+
+2.4 Extended Support
+
+Extended Support extends the Support Validity Term for a release of Licensed
+Software that is no longer generally supported.
+
+Extended Support includes and is by default provided with Standard Support
+rules and limitations, unless Extended Support is purchased with Premium
+Support in which case Premium Support rules and limitations will apply.
+
+Extended Support is optional and purchased with annual fee and separately per
+each Licensee product. Extended Support will need definition of (i) Licensee
+product, (ii) used Platform(s) and (iii) Licensed Software version(s).
+
+2.5 Tool Qualification Kit
+
+The Qt Company shall provide set of customized documents and validation tests
+that enable Licensee to qualify QA testing tool for the purpose of ISO 26262,
+EN 50128, DO-330, IEC 61508, IEC 62304 or IEC 13485 certification Licensee end
+to end solution.
+
+3 RESPONSE TIME
+
+In performing Support, The Qt Company shall commit to following, non-binding,
+Response Times:
+
+Standard Support: Errors and Support requests will have a Response Time not to
+exceed two (2) business days.
+
+Premium Support: Errors and Support requests will have a Response Time not to
+exceed one (1) business day.
+
+For complex issues, The Qt Company may provide an initial response to the
+Designated User and then follow up, without undue delay, with additional
+communication before an Error is properly addressed or Support provided.
+
+4 ADDITIONAL SERVICES IN PREMIUM SUPPORT
+
+The Designated User(s) will be assigned a Dedicated Contact to handle requests
+for Support. Dedicated Contact is subject to change in cases such as sick
+leave, vacation and other similar reasons.
+
+The Designated User(s) can on request ask The Qt Company to access their
+computer remotely in order to resolve problems directly.
+
+The Designated User(s) can request a session via Instant Messaging or phone
+call in the support request to The Qt Company.
+
+Premium Support can assist Licensee in implementing new features, bug fixes
+and accessing patches in Licensed Software or Application Code.
+
+All Support requests will be handled with high priority.
+
+5 MAINTENANCE RELEASES, UPDATES AND UPGRADES
+
+Under the Support the Customer is eligible for Maintenance Releases and Updates
+that The Qt Company generally makes available to customers who has purchased
+Support. Unless otherwise decided by The Company at its free and absolute
+discretion, Upgrades will not be provided under the Support.
+
+The primary focus of Maintenance Releases is product quality. Therefore, each
+Maintenance Release typically includes the following types of changes to the
+previous version of Licensed Software:
+- Bug fixes caused by changes to previously working code;
+- Fixes related to build issues on supported Platforms;
+- Error corrections specific to a single Platform that are not present on other
+ Platforms;
+- Critical Error corrections such as crashes, data corruption, loss of data,
+ race conditions; and
+- Updates to documentation and license information when deemed necessary by
+ The Qt Company.
+
+The primary focus of Updates is introducing new features to Licensed Software
+and covering new platforms. Therefore, each Updates typically includes the
+following types of changes to the previous version of Licensed Software:
+- New platform support;
+- New toolchain support;
+- New features and Qt modules;
+
+6 WARRANTY DISCLAIMER
+
+The Qt Company makes no warranties that the Support provided will be successful
+in resolving any difficulties or problems or in diagnosing faults reported by
+Licensee. Support is provided to Licensee on an "as is" basis. To the maximum
+extent permitted by applicable law, The Qt Company disclaims all warranties and
+conditions, either express or implied, including, but not limited to, implied
+warranties of merchantability and fitness for a particular purpose for the
+Support provided by The Qt Company to Licensee.
+
+APPENDIX 10: CONVERSION TO SUBSCRIPTION
+
+Subject to the terms of this Appendix Licensee's current development licenses
+("Current Licenses") for commercial version of Qt Software and the license
+agreements governing such Current Licenses ("Existing Agreements") are being
+replaced by this Agreement and subscription based Development Licenses
+governed hereunder, as further specified below.
+
++---------------------------------------------------------------------------+
+| Existing Agreement(s) | <Trolltech, Nokia, Digia, The Qt Company> and |
+| signing parties, version | <Licensee> <Version of the Agreement, e.g. 2,0,|
+| and date of signatures | 3.2 or 4.1> <Date of the agreement signatures> |
+| thereof | |
++---------------------------------------------------------------------------+
+
+Parties hereby agree on conversion of Current Licenses listed in attached
+Exhibit A to the subscription licenses listed in attached Exhibit B for use
+through License Term. As of the date hereof,
+
+i. Licensee's Current Licenses as listed in Exhibit A shall terminate and be
+replaced with the Subscription licenses listed in Exhibit B and;
+ii. Existing Agreements are terminated.
+
+Prices for the conversion of Current Licenses are defined in Appendix 2
+Pricing or Quote.
+
+Notwithstanding anything in this Appendix to the contrary, and in addition to
+any payments due pursuant to this Appendix, Licensee remains fully obligated to
+fulfill any and all outstanding payment obligations to The Qt Company under any
+applicable Existing Agreements. For the avoidance of doubt, if any payments
+remain outstanding on the Current Licenses under the applicable terms Licensee
+will continue to make such payments in accordance with the applicable order
+documentation, notwithstanding the fact that the Current Licenses are being
+converted to Development Licenses pursuant to this Appendix.
--- /dev/null
+set(QT_REPO_MODULE_VERSION "6.4.2")
+set(QT_REPO_MODULE_PRERELEASE_VERSION_SEGMENT "alpha1")
--- /dev/null
+5af075e26e243bc155882720e0736bdae01f6da9
--- /dev/null
+cmake_minimum_required(VERSION 3.16)
+
+include(.cmake.conf)
+project(QtRemoteObjects
+ VERSION "${QT_REPO_MODULE_VERSION}"
+ DESCRIPTION "Qt RemoteObjects Libraries"
+ HOMEPAGE_URL "https://qt.io/"
+ LANGUAGES CXX C
+)
+
+find_package(Qt6 ${PROJECT_VERSION} CONFIG REQUIRED COMPONENTS BuildInternals Core Network)
+find_package(Qt6 ${PROJECT_VERSION} CONFIG OPTIONAL_COMPONENTS Gui Widgets Quick QuickTest)
+
+if(NOT TARGET Qt::Network)
+ message(NOTICE "Skipping the build as the condition \"TARGET Qt::Network\" is not met.")
+ return()
+endif()
+qt_build_repo()
+
+if(NOT QT_BUILD_STANDALONE_TESTS)
+ # Copy mkspecs for users preferring qmake builds
+ set(mkspecs_install_dir "${INSTALL_MKSPECSDIR}")
+ qt_path_join(mkspecs_install_dir ${QT_INSTALL_DIR} ${mkspecs_install_dir})
+
+ qt_copy_or_install(DIRECTORY mkspecs/
+ DESTINATION "${mkspecs_install_dir}"
+ FILES_MATCHING PATTERN "*.pr[if]"
+ )
+endif()
--- /dev/null
+Copyright (c) <year> <owner>.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--- /dev/null
+
+ GNU Free Documentation License
+ Version 1.3, 3 November 2008
+
+
+ Copyright (C) 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc.
+ <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+0. PREAMBLE
+
+The purpose of this License is to make a manual, textbook, or other
+functional and useful document "free" in the sense of freedom: to
+assure everyone the effective freedom to copy and redistribute it,
+with or without modifying it, either commercially or noncommercially.
+Secondarily, this License preserves for the author and publisher a way
+to get credit for their work, while not being considered responsible
+for modifications made by others.
+
+This License is a kind of "copyleft", which means that derivative
+works of the document must themselves be free in the same sense. It
+complements the GNU General Public License, which is a copyleft
+license designed for free software.
+
+We have designed this License in order to use it for manuals for free
+software, because free software needs free documentation: a free
+program should come with manuals providing the same freedoms that the
+software does. But this License is not limited to software manuals;
+it can be used for any textual work, regardless of subject matter or
+whether it is published as a printed book. We recommend this License
+principally for works whose purpose is instruction or reference.
+
+
+1. APPLICABILITY AND DEFINITIONS
+
+This License applies to any manual or other work, in any medium, that
+contains a notice placed by the copyright holder saying it can be
+distributed under the terms of this License. Such a notice grants a
+world-wide, royalty-free license, unlimited in duration, to use that
+work under the conditions stated herein. The "Document", below,
+refers to any such manual or work. Any member of the public is a
+licensee, and is addressed as "you". You accept the license if you
+copy, modify or distribute the work in a way requiring permission
+under copyright law.
+
+A "Modified Version" of the Document means any work containing the
+Document or a portion of it, either copied verbatim, or with
+modifications and/or translated into another language.
+
+A "Secondary Section" is a named appendix or a front-matter section of
+the Document that deals exclusively with the relationship of the
+publishers or authors of the Document to the Document's overall
+subject (or to related matters) and contains nothing that could fall
+directly within that overall subject. (Thus, if the Document is in
+part a textbook of mathematics, a Secondary Section may not explain
+any mathematics.) The relationship could be a matter of historical
+connection with the subject or with related matters, or of legal,
+commercial, philosophical, ethical or political position regarding
+them.
+
+The "Invariant Sections" are certain Secondary Sections whose titles
+are designated, as being those of Invariant Sections, in the notice
+that says that the Document is released under this License. If a
+section does not fit the above definition of Secondary then it is not
+allowed to be designated as Invariant. The Document may contain zero
+Invariant Sections. If the Document does not identify any Invariant
+Sections then there are none.
+
+The "Cover Texts" are certain short passages of text that are listed,
+as Front-Cover Texts or Back-Cover Texts, in the notice that says that
+the Document is released under this License. A Front-Cover Text may
+be at most 5 words, and a Back-Cover Text may be at most 25 words.
+
+A "Transparent" copy of the Document means a machine-readable copy,
+represented in a format whose specification is available to the
+general public, that is suitable for revising the document
+straightforwardly with generic text editors or (for images composed of
+pixels) generic paint programs or (for drawings) some widely available
+drawing editor, and that is suitable for input to text formatters or
+for automatic translation to a variety of formats suitable for input
+to text formatters. A copy made in an otherwise Transparent file
+format whose markup, or absence of markup, has been arranged to thwart
+or discourage subsequent modification by readers is not Transparent.
+An image format is not Transparent if used for any substantial amount
+of text. A copy that is not "Transparent" is called "Opaque".
+
+Examples of suitable formats for Transparent copies include plain
+ASCII without markup, Texinfo input format, LaTeX input format, SGML
+or XML using a publicly available DTD, and standard-conforming simple
+HTML, PostScript or PDF designed for human modification. Examples of
+transparent image formats include PNG, XCF and JPG. Opaque formats
+include proprietary formats that can be read and edited only by
+proprietary word processors, SGML or XML for which the DTD and/or
+processing tools are not generally available, and the
+machine-generated HTML, PostScript or PDF produced by some word
+processors for output purposes only.
+
+The "Title Page" means, for a printed book, the title page itself,
+plus such following pages as are needed to hold, legibly, the material
+this License requires to appear in the title page. For works in
+formats which do not have any title page as such, "Title Page" means
+the text near the most prominent appearance of the work's title,
+preceding the beginning of the body of the text.
+
+The "publisher" means any person or entity that distributes copies of
+the Document to the public.
+
+A section "Entitled XYZ" means a named subunit of the Document whose
+title either is precisely XYZ or contains XYZ in parentheses following
+text that translates XYZ in another language. (Here XYZ stands for a
+specific section name mentioned below, such as "Acknowledgements",
+"Dedications", "Endorsements", or "History".) To "Preserve the Title"
+of such a section when you modify the Document means that it remains a
+section "Entitled XYZ" according to this definition.
+
+The Document may include Warranty Disclaimers next to the notice which
+states that this License applies to the Document. These Warranty
+Disclaimers are considered to be included by reference in this
+License, but only as regards disclaiming warranties: any other
+implication that these Warranty Disclaimers may have is void and has
+no effect on the meaning of this License.
+
+2. VERBATIM COPYING
+
+You may copy and distribute the Document in any medium, either
+commercially or noncommercially, provided that this License, the
+copyright notices, and the license notice saying this License applies
+to the Document are reproduced in all copies, and that you add no
+other conditions whatsoever to those of this License. You may not use
+technical measures to obstruct or control the reading or further
+copying of the copies you make or distribute. However, you may accept
+compensation in exchange for copies. If you distribute a large enough
+number of copies you must also follow the conditions in section 3.
+
+You may also lend copies, under the same conditions stated above, and
+you may publicly display copies.
+
+
+3. COPYING IN QUANTITY
+
+If you publish printed copies (or copies in media that commonly have
+printed covers) of the Document, numbering more than 100, and the
+Document's license notice requires Cover Texts, you must enclose the
+copies in covers that carry, clearly and legibly, all these Cover
+Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on
+the back cover. Both covers must also clearly and legibly identify
+you as the publisher of these copies. The front cover must present
+the full title with all words of the title equally prominent and
+visible. You may add other material on the covers in addition.
+Copying with changes limited to the covers, as long as they preserve
+the title of the Document and satisfy these conditions, can be treated
+as verbatim copying in other respects.
+
+If the required texts for either cover are too voluminous to fit
+legibly, you should put the first ones listed (as many as fit
+reasonably) on the actual cover, and continue the rest onto adjacent
+pages.
+
+If you publish or distribute Opaque copies of the Document numbering
+more than 100, you must either include a machine-readable Transparent
+copy along with each Opaque copy, or state in or with each Opaque copy
+a computer-network location from which the general network-using
+public has access to download using public-standard network protocols
+a complete Transparent copy of the Document, free of added material.
+If you use the latter option, you must take reasonably prudent steps,
+when you begin distribution of Opaque copies in quantity, to ensure
+that this Transparent copy will remain thus accessible at the stated
+location until at least one year after the last time you distribute an
+Opaque copy (directly or through your agents or retailers) of that
+edition to the public.
+
+It is requested, but not required, that you contact the authors of the
+Document well before redistributing any large number of copies, to
+give them a chance to provide you with an updated version of the
+Document.
+
+
+4. MODIFICATIONS
+
+You may copy and distribute a Modified Version of the Document under
+the conditions of sections 2 and 3 above, provided that you release
+the Modified Version under precisely this License, with the Modified
+Version filling the role of the Document, thus licensing distribution
+and modification of the Modified Version to whoever possesses a copy
+of it. In addition, you must do these things in the Modified Version:
+
+A. Use in the Title Page (and on the covers, if any) a title distinct
+ from that of the Document, and from those of previous versions
+ (which should, if there were any, be listed in the History section
+ of the Document). You may use the same title as a previous version
+ if the original publisher of that version gives permission.
+B. List on the Title Page, as authors, one or more persons or entities
+ responsible for authorship of the modifications in the Modified
+ Version, together with at least five of the principal authors of the
+ Document (all of its principal authors, if it has fewer than five),
+ unless they release you from this requirement.
+C. State on the Title page the name of the publisher of the
+ Modified Version, as the publisher.
+D. Preserve all the copyright notices of the Document.
+E. Add an appropriate copyright notice for your modifications
+ adjacent to the other copyright notices.
+F. Include, immediately after the copyright notices, a license notice
+ giving the public permission to use the Modified Version under the
+ terms of this License, in the form shown in the Addendum below.
+G. Preserve in that license notice the full lists of Invariant Sections
+ and required Cover Texts given in the Document's license notice.
+H. Include an unaltered copy of this License.
+I. Preserve the section Entitled "History", Preserve its Title, and add
+ to it an item stating at least the title, year, new authors, and
+ publisher of the Modified Version as given on the Title Page. If
+ there is no section Entitled "History" in the Document, create one
+ stating the title, year, authors, and publisher of the Document as
+ given on its Title Page, then add an item describing the Modified
+ Version as stated in the previous sentence.
+J. Preserve the network location, if any, given in the Document for
+ public access to a Transparent copy of the Document, and likewise
+ the network locations given in the Document for previous versions
+ it was based on. These may be placed in the "History" section.
+ You may omit a network location for a work that was published at
+ least four years before the Document itself, or if the original
+ publisher of the version it refers to gives permission.
+K. For any section Entitled "Acknowledgements" or "Dedications",
+ Preserve the Title of the section, and preserve in the section all
+ the substance and tone of each of the contributor acknowledgements
+ and/or dedications given therein.
+L. Preserve all the Invariant Sections of the Document,
+ unaltered in their text and in their titles. Section numbers
+ or the equivalent are not considered part of the section titles.
+M. Delete any section Entitled "Endorsements". Such a section
+ may not be included in the Modified Version.
+N. Do not retitle any existing section to be Entitled "Endorsements"
+ or to conflict in title with any Invariant Section.
+O. Preserve any Warranty Disclaimers.
+
+If the Modified Version includes new front-matter sections or
+appendices that qualify as Secondary Sections and contain no material
+copied from the Document, you may at your option designate some or all
+of these sections as invariant. To do this, add their titles to the
+list of Invariant Sections in the Modified Version's license notice.
+These titles must be distinct from any other section titles.
+
+You may add a section Entitled "Endorsements", provided it contains
+nothing but endorsements of your Modified Version by various
+parties--for example, statements of peer review or that the text has
+been approved by an organization as the authoritative definition of a
+standard.
+
+You may add a passage of up to five words as a Front-Cover Text, and a
+passage of up to 25 words as a Back-Cover Text, to the end of the list
+of Cover Texts in the Modified Version. Only one passage of
+Front-Cover Text and one of Back-Cover Text may be added by (or
+through arrangements made by) any one entity. If the Document already
+includes a cover text for the same cover, previously added by you or
+by arrangement made by the same entity you are acting on behalf of,
+you may not add another; but you may replace the old one, on explicit
+permission from the previous publisher that added the old one.
+
+The author(s) and publisher(s) of the Document do not by this License
+give permission to use their names for publicity for or to assert or
+imply endorsement of any Modified Version.
+
+
+5. COMBINING DOCUMENTS
+
+You may combine the Document with other documents released under this
+License, under the terms defined in section 4 above for modified
+versions, provided that you include in the combination all of the
+Invariant Sections of all of the original documents, unmodified, and
+list them all as Invariant Sections of your combined work in its
+license notice, and that you preserve all their Warranty Disclaimers.
+
+The combined work need only contain one copy of this License, and
+multiple identical Invariant Sections may be replaced with a single
+copy. If there are multiple Invariant Sections with the same name but
+different contents, make the title of each such section unique by
+adding at the end of it, in parentheses, the name of the original
+author or publisher of that section if known, or else a unique number.
+Make the same adjustment to the section titles in the list of
+Invariant Sections in the license notice of the combined work.
+
+In the combination, you must combine any sections Entitled "History"
+in the various original documents, forming one section Entitled
+"History"; likewise combine any sections Entitled "Acknowledgements",
+and any sections Entitled "Dedications". You must delete all sections
+Entitled "Endorsements".
+
+
+6. COLLECTIONS OF DOCUMENTS
+
+You may make a collection consisting of the Document and other
+documents released under this License, and replace the individual
+copies of this License in the various documents with a single copy
+that is included in the collection, provided that you follow the rules
+of this License for verbatim copying of each of the documents in all
+other respects.
+
+You may extract a single document from such a collection, and
+distribute it individually under this License, provided you insert a
+copy of this License into the extracted document, and follow this
+License in all other respects regarding verbatim copying of that
+document.
+
+
+7. AGGREGATION WITH INDEPENDENT WORKS
+
+A compilation of the Document or its derivatives with other separate
+and independent documents or works, in or on a volume of a storage or
+distribution medium, is called an "aggregate" if the copyright
+resulting from the compilation is not used to limit the legal rights
+of the compilation's users beyond what the individual works permit.
+When the Document is included in an aggregate, this License does not
+apply to the other works in the aggregate which are not themselves
+derivative works of the Document.
+
+If the Cover Text requirement of section 3 is applicable to these
+copies of the Document, then if the Document is less than one half of
+the entire aggregate, the Document's Cover Texts may be placed on
+covers that bracket the Document within the aggregate, or the
+electronic equivalent of covers if the Document is in electronic form.
+Otherwise they must appear on printed covers that bracket the whole
+aggregate.
+
+
+8. TRANSLATION
+
+Translation is considered a kind of modification, so you may
+distribute translations of the Document under the terms of section 4.
+Replacing Invariant Sections with translations requires special
+permission from their copyright holders, but you may include
+translations of some or all Invariant Sections in addition to the
+original versions of these Invariant Sections. You may include a
+translation of this License, and all the license notices in the
+Document, and any Warranty Disclaimers, provided that you also include
+the original English version of this License and the original versions
+of those notices and disclaimers. In case of a disagreement between
+the translation and the original version of this License or a notice
+or disclaimer, the original version will prevail.
+
+If a section in the Document is Entitled "Acknowledgements",
+"Dedications", or "History", the requirement (section 4) to Preserve
+its Title (section 1) will typically require changing the actual
+title.
+
+
+9. TERMINATION
+
+You may not copy, modify, sublicense, or distribute the Document
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense, or distribute it is void, and
+will automatically terminate your rights under this License.
+
+However, if you cease all violation of this License, then your license
+from a particular copyright holder is reinstated (a) provisionally,
+unless and until the copyright holder explicitly and finally
+terminates your license, and (b) permanently, if the copyright holder
+fails to notify you of the violation by some reasonable means prior to
+60 days after the cessation.
+
+Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, receipt of a copy of some or all of the same material does
+not give you any rights to use it.
+
+
+10. FUTURE REVISIONS OF THIS LICENSE
+
+The Free Software Foundation may publish new, revised versions of the
+GNU Free Documentation License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in
+detail to address new problems or concerns. See
+https://www.gnu.org/licenses/.
+
+Each version of the License is given a distinguishing version number.
+If the Document specifies that a particular numbered version of this
+License "or any later version" applies to it, you have the option of
+following the terms and conditions either of that specified version or
+of any later version that has been published (not as a draft) by the
+Free Software Foundation. If the Document does not specify a version
+number of this License, you may choose any version ever published (not
+as a draft) by the Free Software Foundation. If the Document
+specifies that a proxy can decide which future versions of this
+License can be used, that proxy's public statement of acceptance of a
+version permanently authorizes you to choose that version for the
+Document.
+
+11. RELICENSING
+
+"Massive Multiauthor Collaboration Site" (or "MMC Site") means any
+World Wide Web server that publishes copyrightable works and also
+provides prominent facilities for anybody to edit those works. A
+public wiki that anybody can edit is an example of such a server. A
+"Massive Multiauthor Collaboration" (or "MMC") contained in the site
+means any set of copyrightable works thus published on the MMC site.
+
+"CC-BY-SA" means the Creative Commons Attribution-Share Alike 3.0
+license published by Creative Commons Corporation, a not-for-profit
+corporation with a principal place of business in San Francisco,
+California, as well as future copyleft versions of that license
+published by that same organization.
+
+"Incorporate" means to publish or republish a Document, in whole or in
+part, as part of another Document.
+
+An MMC is "eligible for relicensing" if it is licensed under this
+License, and if all works that were first published under this License
+somewhere other than this MMC, and subsequently incorporated in whole or
+in part into the MMC, (1) had no cover texts or invariant sections, and
+(2) were thus incorporated prior to November 1, 2008.
+
+The operator of an MMC Site may republish an MMC contained in the site
+under CC-BY-SA on the same site at any time before August 1, 2009,
+provided the MMC is eligible for relicensing.
+
+
+ADDENDUM: How to use this License for your documents
+
+To use this License in a document you have written, include a copy of
+the License in the document and put the following copyright and
+license notices just after the title page:
+
+ Copyright (c) YEAR YOUR NAME.
+ Permission is granted to copy, distribute and/or modify this document
+ under the terms of the GNU Free Documentation License, Version 1.3
+ or any later version published by the Free Software Foundation;
+ with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
+ A copy of the license is included in the section entitled "GNU
+ Free Documentation License".
+
+If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts,
+replace the "with...Texts." line with this:
+
+ with the Invariant Sections being LIST THEIR TITLES, with the
+ Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
+
+If you have Invariant Sections without Cover Texts, or some other
+combination of the three, merge those two alternatives to suit the
+situation.
+
+If your document contains nontrivial examples of program code, we
+recommend releasing these examples in parallel under your choice of
+free software license, such as the GNU General Public License,
+to permit their use in free software.
--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
--- /dev/null
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
--- /dev/null
+Licensees holding valid commercial Qt licenses may use this software in
+accordance with the the terms contained in a written agreement between
+you and The Qt Company. Alternatively, the terms and conditions that were
+accepted by the licensee when buying and/or downloading the
+software do apply.
+
+For the latest licensing terms and conditions, see https://www.qt.io/terms-conditions.
+For further information use the contact form at https://www.qt.io/contact-us.
--- /dev/null
+The Qt Company GPL Exception 1.0
+
+Exception 1:
+
+As a special exception you may create a larger work which contains the
+output of this application and distribute that work under terms of your
+choice, so long as the work is not otherwise derived from or based on
+this application and so long as the work does not in itself generate
+output that contains the output from this application in its original
+or modified form.
+
+Exception 2:
+
+As a special exception, you have permission to combine this application
+with Plugins licensed under the terms of your choice, to produce an
+executable, and to copy and distribute the resulting executable under
+the terms of your choice. However, the executable must be accompanied
+by a prominent notice offering all users of the executable the entire
+source code to this application, excluding the source code of the
+independent modules, but including any changes you have made to this
+application, under the terms of this license.
+
--- /dev/null
+version: 2
+accept_configuration:
+ condition: property
+ property: features
+ not_contains_value: Disable
+
+instructions:
+ Build:
+ - !include "{{qt/qtbase}}/coin_module_build_template_v2.yaml"
+
+ Test:
+ - !include "{{qt/qtbase}}/coin_module_test_template_v3.yaml"
--- /dev/null
+# Copyright (C) 2021 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+from conans import ConanFile
+import re
+from pathlib import Path
+
+
+def _parse_qt_version_by_key(key: str) -> str:
+ with open(Path(__file__).parent.resolve() / ".cmake.conf") as f:
+ m = re.search(fr'{key} .*"(.*)"', f.read())
+ return m.group(1) if m else ""
+
+
+def _get_qt_minor_version() -> str:
+ return ".".join(_parse_qt_version_by_key("QT_REPO_MODULE_VERSION").split(".")[:2])
+
+
+class QtRemoteObjects(ConanFile):
+ name = "qtremoteobjects"
+ license = "LGPL-3.0, GPL-2.0+, Commercial Qt License Agreement"
+ author = "Brett Stottlemyer, Ford Motor Company"
+ url = "https://code.qt.io/cgit/qt/qtremoteobjects.git"
+ description = (
+ "Qt Remote Objects (QtRO) is an Inter-Process Communication (IPC) module developed for Qt. "
+ "This module extends Qt's existing functionalities to enable information exchange between "
+ "processes or computers, easily."
+ )
+ topics = "qt", "qt6", "QtRO", "IPC"
+ settings = "os", "compiler", "arch", "build_type"
+ # for referencing the version number and prerelease tag and dependencies info
+ exports = ".cmake.conf", "dependencies.yaml"
+ exports_sources = "*", "!conan*.*"
+ python_requires = f"qt-conan-common/{_get_qt_minor_version()}@qt/everywhere"
+ python_requires_extend = "qt-conan-common.QtLeafModule"
--- /dev/null
+dependencies:
+ ../qtbase:
+ ref: e3e40c44d3f998a433a6a1080297c5f28e9a768f
+ required: true
+ ../qtdeclarative:
+ ref: 1b58f30087eedf42f16572d8ae1d6a5b18f3d698
+ required: false
--- /dev/null
+Qt 5.11 introduces many new features and improvements as well as bugfixes
+over the 5.10.x series. For more details, refer to the online documentation
+included in this distribution. The documentation is also available online:
+
+http://doc.qt.io/qt-5/index.html
+
+Note: Tech Preview modules are able to change APIs to refine or enhance the
+module's functionality. Thus Qt's binary compatibility quarantees aren't
+applicable. Code switching to 5.11.0 from earlier versions of Qt Remote
+Objects will need to be recompiled.
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+* Qt 5.11.0 Changes *
+****************************************************************************
+
+ - QRemoteObjectNode
+ * [QTBUG-64086] Added heartbeatInterval property, which can be used to
+ periodically check the connection between replica and source. If the
+ connection is lost, the replica enters the "Suspect" state and will
+ attempt to reconnect.
+ * Extended acquireModel to allow prefetching data.
+
+ - QRemoteObjectAbstractPersistedStore
+ * Renamed from QRemoteObjectPersistedStore, and made a QObject to allow
+ use from QML.
+ * Added QRemoteObjectSettingsStore (SettingsStore in QML) as a concrete
+ implementation using QSettings.
+
+ - repc
+ * Added support for nested classes, via an extension to the .rep file
+ format. The syntax is `CLASS <name>(<type>), where type needs to be a
+ class defined in the same rep file, and name is the property name
+ used to access the sub-QObject.
--- /dev/null
+Qt 5.11.1 is a bug-fix release. It maintains both forward and backward
+compatibility (source and binary) with Qt 5.11.0.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+http://doc.qt.io/qt-5/index.html
+
+Note: Tech Preview modules are able to change APIs to refine or enhance the
+module's functionality. Thus Qt's binary compatibility quarantees aren't
+applicable. Code switching to 5.11.0 from earlier versions of Qt Remote
+Objects will need to be recompiled.
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+* Qt 5.11.1 Changes *
+****************************************************************************
+
+ - This release contains several fixes for MODEL/SUBCLASS handling and tests.
--- /dev/null
+Qt 5.11.2 is a bug-fix release. It maintains both forward and backward
+compatibility (source and binary) with Qt 5.11.0 through 5.11.1.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+http://doc.qt.io/qt-5/index.html
+
+The Qt version 5.11 series is binary compatible with the 5.10.x series.
+Applications compiled for 5.10 will continue to run with 5.11.
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+* Qt 5.11.2 Changes *
+****************************************************************************
+
+ - This release contains only minor code improvements.
--- /dev/null
+Qt 5.11.3 is a bug-fix release. It maintains both forward and backward
+compatibility (source and binary) with Qt 5.11.0 through 5.11.2.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+http://doc.qt.io/qt-5/index.html
+
+Note: Tech Preview modules are able to change APIs to refine or enhance the
+module's functionality. Thus Qt's binary compatibility quarantees aren't
+applicable. Code switching to 5.11.0 from earlier versions of Qt Remote
+Objects will need to be recompiled.
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+* Qt 5.11.3 Changes *
+****************************************************************************
+
+ - QRemoteObjectNode
+ * Fix a constant reconnect issue
--- /dev/null
+Qt 5.12 introduces many new features and improvements as well as bugfixes
+over the 5.11.x series. For more details, refer to the online documentation
+included in this distribution. The documentation is also available online:
+
+https://doc.qt.io/qt-5/index.html
+
+Note: Tech Preview modules are able to change APIs to refine or enhance the
+module's functionality. Thus Qt's binary compatibility quarantees aren't
+applicable. Code switching to 5.12.0 from earlier versions of Qt Remote
+Objects will need to be recompiled.
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+* Qt 5.12.0 Changes *
+****************************************************************************
+ - General
+ * [QTBUG-68678] Graduate Qt Remote Objects from TP to fully supported
+ module of Qt
+ * [QTBUG-65727] QtRemoteObjects: Documentation doesn't match the code
+
+ - QRemoteObjectNode
+ * Support externally created QIODevices, including SSL sockets.
+ * Added proxy() method to support use-cases like having a single node
+ provide a (for instance) tcp url for a device allowing access from
+ other devices, while using internal connections (like "local:*") on
+ the device.
+
+ - repc
+ * [QTBUG-68976] Cannot add comments in .rep file
+ * [QTBUG-68975] Support multiline PODs and ENUMs in repc
+ * [QTBUG-67770] repc should be able to use tab characters as a normal
+ whitespace
+
+****************************************************************************
+* QML *
+****************************************************************************
+
+ - Updated import statement to better match typical module pattern. From
+ QML "import QtRemoteObjects 5.12" should now be used.
--- /dev/null
+Qt 5.12.1 is a bug-fix release. It maintains both forward and backward
+compatibility (source and binary) with Qt 5.12.0.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+http://doc.qt.io/qt-5/index.html
+
+Note: Tech Preview modules are able to change APIs to refine or enhance the
+module's functionality. Thus Qt's binary compatibility quarantees aren't
+applicable. Code switching to 5.12 (when Remote Objects "graduated" from
+Tech Preview) from earlier versions of Qt Remote Objects will need to be
+recompiled.
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+* Qt 5.12.1 Changes *
+****************************************************************************
+ - General
+ * [QTBUG-72064] QVariant is also a standalone type and we must treat it
+ accordingly
+ * Convert QNX backend to use shared pointers
+ * Various code cleanup
--- /dev/null
+Qt 5.12.2 is a bug-fix release. It maintains both forward and backward
+compatibility (source and binary) with Qt 5.12.0 through 5.12.1.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+https://doc.qt.io/qt-5/index.html
+
+The Qt version 5.12 series is binary compatible with the 5.11.x series.
+Applications compiled for 5.11 will continue to run with 5.12.
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+ - This release contains only minor code improvements.
--- /dev/null
+Qt 5.12.3 is a bug-fix release. It maintains both forward and backward
+compatibility (source and binary) with Qt 5.12.0 through 5.12.2.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+https://doc.qt.io/qt-5/index.html
+
+Note: Tech Preview modules are able to change APIs to refine or enhance the
+module's functionality. Thus Qt's binary compatibility quarantees aren't
+applicable. Code switching to 5.12 (when Remote Objects "graduated" from
+Tech Preview) from earlier versions of Qt Remote Objects will need to be
+recompiled.
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+* Qt 5.12.1 Changes *
+****************************************************************************
+ - General
+ * [QTBUG-74221] Fix restart/nullptr crash
+ * [QTBUG-73962] Fix crash with AllowExternalRegistration and QtRO schema
+ * Doc: Add link from index page to QML Types
--- /dev/null
+Qt 5.12.4 is a bug-fix release. It maintains both forward and backward
+compatibility (source and binary) with Qt 5.12.0 through 5.12.3 under most
+conditions. There may be exceptions if you are using Qt Remote Objects
+between proesses or devices running different versions Qt. The different
+versions of Qt must be running compatible versions of QtRO's protocol.
+
+Changes to the Qt Remote Objects protocol are documented online:
+
+https://doc.qt.io/qt-5/qtremoteobjects-compatibility.html
+
+The QtRO protocol has been updated in Qt 5.12.4 and Qt 5.13.0 to 1.3.
+
+Background: Qt's QueuedConnections work by copying signal parameters so
+the copy can be held and then later processed in another threads
+eventloop, allowing the original data to be changed in the originating
+thread.
+
+Qt Remote Objects leverages this capability and sends that data from a
+QObject in one process to a QObject in another. There is a specific
+protocol for this, as both sides need to interpret what is sent the
+same way. For example, there are Invoke and PropertyChanged packets
+exchanged. In addition to this, both sides need to know how to encode and
+decode all types shared. This type awareness is easy to ensure if the
+repc compiler is used to generate the headers for all sides.
+
+However, there are use cases where this isn't possible. In these cases
+QtRO will send the type (metaobject) information as well, allowing the
+receiving side to generate the required type information at runtime for
+certain types (PODs and enumerations).
+
+There were issues found in this type serialization code that required a
+change to the protocol. There are a number of commits going into both
+Qt 5.13.0 and Qt 5.12.4 with fixes. This means that you will not be able
+to use Qt Remote Objects on a device using Qt 5.12.3 or earlier and
+communicate to another device using Qt 5.12.4/5.13.0.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+https://doc.qt.io/qt-5/index.html
+
+The Qt version 5.12 series is binary compatible with the 5.11.x series.
+Applications compiled for 5.11 will continue to run with 5.12 as long as
+the protocol change noted above is taken into account.
+
+****************************************************************************
+* Important Behavior Changes *
+****************************************************************************
+
+ - Qt Remote Objects uses an internal protocol to pass data between
+ processes and/or devices. The same protocol version needs to be used on
+ all sides. The version was bumped from 1.2 to 1.3 in this release,
+ fixing potential crashes (see QTBUG-75017). If there is a mismatch, the
+ connecting node will output a warning and the host node will not send
+ any data.
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+The following fixes were part of the above protocol change and are in
+5.12.4 and 5.13.0.
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+ - QTBUG-75017: QtRO processes can crash if type registration is incorrect
+ - QTBUG-75056: Correctly handle QVariant properties on the replica side
+ - QTBUG-74084: QT remote objects false conversion warning in case of
+ QVariant property
--- /dev/null
+Qt 5.12.5 is a bug-fix release. It maintains both forward and backward
+compatibility (source and binary) with Qt 5.12.0 through 5.12.4.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+https://doc.qt.io/qt-5/index.html
+
+The Qt version 5.12 series is binary compatible with the 5.11.x series.
+Applications compiled for 5.11 will continue to run with 5.12.
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+ - This release contains only minor code improvements.
--- /dev/null
+Qt 5.13 introduces many new features and improvements as well as bugfixes
+over the 5.12.x series. For more details, refer to the online documentation
+included in this distribution. The documentation is also available online:
+
+https://doc.qt.io/qt-5/index.html
+
+The Qt version 5.13 series is binary compatible with the 5.12.x series.
+Applications compiled for 5.12 will continue to run with 5.13 under most
+conditions. There may be exceptions if you are using Qt Remote Objects
+between proesses or devices running different versions Qt. The different
+versions of Qt must be running compatible versions of QtRO's protocol.
+
+Changes to the Qt Remote Objects protocol are documented online:
+
+https://doc.qt.io/qt-5/qtremoteobjects-compatibility.html
+
+The QtRO protocol has been updated in Qt 5.12.4 and Qt 5.13.0 to 1.3.
+
+Background: Qt's QueuedConnections work by copying signal parameters so
+the copy can be held and then later processed in another threads
+eventloop, allowing the original data to be changed in the originating
+thread.
+
+Qt Remote Objects leverages this capability and sends that data from a
+QObject in one process to a QObject in another. There is a specific
+protocol for this, as both sides need to interpret what is sent the
+same way. For example, there are Invoke and PropertyChanged packets
+exchanged. In addition to this, both sides need to know how to encode and
+decode all types shared. This type awareness is easy to ensure if the
+repc compiler is used to generate the headers for all sides.
+
+However, there are use cases where this isn't possible. In these cases
+QtRO will send the type (metaobject) information as well, allowing the
+receiving side to generate the required type information at runtime for
+certain types (PODs and enumerations).
+
+There were issues found in this type serialization code that required a
+change to the protocol. There are a number of commits going into both
+Qt 5.13.0 and Qt 5.12.4 with fixes. This means that you will not be able
+to use Qt Remote Objects on a device using Qt 5.12.3 or earlier and
+communicate to another device using Qt 5.12.4/5.13.0.
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+The following fixes were part of the above protocol change and are in
+5.12.4 and 5.13.0.
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+ - QTBUG-75017: QtRO processes can crash if type registration is incorrect
+ - QTBUG-75056: Correctly handle QVariant properties on the replica side
+ - QTBUG-74084: QT remote objects false conversion warning in case of
+ QVariant property
--- /dev/null
+Qt 5.13.1 is a bug-fix release. It maintains both forward and backward
+compatibility (source and binary) with Qt 5.13.0.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+https://doc.qt.io/qt-5/index.html
+
+The Qt version 5.13 series is binary compatible with the 5.12.x series.
+Applications compiled for 5.12 will continue to run with 5.13.
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+ - Improved clarity and consistency of documentation.
+ - Made a number of fixes and improvements in the handling of enumerations.
--- /dev/null
+Qt 5.13.2 is a bug-fix release. It maintains both forward and backward
+compatibility (source and binary) with Qt 5.13.0 through 5.13.1.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+https://doc.qt.io/qt-5/index.html
+
+The Qt version 5.13 series is binary compatible with the 5.12.x series.
+Applications compiled for 5.12 will continue to run with 5.13 under most
+conditions. There may be exceptions if you are using Qt Remote Objects
+between proesses or devices running different versions Qt. The different
+versions of Qt must be running compatible versions of QtRO's protocol.
+
+Changes to the Qt Remote Objects protocol are documented online:
+
+https://doc.qt.io/qt-5/qtremoteobjects-compatibility.html
+
+The QtRO protocol has been updated in Qt 5.12.4 and Qt 5.13.0 to 1.3.
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+ - This release contains only minor code improvements and some improvements
+ to the documentation.
--- /dev/null
+Qt 5.14 introduces many new features and improvements as well as bugfixes
+over the 5.13.x series. For more details, refer to the online documentation
+included in this distribution. The documentation is also available online:
+
+https://doc.qt.io/qt-5/index.html
+
+The Qt version 5.14 series is binary compatible with the 5.13.x series.
+Applications compiled for 5.13 will continue to run with 5.14.
+
+If you are using Qt Remote Objects between proesses or devices running
+different versions Qt, the different versions of Qt must be running
+compatible versions of QtRO's protocol. Changes to the Qt Remote Objects
+protocol are documented online:
+
+https://doc.qt.io/qt-5/qtremoteobjects-compatibility.html
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+ - QTBUG-77178 Support SLOTs with return values in QML
--- /dev/null
+Qt 5.14.1 is a bug-fix release. It maintains both forward and backward
+compatibility (source and binary) with Qt 5.14.0.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+https://doc.qt.io/qt-5/index.html
+
+The Qt version 5.14 series is binary compatible with the 5.13.x series.
+Applications compiled for 5.13 will continue to run with 5.14.
+
+If you are using Qt Remote Objects between processes or devices running
+different versions Qt, the different versions of Qt must be running
+compatible versions of QtRO's protocol. Changes to the Qt Remote Objects
+protocol are documented online:
+
+https://doc.qt.io/qt-5/qtremoteobjects-compatibility.html
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+ - This release contains only minor code improvements.
--- /dev/null
+Qt 5.14.2 is a bug-fix release. It maintains both forward and backward
+compatibility (source and binary) with Qt 5.14.0 through 5.14.1.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+https://doc.qt.io/qt-5/index.html
+
+The Qt version 5.14 series is binary compatible with the 5.13.x series.
+Applications compiled for 5.13 will continue to run with 5.14.
+
+If you are using Qt Remote Objects between processes or devices running
+different versions Qt, the different versions of Qt must be running
+compatible versions of QtRO's protocol. Changes to the Qt Remote Objects
+protocol are documented online:
+
+https://doc.qt.io/qt-5/qtremoteobjects-compatibility.html
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+ - This release contains only minor code improvements.
--- /dev/null
+Qt 5.15 introduces many new features and improvements as well as bugfixes
+over the 5.14.x series. For more details, refer to the online documentation
+included in this distribution. The documentation is also available online:
+
+https://doc.qt.io/qt-5/index.html
+
+The Qt version 5.15 series is binary compatible with the 5.14.x series.
+Applications compiled for 5.14 will continue to run with 5.15.
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+* QML *
+****************************************************************************
+
+ - Exposed Host to enable remoting of source objects from QML.
--- /dev/null
+Qt 5.15.1 is a bug-fix release. It maintains both forward and backward
+compatibility (source and binary) with Qt 5.15.0.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+https://doc.qt.io/qt-5/index.html
+
+The Qt version 5.15 series is binary compatible with the 5.14.x series.
+Applications compiled for 5.14 will continue to run with 5.15.
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+ - This release contains only minor code improvements.
--- /dev/null
+qt_examples_build_begin(EXTERNAL_BUILD)
+
+add_subdirectory(remoteobjects)
+
+qt_examples_build_end()
--- /dev/null
+TEMPLATE = subdirs
+SUBDIRS = \
+ remoteobjects
--- /dev/null
+qt_internal_add_example(remoteobjects_server)
+qt_internal_add_example(cppclient)
+add_subdirectory(simpleswitch)
+add_subdirectory(websockets)
+if(TARGET Qt::Widgets)
+ qt_internal_add_example(modelviewclient)
+ qt_internal_add_example(modelviewserver)
+endif()
+if(QT_FEATURE_ssl)
+ add_subdirectory(ssl)
+endif()
+if(TARGET Qt::Quick)
+ qt_internal_add_example(plugins)
+ qt_internal_add_example(clientapp)
+endif()
+if(TARGET Qt::Quick AND UNIX AND NOT ANDROID)
+ qt_internal_add_example(qmlmodelviewclient)
+endif()
--- /dev/null
+cmake_minimum_required(VERSION 3.16)
+project(clientapp LANGUAGES CXX)
+
+set(CMAKE_AUTOMOC ON)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/remoteobjects/clientapp")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Quick RemoteObjects)
+
+qt_add_executable(clientapp
+ main.cpp
+)
+
+set_target_properties(clientapp PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(clientapp PUBLIC
+ Qt::Core
+ Qt::Gui
+ Qt::Quick
+ Qt::RemoteObjects
+)
+
+# Resources:
+set(clientapp_resource_files
+ "qml/plugins.qml"
+ "qml/plugins0.qml"
+ "qml/plugins1.qml"
+ "qml/plugins2.qml"
+)
+
+qt6_add_resources(clientapp "clientapp"
+ PREFIX
+ "/qml"
+ FILES
+ ${clientapp_resource_files}
+)
+
+install(TARGETS clientapp
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
--- /dev/null
+SOURCES += main.cpp
+
+RESOURCES += \
+ clientapp.qrc
+
+QT += remoteobjects quick
+
+contains(QT_CONFIG, c++11): CONFIG += c++11
+
+target.path = $$[QT_INSTALL_EXAMPLES]/remoteobjects/clientapp
+INSTALLS += target
--- /dev/null
+<RCC>
+ <qresource prefix="/qml">
+ <file>qml/plugins0.qml</file>
+ <file>qml/plugins.qml</file>
+ <file>qml/plugins1.qml</file>
+ <file>qml/plugins2.qml</file>
+ </qresource>
+</RCC>
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtGui/QGuiApplication>
+#include <QtQml/QQmlEngine>
+#include <QtQuick/QQuickView>
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+
+ QQuickView viewer;
+ viewer.engine()->addImportPath(QStringLiteral("qrc:/qml"));
+ viewer.setSource(QUrl(QStringLiteral("qrc:/qml/qml/plugins.qml")));
+ viewer.show();
+
+ return app.exec();
+}
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick 2.0
+
+Item {
+ width: 200
+ height: 400
+ property int counter: 0;
+ MouseArea {
+ anchors.fill: parent
+ onClicked:
+ {
+ counter = (counter + 1) % 3;
+ console.log(counter);
+ pageLoader.source = "plugins"+counter+".qml"
+ }
+ }
+ Loader {
+ id: pageLoader
+ source: "plugins0.qml"
+ }
+}
+//![0]
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+//![0]
+import QtQuick 2.0
+
+Rectangle {
+ width: 200
+ height: 400
+ color: "blue"
+}
+//![0]
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+//![0]
+import QtQuick 2.0
+import TimeExample 1.0 // import types from the plugin
+
+Rectangle {
+ width: 200
+ height: 400
+ color: "blue"
+ Clock { // this class is defined in QML (imports/TimeExample/Clock.qml)
+ id: clock1
+ anchors.top: parent.top
+ Time { // this class is defined in C++ (plugin.cpp)
+ id: time
+ }
+
+ hours: time.hour
+ minutes: time.minute
+
+ }
+}
+//![0]
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+//![0]
+import QtQuick 2.0
+import TimeExample 1.0 // import types from the plugin
+
+Rectangle {
+ width: 200
+ height: 400
+ Clock { // this class is defined in QML (imports/TimeExample/Clock.qml)
+ id: clock1
+ anchors.top: parent.top
+ Time { // this class is defined in C++ (plugin.cpp)
+ id: time
+ }
+
+ hours: time.hour
+ minutes: time.minute
+
+ }
+ Clock { // this class is defined in QML (imports/TimeExample/Clock.qml)
+ id: clock2
+ anchors.top: clock1.bottom
+ Time { // this class is defined in C++ (plugin.cpp)
+ id: time2
+ }
+
+ hours: time2.hour
+ minutes: time2.minute
+
+ }
+
+}
+//![0]
--- /dev/null
+cmake_minimum_required(VERSION 3.16)
+project(CppClient LANGUAGES CXX)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+set(CMAKE_AUTOMOC ON)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/remoteobjects/cppclient")
+
+find_package(Qt6 REQUIRED COMPONENTS Core RemoteObjects)
+
+qt_add_executable(CppClient
+ main.cpp
+)
+
+set_target_properties(CppClient PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE FALSE
+)
+
+target_link_libraries(CppClient PUBLIC
+ Qt::Core
+ Qt::RemoteObjects
+)
+
+qt6_add_repc_replicas(CppClient
+ timemodel.rep
+)
+
+install(TARGETS CppClient
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
--- /dev/null
+QT += core
+
+REPC_REPLICA += timemodel.rep
+QT += remoteobjects
+
+QT -= gui
+
+TARGET = CppClient
+CONFIG -= app_bundle
+
+TEMPLATE = app
+
+SOURCES += main.cpp
+
+OTHER_FILES += \
+ timemodel.rep
+
+target.path = $$[QT_INSTALL_EXAMPLES]/remoteobjects/cppclient
+INSTALLS += target
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QCoreApplication>
+#include <QTimer>
+#include "rep_timemodel_replica.h"
+
+class tester : public QObject
+{
+ Q_OBJECT
+public:
+ tester() : QObject(nullptr)
+ {
+ QRemoteObjectNode m_client(QUrl(QStringLiteral("local:registry")));
+ ptr1.reset(m_client.acquire< MinuteTimerReplica >());
+ ptr2.reset(m_client.acquire< MinuteTimerReplica >());
+ ptr3.reset(m_client.acquire< MinuteTimerReplica >());
+ QTimer::singleShot(0, this, &tester::clear);
+ QTimer::singleShot(1, this, &tester::clear);
+ QTimer::singleShot(10000, this, &tester::clear);
+ QTimer::singleShot(11000, this, &tester::clear);
+ }
+public slots:
+ void clear()
+ {
+ static int i = 0;
+ if (i == 0) {
+ i++;
+ ptr1.reset();
+ } else if (i == 1) {
+ i++;
+ ptr2.reset();
+ } else if (i == 2) {
+ i++;
+ ptr3.reset();
+ } else {
+ qApp->quit();
+ }
+ }
+
+private:
+ QScopedPointer<MinuteTimerReplica> ptr1, ptr2, ptr3;
+};
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication a(argc, argv);
+ tester t;
+ return a.exec();
+}
+
+#include "main.moc"
--- /dev/null
+#include <QtCore>
+
+POD PresetInfo(int presetNumber, float frequency, QString stationName)
+class MinuteTimer
+{
+ PROP(int hour=1);
+ PROP(int minute=51);
+ SIGNAL(timeChanged());
+ SIGNAL(timeChanged2(QTime t));
+ SIGNAL(sendCustom(PresetInfo info));
+ SLOT(void SetTimeZone(int zn));
+};
--- /dev/null
+cmake_minimum_required(VERSION 3.16)
+project(modelviewclient LANGUAGES CXX)
+
+set(CMAKE_AUTOMOC ON)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/remoteobjects/modelviewclient")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui RemoteObjects Widgets)
+
+qt_add_executable(modelviewclient
+ main.cpp
+)
+
+set_target_properties(modelviewclient PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE FALSE
+)
+
+target_link_libraries(modelviewclient PUBLIC
+ Qt::Core
+ Qt::Gui
+ Qt::RemoteObjects
+ Qt::Widgets
+)
+
+install(TARGETS modelviewclient
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
--- /dev/null
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \example modelviewclient
+ \title Model-View Client
+
+ This is the client-side application that accompanies the
+ \l {Model-View Server}.
+
+ This example showcases how to make a very simple client program which
+ displays the content and changes made on a server.
+
+ \snippet modelviewclient/main.cpp ObjectNode creation
+
+ We start by creating a QRemoteObjectNode and connecting it to a registry
+ found on the local machine. We also set a
+ \l{QRemoteObjectNode::heartbeatInterval}{heartbeat interval}.
+ The heartbeat is useful to detect if the connection to the \l{Source} has
+ been disrupted. In this case, since all the traffic is local, it would
+ detect when the server has been closed.
+
+ \snippet modelviewclient/main.cpp Model acquisition
+
+ We then \l {QRemoteObjectNode::acquireModel}{acquire} the model which
+ contains all of our data. In this case, we're looking to acquire a model
+ named \c{RemoteModel} from the remote object network we are connected to.
+
+ \snippet modelviewclient/main.cpp QTreeView-creation
+
+ And finally, we display the model in a very basic application.
+*/
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QTreeView>
+#include <QApplication>
+#include <QRemoteObjectNode>
+#include <QAbstractItemModelReplica>
+
+int main(int argc, char **argv)
+{
+
+ QLoggingCategory::setFilterRules("qt.remoteobjects.debug=false\n"
+ "qt.remoteobjects.warning=false\n"
+ "qt.remoteobjects.models.debug=false\n"
+ "qt.remoteobjects.models.debug=false");
+
+ QApplication app(argc, argv);
+
+//! [ObjectNode creation]
+ QRemoteObjectNode node(QUrl(QStringLiteral("local:registry")));
+ node.setHeartbeatInterval(1000);
+//! [ObjectNode creation]
+//! [Model acquisition]
+ QScopedPointer<QAbstractItemModelReplica> model(node.acquireModel(QStringLiteral("RemoteModel")));
+//! [Model acquisition]
+
+//! [QTreeView-creation]
+ QTreeView view;
+ view.setWindowTitle(QStringLiteral("RemoteView"));
+ view.resize(640,480);
+ view.setModel(model.data());
+ view.show();
+//! [QTreeView-creation]
+
+ return app.exec();
+}
--- /dev/null
+SOURCES = main.cpp
+
+CONFIG -= app_bundle
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/remoteobjects/modelviewclient
+INSTALLS += target
+
+QT += widgets remoteobjects
+requires(qtConfig(treeview))
--- /dev/null
+cmake_minimum_required(VERSION 3.16)
+project(modelviewserver LANGUAGES CXX)
+
+set(CMAKE_AUTOMOC ON)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/remoteobjects/modelviewserver")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui RemoteObjects Widgets)
+
+qt_add_executable(modelviewserver
+ main.cpp
+)
+
+set_target_properties(modelviewserver PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(modelviewserver PUBLIC
+ Qt::Core
+ Qt::Gui
+ Qt::RemoteObjects
+ Qt::Widgets
+)
+
+install(TARGETS modelviewserver
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
--- /dev/null
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \example modelviewserver
+ \title Model-View Server
+
+ This is the server-side application that accompanies the
+ \l {Model-View Client}.
+
+ This example showcases how to make a simple server program that displays
+ and makes changes to a QTreeView which is made available on a Remote
+ Objects network.
+
+ \snippet modelviewserver/main.cpp RegistryHost setup
+
+ We start by creating a QRemoteObjectRegistryHost with which other
+ Remote Objects will connect, be registered and then be advertised by.
+ The model we create can then be easily acquired from the client side by just
+ connecting to the registry.
+
+ \snippet modelviewserver/main.cpp Model-creation and role-selection
+
+ Now we have to create the model we need. The exact implementation is
+ available in the source code, to which you can navigate by pressing the
+ link further down on this page. We also define which \e roles we want to
+ expose to the \l{Replica} on the client side.
+
+ \snippet modelviewserver/main.cpp Model-remoting
+
+ Here, we create the QRemoteObjectHost that connects to, and shares all its
+ Remote Objects with, the Registry we created earlier. We then start remoting
+ the model we just created with the name \c{RemoteModel}. We also pass the
+ \e roles argument here.
+
+ \snippet modelviewserver/main.cpp TreeView-creation
+
+ We then display the model with a QTreeView widget.
+
+ \snippet modelviewserver/main.cpp Automated actions
+
+ For the sake of keeping the example light-weight it performs some
+ automated actions to affect the model shortly after the server application
+ launches. These changes can then be seen on both the server and client side.
+ You can also change the text in the fields on the server side and see it
+ update on the client side.
+*/
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+#include <QTreeView>
+#include <QApplication>
+#include <QRemoteObjectNode>
+#include <QTimer>
+#include <QStandardItemModel>
+#include <QStandardItem>
+
+#include <memory>
+
+struct TimerHandler : public QObject
+{
+ Q_OBJECT
+public:
+ QStandardItemModel *model;
+public Q_SLOTS:
+ void changeData() {
+ Q_ASSERT(model);
+ Q_ASSERT(model->rowCount() > 50);
+ Q_ASSERT(model->columnCount() > 1);
+ for (int i = 10; i < 50; ++i)
+ model->setData(model->index(i, 1), QColor(Qt::blue), Qt::BackgroundRole);
+ }
+ void insertData() {
+ Q_ASSERT(model);
+ Q_ASSERT(model->rowCount() > 50);
+ Q_ASSERT(model->columnCount() > 1);
+ model->insertRows(2, 9);
+ for (int i = 2; i < 11; ++i) {
+ model->setData(model->index(i, 1), QColor(Qt::green), Qt::BackgroundRole);
+ model->setData(model->index(i, 1), QLatin1String("InsertedRow"), Qt::DisplayRole);
+ }
+ }
+ void removeData() {
+ model->removeRows(2, 4);
+ }
+
+ void changeFlags() {
+ QStandardItem *item = model->item(0, 0);
+ item->setEnabled(false);
+ item = item->child(0, 0);
+ item->setFlags(item->flags() & Qt::ItemIsSelectable);
+ }
+
+ void moveData() {
+ model->moveRows(QModelIndex(), 2, 4, QModelIndex(), 10);
+ }
+};
+
+QList<QStandardItem*> addChild(int numChildren, int nestingLevel)
+{
+ QList<QStandardItem*> result;
+ if (nestingLevel == 0)
+ return result;
+ for (int i = 0; i < numChildren; ++i) {
+ QStandardItem *child = new QStandardItem(QStringLiteral("Child num %1, nesting Level %2").arg(i+1).arg(nestingLevel));
+ if (i == 0)
+ child->appendRow(addChild(numChildren, nestingLevel -1));
+ result.push_back(child);
+ }
+ return result;
+}
+
+std::unique_ptr<QStandardItemModel> createModel()
+{
+ std::unique_ptr<QStandardItemModel> sourceModel = std::make_unique<QStandardItemModel>();
+ const int modelSize = 100000;
+ QStringList list;
+ QStringList hHeaderList;
+ hHeaderList << QStringLiteral("First Column with spacing") << QStringLiteral("Second Column with spacing");
+ sourceModel->setHorizontalHeaderLabels(hHeaderList);
+ list.reserve(modelSize);
+ for (int i = 0; i < modelSize; ++i) {
+ QStandardItem *firstItem = new QStandardItem(QStringLiteral("FancyTextNumber %1").arg(i));
+ if (i == 0)
+ firstItem->appendRow(addChild(2, 2));
+ QStandardItem *secondItem = new QStandardItem(QStringLiteral("FancyRow2TextNumber %1").arg(i));
+ if (i % 2 == 0)
+ firstItem->setBackground(Qt::red);
+ QList<QStandardItem*> row;
+ row << firstItem << secondItem;
+ sourceModel->invisibleRootItem()->appendRow(row);
+ list << QStringLiteral("FancyTextNumber %1").arg(i);
+ }
+
+ // Needed by QMLModelViewClient
+ QHash<int,QByteArray> roleNames = {
+ {Qt::DisplayRole, "_text"},
+ {Qt::BackgroundRole, "_color"}
+ };
+ sourceModel->setItemRoleNames(roleNames);
+ return sourceModel;
+}
+
+int main(int argc, char *argv[])
+{
+ QLoggingCategory::setFilterRules("qt.remoteobjects.debug=false\n"
+ "qt.remoteobjects.warning=false");
+ QApplication app(argc, argv);
+
+ qDebug() << "Creating registry host";
+//! [RegistryHost setup]
+ QRemoteObjectRegistryHost node(QUrl(QStringLiteral("local:registry")));
+//! [RegistryHost setup]
+
+//! [Model-creation and role-selection]
+ std::unique_ptr<QStandardItemModel> sourceModel = createModel();
+
+ QList<int> roles;
+ roles << Qt::DisplayRole << Qt::BackgroundRole;
+//! [Model-creation and role-selection]
+
+//! [Model-remoting]
+ QRemoteObjectHost node2(QUrl(QStringLiteral("local:replica")), QUrl(QStringLiteral("local:registry")));
+ node2.enableRemoting(sourceModel.get(), QStringLiteral("RemoteModel"), roles);
+//! [Model-remoting]
+
+//! [TreeView-creation]
+ QTreeView view;
+ view.setWindowTitle(QStringLiteral("SourceView"));
+ view.setModel(sourceModel.get());
+ view.show();
+//! [TreeView-creation]
+
+//! [Automated actions]
+ TimerHandler handler;
+ handler.model = sourceModel.get();
+ QTimer::singleShot(5000, &handler, &TimerHandler::changeData);
+ QTimer::singleShot(10000, &handler, &TimerHandler::insertData);
+ QTimer::singleShot(11000, &handler, &TimerHandler::changeFlags);
+ QTimer::singleShot(12000, &handler, &TimerHandler::removeData);
+ QTimer::singleShot(13000, &handler, &TimerHandler::moveData);
+//! [Automated actions]
+
+
+ return app.exec();
+}
+
+#include "main.moc"
--- /dev/null
+QT += widgets remoteobjects
+requires(qtConfig(treeview))
+
+TEMPLATE = app
+
+CONFIG += c++11
+
+SOURCES += main.cpp
+
+target.path = $$[QT_INSTALL_EXAMPLES]/remoteobjects/modelviewserver
+INSTALLS += target
--- /dev/null
+cmake_minimum_required(VERSION 3.16)
+project(qmlqrotimeexampleplugin LANGUAGES CXX)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+set(CMAKE_AUTOMOC ON)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/remoteobjects/plugins/imports/TimeExample")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml RemoteObjects)
+
+qt6_add_qml_module(qmlqrotimeexampleplugin
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/imports/TimeExample"
+ VERSION 1.0
+ URI "TimeExample"
+)
+
+target_sources(qmlqrotimeexampleplugin PRIVATE
+ plugin.cpp
+)
+
+set_target_properties(qmlqrotimeexampleplugin PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(qmlqrotimeexampleplugin PUBLIC
+ Qt::Core
+ Qt::Gui
+ Qt::Qml
+ Qt::RemoteObjects
+)
+
+qt6_add_repc_replicas(qmlqrotimeexampleplugin
+ ../timemodel.rep
+)
+
+install(TARGETS qmlqrotimeexampleplugin
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick 2.0
+
+Rectangle {
+ id: clock
+ width: 200; height: 200; color: "gray"
+
+ property alias city: cityLabel.text
+ property variant hours
+ property variant minutes
+ property variant shift : 0
+
+ Image { id: background; source: "clock.png" }
+
+ Image {
+ x: 92.5; y: 27
+ source: "hour.png"
+ transform: Rotation {
+ id: hourRotation
+ origin.x: 7.5; origin.y: 73;
+ angle: (clock.hours * 30) + (clock.minutes * 0.5)
+ Behavior on angle {
+ SpringAnimation{ spring: 2; damping: 0.2; modulus: 360 }
+ }
+ }
+ }
+
+ Image {
+ x: 93.5; y: 17
+ source: "minute.png"
+ transform: Rotation {
+ id: minuteRotation
+ origin.x: 6.5; origin.y: 83;
+ angle: clock.minutes * 6
+ Behavior on angle {
+ SpringAnimation{ spring: 2; damping: 0.2; modulus: 360 }
+ }
+ }
+ }
+
+ Image {
+ anchors.centerIn: background; source: "center.png"
+ }
+
+ Text {
+ id: cityLabel; font.bold: true; font.pixelSize: 14; y:200; color: "white"
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+}
--- /dev/null
+module TimeExample
+Clock 1.0 Clock.qml
+plugin qmlqrotimeexampleplugin
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtQml/QQmlExtensionPlugin>
+#include <QtQml/QQmlEngine>
+#include <QtQml/qqml.h>
+#include <QDebug>
+#include <QDateTime>
+#include <QBasicTimer>
+#include <QCoreApplication>
+#include <QRemoteObjectReplica>
+#include <QRemoteObjectNode>
+#include "rep_timemodel_replica.h"
+
+// Implements a "TimeModel" class with hour and minute properties
+// that change on-the-minute yet efficiently sleep the rest
+// of the time.
+
+static QRemoteObjectNode m_client;
+
+class TimeModel : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int hour READ hour NOTIFY timeChanged)
+ Q_PROPERTY(int minute READ minute NOTIFY timeChanged)
+ Q_PROPERTY(bool isValid READ isValid NOTIFY isValidChanged)
+
+public:
+ TimeModel(QObject *parent = nullptr) : QObject(parent), d_ptr(nullptr)
+ {
+ d_ptr.reset(m_client.acquire< MinuteTimerReplica >());
+ connect(d_ptr.data(), &MinuteTimerReplica::hourChanged, this, &TimeModel::timeChanged);
+ connect(d_ptr.data(), &MinuteTimerReplica::minuteChanged, this, &TimeModel::timeChanged);
+ connect(d_ptr.data(), &MinuteTimerReplica::timeChanged, this, &TimeModel::timeChanged);
+ connect(d_ptr.data(), &MinuteTimerReplica::timeChanged2, this, &TimeModel::test);
+ connect(d_ptr.data(), &MinuteTimerReplica::sendCustom, this, &TimeModel::testCustom);
+ }
+
+ ~TimeModel() override
+ {
+ }
+
+ int minute() const { return d_ptr->minute(); }
+ int hour() const { return d_ptr->hour(); }
+ bool isValid() const { return d_ptr->state() == QRemoteObjectReplica::Valid; }
+
+public slots:
+ //Test a signal with parameters
+ void test(QTime t)
+ {
+ qDebug()<<"Test"<<t;
+ d_ptr->SetTimeZone(t.minute());
+ }
+ //Test a signal with a custom type
+ void testCustom(PresetInfo info)
+ {
+ qDebug()<<"testCustom"<<info.presetNumber()<<info.frequency()<<info.stationName();
+ }
+
+signals:
+ void timeChanged();
+ void timeChanged2(QTime t);
+ void sendCustom(PresetInfo info);
+ void isValidChanged();
+
+private:
+ QScopedPointer<MinuteTimerReplica> d_ptr;
+};
+
+class QExampleQmlPlugin : public QQmlExtensionPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
+
+public:
+ void initializeEngine(QQmlEngine *engine, const char *uri) override
+ {
+ Q_UNUSED(engine)
+ Q_UNUSED(uri)
+ Q_ASSERT(uri == QLatin1String("TimeExample"));
+ engine->addImportPath(QStringLiteral("qrc:/qml"));
+ m_client.setRegistryUrl(QUrl(QStringLiteral("local:registry")));
+ }
+ void registerTypes(const char *uri) override
+ {
+ Q_ASSERT(uri == QLatin1String("TimeExample"));
+ qmlRegisterType<TimeModel>(uri, 1, 0, "Time");
+ }
+
+};
+
+#include "plugin.moc"
--- /dev/null
+QT += qml remoteobjects
+
+TEMPLATE = lib
+CONFIG += plugin
+
+REPC_REPLICA += $$PWD/../timemodel.rep
+
+DESTDIR = imports/TimeExample
+TARGET = qmlqrotimeexampleplugin
+
+SOURCES += plugin.cpp
+
+pluginfiles.files += \
+ imports/TimeExample/qmldir \
+ imports/TimeExample/center.png \
+ imports/TimeExample/clock.png \
+ imports/TimeExample/Clock.qml \
+ imports/TimeExample/hour.png \
+ imports/TimeExample/minute.png
+
+qml.files = plugins.qml
+qml.path += $$[QT_INSTALL_EXAMPLES]/remoteobjects/plugins
+target.path += $$[QT_INSTALL_EXAMPLES]/remoteobjects/plugins/imports/TimeExample
+pluginfiles.path += $$[QT_INSTALL_EXAMPLES]/remoteobjects/plugins/imports/TimeExample
+
+INSTALLS += target qml pluginfiles
+
+CONFIG += install_ok # Do not cargo-cult this!
+
+contains(QT_CONFIG, c++11): CONFIG += c++11
--- /dev/null
+// Copyright (C) 2014 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick 2.0
+
+Item {
+ width: 200
+ height: 400
+ property int counter: 0;
+ MouseArea {
+ anchors.fill: parent
+ onClicked:
+ {
+ counter = (counter + 1) % 3;
+ console.log(counter);
+ pageLoader.source = "plugins"+counter+".qml"
+ }
+ }
+ Loader {
+ id: pageLoader
+ source: "plugins0.qml"
+ }
+}
+//![0]
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+//![0]
+import QtQuick 2.0
+
+Rectangle {
+ width: 200
+ height: 400
+ color: "blue"
+}
+//![0]
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+//![0]
+import QtQuick 2.0
+import TimeExample 1.0 // import types from the plugin
+
+Rectangle {
+ width: 200
+ height: 400
+ color: "blue"
+ Clock { // this class is defined in QML (imports/TimeExample/Clock.qml)
+ id: clock1
+ anchors.top: parent.top
+ Time { // this class is defined in C++ (plugin.cpp)
+ id: time
+ }
+
+ hours: time.hour
+ minutes: time.minute
+
+ }
+}
+//![0]
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+//![0]
+import QtQuick 2.0
+import TimeExample 1.0 // import types from the plugin
+
+Rectangle {
+ width: 200
+ height: 400
+ Clock { // this class is defined in QML (imports/TimeExample/Clock.qml)
+ id: clock1
+ anchors.top: parent.top
+ Time { // this class is defined in C++ (plugin.cpp)
+ id: time
+ }
+
+ hours: time.hour
+ minutes: time.minute
+
+ }
+ Clock { // this class is defined in QML (imports/TimeExample/Clock.qml)
+ id: clock2
+ anchors.top: clock1.bottom
+ Time { // this class is defined in C++ (plugin.cpp)
+ id: time2
+ }
+
+ hours: time2.hour
+ minutes: time2.minute
+
+ }
+
+}
+//![0]
--- /dev/null
+cmake_minimum_required(VERSION 3.16)
+project(qmlmodelviewclient LANGUAGES CXX)
+
+set(CMAKE_AUTOMOC ON)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/remoteobjects/qmlmodelviewclient")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick RemoteObjects)
+
+qt_add_executable(qmlmodelviewclient
+ main.cpp
+)
+
+set_target_properties(qmlmodelviewclient PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(qmlmodelviewclient PUBLIC
+ Qt::Core
+ Qt::Gui
+ Qt::Qml
+ Qt::Quick
+ Qt::RemoteObjects
+)
+
+# Resources:
+set(qml_resource_files
+ "main.qml"
+)
+
+qt6_add_resources(qmlmodelviewclient "qml"
+ PREFIX
+ "/"
+ FILES
+ ${qml_resource_files}
+)
+
+install(TARGETS qmlmodelviewclient
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
--- /dev/null
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \example qmlmodelviewclient
+ \title QML Model-View Client
+
+ This is an alternate client application you can use along with the
+ \l {Model-View Server}.
+
+ This short example showcases how to quickly make a QAbstractItemModelReplica
+ available to and use from QML. See the \l {Model-View Client} example for a
+ more detailed explanation on how the model is acquired and used.
+*/
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QGuiApplication>
+#include <QQmlApplicationEngine>
+#include <QQmlContext>
+#include <QRemoteObjectNode>
+#include <QAbstractItemModelReplica>
+
+int main(int argc, char *argv[])
+{
+ QLoggingCategory::setFilterRules("qt.remoteobjects.debug=false\n"
+ "qt.remoteobjects.warning=false\n"
+ "qt.remoteobjects.models.debug=false\n"
+ "qt.remoteobjects.models.debug=false");
+
+ QGuiApplication app(argc, argv);
+
+ QRemoteObjectNode node(QUrl(QStringLiteral("local:registry")));
+ QQmlApplicationEngine engine;
+ QScopedPointer<QAbstractItemModelReplica> model(node.acquireModel(QStringLiteral("RemoteModel")));
+ engine.rootContext()->setContextProperty("remoteModel", model.data());
+ engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
+
+ return app.exec();
+}
--- /dev/null
+// Copyright (C) 2016 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick 2.5
+import QtQuick.Window 2.2
+
+Window {
+ visible: true
+ width: 800
+ height: 600
+ Text {
+ id: dummy
+ }
+
+ ListView {
+ id: view
+ anchors.fill: parent
+ focus: true
+
+ currentIndex: 10
+
+ model: remoteModel
+
+ snapMode: ListView.SnapToItem
+ highlightFollowsCurrentItem: true
+ highlightMoveDuration: 0
+
+ delegate: Rectangle {
+ width: view.width
+ height: dummy.font.pixelSize * 2
+ color: _color
+ Text {
+ anchors.centerIn: parent
+ text: _text
+ }
+ }
+ Keys.onPressed: {
+ switch (event.key) {
+ case Qt.Key_Home:
+ view.currentIndex = 0;
+ break;
+ case Qt.Key_PageUp:
+ currentIndex -= Math.random() * 300;
+ if (currentIndex < 0)
+ currentIndex = 0;
+ break;
+ case Qt.Key_PageDown:
+ currentIndex += Math.random() * 300;
+ if (currentIndex >= count)
+ currentIndex = count - 1;
+ break;
+ case Qt.Key_End:
+ currentIndex = count - 1;
+ break;
+ }
+ }
+ }
+}
--- /dev/null
+<RCC>
+ <qresource prefix="/">
+ <file>main.qml</file>
+ </qresource>
+</RCC>
--- /dev/null
+TEMPLATE = app
+
+QT += qml quick remoteobjects
+CONFIG += c++11
+
+SOURCES += main.cpp
+
+RESOURCES += qml.qrc
+
+target.path = $$[QT_INSTALL_EXAMPLES]/remoteobjects/qmlmodelviewclient
+INSTALLS += target
--- /dev/null
+TEMPLATE = subdirs
+CONFIG += debug_and_release ordered
+SUBDIRS = \
+ remoteobjects_server \
+ cppclient \
+ simpleswitch \
+ websockets
+
+qtHaveModule(widgets) {
+ SUBDIRS += \
+ modelviewclient \
+ modelviewserver
+}
+
+contains(QT_CONFIG, ssl): SUBDIRS += ssl
+
+qtHaveModule(quick) {
+ SUBDIRS += \
+ plugins \
+ clientapp
+
+ unix:!android: SUBDIRS += qmlmodelviewclient
+}
--- /dev/null
+cmake_minimum_required(VERSION 3.16)
+project(remoteobjects_server LANGUAGES CXX)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+set(CMAKE_AUTOMOC ON)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/remoteobjects/remoteobjects_server")
+
+find_package(Qt6 REQUIRED COMPONENTS Core RemoteObjects)
+
+qt_add_executable(remoteobjects_server
+ main.cpp
+ timemodel.cpp timemodel.h
+)
+
+set_target_properties(remoteobjects_server PROPERTIES
+ WIN32_EXECUTABLE FALSE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(remoteobjects_server PUBLIC
+ Qt::Core
+ Qt::RemoteObjects
+)
+
+qt6_add_repc_sources(remoteobjects_server
+ ../timemodel.rep
+)
+
+install(TARGETS remoteobjects_server
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "timemodel.h"
+
+#include <QCoreApplication>
+/*
+* http://stackoverflow.com/questions/7404163/windows-handling-ctrlc-in-different-thread
+*/
+
+void SigIntHandler()
+{
+ qDebug()<<"Ctrl-C received. Quitting.";
+ qApp->quit();
+}
+
+#if defined(Q_OS_UNIX) || defined(Q_OS_LINUX) || defined(Q_OS_QNX)
+ #include <signal.h>
+
+ void unix_handler(int s)
+ {
+ if (s==SIGINT)
+ SigIntHandler();
+ }
+
+#elif defined(Q_OS_WIN32)
+ #include <windows.h>
+
+ BOOL WINAPI WinHandler(DWORD CEvent)
+ {
+ switch (CEvent)
+ {
+ case CTRL_C_EVENT:
+ SigIntHandler();
+ break;
+ }
+ return TRUE;
+ }
+#endif
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication app(argc, argv);
+
+ #if defined(Q_OS_UNIX) || defined(Q_OS_LINUX) || defined(Q_OS_QNX)
+ signal(SIGINT, &unix_handler);
+ #elif defined(Q_OS_WIN32)
+ SetConsoleCtrlHandler((PHANDLER_ROUTINE)WinHandler, TRUE);
+ #endif
+ QRemoteObjectHost node(QUrl(QStringLiteral("local:replica")),QUrl(QStringLiteral("local:registry")));
+ QRemoteObjectRegistryHost node2(QUrl(QStringLiteral("local:registry")));
+ MinuteTimer timer;
+ node2.enableRemoting(&timer);
+
+ Q_UNUSED(timer)
+ return app.exec();
+}
--- /dev/null
+CONFIG += console
+
+
+REPC_SOURCE += ../timemodel.rep
+QT = remoteobjects core
+
+SOURCES += timemodel.cpp main.cpp
+HEADERS += timemodel.h
+
+contains(QT_CONFIG, c++11): CONFIG += c++11
+
+target.path = $$[QT_INSTALL_EXAMPLES]/remoteobjects/remoteobjects_server
+INSTALLS += target
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "timemodel.h"
+
+MinuteTimer::MinuteTimer(QObject *parent) : MinuteTimerSimpleSource(parent), zone(0)
+{
+ time = QTime::currentTime();
+ setHour(time.hour());
+ setMinute(time.minute());
+ timer.start(60000-time.second()*1000, this);
+}
+MinuteTimer::~MinuteTimer()
+{
+ timer.stop();
+}
+void MinuteTimer::timerEvent(QTimerEvent *)
+{
+ QTime now = QTime::currentTime();
+ if (now.second() == 59 && now.minute() == time.minute() && now.hour() == time.hour()) {
+ // just missed time tick over, force it, wait extra 0.5 seconds
+ time = time.addSecs(60);
+ timer.start(60500, this);
+ } else {
+ time = now;
+ timer.start(60000-time.second()*1000, this);
+ }
+ qDebug()<<"Time"<<time;
+ setHour(time.hour());
+ setMinute(time.minute());
+ emit timeChanged();
+ emit timeChanged2(time);
+ static PresetInfo bla(3, 93.9f, "Best Station");
+ emit sendCustom(bla);
+}
+void MinuteTimer::SetTimeZone(const int &zn)
+{
+ qDebug()<<"SetTimeZone"<<zn;
+ if (zn != zone)
+ {
+ zone = zn;
+ }
+}
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtCore>
+#include "rep_timemodel_source.h"
+
+class MinuteTimer : public MinuteTimerSimpleSource
+{
+ Q_OBJECT
+public:
+ MinuteTimer(QObject *parent = nullptr);
+ ~MinuteTimer() override;
+
+public slots:
+ void SetTimeZone(const int &zn) override;
+
+protected:
+ void timerEvent(QTimerEvent *) override;
+
+private:
+ QTime time;
+ QBasicTimer timer;
+ int zone;
+};
--- /dev/null
+qt_internal_add_example(directconnectclient)
+qt_internal_add_example(directconnectdynamicclient)
+qt_internal_add_example(directconnectserver)
+qt_internal_add_example(registryconnectedclient)
+qt_internal_add_example(registryconnectedserver)
--- /dev/null
+cmake_minimum_required(VERSION 3.16)
+project(directconnectclient LANGUAGES CXX)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+set(CMAKE_AUTOMOC ON)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/remoteobjects/simpleswitch/directconnectclient")
+
+find_package(Qt6 REQUIRED COMPONENTS Core RemoteObjects)
+
+qt_add_executable(directconnectclient
+ client.cpp client.h
+ main.cpp
+)
+
+set_target_properties(directconnectclient PROPERTIES
+ WIN32_EXECUTABLE FALSE
+ MACOSX_BUNDLE FALSE
+)
+
+target_link_libraries(directconnectclient PUBLIC
+ Qt::Core
+ Qt::RemoteObjects
+)
+
+qt6_add_repc_replicas(directconnectclient
+ simpleswitch.rep
+)
+
+install(TARGETS directconnectclient
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "client.h"
+
+// constructor
+Client::Client(QSharedPointer<SimpleSwitchReplica> ptr) :
+ QObject(nullptr), clientSwitchState(false), reptr(ptr)
+{
+ //connect signal for replica initialized with initialization slot
+ initConnections();
+ // We can connect to SimpleSwitchReplica signals/slots
+ //directly because our Replica was generated by repc
+}
+
+//destructor
+Client::~Client()
+{
+
+}
+
+
+void Client::initConnections(void)
+{
+ // initialize connections between signals and slots
+
+ // connect source replica signal currStateChanged() with client's recSwitchState() slot to receive source's current state
+ QObject::connect(reptr.data(), &SimpleSwitchReplica::currStateChanged, this, &Client::recSwitchState_slot);
+ // connect client's echoSwitchState(..) signal with replica's server_slot(..) to echo back received state
+ QObject::connect(this, &Client::echoSwitchState, reptr.data(), &SimpleSwitchReplica::server_slot);
+}
+
+void Client::recSwitchState_slot(bool value)
+{
+ qDebug() << "Received source state "<< value << reptr.data()->currState();
+ clientSwitchState = reptr.data()->currState();
+ Q_EMIT echoSwitchState(clientSwitchState); // Emit signal to echo received state back to server
+}
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef _CLIENT_H
+#define _CLIENT_H
+
+#include <QObject>
+#include <QSharedPointer>
+
+#include "rep_simpleswitch_replica.h"
+
+class Client : public QObject
+{
+ Q_OBJECT
+public:
+ Client(QSharedPointer<SimpleSwitchReplica> ptr);
+ ~Client() override;
+ void initConnections();// function connect signals and slots of source and client
+
+Q_SIGNALS:
+ void echoSwitchState(bool switchState);// this signal is connected with server_slot(..) on the source object and echoes back switch state received from source
+
+public Q_SLOTS:
+ void recSwitchState_slot(bool); // slot to receive source state
+private:
+ bool clientSwitchState; // holds received server switch state
+ QSharedPointer<SimpleSwitchReplica> reptr;// holds reference to replica
+
+ };
+
+#endif
--- /dev/null
+QT += remoteobjects core
+
+QT -= gui
+
+TARGET = directconnectclient
+CONFIG += console
+CONFIG -= app_bundle
+
+REPC_REPLICA = simpleswitch.rep
+
+TEMPLATE = app
+
+target.path = $$[QT_INSTALL_EXAMPLES]/remoteobjects/simpleswitch/directconnectclient
+INSTALLS += target
+
+SOURCES += main.cpp \
+ client.cpp
+
+HEADERS += \
+ client.h
+
+DISTFILES += \
+ simpleswitch.rep
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QCoreApplication>
+#include "client.h"
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication a(argc, argv);
+
+
+ QSharedPointer<SimpleSwitchReplica> ptr;
+
+ QRemoteObjectNode repNode; // create remote object node
+ repNode.connectToNode(QUrl(QStringLiteral("local:replica"))); // connect with remote host node
+
+ ptr.reset(repNode.acquire<SimpleSwitchReplica>()); // acquire replica of source from host node
+
+ Client rswitch(ptr); // create client switch object and pass reference of replica to it
+
+ return a.exec();
+}
--- /dev/null
+#include <QtCore>
+
+class SimpleSwitch
+{
+ PROP(bool currState=false);
+ SLOT(void server_slot(bool clientState));
+};
--- /dev/null
+cmake_minimum_required(VERSION 3.16)
+project(directconnectdynamicclient LANGUAGES CXX)
+
+set(CMAKE_AUTOMOC ON)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/remoteobjects/simpleswitch/directconnectdynamicclient")
+
+find_package(Qt6 REQUIRED COMPONENTS Core RemoteObjects)
+
+qt_add_executable(directconnectdynamicclient
+ dynamicclient.cpp dynamicclient.h
+ main.cpp
+)
+
+set_target_properties(directconnectdynamicclient PROPERTIES
+ WIN32_EXECUTABLE FALSE
+ MACOSX_BUNDLE FALSE
+)
+
+target_link_libraries(directconnectdynamicclient PUBLIC
+ Qt::Core
+ Qt::RemoteObjects
+)
+
+install(TARGETS directconnectdynamicclient
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
--- /dev/null
+QT += remoteobjects core
+
+QT -= gui
+
+TARGET = directconnectdynamicclient
+CONFIG += console
+CONFIG -= app_bundle
+
+TEMPLATE = app
+
+target.path = $$[QT_INSTALL_EXAMPLES]/remoteobjects/simpleswitch/directconnectdynamicclient
+INSTALLS += target
+
+SOURCES += main.cpp \
+ dynamicclient.cpp
+
+HEADERS += \
+ dynamicclient.h
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "dynamicclient.h"
+ #include <QMetaMethod>
+
+// constructor
+DynamicClient::DynamicClient(QSharedPointer<QRemoteObjectDynamicReplica> ptr) :
+ QObject(nullptr), clientSwitchState(false), reptr(ptr)
+{
+ //connect signal for replica valid changed with signal slot initialization
+ QObject::connect(reptr.data(), &QRemoteObjectDynamicReplica::initialized, this,
+ &DynamicClient::initConnection_slot);
+}
+
+//destructor
+DynamicClient::~DynamicClient()
+{
+
+}
+
+// Function to initialize connections between slots and signals
+void DynamicClient::initConnection_slot()
+{
+
+ // connect source replica signal currStateChanged() with client's recSwitchState() slot to receive source's current state
+ QObject::connect(reptr.data(), SIGNAL(currStateChanged(bool)), this, SLOT(recSwitchState_slot(bool)));
+ // connect client's echoSwitchState(..) signal with replica's server_slot(..) to echo back received state
+ QObject::connect(this, SIGNAL(echoSwitchState(bool)),reptr.data(), SLOT(server_slot(bool)));
+}
+
+
+void DynamicClient::recSwitchState_slot(bool value)
+{
+ clientSwitchState = reptr->property("currState").toBool(); // use replica property to get "currState" from source
+ qDebug() << "Received source state " << value << clientSwitchState;
+ Q_EMIT echoSwitchState(clientSwitchState); // Emit signal to echo received state back to server
+}
+
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef _DYNAMICCLIENT_H
+#define _DYNAMICCLIENT_H
+
+#include <QObject>
+#include <QSharedPointer>
+
+#include <QRemoteObjectNode>
+#include <qremoteobjectdynamicreplica.h>
+
+class DynamicClient : public QObject
+{
+ Q_OBJECT
+public:
+ DynamicClient(QSharedPointer<QRemoteObjectDynamicReplica> ptr);
+ ~DynamicClient() override;
+
+Q_SIGNALS:
+ void echoSwitchState(bool switchState);// this signal is connected with server_slot(..) slot of source object and echoes back switch state received from source
+
+public Q_SLOTS:
+ void recSwitchState_slot(bool); // slot to receive source state
+ void initConnection_slot();
+
+private:
+ bool clientSwitchState; // holds received server switch state
+ QSharedPointer<QRemoteObjectDynamicReplica> reptr;// holds reference to replica
+ };
+
+#endif
+
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QCoreApplication>
+
+#include "dynamicclient.h"
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication a(argc, argv);
+
+ QSharedPointer<QRemoteObjectDynamicReplica> ptr; // shared pointer to hold replica
+
+ QRemoteObjectNode repNode; // create remote object node
+ repNode.connectToNode(QUrl(QStringLiteral("local:replica"))); // connect with remote host node
+
+ ptr.reset(repNode.acquireDynamic("SimpleSwitch")); // acquire replica of source from host node
+
+ DynamicClient rswitch(ptr); // create client switch object and pass replica reference to it
+
+ return a.exec();
+}
--- /dev/null
+cmake_minimum_required(VERSION 3.16)
+project(directconnectserver LANGUAGES CXX)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+set(CMAKE_AUTOMOC ON)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/remoteobjects/simpleswitch/directconnectserver")
+
+find_package(Qt6 REQUIRED COMPONENTS Core RemoteObjects)
+
+qt_add_executable(directconnectserver
+ main.cpp
+ simpleswitch.cpp simpleswitch.h
+)
+
+set_target_properties(directconnectserver PROPERTIES
+ WIN32_EXECUTABLE FALSE
+ MACOSX_BUNDLE FALSE
+)
+
+target_link_libraries(directconnectserver PUBLIC
+ Qt::Core
+ Qt::RemoteObjects
+)
+
+qt6_add_repc_sources(directconnectserver
+ simpleswitch.rep
+)
+
+install(TARGETS directconnectserver
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
--- /dev/null
+QT += remoteobjects core
+
+QT -= gui
+
+TARGET = directconnectserver
+CONFIG += console
+CONFIG -= app_bundle
+
+REPC_SOURCE = simpleswitch.rep
+
+TEMPLATE = app
+
+target.path = $$[QT_INSTALL_EXAMPLES]/remoteobjects/simpleswitch/directconnectserver
+INSTALLS += target
+
+SOURCES += main.cpp \
+ simpleswitch.cpp
+
+
+HEADERS += \
+ simpleswitch.h
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QCoreApplication>
+#include "simpleswitch.h"
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication a(argc, argv);
+
+ SimpleSwitch srcSwitch; // create simple switch
+
+ QRemoteObjectHost srcNode(QUrl(QStringLiteral("local:replica"))); // create host node without Registry
+ srcNode.enableRemoting(&srcSwitch); // enable remoting/Sharing
+
+ return a.exec();
+}
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "simpleswitch.h"
+
+// constructor
+SimpleSwitch::SimpleSwitch(QObject *parent) : SimpleSwitchSimpleSource(parent)
+{
+ stateChangeTimer = new QTimer(this); // Initialize timer
+ QObject::connect(stateChangeTimer, &QTimer::timeout, this, &SimpleSwitch::timeout_slot); // connect timeout() signal from stateChangeTimer to timeout_slot() of simpleSwitch
+ stateChangeTimer->start(2000); // Start timer and set timout to 2 seconds
+ qDebug() << "Source Node Started";
+}
+
+//destructor
+SimpleSwitch::~SimpleSwitch()
+{
+ stateChangeTimer->stop();
+}
+
+void SimpleSwitch::server_slot(bool clientState)
+{
+ qDebug() << "Replica state is " << clientState; // print switch state echoed back by client
+}
+
+void SimpleSwitch::timeout_slot(void)
+{
+ // slot called on timer timeout
+ if (currState()) // check if current state is true, currState() is defined in repc generated rep_simpleswitch_source.h
+ setCurrState(false); // set state to false
+ else
+ setCurrState(true); // set state to true
+ qDebug() << "Source State is "<<currState();
+
+}
+
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef SIMPLESWITCH_H
+#define SIMPLESWITCH_H
+
+#include "rep_simpleswitch_source.h"
+
+class SimpleSwitch : public SimpleSwitchSimpleSource
+{
+ Q_OBJECT
+public:
+ SimpleSwitch(QObject *parent = nullptr);
+ ~SimpleSwitch() override;
+ void server_slot(bool clientState) override;
+public Q_SLOTS:
+ void timeout_slot();
+private:
+ QTimer *stateChangeTimer;
+};
+
+#endif
--- /dev/null
+#include <QtCore>
+
+class SimpleSwitch
+{
+ PROP(bool currState=false);
+ SLOT(void server_slot(bool clientState));
+};
--- /dev/null
+cmake_minimum_required(VERSION 3.16)
+project(registryconnectedclient LANGUAGES CXX)
+
+set(CMAKE_AUTOMOC ON)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/remoteobjects/simpleswitch/registryconnectedclient")
+
+find_package(Qt6 REQUIRED COMPONENTS Core RemoteObjects)
+
+qt_add_executable(registryconnectedclient
+ dynamicclient.cpp dynamicclient.h
+ main.cpp
+)
+
+set_target_properties(registryconnectedclient PROPERTIES
+ WIN32_EXECUTABLE FALSE
+ MACOSX_BUNDLE FALSE
+)
+
+target_link_libraries(registryconnectedclient PUBLIC
+ Qt::Core
+ Qt::RemoteObjects
+)
+
+install(TARGETS registryconnectedclient
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "dynamicclient.h"
+ #include <QMetaMethod>
+
+// constructor
+DynamicClient::DynamicClient(QSharedPointer<QRemoteObjectDynamicReplica> ptr) :
+ QObject(nullptr), clientSwitchState(false), reptr(ptr)
+{
+ //connect signal for replica valid changed with signal slot initialization
+ QObject::connect(reptr.data(), &QRemoteObjectDynamicReplica::initialized, this,
+ &DynamicClient::initConnection_slot);
+}
+
+//destructor
+DynamicClient::~DynamicClient()
+{
+
+}
+
+// Function to initialize connections between slots and signals
+void DynamicClient::initConnection_slot()
+{
+
+ // connect source replica signal currStateChanged() with client's recSwitchState() slot to receive source's current state
+ QObject::connect(reptr.data(), SIGNAL(currStateChanged(bool)), this, SLOT(recSwitchState_slot(bool)));
+ // connect client's echoSwitchState(..) signal with replica's server_slot(..) to echo back received state
+ QObject::connect(this, SIGNAL(echoSwitchState(bool)),reptr.data(), SLOT(server_slot(bool)));
+}
+
+
+void DynamicClient::recSwitchState_slot(bool value)
+{
+ clientSwitchState = reptr->property("currState").toBool(); // use replica property to get "currState" from source
+ qDebug() << "Received source state " << value << clientSwitchState;
+ Q_EMIT echoSwitchState(clientSwitchState); // Emit signal to echo received state back to server
+}
+
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef _DYNAMICCLIENT_H
+#define _DYNAMICCLIENT_H
+
+#include <QObject>
+#include <QSharedPointer>
+
+#include <QRemoteObjectNode>
+#include <qremoteobjectdynamicreplica.h>
+
+class DynamicClient : public QObject
+{
+ Q_OBJECT
+public:
+ DynamicClient(QSharedPointer<QRemoteObjectDynamicReplica> ptr);
+ ~DynamicClient() override;
+
+Q_SIGNALS:
+ void echoSwitchState(bool switchState);// this signal is connected with server_slot(..) slot of source object and echoes back switch state received from source
+
+public Q_SLOTS:
+ void recSwitchState_slot(bool); // slot to receive source state
+ void initConnection_slot();
+
+private:
+ bool clientSwitchState; // holds received server switch state
+ QSharedPointer<QRemoteObjectDynamicReplica> reptr;// holds reference to replica
+ };
+
+#endif
+
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QCoreApplication>
+#include "dynamicclient.h"
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication a(argc, argv);
+
+ QSharedPointer<QRemoteObjectDynamicReplica> ptr; // shared pointer to hold replica
+
+ QRemoteObjectNode repNode(QUrl(QStringLiteral("local:registry")));
+
+ ptr.reset(repNode.acquireDynamic("SimpleSwitch")); // acquire replica of source from host node
+
+ DynamicClient rswitch(ptr); // create client switch object and pass replica reference to it
+
+ return a.exec();
+}
--- /dev/null
+#-------------------------------------------------
+#
+# Project created by QtCreator 2015-02-13T15:22:50
+#
+#-------------------------------------------------
+
+QT += remoteobjects core
+
+QT -= gui
+
+TARGET = registryconnectedclient
+CONFIG += console
+CONFIG -= app_bundle
+
+TEMPLATE = app
+
+target.path = $$[QT_INSTALL_EXAMPLES]/remoteobjects/simpleswitch/registryconnectedclient
+INSTALLS += target
+
+SOURCES += main.cpp \
+ dynamicclient.cpp
+
+HEADERS += \
+ dynamicclient.h
--- /dev/null
+cmake_minimum_required(VERSION 3.16)
+project(registryconnectedserver LANGUAGES CXX)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+set(CMAKE_AUTOMOC ON)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/remoteobjects/simpleswitch/registryconnectedserver")
+
+find_package(Qt6 REQUIRED COMPONENTS Core RemoteObjects)
+
+qt_add_executable(registryconnectedserver
+ main.cpp
+ simpleswitch.cpp simpleswitch.h
+)
+
+set_target_properties(registryconnectedserver PROPERTIES
+ WIN32_EXECUTABLE FALSE
+ MACOSX_BUNDLE FALSE
+)
+
+target_link_libraries(registryconnectedserver PUBLIC
+ Qt::Core
+ Qt::RemoteObjects
+)
+
+qt6_add_repc_sources(registryconnectedserver
+ simpleswitch.rep
+)
+
+install(TARGETS registryconnectedserver
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QCoreApplication>
+#include "simpleswitch.h"
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication a(argc, argv);
+
+ SimpleSwitch srcSwitch; // create simple switch
+
+ QRemoteObjectRegistryHost regNode(QUrl(QStringLiteral("local:registry"))); // create node that hosts registy
+ QRemoteObjectHost srcNode(QUrl(QStringLiteral("local:replica")), QUrl(QStringLiteral("local:registry"))); // create node that will host source and connect to registry
+
+ //Note, you can add srcSwitch directly to regNode if desired.
+ //We use two Nodes here, as the regNode could easily be in a third process.
+
+ srcNode.enableRemoting(&srcSwitch); // enable remoting of source object
+
+ return a.exec();
+}
+
--- /dev/null
+#-------------------------------------------------
+#
+# Project created by QtCreator 2015-02-13T15:02:06
+#
+#-------------------------------------------------
+
+QT += remoteobjects core
+
+QT -= gui
+
+TARGET = registryconnectedserver
+CONFIG += console
+CONFIG -= app_bundle
+
+REPC_SOURCE = simpleswitch.rep
+
+
+TEMPLATE = app
+
+target.path = $$[QT_INSTALL_EXAMPLES]/remoteobjects/simpleswitch/registryconnectedserver
+INSTALLS += target
+
+SOURCES += main.cpp \
+ simpleswitch.cpp
+
+HEADERS += \
+ simpleswitch.h
+
+DISTFILES += \
+ simpleswitch.rep
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "simpleswitch.h"
+
+// constructor
+SimpleSwitch::SimpleSwitch(QObject *parent) : SimpleSwitchSimpleSource(parent)
+{
+ stateChangeTimer = new QTimer(this); // Initialize timer
+ QObject::connect(stateChangeTimer, &QTimer::timeout, this, &SimpleSwitch::timeout_slot); // connect timeout() signal from stateChangeTimer to timeout_slot() of simpleSwitch
+ stateChangeTimer->start(2000); // Start timer and set timout to 2 seconds
+ qDebug() << "Source Node Started";
+}
+
+//destructor
+SimpleSwitch::~SimpleSwitch()
+{
+ stateChangeTimer->stop();
+}
+
+void SimpleSwitch::server_slot(bool clientState)
+{
+ qDebug() << "Replica state is " << clientState; // print switch state echoed back by client
+}
+
+void SimpleSwitch::timeout_slot(void)
+{
+ // slot called on timer timeout
+ if (currState()) // check if current state is true, currState() is defined in repc generated rep_simpleswitch_source.h
+ setCurrState(false); // set state to false
+ else
+ setCurrState(true); // set state to true
+ qDebug() << "Source State is "<<currState();
+
+}
+
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef SIMPLESWITCH_H
+#define SIMPLESWITCH_H
+
+#include "rep_simpleswitch_source.h"
+
+class SimpleSwitch : public SimpleSwitchSimpleSource
+{
+ Q_OBJECT
+public:
+ SimpleSwitch(QObject *parent = nullptr);
+ ~SimpleSwitch() override;
+ void server_slot(bool clientState) override;
+public Q_SLOTS:
+ void timeout_slot();
+private:
+ QTimer *stateChangeTimer;
+};
+
+#endif
--- /dev/null
+#include <QtCore>
+
+class SimpleSwitch
+{
+ PROP(bool currState=false);
+ SLOT(void server_slot(bool clientState));
+};
+
--- /dev/null
+TEMPLATE = subdirs
+
+SUBDIRS += \
+ directconnectclient \
+ directconnectdynamicclient \
+ directconnectserver \
+ registryconnectedclient \
+ registryconnectedserver
--- /dev/null
+qt_internal_add_example(sslserver)
+qt_internal_add_example(sslcppclient)
--- /dev/null
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \example ssl
+ \title QtRemoteObjects SSL Example
+
+ This example shows how you can set up a secure Remote Object network using
+ QSslSockets! Encrypting communication is critical when you need to pass data
+ through a network you don't have full control over.
+*/
--- /dev/null
+TEMPLATE = subdirs
+CONFIG += debug_and_release ordered
+
+SUBDIRS = \
+ sslserver \
+ sslcppclient
--- /dev/null
+cmake_minimum_required(VERSION 3.16)
+project(SslCppClient LANGUAGES CXX)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+set(CMAKE_AUTOMOC ON)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/remoteobjects/ssl/sslcppclient")
+
+find_package(Qt6 REQUIRED COMPONENTS Core RemoteObjects)
+
+qt_add_executable(SslCppClient
+ main.cpp
+)
+
+set_target_properties(SslCppClient PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE FALSE
+)
+
+target_link_libraries(SslCppClient PUBLIC
+ # Remove: gui
+ Qt::Core
+ Qt::RemoteObjects
+ Qt::RemoteObjectsPrivate
+)
+
+# Resources:
+set(cert_resource_files
+ "../sslserver/cert/client.crt"
+ "../sslserver/cert/client.key"
+ "../sslserver/cert/rootCA.key"
+ "../sslserver/cert/rootCA.pem"
+ "../sslserver/cert/rootCA.srl"
+ "../sslserver/cert/server.crt"
+ "../sslserver/cert/server.key"
+)
+
+qt6_add_resources(SslCppClient "cert"
+ PREFIX
+ "/sslcert"
+ BASE
+ "../sslserver/cert"
+ FILES
+ ${cert_resource_files}
+)
+
+qt6_add_repc_replicas(SslCppClient
+ timemodel.rep
+)
+
+install(TARGETS SslCppClient
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QCoreApplication>
+#include <QHostAddress>
+#include <QSslSocket>
+#include <QSslConfiguration>
+#include <QSslKey>
+#include <QTimer>
+#include "rep_timemodel_replica.h"
+
+#include <QRemoteObjectNode>
+
+class tester : public QObject
+{
+ Q_OBJECT
+public:
+ tester() : QObject(nullptr)
+ {
+ QRemoteObjectNode m_client;
+ auto socket = setupConnection();
+ connect(socket, &QSslSocket::errorOccurred,
+ socket, [](QAbstractSocket::SocketError error){
+ qDebug() << "QSslSocket::error" << error;
+ }) ;
+ m_client.addClientSideConnection(socket);
+
+ ptr1.reset(m_client.acquire< MinuteTimerReplica >());
+ ptr2.reset(m_client.acquire< MinuteTimerReplica >());
+ ptr3.reset(m_client.acquire< MinuteTimerReplica >());
+ QTimer::singleShot(0, this, &tester::clear);
+ QTimer::singleShot(1, this, &tester::clear);
+ QTimer::singleShot(10000, this, &tester::clear);
+ QTimer::singleShot(11000, this, &tester::clear);
+ }
+public slots:
+ void clear()
+ {
+ static int i = 0;
+ if (i == 0) {
+ i++;
+ ptr1.reset();
+ } else if (i == 1) {
+ i++;
+ ptr2.reset();
+ } else if (i == 2) {
+ i++;
+ ptr3.reset();
+ } else {
+ qApp->quit();
+ }
+ }
+
+private:
+ QScopedPointer<MinuteTimerReplica> ptr1, ptr2, ptr3;
+
+ QSslSocket *setupConnection()
+ {
+ auto socketClient = new QSslSocket;
+ socketClient->setLocalCertificate(QStringLiteral(":/sslcert/client.crt"));
+ socketClient->setPrivateKey(QStringLiteral(":/sslcert/client.key"));
+ socketClient->setPeerVerifyMode(QSslSocket::VerifyPeer);
+ socketClient->connectToHostEncrypted(QStringLiteral("127.0.0.1"), 65511);
+ if (!socketClient->waitForEncrypted(-1)) {
+ qWarning("Failed to connect to server %s",
+ qPrintable(socketClient->errorString()));
+ exit(0);
+ }
+ return socketClient;
+ }
+};
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication a(argc, argv);
+
+ auto config = QSslConfiguration::defaultConfiguration();
+ config.setCaCertificates(QSslCertificate::fromPath(QStringLiteral(":/sslcert/rootCA.pem")));
+ QSslConfiguration::setDefaultConfiguration(config);
+
+ tester t;
+ return a.exec();
+}
+
+#include "main.moc"
--- /dev/null
+QT_FOR_CONFIG += network
+requires(qtConfig(ssl))
+
+REPC_REPLICA += timemodel.rep
+QT = remoteobjects remoteobjects-private core
+
+QT -= gui
+
+TARGET = SslCppClient
+CONFIG -= app_bundle
+
+TEMPLATE = app
+
+SOURCES += main.cpp
+
+OTHER_FILES += \
+ timemodel.rep
+
+RESOURCES += \
+ ../sslserver/cert/cert.qrc
+
+
+target.path = $$[QT_INSTALL_EXAMPLES]/remoteobjects/ssl/sslcppclient
+INSTALLS += target
--- /dev/null
+#include <QtCore>
+
+POD PresetInfo(int presetNumber, float frequency, QString stationName)
+class MinuteTimer
+{
+ PROP(int hour=1);
+ PROP(int minute=51);
+ SIGNAL(timeChanged());
+ SIGNAL(timeChanged2(QTime t));
+ SIGNAL(sendCustom(PresetInfo info));
+ SLOT(void SetTimeZone(int zn));
+};
--- /dev/null
+cmake_minimum_required(VERSION 3.16)
+project(sslserver LANGUAGES CXX)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+set(CMAKE_AUTOMOC ON)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/remoteobjects/ssl/sslserver")
+
+find_package(Qt6 REQUIRED COMPONENTS Core RemoteObjects)
+
+qt_add_executable(sslserver
+ main.cpp
+ sslserver.cpp sslserver.h
+ timemodel.cpp timemodel.h
+)
+
+set_target_properties(sslserver PROPERTIES
+ WIN32_EXECUTABLE FALSE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(sslserver PUBLIC
+ Qt::Core
+ Qt::RemoteObjects
+ Qt::RemoteObjectsPrivate
+)
+
+# Resources:
+set(cert_resource_files
+ "cert/client.crt"
+ "cert/client.key"
+ "cert/rootCA.key"
+ "cert/rootCA.pem"
+ "cert/rootCA.srl"
+ "cert/server.crt"
+ "cert/server.key"
+)
+
+qt6_add_resources(sslserver "cert"
+ PREFIX
+ "/sslcert"
+ BASE
+ "cert"
+ FILES
+ ${cert_resource_files}
+)
+
+qt6_add_repc_sources(sslserver
+ ../../timemodel.rep
+)
+
+install(TARGETS sslserver
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
--- /dev/null
+<RCC>
+ <qresource prefix="/sslcert">
+ <file>client.crt</file>
+ <file>client.key</file>
+ <file>rootCA.key</file>
+ <file>rootCA.pem</file>
+ <file>rootCA.srl</file>
+ <file>server.crt</file>
+ <file>server.key</file>
+ </qresource>
+</RCC>
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIICrTCCAZUCFHOQggvUf1o8c5i3yNyiGLNcLC4pMA0GCSqGSIb3DQEBCwUAMBIx
+EDAOBgNVBAMMB1F0Uk8gQ0EwHhcNMjEwMjI0MTEzMzU1WhcNMjMwNTMwMTEzMzU1
+WjAUMRIwEAYDVQQDDAkxMjcuMC4wLjEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQDbl9iuedw0oSbtpC2m30YdzwRmemijasP9SQGQ6+piUOFUKCZsoGWc
+RcEnLGzC+KJ7FXh8jA1kTXSW6ghqvrUysN8VzjgmcCLFee4JAkCUY8yNrlq13ciR
+19BE09kJdOPZeI57pCSBNA6iy03Q4nc/GJpG63QTqJv/WUUgMek0UsmZIzDcWaqr
+MCMnLMaRi5oKFCnnl8E0XDuRm1nqPAzT+us/4upMv+7Q2xs4QFXbLUpSIToNc1wm
+tP6OAGaYClbJZgZbUNowj0wJeCUAwGGcDpliYj1JB8R015z8Kd8pDCvdD7XL35JR
+rT+eaBFNLUrl30aIl3lWf/buv3OoRmuVAgMBAAEwDQYJKoZIhvcNAQELBQADggEB
+AJjdfuy2pb3KgnpxYiXfKXCqGlN7E1RyoCIsMhldWcxAN5cwIJCrvde5MNI8kDvd
+0SfDpRpCP/hZqpR6DsR9iNYJprXlQNZ7Rs41Eswwlb66DqmBlb5ZQcYl8KsKV5fw
+7PhvLpjC5hEg1OBg1Ooz+aNvI9OJYIRFUJ1smtRzwXWuQd5QoqYVRpzvmrFawnGa
+2NHywiwgKyGvY/y82pPuj1rt0L+bae85cZm32f6gp1me9OuLIqA2G5UafSiigWBY
+YL249Rd4rrT87GAeaiBo8ZxZ8de8O7TOBjSNrfAMySepDWjfFfoNpyp+4foRKmpE
+aZmgGTIj5rfhYh4Gcj1nZBw=
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEA25fYrnncNKEm7aQtpt9GHc8EZnpoo2rD/UkBkOvqYlDhVCgm
+bKBlnEXBJyxswviiexV4fIwNZE10luoIar61MrDfFc44JnAixXnuCQJAlGPMja5a
+td3IkdfQRNPZCXTj2XiOe6QkgTQOostN0OJ3PxiaRut0E6ib/1lFIDHpNFLJmSMw
+3FmqqzAjJyzGkYuaChQp55fBNFw7kZtZ6jwM0/rrP+LqTL/u0NsbOEBV2y1KUiE6
+DXNcJrT+jgBmmApWyWYGW1DaMI9MCXglAMBhnA6ZYmI9SQfEdNec/CnfKQwr3Q+1
+y9+SUa0/nmgRTS1K5d9GiJd5Vn/27r9zqEZrlQIDAQABAoIBAEDLm4pQNuPosV3p
+1fapZz0gesHqWLnvpQk145ppom2ERBjbCAuBgLoN8yKl/ynAx+DdwwGtKb5xBHgL
+cpRc1YaxngIHKZZd/ESc59oMqhWfJRqhWe7UFHzEW5YTlLUvopPm+NQO6R6ex7rN
+lpaOXHVnww4uJ8AtPmqoYrdPQurG/txveRMLo84JJT+IH2YVWOzccp809zw4WZZD
+qBcgm/dV8ir+8nUHQlR+loMMrEoKeacNxtHUXWL6d6P93Q72L07t41/l0XmXXq7I
+cVJnGxcJtkeqj03FSHqDU3XM5fRg6f+XnnSnhnd4AUmHe8cvyeCnEf4bdh4UpzBG
+sCie+XkCgYEA93FU0X6ttWdb+rJNHRnHmb4DxOVo2LeXEk1A1ul+Yj+jFP+TwJH+
+bm8PbV7ALdyH2u66ElQG60gW9ztu86xl5ZLVdhijWJpjHKB45eXVhnRcb2Fy9tDc
+pUeRs8+IrrYbWDrNZZYWby83MqPHimCLTmAZl11NMB2ohyFDxr5voGMCgYEA4y/0
+2WN8r74H9I3L2Ghfe8e3i/W35BpjtElJxiL3L1vzGdU5Wo1hDnvjoHvdTxB7LtGU
+I+P0l77fwuAC8G8bh4SZ59jcxlqCmbXy7wDAyrYaCja5OWK9xWXvYuya5CCPrg6h
+wo7TcrxjdEvEVQ97PMZcq6HVBOtINZGfJeSieacCgYAHyQsQJFo20O+17ZI7jioX
+jkD0Gvu3hd889i1KFcKiOLpa2Me/UVieBOSJXmfRiZTEsKouFXK6SGRglwAgrpXu
+KTaKJrBNA16G8g2bviV/u32FC53gYiXvFVdiPu9f/97QYdlAjv5ZtTSZZUnL8smv
+R5rGhmr9TpGU3tkREcDVXQKBgBUfJ0dyvWvlYf31lOcYxQ/QAJuNi7w0S+K+EZLP
+O2X2yYI0VbG6hTSAhigse+XW5Wzz5S71CY92Gn2WsA9EdS3DQT/R5Ky4S34Y8W4R
+BtuR1JfwgIX6TSRmFrx+vOPKtzD6gUWCW9xF8YUlaipyVwXOd10pnZFogn0gfchb
+GlPvAoGAG2xikjlCTrnKv7KRF9sxO1eLixfzHwWKiAhrtFBoHSM4AwynrpAb0eMf
+ObSIjXeBy93LhTluVOsD5J9iXA/SKYoXqt/tDMCHRdwpTsJNBa56GMkpFHHLo6oC
+si20nmMXP949gpRIvrYsgYC8WObbi+RQEWDVutv7hVPCF0QvUHs=
+-----END RSA PRIVATE KEY-----
--- /dev/null
+These files are generated by the script located at tests/auto/external_IODevice/cert/generate.sh
+
--- /dev/null
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAulVnnHwRF6e2aAThSi1cZpUlO3ZdqzPIuf75NBbRY2y9Vm+F
+cyCqUquNxP/qDE02nfQFBd/PUKqUWQs0EXVEVZPEG6s/l7ionYHkMmWSjh+AAWp7
+Iwx3MlHFNi9c5Xrod7iS1igg0YhDQlnT0xGfTXZasUJ/s6NuNoZiN5L6sEKYDSAu
+kzyyqS23WrqE4RvsGAaoaaJqu1MT8DBqI9xoPpIvwb/4gdOZn4YClW2WWrVjCTT2
+zEzUAh1BjdH3dktXogiFfXHuOP4W8suOx46NXDcZ3f5LF8CT/2uq9l8vta+pV2ci
+BAIctGu5z+fEdTCojvCWOvCzYmjtep/yTukT+QIDAQABAoIBAQCRvRjkCRnpUZDW
+vPJk7SO3THIplwPeUwtthqLtfedaB4PzphhPmr39GRcyfSNXadENK/39wTbKlhCf
+sKaR/RFsib26EnATwCeJwj10uYMuTC73bIxRNn/ISLKiFdtn1YEbmq6meA5rNFO/
+Arkt/juF/8shl6yAGZSrauJJK1mOH8ItMaGR+6tVPWLIZOLk6TiOJhj9SXvMTCw/
+HzgNZMgFGfqnbctg1ki/CY0BkIkYNUTCjhoCwjcgBJl4ERCfTQS6UeEG9Ad+beIH
+g8WKzpfjF5+Jnjzqw87aWx1200OdlEdouTt677RXHJFN5naUc+GJZGpmA3RGZA17
+LqA4zBYNAoGBAOGwtg7JQkBOmUC0SiKdXnxG1hVnS4N3DMIDVX2tAe/wWIrP168e
+0UpCvswLD+JqO1IgWqw9+QUPnhJSQ9JbYB+678esOTLsT5Yd18VcsiRxSacvQfUw
+H4YJaHrFuuFlnxYMlMdPYS3knbIPsft9DVQLFBLL7qPVHbrJ3V6Sn4XrAoGBANNb
+mfhgVr5m0n3sQVTlYhWwbJq5K+Htzzl7Xl3JHpMLm2C/GoorP/2zLVhbH20lsE3A
+FyIfjcwRxGRu2TXCVnMc4GttlMX5leTxykEd2VrZuEVnTdrudm45Z6sZQpdf1QTg
+WebwKgN1eCg7Jkuk5YlRX/KwMtuq4MVzPtOvR+CrAoGAA8uC5DDCKm6n6QyfCoH2
+6sQOKYH5JRbFYiXINDrKg4xZEMx55fnwrvz8VFYDSF1c7f6ZR7grDci7cbdsaIcc
+0KvGCGd+9ro+hFmwHSN342D8ShFjXIoYnZpe5WGZyNx6llZT0h4lli338NyOs5ng
+tX8SMVa4hoy42UE3tbVldU0CgYA0l/K0b6SmNIfkdcm8Cmhh5UjhJ3rX+Yk7UIum
+4skM5jJ/3I4KG8EMrG14MxSa4GoCru4Su69ZPIKWS08ZpYZFlsXxdY8zxGucUN53
+XaochVjpTE9/Tx+BRh+Z3+tGJ76mO/2jDdgmjDCeMjnRUPMdPHaXuWiuaNMNzyOv
+IUrNiQKBgGvxEQ0Oe3d/om2Lp/cHbkhZkw/jO/FG5HtodxiO3+1YLhExsDOc5GVn
++x2eNv+dQSIrGagko9TJe1p9WqFnD19Ls+ezqfw2fR5Amg1KHKGUA7k1+Qe/QgoK
+D+T4/RkvdGRoBv/il+Rj1rfmMAhEzdD7Axek9a6rUj8geO22kp7I
+-----END RSA PRIVATE KEY-----
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIDBTCCAe2gAwIBAgIUV9eILCYaC+qwZHR7OO23uyd2UjwwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHUXRSTyBDQTAeFw0yMTAyMjQxMTMzNTVaFw0yMzA2MTAx
+MTMzNTVaMBIxEDAOBgNVBAMMB1F0Uk8gQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQC6VWecfBEXp7ZoBOFKLVxmlSU7dl2rM8i5/vk0FtFjbL1Wb4Vz
+IKpSq43E/+oMTTad9AUF389QqpRZCzQRdURVk8Qbqz+XuKidgeQyZZKOH4ABansj
+DHcyUcU2L1zleuh3uJLWKCDRiENCWdPTEZ9NdlqxQn+zo242hmI3kvqwQpgNIC6T
+PLKpLbdauoThG+wYBqhpomq7UxPwMGoj3Gg+ki/Bv/iB05mfhgKVbZZatWMJNPbM
+TNQCHUGN0fd2S1eiCIV9ce44/hbyy47Hjo1cNxnd/ksXwJP/a6r2Xy+1r6lXZyIE
+Ahy0a7nP58R1MKiO8JY68LNiaO16n/JO6RP5AgMBAAGjUzBRMB0GA1UdDgQWBBSu
+ehS/XLejTiDbCddGU2mMZ1t3CjAfBgNVHSMEGDAWgBSuehS/XLejTiDbCddGU2mM
+Z1t3CjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQB8JzSuhHPK
+cjhLqOHUGMKtKWOd5p9g2D45cAWh6jdzU/AhmslMPbsO5hZkqfE+3xARtcMmQfF2
+k1Qyp3hDTN1ZqHSM6Urq9uR33/wXZbNRUNCD8lAmqKyzF9NF7Q+tmC//IMRtVQhK
+aMN3LciyYGQjT0XhDKFWEz9/AvUQD97mLow2m0/izqE4SI6ekQDNL26IiCWFgFjh
+ScZjcJ1ogluD2a6sEUGywRXLNV/bdSjRgkAbpvJFrok7dDZ8xCNhOg4xJJQJRWm7
+ZusUydiVyfgrFan6MD+EdldRHjAs8S9BJfZ0RTOWnD9V8auKuVomzKDed54QlXXi
+zwowb3Objpqh
+-----END CERTIFICATE-----
--- /dev/null
+7390820BD47F5A3C7398B7C8DCA218B35C2C2E29
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIDFTCCAf2gAwIBAgIUc5CCC9R/WjxzmLfI3KIYs1wsLigwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHUXRSTyBDQTAeFw0yMTAyMjQxMTMzNTVaFw0yMzA1MzAx
+MTMzNTVaMBQxEjAQBgNVBAMMCTEyNy4wLjAuMTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBAOKHte9tB66OD+Um/WkqxHtW3sKrBs4IxKuWAef0UMRt3ld6
+5HvWk+xsCZdPxeL53nMOIy9FS6wKGvEWTwYRR4Id9iX2XQsI4cRJWl25qgCYohnm
+Eet9CUkXa3ywbyrSBWFD0r956sS+mwhHU9z05jphd6iZEonHu2b4BFFXMN7+prwj
+00EtGbte5wSWWE9ZfXzeGYd4cZBReNCRjaS5XJ3IgjZ4tfxsB3JzBjVafCfnth7r
+Is8a2SKCGnhYmV+A6Agth4xtSKDho+BSDYSuMux3dftM/eqtxF0wXzlnX5ApNwGB
+zWjcoUL63vjjy17oNEtbs5X2e1g8bGRaGRxGUHUCAwEAAaNhMF8wHwYDVR0jBBgw
+FoAUrnoUv1y3o04g2wnXRlNpjGdbdwowCQYDVR0TBAIwADALBgNVHQ8EBAMCBPAw
+EwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0RBAgwBocEfwAAATANBgkqhkiG9w0B
+AQsFAAOCAQEAqhBhxRgG9N1ZghwWC3ZhWSx4BFl3YrStWlQcjffcQ6p8NDxsrkFc
+gMG51TmJdaz8J4v2AZW8k9GJlEIaZdV/8czeyEwvjKD4vrUw88waeW7n6o8H+8k+
+ak9fRFvnerFrLEWNpyRqbjJWwm8bQ4T5UKsVNXkZnNLyG2Ha29L9gUHffgSMiyLO
+hWqcanPxsMJaDVhw/Gd8JwqaEC1nRPCGxhog2/D2sh4vCj1UykykjPwNz5fP/vfA
+VujNCA23eXAdgD3lALHu2WrmyPkQCM7Z61g4k8+v0KjhyJjdLSVTwkPePEo87Fv4
+sn4Jp5gPPBf7jDFKp8PDdbPmk0qN+Wm8gA==
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEA4oe1720Hro4P5Sb9aSrEe1bewqsGzgjEq5YB5/RQxG3eV3rk
+e9aT7GwJl0/F4vnecw4jL0VLrAoa8RZPBhFHgh32JfZdCwjhxElaXbmqAJiiGeYR
+630JSRdrfLBvKtIFYUPSv3nqxL6bCEdT3PTmOmF3qJkSice7ZvgEUVcw3v6mvCPT
+QS0Zu17nBJZYT1l9fN4Zh3hxkFF40JGNpLlcnciCNni1/GwHcnMGNVp8J+e2Husi
+zxrZIoIaeFiZX4DoCC2HjG1IoOGj4FINhK4y7Hd1+0z96q3EXTBfOWdfkCk3AYHN
+aNyhQvre+OPLXug0S1uzlfZ7WDxsZFoZHEZQdQIDAQABAoIBAGuKEYzALc1oE5Ot
+ls++RdhnvQidOHXHI9ZxOCZtjYoyvkK5TI6dp0utXkA+1qqSBFCKfZmLRAlAItog
+xRMUUOYsYxZShokehk8wo32rDlGKJCo3Vnp8uVPBkn13JM8nNPafxASyVAlikyay
+9dUHTeSZML0RLgPKleSkCSi0Q7cYOFG/HB9aNjp8F5rdut02KrmC3cxlHKF7QXXG
+VU+op1Z9o0V2/iUFJnF5CR40sW2THEbBJkkeYwbvUTnavz4XQtZst//DKsDQEe2r
+UrhsIHduvG4tWiBE77m1vyigTxUWCsLQ2KCnn9O+4KyTg9HWCiQ2QSU3istX/rpI
+zN2lOgECgYEA9PVVMnY+t59Q73IQ9LRg5KRqg6YyGQOrwJKbCUxDrA6ikh3MDgwV
+CkC6Jdl6e4DMog51l3CizrfR2+mtNSTUJDGFE1iGgI+Faem4aopRtFRiLWJ8n4m7
+U8pl3XTP0XFT68aBCAE6O/xVPXs0I/eKNvaF5vokB5zm4R79o37WP+UCgYEA7L26
+TiCFA73Fil/bPupqWJnvm896RlO1S+IBOKlPyCHVvxiGLvtv+YTucCFwXQ4FeNRh
+bQEWlURsgeNr7PHATtFUZ/zo/7l1WYNLXZDZwWD+JYllVPwskJOJMx5Rc77Q0aQ6
+7v60XMGwD5cxQ29RHuJs09Iwc9b1WqwOAEJAJVECgYBNsxQXMZKrRAm0KgZe2Ghz
+ngN7RthVPujX6KjsxhghF3NRzcnQGt0Bp45kOxuy2SQPs25xXvUFhSE4FGMwnEH+
+SQbhIA9p8BxtgAlTIhTQkoOhyb+mC1Y0Odsd59OTp9Lq0shS9bC3Hk8bdV0Qm5Bn
+5sKKhYWwNIC3n9Dsb2seUQKBgAS7biPtpnsCqhYwAFPrn6CRwyZcKVeKiM8xf1DA
+oaWgd4NQXC5IPF7Cd3mqUXKquxVFOYVSRj9JlNmr0BZ2Zp+ss4E4nvetn1jgtPrz
+0EZ7R9k8O9hNCh8Bs/ZfnsUvhUELhVoNoVFRVdGZ9hQg/4AcioxZYTqPi2v6kHUU
+3e9hAoGAec7anF5TiTx2jjcDFS9hrRw0w2PsNX24qjqPFqeuzDIorh6rq4Ip4aA0
+7rxeIXmxjmYA7pPCT9rPxtpEp4BQovF9kHMutd8lyB4rGbLpNpOY4m5v8Oo7cLQ3
+kLAwE+jrEwLNtuq+kUlGwK7YLeiGUm4Rsof5IXlSkXzL/99gHC4=
+-----END RSA PRIVATE KEY-----
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "timemodel.h"
+
+#include <QCoreApplication>
+#include <QSslConfiguration>
+
+#include "sslserver.h"
+
+#include <QRemoteObjectHost>
+/*
+* http://stackoverflow.com/questions/7404163/windows-handling-ctrlc-in-different-thread
+*/
+
+void SigIntHandler()
+{
+ qDebug()<<"Ctrl-C received. Quitting.";
+ qApp->quit();
+}
+
+#if defined(Q_OS_UNIX) || defined(Q_OS_LINUX) || defined(Q_OS_QNX)
+#include <signal.h>
+
+void unix_handler(int s)
+{
+ if (s==SIGINT)
+ SigIntHandler();
+}
+
+#elif defined(Q_OS_WIN32)
+#include <windows.h>
+
+BOOL WINAPI WinHandler(DWORD CEvent)
+{
+ switch (CEvent)
+ {
+ case CTRL_C_EVENT:
+ SigIntHandler();
+ break;
+ }
+ return TRUE;
+}
+#endif
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication app(argc, argv);
+
+ auto config = QSslConfiguration::defaultConfiguration();
+ config.setCaCertificates(QSslCertificate::fromPath(QStringLiteral(":/sslcert/rootCA.pem")));
+ QSslConfiguration::setDefaultConfiguration(config);
+
+#if defined(Q_OS_UNIX) || defined(Q_OS_LINUX) || defined(Q_OS_QNX)
+ signal(SIGINT, &unix_handler);
+#elif defined(Q_OS_WIN32)
+ SetConsoleCtrlHandler((PHANDLER_ROUTINE)WinHandler, TRUE);
+#endif
+ QRemoteObjectHost host;
+ SslServer server;
+ server.listen(QHostAddress::Any, 65511);
+
+ host.setHostUrl(server.serverAddress().toString(), QRemoteObjectHost::AllowExternalRegistration);
+
+ QObject::connect(&server, &SslServer::encryptedSocketReady, &server, [&host](QSslSocket *socket) {
+ QObject::connect(socket, &QSslSocket::errorOccurred,
+ socket, [](QAbstractSocket::SocketError error){
+ qDebug() << "QSslSocket::error" << error;
+ }) ;
+ host.addHostSideConnection(socket);
+ });
+
+ MinuteTimer timer;
+ host.enableRemoting(&timer);
+
+ Q_UNUSED(timer)
+ return app.exec();
+}
+
--- /dev/null
+// Copyright (C) 2018 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "sslserver.h"
+#include <QSslSocket>
+
+SslServer::SslServer(QObject *parent)
+ : QTcpServer(parent)
+{}
+
+
+void SslServer::incomingConnection(qintptr socketDescriptor)
+{
+ auto serverSocket = new QSslSocket;
+ if (serverSocket->setSocketDescriptor(socketDescriptor)) {
+ addPendingConnection(serverSocket);
+ connect(serverSocket, &QSslSocket::encrypted, this, [this, serverSocket] {
+ Q_EMIT encryptedSocketReady(serverSocket);
+ });
+ connect(serverSocket, static_cast<void (QSslSocket::*)(const QList<QSslError>&)>(&QSslSocket::sslErrors),
+ this, [serverSocket](const QList<QSslError>& errors){
+ qWarning() << "Error:" << serverSocket << errors;
+ delete serverSocket;
+ });
+ serverSocket->setPeerVerifyMode(QSslSocket::VerifyPeer);
+ serverSocket->setLocalCertificate(QStringLiteral(":/sslcert/server.crt"));
+ serverSocket->setPrivateKey(QStringLiteral(":/sslcert/server.key"));
+ serverSocket->startServerEncryption();
+ } else {
+ delete serverSocket;
+ }
+}
--- /dev/null
+// Copyright (C) 2018 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef SSLSERVER_H
+#define SSLSERVER_H
+
+#include <QTcpServer>
+
+QT_BEGIN_NAMESPACE
+class QSslSocket;
+QT_END_NAMESPACE
+
+class SslServer : public QTcpServer
+{
+ Q_OBJECT
+public:
+ SslServer(QObject *parent=nullptr);
+ void incomingConnection(qintptr socketDescriptor) override;
+
+signals:
+ void encryptedSocketReady(QSslSocket *socket);
+};
+
+#endif // SSLSERVER_H
--- /dev/null
+QT_FOR_CONFIG += network
+requires(qtConfig(ssl))
+
+CONFIG += console
+
+
+REPC_SOURCE += ../../timemodel.rep
+QT = remoteobjects remoteobjects-private core
+
+SOURCES += timemodel.cpp main.cpp \
+ sslserver.cpp
+HEADERS += timemodel.h \
+ sslserver.h
+
+contains(QT_CONFIG, c++11): CONFIG += c++11
+
+target.path = $$[QT_INSTALL_EXAMPLES]/remoteobjects/ssl/sslserver
+INSTALLS += target
+
+RESOURCES += \
+ cert/cert.qrc
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "timemodel.h"
+
+MinuteTimer::MinuteTimer(QObject *parent) : MinuteTimerSimpleSource(parent), zone(0)
+{
+ time = QTime::currentTime();
+ setHour(time.hour());
+ setMinute(time.minute());
+ timer.start(60000-time.second()*1000, this);
+}
+MinuteTimer::~MinuteTimer()
+{
+ timer.stop();
+}
+void MinuteTimer::timerEvent(QTimerEvent *)
+{
+ QTime now = QTime::currentTime();
+ if (now.second() == 59 && now.minute() == time.minute() && now.hour() == time.hour()) {
+ // just missed time tick over, force it, wait extra 0.5 seconds
+ time = time.addSecs(60);
+ timer.start(60500, this);
+ } else {
+ time = now;
+ timer.start(60000-time.second()*1000, this);
+ }
+ qDebug()<<"Time"<<time;
+ setHour(time.hour());
+ setMinute(time.minute());
+ emit timeChanged();
+ emit timeChanged2(time);
+ static PresetInfo bla(3, 93.9f, "Best Station");
+ emit sendCustom(bla);
+}
+void MinuteTimer::SetTimeZone(const int &zn)
+{
+ qDebug()<<"SetTimeZone"<<zn;
+ if (zn != zone)
+ {
+ zone = zn;
+ }
+}
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtCore>
+#include "rep_timemodel_source.h"
+
+class MinuteTimer : public MinuteTimerSimpleSource
+{
+ Q_OBJECT
+public:
+ MinuteTimer(QObject *parent = nullptr);
+ ~MinuteTimer() override;
+
+public slots:
+ void SetTimeZone(const int &zn) override;
+
+protected:
+ void timerEvent(QTimerEvent *) override;
+
+private:
+ QTime time;
+ QBasicTimer timer;
+ int zone;
+};
--- /dev/null
+#include <QtCore>
+
+POD PresetInfo(int presetNumber, float frequency, QString stationName)
+POD PresetData(QList<QString> bla)
+class MinuteTimer
+{
+ PROP(int hour=1);
+ PROP(int minute=51);
+ SIGNAL(timeChanged());
+ SIGNAL(timeChanged2(QTime t));
+ SIGNAL(sendCustom(PresetInfo info));
+ SIGNAL(foo(QMap<QString, QString> foo));
+ SLOT(void SetTimeZone(const int &));
+};
--- /dev/null
+if(TARGET Qt::WebSockets AND TARGET Qt::Widgets)
+ qt_internal_add_example(wsclient)
+ qt_internal_add_example(wsserver)
+endif()
--- /dev/null
+<RCC>
+ <qresource prefix="/sslcert">
+ <file>client.crt</file>
+ <file>client.key</file>
+ <file>rootCA.key</file>
+ <file>rootCA.pem</file>
+ <file>rootCA.srl</file>
+ <file>server.crt</file>
+ <file>server.key</file>
+ </qresource>
+</RCC>
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIICrTCCAZUCFHOQggvUf1o8c5i3yNyiGLNcLC4pMA0GCSqGSIb3DQEBCwUAMBIx
+EDAOBgNVBAMMB1F0Uk8gQ0EwHhcNMjEwMjI0MTEzMzU1WhcNMjMwNTMwMTEzMzU1
+WjAUMRIwEAYDVQQDDAkxMjcuMC4wLjEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQDbl9iuedw0oSbtpC2m30YdzwRmemijasP9SQGQ6+piUOFUKCZsoGWc
+RcEnLGzC+KJ7FXh8jA1kTXSW6ghqvrUysN8VzjgmcCLFee4JAkCUY8yNrlq13ciR
+19BE09kJdOPZeI57pCSBNA6iy03Q4nc/GJpG63QTqJv/WUUgMek0UsmZIzDcWaqr
+MCMnLMaRi5oKFCnnl8E0XDuRm1nqPAzT+us/4upMv+7Q2xs4QFXbLUpSIToNc1wm
+tP6OAGaYClbJZgZbUNowj0wJeCUAwGGcDpliYj1JB8R015z8Kd8pDCvdD7XL35JR
+rT+eaBFNLUrl30aIl3lWf/buv3OoRmuVAgMBAAEwDQYJKoZIhvcNAQELBQADggEB
+AJjdfuy2pb3KgnpxYiXfKXCqGlN7E1RyoCIsMhldWcxAN5cwIJCrvde5MNI8kDvd
+0SfDpRpCP/hZqpR6DsR9iNYJprXlQNZ7Rs41Eswwlb66DqmBlb5ZQcYl8KsKV5fw
+7PhvLpjC5hEg1OBg1Ooz+aNvI9OJYIRFUJ1smtRzwXWuQd5QoqYVRpzvmrFawnGa
+2NHywiwgKyGvY/y82pPuj1rt0L+bae85cZm32f6gp1me9OuLIqA2G5UafSiigWBY
+YL249Rd4rrT87GAeaiBo8ZxZ8de8O7TOBjSNrfAMySepDWjfFfoNpyp+4foRKmpE
+aZmgGTIj5rfhYh4Gcj1nZBw=
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEA25fYrnncNKEm7aQtpt9GHc8EZnpoo2rD/UkBkOvqYlDhVCgm
+bKBlnEXBJyxswviiexV4fIwNZE10luoIar61MrDfFc44JnAixXnuCQJAlGPMja5a
+td3IkdfQRNPZCXTj2XiOe6QkgTQOostN0OJ3PxiaRut0E6ib/1lFIDHpNFLJmSMw
+3FmqqzAjJyzGkYuaChQp55fBNFw7kZtZ6jwM0/rrP+LqTL/u0NsbOEBV2y1KUiE6
+DXNcJrT+jgBmmApWyWYGW1DaMI9MCXglAMBhnA6ZYmI9SQfEdNec/CnfKQwr3Q+1
+y9+SUa0/nmgRTS1K5d9GiJd5Vn/27r9zqEZrlQIDAQABAoIBAEDLm4pQNuPosV3p
+1fapZz0gesHqWLnvpQk145ppom2ERBjbCAuBgLoN8yKl/ynAx+DdwwGtKb5xBHgL
+cpRc1YaxngIHKZZd/ESc59oMqhWfJRqhWe7UFHzEW5YTlLUvopPm+NQO6R6ex7rN
+lpaOXHVnww4uJ8AtPmqoYrdPQurG/txveRMLo84JJT+IH2YVWOzccp809zw4WZZD
+qBcgm/dV8ir+8nUHQlR+loMMrEoKeacNxtHUXWL6d6P93Q72L07t41/l0XmXXq7I
+cVJnGxcJtkeqj03FSHqDU3XM5fRg6f+XnnSnhnd4AUmHe8cvyeCnEf4bdh4UpzBG
+sCie+XkCgYEA93FU0X6ttWdb+rJNHRnHmb4DxOVo2LeXEk1A1ul+Yj+jFP+TwJH+
+bm8PbV7ALdyH2u66ElQG60gW9ztu86xl5ZLVdhijWJpjHKB45eXVhnRcb2Fy9tDc
+pUeRs8+IrrYbWDrNZZYWby83MqPHimCLTmAZl11NMB2ohyFDxr5voGMCgYEA4y/0
+2WN8r74H9I3L2Ghfe8e3i/W35BpjtElJxiL3L1vzGdU5Wo1hDnvjoHvdTxB7LtGU
+I+P0l77fwuAC8G8bh4SZ59jcxlqCmbXy7wDAyrYaCja5OWK9xWXvYuya5CCPrg6h
+wo7TcrxjdEvEVQ97PMZcq6HVBOtINZGfJeSieacCgYAHyQsQJFo20O+17ZI7jioX
+jkD0Gvu3hd889i1KFcKiOLpa2Me/UVieBOSJXmfRiZTEsKouFXK6SGRglwAgrpXu
+KTaKJrBNA16G8g2bviV/u32FC53gYiXvFVdiPu9f/97QYdlAjv5ZtTSZZUnL8smv
+R5rGhmr9TpGU3tkREcDVXQKBgBUfJ0dyvWvlYf31lOcYxQ/QAJuNi7w0S+K+EZLP
+O2X2yYI0VbG6hTSAhigse+XW5Wzz5S71CY92Gn2WsA9EdS3DQT/R5Ky4S34Y8W4R
+BtuR1JfwgIX6TSRmFrx+vOPKtzD6gUWCW9xF8YUlaipyVwXOd10pnZFogn0gfchb
+GlPvAoGAG2xikjlCTrnKv7KRF9sxO1eLixfzHwWKiAhrtFBoHSM4AwynrpAb0eMf
+ObSIjXeBy93LhTluVOsD5J9iXA/SKYoXqt/tDMCHRdwpTsJNBa56GMkpFHHLo6oC
+si20nmMXP949gpRIvrYsgYC8WObbi+RQEWDVutv7hVPCF0QvUHs=
+-----END RSA PRIVATE KEY-----
--- /dev/null
+These files are generated by the script located at tests/auto/external_IODevice/cert/generate.sh
+
--- /dev/null
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAulVnnHwRF6e2aAThSi1cZpUlO3ZdqzPIuf75NBbRY2y9Vm+F
+cyCqUquNxP/qDE02nfQFBd/PUKqUWQs0EXVEVZPEG6s/l7ionYHkMmWSjh+AAWp7
+Iwx3MlHFNi9c5Xrod7iS1igg0YhDQlnT0xGfTXZasUJ/s6NuNoZiN5L6sEKYDSAu
+kzyyqS23WrqE4RvsGAaoaaJqu1MT8DBqI9xoPpIvwb/4gdOZn4YClW2WWrVjCTT2
+zEzUAh1BjdH3dktXogiFfXHuOP4W8suOx46NXDcZ3f5LF8CT/2uq9l8vta+pV2ci
+BAIctGu5z+fEdTCojvCWOvCzYmjtep/yTukT+QIDAQABAoIBAQCRvRjkCRnpUZDW
+vPJk7SO3THIplwPeUwtthqLtfedaB4PzphhPmr39GRcyfSNXadENK/39wTbKlhCf
+sKaR/RFsib26EnATwCeJwj10uYMuTC73bIxRNn/ISLKiFdtn1YEbmq6meA5rNFO/
+Arkt/juF/8shl6yAGZSrauJJK1mOH8ItMaGR+6tVPWLIZOLk6TiOJhj9SXvMTCw/
+HzgNZMgFGfqnbctg1ki/CY0BkIkYNUTCjhoCwjcgBJl4ERCfTQS6UeEG9Ad+beIH
+g8WKzpfjF5+Jnjzqw87aWx1200OdlEdouTt677RXHJFN5naUc+GJZGpmA3RGZA17
+LqA4zBYNAoGBAOGwtg7JQkBOmUC0SiKdXnxG1hVnS4N3DMIDVX2tAe/wWIrP168e
+0UpCvswLD+JqO1IgWqw9+QUPnhJSQ9JbYB+678esOTLsT5Yd18VcsiRxSacvQfUw
+H4YJaHrFuuFlnxYMlMdPYS3knbIPsft9DVQLFBLL7qPVHbrJ3V6Sn4XrAoGBANNb
+mfhgVr5m0n3sQVTlYhWwbJq5K+Htzzl7Xl3JHpMLm2C/GoorP/2zLVhbH20lsE3A
+FyIfjcwRxGRu2TXCVnMc4GttlMX5leTxykEd2VrZuEVnTdrudm45Z6sZQpdf1QTg
+WebwKgN1eCg7Jkuk5YlRX/KwMtuq4MVzPtOvR+CrAoGAA8uC5DDCKm6n6QyfCoH2
+6sQOKYH5JRbFYiXINDrKg4xZEMx55fnwrvz8VFYDSF1c7f6ZR7grDci7cbdsaIcc
+0KvGCGd+9ro+hFmwHSN342D8ShFjXIoYnZpe5WGZyNx6llZT0h4lli338NyOs5ng
+tX8SMVa4hoy42UE3tbVldU0CgYA0l/K0b6SmNIfkdcm8Cmhh5UjhJ3rX+Yk7UIum
+4skM5jJ/3I4KG8EMrG14MxSa4GoCru4Su69ZPIKWS08ZpYZFlsXxdY8zxGucUN53
+XaochVjpTE9/Tx+BRh+Z3+tGJ76mO/2jDdgmjDCeMjnRUPMdPHaXuWiuaNMNzyOv
+IUrNiQKBgGvxEQ0Oe3d/om2Lp/cHbkhZkw/jO/FG5HtodxiO3+1YLhExsDOc5GVn
++x2eNv+dQSIrGagko9TJe1p9WqFnD19Ls+ezqfw2fR5Amg1KHKGUA7k1+Qe/QgoK
+D+T4/RkvdGRoBv/il+Rj1rfmMAhEzdD7Axek9a6rUj8geO22kp7I
+-----END RSA PRIVATE KEY-----
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIDBTCCAe2gAwIBAgIUV9eILCYaC+qwZHR7OO23uyd2UjwwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHUXRSTyBDQTAeFw0yMTAyMjQxMTMzNTVaFw0yMzA2MTAx
+MTMzNTVaMBIxEDAOBgNVBAMMB1F0Uk8gQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQC6VWecfBEXp7ZoBOFKLVxmlSU7dl2rM8i5/vk0FtFjbL1Wb4Vz
+IKpSq43E/+oMTTad9AUF389QqpRZCzQRdURVk8Qbqz+XuKidgeQyZZKOH4ABansj
+DHcyUcU2L1zleuh3uJLWKCDRiENCWdPTEZ9NdlqxQn+zo242hmI3kvqwQpgNIC6T
+PLKpLbdauoThG+wYBqhpomq7UxPwMGoj3Gg+ki/Bv/iB05mfhgKVbZZatWMJNPbM
+TNQCHUGN0fd2S1eiCIV9ce44/hbyy47Hjo1cNxnd/ksXwJP/a6r2Xy+1r6lXZyIE
+Ahy0a7nP58R1MKiO8JY68LNiaO16n/JO6RP5AgMBAAGjUzBRMB0GA1UdDgQWBBSu
+ehS/XLejTiDbCddGU2mMZ1t3CjAfBgNVHSMEGDAWgBSuehS/XLejTiDbCddGU2mM
+Z1t3CjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQB8JzSuhHPK
+cjhLqOHUGMKtKWOd5p9g2D45cAWh6jdzU/AhmslMPbsO5hZkqfE+3xARtcMmQfF2
+k1Qyp3hDTN1ZqHSM6Urq9uR33/wXZbNRUNCD8lAmqKyzF9NF7Q+tmC//IMRtVQhK
+aMN3LciyYGQjT0XhDKFWEz9/AvUQD97mLow2m0/izqE4SI6ekQDNL26IiCWFgFjh
+ScZjcJ1ogluD2a6sEUGywRXLNV/bdSjRgkAbpvJFrok7dDZ8xCNhOg4xJJQJRWm7
+ZusUydiVyfgrFan6MD+EdldRHjAs8S9BJfZ0RTOWnD9V8auKuVomzKDed54QlXXi
+zwowb3Objpqh
+-----END CERTIFICATE-----
--- /dev/null
+7390820BD47F5A3C7398B7C8DCA218B35C2C2E29
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIDFTCCAf2gAwIBAgIUc5CCC9R/WjxzmLfI3KIYs1wsLigwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHUXRSTyBDQTAeFw0yMTAyMjQxMTMzNTVaFw0yMzA1MzAx
+MTMzNTVaMBQxEjAQBgNVBAMMCTEyNy4wLjAuMTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBAOKHte9tB66OD+Um/WkqxHtW3sKrBs4IxKuWAef0UMRt3ld6
+5HvWk+xsCZdPxeL53nMOIy9FS6wKGvEWTwYRR4Id9iX2XQsI4cRJWl25qgCYohnm
+Eet9CUkXa3ywbyrSBWFD0r956sS+mwhHU9z05jphd6iZEonHu2b4BFFXMN7+prwj
+00EtGbte5wSWWE9ZfXzeGYd4cZBReNCRjaS5XJ3IgjZ4tfxsB3JzBjVafCfnth7r
+Is8a2SKCGnhYmV+A6Agth4xtSKDho+BSDYSuMux3dftM/eqtxF0wXzlnX5ApNwGB
+zWjcoUL63vjjy17oNEtbs5X2e1g8bGRaGRxGUHUCAwEAAaNhMF8wHwYDVR0jBBgw
+FoAUrnoUv1y3o04g2wnXRlNpjGdbdwowCQYDVR0TBAIwADALBgNVHQ8EBAMCBPAw
+EwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0RBAgwBocEfwAAATANBgkqhkiG9w0B
+AQsFAAOCAQEAqhBhxRgG9N1ZghwWC3ZhWSx4BFl3YrStWlQcjffcQ6p8NDxsrkFc
+gMG51TmJdaz8J4v2AZW8k9GJlEIaZdV/8czeyEwvjKD4vrUw88waeW7n6o8H+8k+
+ak9fRFvnerFrLEWNpyRqbjJWwm8bQ4T5UKsVNXkZnNLyG2Ha29L9gUHffgSMiyLO
+hWqcanPxsMJaDVhw/Gd8JwqaEC1nRPCGxhog2/D2sh4vCj1UykykjPwNz5fP/vfA
+VujNCA23eXAdgD3lALHu2WrmyPkQCM7Z61g4k8+v0KjhyJjdLSVTwkPePEo87Fv4
+sn4Jp5gPPBf7jDFKp8PDdbPmk0qN+Wm8gA==
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEA4oe1720Hro4P5Sb9aSrEe1bewqsGzgjEq5YB5/RQxG3eV3rk
+e9aT7GwJl0/F4vnecw4jL0VLrAoa8RZPBhFHgh32JfZdCwjhxElaXbmqAJiiGeYR
+630JSRdrfLBvKtIFYUPSv3nqxL6bCEdT3PTmOmF3qJkSice7ZvgEUVcw3v6mvCPT
+QS0Zu17nBJZYT1l9fN4Zh3hxkFF40JGNpLlcnciCNni1/GwHcnMGNVp8J+e2Husi
+zxrZIoIaeFiZX4DoCC2HjG1IoOGj4FINhK4y7Hd1+0z96q3EXTBfOWdfkCk3AYHN
+aNyhQvre+OPLXug0S1uzlfZ7WDxsZFoZHEZQdQIDAQABAoIBAGuKEYzALc1oE5Ot
+ls++RdhnvQidOHXHI9ZxOCZtjYoyvkK5TI6dp0utXkA+1qqSBFCKfZmLRAlAItog
+xRMUUOYsYxZShokehk8wo32rDlGKJCo3Vnp8uVPBkn13JM8nNPafxASyVAlikyay
+9dUHTeSZML0RLgPKleSkCSi0Q7cYOFG/HB9aNjp8F5rdut02KrmC3cxlHKF7QXXG
+VU+op1Z9o0V2/iUFJnF5CR40sW2THEbBJkkeYwbvUTnavz4XQtZst//DKsDQEe2r
+UrhsIHduvG4tWiBE77m1vyigTxUWCsLQ2KCnn9O+4KyTg9HWCiQ2QSU3istX/rpI
+zN2lOgECgYEA9PVVMnY+t59Q73IQ9LRg5KRqg6YyGQOrwJKbCUxDrA6ikh3MDgwV
+CkC6Jdl6e4DMog51l3CizrfR2+mtNSTUJDGFE1iGgI+Faem4aopRtFRiLWJ8n4m7
+U8pl3XTP0XFT68aBCAE6O/xVPXs0I/eKNvaF5vokB5zm4R79o37WP+UCgYEA7L26
+TiCFA73Fil/bPupqWJnvm896RlO1S+IBOKlPyCHVvxiGLvtv+YTucCFwXQ4FeNRh
+bQEWlURsgeNr7PHATtFUZ/zo/7l1WYNLXZDZwWD+JYllVPwskJOJMx5Rc77Q0aQ6
+7v60XMGwD5cxQ29RHuJs09Iwc9b1WqwOAEJAJVECgYBNsxQXMZKrRAm0KgZe2Ghz
+ngN7RthVPujX6KjsxhghF3NRzcnQGt0Bp45kOxuy2SQPs25xXvUFhSE4FGMwnEH+
+SQbhIA9p8BxtgAlTIhTQkoOhyb+mC1Y0Odsd59OTp9Lq0shS9bC3Hk8bdV0Qm5Bn
+5sKKhYWwNIC3n9Dsb2seUQKBgAS7biPtpnsCqhYwAFPrn6CRwyZcKVeKiM8xf1DA
+oaWgd4NQXC5IPF7Cd3mqUXKquxVFOYVSRj9JlNmr0BZ2Zp+ss4E4nvetn1jgtPrz
+0EZ7R9k8O9hNCh8Bs/ZfnsUvhUELhVoNoVFRVdGZ9hQg/4AcioxZYTqPi2v6kHUU
+3e9hAoGAec7anF5TiTx2jjcDFS9hrRw0w2PsNX24qjqPFqeuzDIorh6rq4Ip4aA0
+7rxeIXmxjmYA7pPCT9rPxtpEp4BQovF9kHMutd8lyB4rGbLpNpOY4m5v8Oo7cLQ3
+kLAwE+jrEwLNtuq+kUlGwK7YLeiGUm4Rsof5IXlSkXzL/99gHC4=
+-----END RSA PRIVATE KEY-----
--- /dev/null
+CONFIG -= app_bundle
+
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/websocketiodevice.h
+
+SOURCES += \
+ $$PWD/websocketiodevice.cpp
+
+RESOURCES += $$PWD/cert/cert.qrc
--- /dev/null
+// Copyright (C) 2019 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "websocketiodevice.h"
+
+#include <QWebSocket>
+
+WebSocketIoDevice::WebSocketIoDevice(QWebSocket *webSocket, QObject *parent)
+ : QIODevice(parent)
+ , m_socket(webSocket)
+{
+ open(QIODevice::ReadWrite);
+ connect(webSocket, &QWebSocket::disconnected, this, &WebSocketIoDevice::disconnected);
+ connect(webSocket, &QWebSocket::binaryMessageReceived, this, [this](const QByteArray &message){
+ m_buffer.append(message);
+ emit readyRead();
+ });
+ connect(webSocket, &QWebSocket::bytesWritten, this, &WebSocketIoDevice::bytesWritten);
+}
+
+qint64 WebSocketIoDevice::bytesAvailable() const
+{
+ return QIODevice::bytesAvailable() + m_buffer.size();
+}
+
+bool WebSocketIoDevice::isSequential() const
+{
+ return true;
+}
+
+void WebSocketIoDevice::close()
+{
+ if (m_socket)
+ m_socket->close();
+}
+
+qint64 WebSocketIoDevice::readData(char *data, qint64 maxlen)
+{
+ auto sz = std::min(maxlen, qint64(m_buffer.size()));
+ if (sz <= 0)
+ return sz;
+ memcpy(data, m_buffer.constData(), size_t(sz));
+ m_buffer.remove(0, sz);
+ return sz;
+}
+
+qint64 WebSocketIoDevice::writeData(const char *data, qint64 len)
+{
+ if (m_socket)
+ return m_socket->sendBinaryMessage(QByteArray{data, int(len)});
+ return -1;
+}
--- /dev/null
+// Copyright (C) 2019 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef WEBSOCKETIODEVICE_H
+#define WEBSOCKETIODEVICE_H
+
+#include <QBuffer>
+#include <QIODevice>
+#include <QPointer>
+
+class QWebSocket;
+
+class WebSocketIoDevice : public QIODevice
+{
+ Q_OBJECT
+public:
+ WebSocketIoDevice(QWebSocket *webSocket, QObject *parent = nullptr);
+
+signals:
+ void disconnected();
+
+ // QIODevice interface
+public:
+ qint64 bytesAvailable() const override;
+ bool isSequential() const override;
+ void close() override;
+
+protected:
+ qint64 readData(char *data, qint64 maxlen) override;
+ qint64 writeData(const char *data, qint64 len) override;
+
+private:
+ QPointer<QWebSocket> m_socket;
+ QByteArray m_buffer;
+};
+
+#endif // WEBSOCKETIODEVICE_H
--- /dev/null
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \example websockets
+ \title QtRemoteObjects WebSockets Example
+
+ This example shows how you could use a non-QIODevice-based transport
+ (QWebSocket) with QtRemoteObjects. This is achieved by implementing a
+ small QIODevice-derived wrapper for QWebSocket.
+*/
--- /dev/null
+TEMPLATE = subdirs
+
+qtHaveModule(widgets): qtHaveModule(websockets) {
+ SUBDIRS += \
+ wsclient \
+ wsserver
+}
--- /dev/null
+cmake_minimum_required(VERSION 3.16)
+project(wsclient LANGUAGES CXX)
+
+set(CMAKE_AUTOMOC ON)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/remoteobjects/websockets/wsclient")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui RemoteObjects WebSockets Widgets)
+
+qt_add_executable(wsclient
+ ../common/websocketiodevice.cpp ../common/websocketiodevice.h
+ main.cpp
+)
+
+set_target_properties(wsclient PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE FALSE
+)
+
+target_include_directories(wsclient PUBLIC
+ ../common
+)
+
+target_link_libraries(wsclient PUBLIC
+ Qt::Core
+ Qt::Gui
+ Qt::RemoteObjects
+ Qt::WebSockets
+ Qt::Widgets
+)
+
+# Resources:
+set(cert_resource_files
+ "../common/cert/client.crt"
+ "../common/cert/client.key"
+ "../common/cert/rootCA.key"
+ "../common/cert/rootCA.pem"
+ "../common/cert/rootCA.srl"
+ "../common/cert/server.crt"
+ "../common/cert/server.key"
+)
+
+qt6_add_resources(wsclient "cert"
+ PREFIX
+ "/sslcert"
+ BASE
+ "../common/cert"
+ FILES
+ ${cert_resource_files}
+)
+
+install(TARGETS wsclient
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
--- /dev/null
+// Copyright (C) 2019 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QTreeView>
+#include <QApplication>
+#include <QRemoteObjectNode>
+#include <QAbstractItemModelReplica>
+#include <QWebSocket>
+
+#ifndef QT_NO_SSL
+# include <QFile>
+# include <QSslConfiguration>
+# include <QSslKey>
+#endif
+#include "websocketiodevice.h"
+
+int main(int argc, char **argv)
+{
+
+ QLoggingCategory::setFilterRules("qt.remoteobjects.debug=false\n"
+ "qt.remoteobjects.warning=false\n"
+ "qt.remoteobjects.models.debug=false\n"
+ "qt.remoteobjects.models.debug=false");
+
+ QApplication app(argc, argv);
+
+
+
+ QScopedPointer<QWebSocket> webSocket{new QWebSocket};
+ WebSocketIoDevice socket(webSocket.data());
+#ifndef QT_NO_SSL
+ // Always use secure connections when available
+ QSslConfiguration sslConf;
+ QFile certFile(QStringLiteral(":/sslcert/client.crt"));
+ if (!certFile.open(QIODevice::ReadOnly))
+ qFatal("Can't open client.crt file");
+ sslConf.setLocalCertificate(QSslCertificate{certFile.readAll()});
+
+ QFile keyFile(QStringLiteral(":/sslcert/client.key"));
+ if (!keyFile.open(QIODevice::ReadOnly))
+ qFatal("Can't open client.key file");
+ sslConf.setPrivateKey(QSslKey{keyFile.readAll(), QSsl::Rsa});
+
+ sslConf.setPeerVerifyMode(QSslSocket::VerifyPeer);
+ webSocket->setSslConfiguration(sslConf);
+#endif
+ QRemoteObjectNode node;
+ node.addClientSideConnection(&socket);
+ node.setHeartbeatInterval(1000);
+ webSocket->open(QStringLiteral("ws://localhost:8088"));
+
+ QTreeView view;
+ view.setWindowTitle(QStringLiteral("RemoteView"));
+ view.resize(640,480);
+ QScopedPointer<QAbstractItemModelReplica> model(node.acquireModel(QStringLiteral("RemoteModel")));
+ view.setModel(model.data());
+ view.show();
+
+ return app.exec();
+}
--- /dev/null
+QT += widgets remoteobjects websockets
+requires(qtConfig(treeview))
+
+SOURCES += main.cpp
+
+include(../common/common.pri)
+
+target.path = $$[QT_INSTALL_EXAMPLES]/remoteobjects/websockets/wsclient
+INSTALLS += target
--- /dev/null
+cmake_minimum_required(VERSION 3.16)
+project(wsserver LANGUAGES CXX)
+
+set(CMAKE_AUTOMOC ON)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/remoteobjects/websockets/wsserver")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui RemoteObjects WebSockets Widgets)
+
+qt_add_executable(wsserver
+ ../common/websocketiodevice.cpp ../common/websocketiodevice.h
+ main.cpp
+)
+
+set_target_properties(wsserver PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE FALSE
+)
+
+target_include_directories(wsserver PUBLIC
+ ../common
+)
+
+target_link_libraries(wsserver PUBLIC
+ Qt::Core
+ Qt::Gui
+ Qt::RemoteObjects
+ Qt::WebSockets
+ Qt::Widgets
+)
+
+# Resources:
+set(cert_resource_files
+ "../common/cert/client.crt"
+ "../common/cert/client.key"
+ "../common/cert/rootCA.key"
+ "../common/cert/rootCA.pem"
+ "../common/cert/rootCA.srl"
+ "../common/cert/server.crt"
+ "../common/cert/server.key"
+)
+
+qt6_add_resources(wsserver "cert"
+ PREFIX
+ "/sslcert"
+ BASE
+ "../common/cert"
+ FILES
+ ${cert_resource_files}
+)
+
+install(TARGETS wsserver
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
--- /dev/null
+// Copyright (C) 2019 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+#include <QTreeView>
+#include <QApplication>
+#include <QRemoteObjectNode>
+#include <QTimer>
+#include <QStandardItemModel>
+#include <QStandardItem>
+#include <QWebSocket>
+#include <QWebSocketServer>
+
+#ifndef QT_NO_SSL
+# include <QFile>
+# include <QSslConfiguration>
+# include <QSslKey>
+#endif
+
+#include "websocketiodevice.h"
+
+struct TimerHandler : public QObject
+{
+ Q_OBJECT
+public:
+ QStandardItemModel *model;
+public Q_SLOTS:
+ void changeData() {
+ Q_ASSERT(model);
+ Q_ASSERT(model->rowCount() > 50);
+ Q_ASSERT(model->columnCount() > 1);
+ for (int i = 10; i < 50; ++i)
+ model->setData(model->index(i, 1), QColor(Qt::blue), Qt::BackgroundRole);
+ }
+ void insertData() {
+ Q_ASSERT(model);
+ Q_ASSERT(model->rowCount() > 50);
+ Q_ASSERT(model->columnCount() > 1);
+ model->insertRows(2, 9);
+ for (int i = 2; i < 11; ++i) {
+ model->setData(model->index(i, 1), QColor(Qt::green), Qt::BackgroundRole);
+ model->setData(model->index(i, 1), QLatin1String("InsertedRow"), Qt::DisplayRole);
+ }
+ }
+ void removeData() {
+ model->removeRows(2, 4);
+ }
+
+ void changeFlags() {
+ QStandardItem *item = model->item(0, 0);
+ item->setEnabled(false);
+ item = item->child(0, 0);
+ item->setFlags(item->flags() & Qt::ItemIsSelectable);
+ }
+
+ void moveData() {
+ model->moveRows(QModelIndex(), 2, 4, QModelIndex(), 10);
+ }
+};
+
+QList<QStandardItem*> addChild(int numChildren, int nestingLevel)
+{
+ QList<QStandardItem*> result;
+ if (nestingLevel == 0)
+ return result;
+ for (int i = 0; i < numChildren; ++i) {
+ QStandardItem *child = new QStandardItem(QStringLiteral("Child num %1, nesting Level %2").arg(i+1).arg(nestingLevel));
+ if (i == 0)
+ child->appendRow(addChild(numChildren, nestingLevel -1));
+ result.push_back(child);
+ }
+ return result;
+}
+
+int main(int argc, char *argv[])
+{
+ QLoggingCategory::setFilterRules("qt.remoteobjects.debug=false\n"
+ "qt.remoteobjects.warning=false");
+ QApplication app(argc, argv);
+
+ const int modelSize = 100000;
+ QStringList list;
+ QStandardItemModel sourceModel;
+ QStringList hHeaderList;
+ hHeaderList << QStringLiteral("First Column with spacing") << QStringLiteral("Second Column with spacing");
+ sourceModel.setHorizontalHeaderLabels(hHeaderList);
+ list.reserve(modelSize);
+ for (int i = 0; i < modelSize; ++i) {
+ QStandardItem *firstItem = new QStandardItem(QStringLiteral("FancyTextNumber %1").arg(i));
+ if (i == 0)
+ firstItem->appendRow(addChild(2, 2));
+ QStandardItem *secondItem = new QStandardItem(QStringLiteral("FancyRow2TextNumber %1").arg(i));
+ if (i % 2 == 0)
+ firstItem->setBackground(Qt::red);
+ QList<QStandardItem*> row;
+ row << firstItem << secondItem;
+ sourceModel.invisibleRootItem()->appendRow(row);
+ //sourceModel.appendRow(row);
+ list << QStringLiteral("FancyTextNumber %1").arg(i);
+ }
+
+ // Needed by QMLModelViewClient
+ QHash<int,QByteArray> roleNames = {
+ {Qt::DisplayRole, "_text"},
+ {Qt::BackgroundRole, "_color"}
+ };
+ sourceModel.setItemRoleNames(roleNames);
+
+ QList<int> roles;
+ roles << Qt::DisplayRole << Qt::BackgroundRole;
+
+ QWebSocketServer webSockServer{QStringLiteral("WS QtRO"), QWebSocketServer::NonSecureMode};
+ webSockServer.listen(QHostAddress::Any, 8088);
+
+ QRemoteObjectHost hostNode;
+ hostNode.setHostUrl(webSockServer.serverAddress().toString(), QRemoteObjectHost::AllowExternalRegistration);
+
+ hostNode.enableRemoting(&sourceModel, QStringLiteral("RemoteModel"), roles);
+
+ QObject::connect(&webSockServer, &QWebSocketServer::newConnection, &hostNode, [&hostNode, &webSockServer]{
+ while (auto conn = webSockServer.nextPendingConnection()) {
+#ifndef QT_NO_SSL
+ // Always use secure connections when available
+ QSslConfiguration sslConf;
+ QFile certFile(QStringLiteral(":/sslcert/server.crt"));
+ if (!certFile.open(QIODevice::ReadOnly))
+ qFatal("Can't open client.crt file");
+ sslConf.setLocalCertificate(QSslCertificate{certFile.readAll()});
+
+ QFile keyFile(QStringLiteral(":/sslcert/server.key"));
+ if (!keyFile.open(QIODevice::ReadOnly))
+ qFatal("Can't open client.key file");
+ sslConf.setPrivateKey(QSslKey{keyFile.readAll(), QSsl::Rsa});
+
+ sslConf.setPeerVerifyMode(QSslSocket::VerifyPeer);
+ conn->setSslConfiguration(sslConf);
+ QObject::connect(conn, &QWebSocket::sslErrors, conn, &QWebSocket::deleteLater);
+#endif
+ QObject::connect(conn, &QWebSocket::disconnected, conn, &QWebSocket::deleteLater);
+ QObject::connect(conn, QOverload<QAbstractSocket::SocketError>::of(&QWebSocket::error), conn, &QWebSocket::deleteLater);
+ auto ioDevice = new WebSocketIoDevice(conn);
+ QObject::connect(conn, &QWebSocket::destroyed, ioDevice, &WebSocketIoDevice::deleteLater);
+ hostNode.addHostSideConnection(ioDevice);
+ }
+ });
+
+ QTreeView view;
+ view.setWindowTitle(QStringLiteral("SourceView"));
+ view.setModel(&sourceModel);
+ view.show();
+ TimerHandler handler;
+ handler.model = &sourceModel;
+ QTimer::singleShot(5000, &handler, &TimerHandler::changeData);
+ QTimer::singleShot(10000, &handler, &TimerHandler::insertData);
+ QTimer::singleShot(11000, &handler, &TimerHandler::changeFlags);
+ QTimer::singleShot(12000, &handler, &TimerHandler::removeData);
+ QTimer::singleShot(13000, &handler, &TimerHandler::moveData);
+
+ return app.exec();
+}
+
+#include "main.moc"
--- /dev/null
+QT += widgets remoteobjects websockets
+requires(qtConfig(treeview))
+
+SOURCES += main.cpp
+
+include(../common/common.pri)
+
+target.path = $$[QT_INSTALL_EXAMPLES]/remoteobjects/websockets/wsserver
+INSTALLS += target
--- /dev/null
+#include "../../../../../src/remoteobjects/qconnection_local_backend_p.h"
--- /dev/null
+#include "../../../../../src/remoteobjects/qconnection_qnx_backend_p.h"
--- /dev/null
+#include "../../../../../src/remoteobjects/qconnection_qnx_global_p.h"
--- /dev/null
+#include "../../../../../src/remoteobjects/qconnection_qnx_qiodevices_p.h"
--- /dev/null
+#include "../../../../../src/remoteobjects/qconnection_qnx_server_p.h"
--- /dev/null
+#include "../../../../../src/remoteobjects/qconnection_tcpip_backend_p.h"
--- /dev/null
+#include "../../../../../src/remoteobjects/qconnectionfactories_p.h"
--- /dev/null
+#include "../../../../../src/remoteobjects/qremoteobjectabstractitemmodeladapter_p.h"
--- /dev/null
+#include "../../../../../src/remoteobjects/qremoteobjectabstractitemmodelreplica_p.h"
--- /dev/null
+#include "../../../../../src/remoteobjects/qremoteobjectabstractitemmodeltypes_p.h"
--- /dev/null
+#include "../../../../../src/remoteobjects/qremoteobjectcontainers_p.h"
--- /dev/null
+#include "../../../../../src/remoteobjects/qremoteobjectnode_p.h"
--- /dev/null
+#include "../../../../../src/remoteobjects/qremoteobjectpacket_p.h"
--- /dev/null
+#include "../../../../../src/remoteobjects/qremoteobjectpendingcall_p.h"
--- /dev/null
+#include "../../../../../src/remoteobjects/qremoteobjectregistrysource_p.h"
--- /dev/null
+#include "../../../../../src/remoteobjects/qremoteobjectreplica_p.h"
--- /dev/null
+#include "../../../../../src/remoteobjects/qremoteobjectsource_p.h"
--- /dev/null
+#include "../../../../../src/remoteobjects/qremoteobjectsourceio_p.h"
--- /dev/null
+#include "qremoteobjectabstractitemmodelreplica.h"
--- /dev/null
+#include "qconnectionfactories.h"
--- /dev/null
+#include "qconnection_qnx_qiodevices.h"
--- /dev/null
+#include "qtremoteobjectglobal.h"
--- /dev/null
+#include "qconnection_qnx_qiodevices.h"
--- /dev/null
+#include "qconnection_qnx_server.h"
--- /dev/null
+#include "qremoteobjectnode.h"
--- /dev/null
+#include "qremoteobjectdynamicreplica.h"
--- /dev/null
+#include "qremoteobjectnode.h"
--- /dev/null
+#include "qremoteobjectnode.h"
--- /dev/null
+#include "qremoteobjectnode.h"
--- /dev/null
+#include "qremoteobjectpendingcall.h"
--- /dev/null
+#include "qremoteobjectpendingcall.h"
--- /dev/null
+#include "qremoteobjectpendingcall.h"
--- /dev/null
+#include "qremoteobjectregistry.h"
--- /dev/null
+#include "qremoteobjectnode.h"
--- /dev/null
+#include "qremoteobjectreplica.h"
--- /dev/null
+#include "qremoteobjectsettingsstore.h"
--- /dev/null
+#include "qtremoteobjectglobal.h"
--- /dev/null
+#include "qtremoteobjectglobal.h"
--- /dev/null
+#include "qtremoteobjectglobal.h"
--- /dev/null
+#include "qconnectionfactories.h"
--- /dev/null
+#include "qconnectionfactories.h"
--- /dev/null
+#include "qconnectionfactories.h"
--- /dev/null
+#include "qconnectionfactories.h"
--- /dev/null
+#include "qconnectionfactories.h"
--- /dev/null
+#ifndef QT_QTREMOTEOBJECTS_MODULE_H
+#define QT_QTREMOTEOBJECTS_MODULE_H
+#include <QtRemoteObjects/QtRemoteObjectsDepends>
+#include "qtremoteobjectglobal.h"
+#include "qconnectionfactories.h"
+#include "qremoteobjectabstractitemmodelreplica.h"
+#include "qremoteobjectdynamicreplica.h"
+#include "qremoteobjectnode.h"
+#include "qremoteobjectpendingcall.h"
+#include "qremoteobjectregistry.h"
+#include "qremoteobjectreplica.h"
+#include "qremoteobjectsettingsstore.h"
+#include "qremoteobjectsource.h"
+#include "qtremoteobjectsversion.h"
+#endif
--- /dev/null
+#include "qtremoteobjectsversion.h"
--- /dev/null
+SYNCQT.HEADER_FILES = qconnection_qnx_qiodevices.h qconnection_qnx_server.h qconnectionfactories.h qremoteobjectabstractitemmodelreplica.h qremoteobjectdynamicreplica.h qremoteobjectnode.h qremoteobjectpendingcall.h qremoteobjectregistry.h qremoteobjectreplica.h qremoteobjectsettingsstore.h qremoteobjectsource.h qtremoteobjectglobal.h
+SYNCQT.GENERATED_HEADER_FILES = QQnxNativeIo QIOQnxSource QQnxNativeServer QtROIoDeviceBase QtROServerIoDevice QConnectionAbstractServer QtROClientIoDevice QtROServerFactory QtROClientFactory QAbstractItemModelReplica QRemoteObjectDynamicReplica QRemoteObjectAbstractPersistedStore QRemoteObjectNode QRemoteObjectHostBase QRemoteObjectHost QRemoteObjectRegistryHost QRemoteObjectPendingCall QRemoteObjectPendingCallWatcher QRemoteObjectPendingReply QRemoteObjectRegistry QRemoteObjectReplica QRemoteObjectSettingsStore QRemoteObjectSourceLocationInfo QRemoteObjectSourceLocation QRemoteObjectSourceLocations QIntHash qtremoteobjectsversion.h QtRemoteObjectsVersion QtRemoteObjects
+SYNCQT.PRIVATE_HEADER_FILES = qconnection_local_backend_p.h qconnection_qnx_backend_p.h qconnection_qnx_global_p.h qconnection_qnx_qiodevices_p.h qconnection_qnx_server_p.h qconnection_tcpip_backend_p.h qconnectionfactories_p.h qremoteobjectabstractitemmodeladapter_p.h qremoteobjectabstractitemmodelreplica_p.h qremoteobjectabstractitemmodeltypes_p.h qremoteobjectcontainers_p.h qremoteobjectnode_p.h qremoteobjectpacket_p.h qremoteobjectpendingcall_p.h qremoteobjectregistrysource_p.h qremoteobjectreplica_p.h qremoteobjectsource_p.h qremoteobjectsourceio_p.h
+SYNCQT.QPA_HEADER_FILES =
+SYNCQT.CLEAN_HEADER_FILES = qconnection_qnx_qiodevices.h qconnection_qnx_server.h qconnectionfactories.h qremoteobjectabstractitemmodelreplica.h qremoteobjectdynamicreplica.h qremoteobjectnode.h qremoteobjectpendingcall.h qremoteobjectregistry.h qremoteobjectreplica.h qremoteobjectsettingsstore.h qremoteobjectsource.h qtremoteobjectglobal.h
+SYNCQT.INJECTIONS =
--- /dev/null
+#include "../../src/remoteobjects/qconnection_qnx_qiodevices.h"
--- /dev/null
+#include "../../src/remoteobjects/qconnection_qnx_server.h"
--- /dev/null
+#include "../../src/remoteobjects/qconnectionfactories.h"
--- /dev/null
+#include "../../src/remoteobjects/qremoteobjectabstractitemmodelreplica.h"
--- /dev/null
+#include "../../src/remoteobjects/qremoteobjectdynamicreplica.h"
--- /dev/null
+#include "../../src/remoteobjects/qremoteobjectnode.h"
--- /dev/null
+#include "../../src/remoteobjects/qremoteobjectpendingcall.h"
--- /dev/null
+#include "../../src/remoteobjects/qremoteobjectregistry.h"
--- /dev/null
+#include "../../src/remoteobjects/qremoteobjectreplica.h"
--- /dev/null
+#include "../../src/remoteobjects/qremoteobjectsettingsstore.h"
--- /dev/null
+#include "../../src/remoteobjects/qremoteobjectsource.h"
--- /dev/null
+#include "../../src/remoteobjects/qtremoteobjectglobal.h"
--- /dev/null
+/* This file was generated by syncqt. */
+#ifndef QT_QTREMOTEOBJECTS_VERSION_H
+#define QT_QTREMOTEOBJECTS_VERSION_H
+
+#define QTREMOTEOBJECTS_VERSION_STR "6.4.2"
+
+#define QTREMOTEOBJECTS_VERSION 0x060402
+
+#endif // QT_QTREMOTEOBJECTS_VERSION_H
--- /dev/null
+#include "../../../../../src/remoteobjectsqml/qremoteobjectsqml_p.h"
--- /dev/null
+#ifndef QT_QTREMOTEOBJECTSQML_MODULE_H
+#define QT_QTREMOTEOBJECTSQML_MODULE_H
+#include <QtRemoteObjectsQml/QtRemoteObjectsQmlDepends>
+#include "qtremoteobjectsqmlversion.h"
+#endif
--- /dev/null
+#include "qtremoteobjectsqmlversion.h"
--- /dev/null
+SYNCQT.HEADER_FILES =
+SYNCQT.GENERATED_HEADER_FILES = qtremoteobjectsqmlversion.h QtRemoteObjectsQmlVersion QtRemoteObjectsQml
+SYNCQT.PRIVATE_HEADER_FILES = qremoteobjectsqml_p.h
+SYNCQT.QPA_HEADER_FILES =
+SYNCQT.CLEAN_HEADER_FILES =
+SYNCQT.INJECTIONS =
--- /dev/null
+/* This file was generated by syncqt. */
+#ifndef QT_QTREMOTEOBJECTSQML_VERSION_H
+#define QT_QTREMOTEOBJECTSQML_VERSION_H
+
+#define QTREMOTEOBJECTSQML_VERSION_STR "6.4.2"
+
+#define QTREMOTEOBJECTSQML_VERSION 0x060402
+
+#endif // QT_QTREMOTEOBJECTSQML_VERSION_H
--- /dev/null
+#include "qregexparser.h"
--- /dev/null
+#ifndef QT_QTREPPARSER_MODULE_H
+#define QT_QTREPPARSER_MODULE_H
+#include <QtRepParser/QtRepParserDepends>
+#include "qregexparser.h"
+#include "qtrepparserversion.h"
+#endif
--- /dev/null
+#include "qtrepparserversion.h"
--- /dev/null
+SYNCQT.HEADER_FILES = qregexparser.h
+SYNCQT.GENERATED_HEADER_FILES = QRegexParser qtrepparserversion.h QtRepParserVersion QtRepParser
+SYNCQT.PRIVATE_HEADER_FILES =
+SYNCQT.QPA_HEADER_FILES =
+SYNCQT.CLEAN_HEADER_FILES = qregexparser.h
+SYNCQT.INJECTIONS =
--- /dev/null
+#include "../../src/repparser/qregexparser.h"
--- /dev/null
+/* This file was generated by syncqt. */
+#ifndef QT_QTREPPARSER_VERSION_H
+#define QT_QTREPPARSER_VERSION_H
+
+#define QTREPPARSER_VERSION_STR "6.4.2"
+
+#define QTREPPARSER_VERSION 0x060402
+
+#endif // QT_QTREPPARSER_VERSION_H
--- /dev/null
+TEMPLATE = aux
+
+prf.files = remoteobjects_repc.prf repcclient.pri repcserver.pri repcmerged.pri repccommon.pri repparser.prf
+prf.path = $$[QT_HOST_DATA]/mkspecs/features
+INSTALLS += prf
+
+# Ensure files are copied to qtbase mkspecs for non-prefixed builds
+!force_independent:if(!debug_and_release|!build_all|CONFIG(release, debug|release)) {
+ defineReplace(stripSrcDir) {
+ return($$relative_path($$1, $$_PRO_FILE_PWD_))
+ }
+ prffiles2build.input = prf.files
+ prffiles2build.output = $$[QT_HOST_DATA]/mkspecs/features/${QMAKE_FUNC_FILE_IN_stripSrcDir}
+ prffiles2build.commands = $$QMAKE_COPY ${QMAKE_FILE_IN} ${QMAKE_FILE_OUT}
+ prffiles2build.name = COPY ${QMAKE_FILE_IN}
+ prffiles2build.CONFIG = no_link target_predeps
+ QMAKE_EXTRA_COMPILERS += prffiles2build
+}
--- /dev/null
+include(repcclient.pri)
+include(repcserver.pri)
+include(repcmerged.pri)
+
+!isEmpty(QOBJECT_REP) {
+ qtPrepareLibExecTool(QMAKE_REPC, repc)
+
+ for (path, QT.remoteobjects.includes) {
+ REPC_INCLUDEPATH += -I $$shell_quote($$path)
+ }
+
+ qtPrepareLibExecTool(MOC_CREATE_JSON, moc)
+ moc_json.output = ${QMAKE_FILE_BASE}.json
+ moc_json.CONFIG = no_link moc_verify
+ moc_json.commands = $$MOC_CREATE_JSON -o ${QMAKE_FILE_BASE} ${QMAKE_FILE_NAME} --output-json
+ moc_json.depends = ${QMAKE_FILE_NAME}
+ moc_json.input = QOBJECT_REP
+ moc_json.variable_out = MOC_JSON
+
+ source2rep.input = MOC_JSON
+ source2rep.output = ${QMAKE_FILE_BASE}.rep
+ source2rep.commands = $$QMAKE_REPC $$REPC_INCLUDEPATH -o rep -i json ${QMAKE_FILE_NAME} ${QMAKE_FILE_OUT}
+ source2rep.depends = ${QMAKE_FILE_NAME} $$QT_TOOL.repc.binary
+ source2rep.CONFIG += target_predeps no_link
+ QMAKE_EXTRA_COMPILERS += moc_json source2rep
+}
--- /dev/null
+repc_type = replica
+repc_option = -o replica
+
+include(repccommon.pri)
--- /dev/null
+# Detect repc when QtRO is installed into non-Qt prefix
+cmd = $${QT.remoteobjects.bins}/repc
+contains(QMAKE_HOST.os, Windows) {
+ cmd = $$system_path($${cmd}.exe)
+}
+exists($$cmd): QT_TOOL.repc.binary = $$cmd
+
+# qtPrepareLibExecTool honors QT_TOOL.repc.binary if set
+qtPrepareLibExecTool(QMAKE_REPC, repc)
+
+REPC_INCLUDEPATHES = $$QT.remoteobjects.includes
+for (path, REPC_INCLUDEPATHES) {
+ REPC_INCLUDEPATH += -I $$path
+}
+
+isEmpty(QMAKE_MOD_REPC):QMAKE_MOD_REPC = rep_
+
+repc_TYPE = $$upper($$repc_type)
+
+load(moc)
+
+groups =
+for(entry, REPC_$$repc_TYPE) {
+ files = $$eval($${entry}.files)
+ isEmpty(files) {
+ files = $$entry
+ group = repc_$${repc_type}
+ } else {
+ group = $${entry}_repc_$${repc_type}
+ }
+ groups *= $$group
+
+ input_list = $$upper($$group)_LIST
+ json_list = $$upper($$group)_JSONLIST
+ for(subent, $$list($$unique(files))) {
+ if (contains(subent, .*\\.h$)|contains(subent, .*\\.hpp$)) {
+ $$json_list += $$subent
+ } else {
+ $$input_list += $$subent
+ }
+
+ # Add directory of *.rep file to include path
+ file_path = $$_PRO_FILE_PWD_/$$subent
+ INCLUDEPATH *= $$dirname(file_path)
+ }
+}
+
+for(group, groups) {
+ GROUP = $$upper($$group)
+ input_list = $${GROUP}_LIST
+ json_list = $${GROUP}_JSONLIST
+
+ qtPrepareLibExecTool(MOC_CREATE_JSON, moc)
+ $${group}_moc_json.output = ${QMAKE_FILE_BASE}.json
+ $${group}_moc_json.CONFIG = no_link moc_verify
+ $${group}_moc_json.commands = $$MOC_CREATE_JSON -o ${QMAKE_FILE_BASE} ${QMAKE_FILE_NAME} --output-json
+ $${group}_moc_json.depends = ${QMAKE_FILE_NAME}
+ $${group}_moc_json.input = $$json_list
+ $${group}_moc_json.variable_out = MOC_JSON
+
+ $${group}_header.output = $$QMAKE_MOD_REPC${QMAKE_FILE_BASE}_$${repc_type}.h
+ $${group}_header.commands = $$QMAKE_REPC $$repc_option $$REPC_INCLUDEPATH ${QMAKE_FILE_NAME} ${QMAKE_FILE_OUT}
+ $${group}_header.depends = ${QMAKE_FILE_NAME} $$QT_TOOL.repc.binary
+ $${group}_header.variable_out = $${GROUP}_HEADERS
+ $${group}_header.input = $$input_list MOC_JSON
+
+ $${group}_moc.commands = $$moc_header.commands $$REPC_INCLUDEPATH
+ $${group}_moc.output = $$moc_header.output
+ $${group}_moc.input = $${GROUP}_HEADERS
+ $${group}_moc.variable_out = GENERATED_SOURCES
+ !contains(TEMPLATE, vc.*): \
+ $${group}_moc.name = $$moc_header.name
+
+ QMAKE_EXTRA_COMPILERS += $${group}_moc_json $${group}_header $${group}_moc
+}
--- /dev/null
+repc_type = merged
+repc_option = -o merged
+
+include(repccommon.pri)
--- /dev/null
+repc_type = source
+repc_option = -o source
+
+include(repccommon.pri)
--- /dev/null
+CONFIG += qlalr
+INCLUDEPATH *= $$QT.repparser.includes
+
+for (include, INCLUDEPATH) {
+ exists($${include}/parser.g) {
+ msvc: QMAKE_CXXFLAGS += /wd4129
+ QLALRSOURCES += $${include}/parser.g
+ }
+}
--- /dev/null
+TEMPLATE = subdirs
+
+SUBDIRS += features
--- /dev/null
+
+add_subdirectory(repparser)
+if(QT_FEATURE_localserver)
+ add_subdirectory(remoteobjects)
+endif()
+if(TARGET Qt::Quick)
+ add_subdirectory(remoteobjectsqml)
+endif()
--- /dev/null
+
+#####################################################################
+## RemoteObjects Module:
+#####################################################################
+
+qt_internal_add_module(RemoteObjects
+ QMAKE_MODULE_CONFIG remoteobjects_repc
+ SOURCES
+ qconnection_local_backend.cpp qconnection_local_backend_p.h
+ qconnection_tcpip_backend.cpp qconnection_tcpip_backend_p.h
+ qconnectionfactories.cpp qconnectionfactories.h qconnectionfactories_p.h
+ qremoteobjectabstractitemmodeladapter.cpp qremoteobjectabstractitemmodeladapter_p.h
+ qremoteobjectabstractitemmodelreplica.cpp qremoteobjectabstractitemmodelreplica.h qremoteobjectabstractitemmodelreplica_p.h
+ qremoteobjectabstractitemmodeltypes_p.h
+ qremoteobjectcontainers.cpp qremoteobjectcontainers_p.h
+ qremoteobjectdynamicreplica.cpp qremoteobjectdynamicreplica.h
+ qremoteobjectnode.cpp qremoteobjectnode.h qremoteobjectnode_p.h
+ qremoteobjectpacket.cpp qremoteobjectpacket_p.h
+ qremoteobjectpendingcall.cpp qremoteobjectpendingcall.h qremoteobjectpendingcall_p.h
+ qremoteobjectregistry.cpp qremoteobjectregistry.h
+ qremoteobjectregistrysource.cpp qremoteobjectregistrysource_p.h
+ qremoteobjectreplica.cpp qremoteobjectreplica.h qremoteobjectreplica_p.h
+ qremoteobjectsettingsstore.cpp qremoteobjectsettingsstore.h
+ qremoteobjectsource.cpp qremoteobjectsource.h qremoteobjectsource_p.h
+ qremoteobjectsourceio.cpp qremoteobjectsourceio_p.h
+ qtremoteobjectglobal.cpp qtremoteobjectglobal.h
+ DEFINES
+ QT_BUILD_REMOTEOBJECTS_LIB
+ QT_NO_CAST_FROM_ASCII
+ QT_NO_CAST_FROM_BYTEARRAY
+ QT_NO_CAST_TO_ASCII
+ QT_NO_URL_CAST_FROM_STRING
+ INCLUDE_DIRECTORIES
+ .
+ LIBRARIES
+ Qt::CorePrivate
+ PUBLIC_LIBRARIES
+ Qt::Core
+ Qt::Network
+ PRIVATE_MODULE_INTERFACE
+ Qt::CorePrivate
+ GENERATE_CPP_EXPORTS
+)
+
+#### Keys ignored in scope 1:.:.:remoteobjects.pro:<TRUE>:
+# MODULE = "remoteobjects"
+# MODULE_CONFIG = "remoteobjects_repc"
+# OTHER_FILES = "doc/qtremoteobjects.qdocconf" "doc/src/remoteobjects-cpp.qdoc" "doc/src/remoteobjects-index.qdoc" "doc/src/remoteobjects-overview.qdoc" "doc/src/remoteobjects-repc.qdoc"
+
+## Scopes:
+#####################################################################
+
+qt_internal_extend_target(RemoteObjects CONDITION QNX
+ SOURCES
+ qconnection_qnx_backend.cpp qconnection_qnx_backend_p.h
+ qconnection_qnx_global_p.h
+ qconnection_qnx_qiodevices.cpp qconnection_qnx_qiodevices.h qconnection_qnx_qiodevices_p.h
+ qconnection_qnx_server.cpp qconnection_qnx_server.h qconnection_qnx_server_p.h
+)
+
+qt_internal_extend_target(RemoteObjects CONDITION QNX AND QT_FEATURE_use_ham
+ PUBLIC_LIBRARIES
+ ham
+)
+qt_internal_add_docs(RemoteObjects
+ doc/qtremoteobjects.qdocconf
+)
+
+include(Qt6RemoteObjectsMacros.cmake)
--- /dev/null
+# Copyright (C) 2015 Ford Motor Company
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+ add_executable(Qt5::repc IMPORTED)
+
+!!IF isEmpty(CMAKE_BIN_DIR_IS_ABSOLUTE)
+ set(imported_location \"${_qt5RemoteObjects_install_prefix}/$${CMAKE_BIN_DIR}repc$$CMAKE_BIN_SUFFIX\")
+!!ELSE
+ set(imported_location \"$${CMAKE_BIN_DIR}repc$$CMAKE_BIN_SUFFIX\")
+!!ENDIF
+ _qt5_RemoteObjects_check_file_exists(${imported_location})
+
+ set_target_properties(Qt5::repc PROPERTIES
+ IMPORTED_LOCATION ${imported_location}
+ )
+ get_target_property(Qt5RemoteObjects_REPC_EXECUTABLE Qt5::repc LOCATION)
+endif()
+
+# Create versionless tool targets.
+foreach(__qt_tool repc)
+ if(NOT \"${QT_NO_CREATE_VERSIONLESS_TARGETS}\" AND NOT TARGET Qt::${__qt_tool}
+ AND TARGET Qt5::${__qt_tool})
+ add_executable(Qt::${__qt_tool} IMPORTED)
+ get_target_property(__qt_imported_location Qt5::${__qt_tool} IMPORTED_LOCATION)
+ set_target_properties(Qt::${__qt_tool}
+ PROPERTIES IMPORTED_LOCATION \"${__qt_imported_location}\")
+ endif()
+endforeach()
--- /dev/null
+# Copyright (C) 2019 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+# framework style includes like #include <QtCore/qobject.h>
+# Extract the location of the Qt frameworks by querying the imported location of QtRemoteObjects
+# framework parent directory.
+function(_qt_internal_get_remote_objects_framework_path out_var)
+ set(value "")
+ if(APPLE AND QT_FEATURE_framework)
+ get_target_property(ro_path ${QT_CMAKE_EXPORT_NAMESPACE}::RemoteObjects IMPORTED_LOCATION)
+ string(REGEX REPLACE "(.*)/Qt[^/]+\\.framework.*" "\\1" ro_fw_path "${ro_path}")
+ if(ro_fw_path)
+ set(value "${ro_fw_path}")
+ endif()
+ endif()
+ set(${out_var} "${value}" PARENT_SCOPE)
+endfunction()
+
+function(_qt_internal_get_remote_objects_framework_path_moc_options out_var)
+ _qt_internal_get_remote_objects_framework_path(ro_fw_path)
+ if(ro_fw_path)
+ set(${out_var} "OPTIONS" "-F${ro_fw_path}" PARENT_SCOPE)
+ else()
+ set(${out_var} "" PARENT_SCOPE)
+ endif()
+endfunction()
+
+function(_qt_internal_add_repc_files type target)
+ set(options)
+ set(oneValueArgs)
+ set(multiValueArgs SOURCES)
+
+ cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ set(outfiles)
+ if(QT_REPC_DEBUG_MODE)
+ set(debug "-d")
+ else()
+ set(debug "")
+ endif()
+ set(repc_incpath) ########### TODO
+
+ _qt_internal_get_remote_objects_framework_path_moc_options(extra_moc_options)
+ _qt_internal_get_tool_wrapper_script_path(tool_wrapper)
+ set(repc_command
+ COMMAND
+ "${tool_wrapper}"
+ "$<TARGET_FILE:${QT_CMAKE_EXPORT_NAMESPACE}::repc>"
+ )
+ foreach(it ${ARGS_SOURCES})
+ get_filename_component(outfilename ${it} NAME_WE)
+ get_filename_component(extension ${it} EXT)
+ if ("${extension}" STREQUAL ".h" OR "${extension}" STREQUAL ".hpp")
+ # This calls moc on an existing header file to extract metatypes json information
+ # which is then passed to the tool to generate another header.
+ qt6_wrap_cpp(qtro_moc_files "${it}"
+ __QT_INTERNAL_OUTPUT_MOC_JSON_FILES json_list
+ TARGET "${target}"
+ ${extra_moc_options})
+
+ # Pass the generated metatypes .json file to the tool.
+ set(infile ${json_list})
+ set_source_files_properties(${qtro_moc_files} PROPERTIES HEADER_FILE_ONLY ON)
+ list(APPEND outfiles ${qtro_moc_files})
+ else()
+ # Pass the .rep file to the tool.
+ get_filename_component(infile ${it} ABSOLUTE)
+ endif()
+ set(outfile ${CMAKE_CURRENT_BINARY_DIR}/rep_${outfilename}_${type}.h)
+
+ add_custom_command(
+ OUTPUT ${outfile}
+ ${repc_command} ${debug} -o ${type} ${repc_incpath} ${infile} ${outfile}
+ MAIN_DEPENDENCY ${infile}
+ DEPENDS ${QT_CMAKE_EXPORT_NAMESPACE}::repc
+ VERBATIM
+ )
+ list(APPEND outfiles ${outfile})
+
+ # The generated header file needs to be manually moc'ed (without using AUTOMOC) and then
+ # added as source to compile into the target.
+ qt6_wrap_cpp(qtro_moc_files "${outfile}" TARGET "${target}" ${extra_moc_options})
+ set_source_files_properties("${outfile}" PROPERTIES
+ GENERATED TRUE
+ SKIP_AUTOGEN ON)
+ list(APPEND outfiles ${qtro_moc_files})
+ endforeach()
+ target_sources(${target} PRIVATE ${outfiles})
+endfunction()
+
+# Add .rep source files to a target to generate source header files
+function(qt6_add_repc_sources target)
+ list(POP_FRONT ARGV)
+ _qt_internal_add_repc_files(source ${target} SOURCES ${ARGV})
+endfunction()
+
+# Add .rep source files to a target to generate replica header files
+function(qt6_add_repc_replicas target)
+ list(POP_FRONT ARGV)
+ _qt_internal_add_repc_files(replica ${target} SOURCES ${ARGV})
+endfunction()
+
+# Add .rep source files to a target to generate combined (source and replica) header files
+function(qt6_add_repc_merged target)
+ list(POP_FRONT ARGV)
+ _qt_internal_add_repc_files(merged ${target} SOURCES ${ARGV})
+endfunction()
+
+# Create .rep interface file from QObject header
+function(qt6_reps_from_headers target)
+ list(POP_FRONT ARGV)
+ _qt_internal_get_remote_objects_framework_path_moc_options(extra_moc_options)
+ _qt_internal_get_tool_wrapper_script_path(tool_wrapper)
+ set(repc_command
+ COMMAND
+ "${tool_wrapper}"
+ "$<TARGET_FILE:${QT_CMAKE_EXPORT_NAMESPACE}::repc>"
+ )
+
+ foreach(it ${ARGV})
+ get_filename_component(outfilename ${it} NAME_WE)
+ # This calls moc on an existing header file to extract metatypes json information
+ # which is then passed to the tool to generate a .rep file.
+ qt6_wrap_cpp(qtro_moc_files "${it}"
+ __QT_INTERNAL_OUTPUT_MOC_JSON_FILES json_list
+ TARGET "${target}"
+ ${extra_moc_options})
+ set(infile ${json_list})
+ set_source_files_properties(${qtro_moc_files} PROPERTIES HEADER_FILE_ONLY ON)
+ list(APPEND outfiles ${qtro_moc_files})
+ set(outfile ${CMAKE_CURRENT_BINARY_DIR}/${outfilename}.rep)
+ add_custom_command(OUTPUT ${outfile}
+ ${repc_command} -o rep ${infile} ${outfile}
+ MAIN_DEPENDENCY ${infile}
+ DEPENDS ${QT_CMAKE_EXPORT_NAMESPACE}::repc
+ VERBATIM)
+ list(APPEND outfiles ${outfile})
+ endforeach()
+ target_sources(${target} PRIVATE ${outfiles})
+endfunction()
+
+if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS)
+ function(qt_add_repc_sources)
+ if(QT_DEFAULT_MAJOR_VERSION EQUAL 6)
+ qt6_add_repc_sources(${ARGN})
+ else()
+ message(FATAL_ERROR "qt_add_repc_sources() is only available in Qt 6. "
+ "Please check the repc documentation for alternatives.")
+ endif()
+ endfunction()
+ function(qt_add_repc_replicas)
+ if(QT_DEFAULT_MAJOR_VERSION EQUAL 6)
+ qt6_add_repc_replicas(${ARGN})
+ else()
+ message(FATAL_ERROR "qt_add_repc_replicas() is only available in Qt 6. "
+ "Please check the repc documentation for alternatives.")
+ endif()
+ endfunction()
+ function(qt_add_repc_merged)
+ if(QT_DEFAULT_MAJOR_VERSION EQUAL 6)
+ qt6_add_repc_merged(${ARGN})
+ else()
+ message(FATAL_ERROR "qt_add_repc_merged() is only available in Qt 6. "
+ "Please check the repc documentation for alternatives.")
+ endif()
+ endfunction()
+ function(qt_reps_from_headers)
+ if(QT_DEFAULT_MAJOR_VERSION EQUAL 6)
+ qt6_reps_from_headers(${ARGN})
+ else()
+ message(FATAL_ERROR "qt_reps_from_headers() is only available in Qt 6. "
+ "Please check the repc documentation for alternatives.")
+ endif()
+ endfunction()
+endif()
--- /dev/null
+
+
+#### Inputs
+
+
+
+#### Libraries
+
+
+
+#### Tests
+
+
+
+#### Features
+
+qt_feature("use_ham" PUBLIC
+ LABEL "High Availability Manager (ham)"
+ PURPOSE "Use QNX's High Availability Manager (ham) library"
+ AUTODETECT OFF
+ CONDITION QNX
+)
+qt_feature_definition("use_ham" "QT_NO_USE_HAM" NEGATE VALUE "1")
+qt_configure_add_summary_section(NAME "Qt Remote Objects")
+qt_configure_add_summary_entry(ARGS "use_ham")
+qt_configure_end_summary_section() # end of "Qt Remote Objects" section
--- /dev/null
+Put all documentation images into this folder.
--- /dev/null
+include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf)
+include($QT_INSTALL_DOCS/config/exampleurl-qtremoteobjects.qdocconf)
+
+project = QtRemoteObjects
+description = Qt Remote Objects Reference Documentation
+version = $QT_VERSION
+
+qhp.projects = QtRemoteObjects
+
+qhp.QtRemoteObjects.file = qtremoteobjects.qhp
+qhp.QtRemoteObjects.namespace = org.qt-project.qtremoteobjects.$QT_VERSION_TAG
+qhp.QtRemoteObjects.virtualFolder = remoteobjects
+qhp.QtRemoteObjects.indexTitle = Qt Remote Objects
+qhp.QtRemoteObjects.indexRoot =
+
+qhp.QtRemoteObjects.subprojects = overviews classes repc
+qhp.QtRemoteObjects.subprojects.overviews.title = Getting Started
+qhp.QtRemoteObjects.subprojects.overviews.indexTitle = Getting Started with Qt Remote Objects
+qhp.QtRemoteObjects.subprojects.overviews.selectors = fake:page,group,module
+qhp.QtRemoteObjects.subprojects.classes.title = C++ Classes
+qhp.QtRemoteObjects.subprojects.classes.indexTitle = Qt Remote Objects C++ Classes
+qhp.QtRemoteObjects.subprojects.classes.selectors = class fake:headerfile
+qhp.QtRemoteObjects.subprojects.classes.sortPages = true
+
+depends += qtcore \
+ qtnetwork \
+ qtdoc \
+ qtcmake \
+ qmake
+
+tagfile = ../../../doc/qtremoteobjects/qtremoteobjects.tags
+
+headerdirs += .. \
+ ../../remoteobjects
+
+sourcedirs += .. \
+ ../../remoteobjects
+
+exampledirs += ../../../examples/remoteobjects \
+ snippets/
+
+#add thumbnail images for the example docs that does not have an image.
+manifestmeta.thumbnail.names += "QtRemoteObjects/Model*" \
+ "QtRemoteObjects/QML*" \
+ "QtRemoteObjects/QtRemote*"
+
+
+examplesinstallpath = remoteobjects
+
+imagedirs += images
+
+navigation.landingpage = "Qt Remote Objects"
+navigation.cppclassespage = "Qt Remote Objects C++ Classes"
+navigation.qmltypespage = "Qt Remote Objects QML Types"
--- /dev/null
+Put all snipplets into this folder.
--- /dev/null
+cmake_minimum_required(VERSION 3.16)
+
+project(directconnectserver)
+
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTORCC ON)
+set(CMAKE_AUTOUIC ON)
+
+if(CMAKE_VERSION VERSION_LESS "3.7.0")
+ set(CMAKE_INCLUDE_CURRENT_DIR ON)
+endif()
+
+#! [remote_objects_cmake]
+find_package(Qt6 REQUIRED COMPONENTS RemoteObjects)
+#! [remote_objects_cmake]
+
+set(SOURCES
+ main.cpp
+ simpleswitch.cpp
+)
+
+#! [simpleSwitch_cmake_add_repc_source]
+qt6_add_repc_sources(directconnectserver
+ simpleswitch.rep
+)
+#! [simpleSwitch_cmake_add_repc_source]
+
+#! [simpleSwitch_cmake_add_repc_replica]
+qt6_add_repc_replicas(directconnectclient
+ simpleswitch.rep
+)
+#! [simpleSwitch_cmake_add_repc_replica]
+
+#! [simpleSwitch_cmake_add_repc_merged]
+qt6_add_repc_merged(directconnectexample
+ simpleswitch.rep
+)
+#! [simpleSwitch_cmake_add_repc_merged]
+
+add_executable(directconnectserver ${SOURCES})
+#! [remote_objects_cmake_link]
+target_link_libraries(directconnectserver PRIVATE Qt6::RemoteObjects)
+#! [remote_objects_cmake_link]
+
+#! [simpleSwitch_cmake_rep_from_header]
+qt6_reps_from_headers(directconnectexample
+ simpleswitch.h
+)
+#! [simpleSwitch_cmake_rep_from_header]
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QCoreApplication>
+#include "simpleswitch.h"
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication a(argc, argv);
+
+ SimpleSwitch srcSwitch; // create simple switch
+
+ QRemoteObjectHost srcNode(QUrl(QStringLiteral("local:replica"))); // create host node without Registry
+ srcNode.enableRemoting(&srcSwitch); // enable remoting/Sharing
+
+ return a.exec();
+}
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "simpleswitch.h"
+
+// constructor
+SimpleSwitch::SimpleSwitch(QObject *parent) : SimpleSwitchSimpleSource(parent)
+{
+ stateChangeTimer = new QTimer(this); // Initialize timer
+ QObject::connect(stateChangeTimer, &SimpleSwitch::timeout, this, &SimpleSwitch::timeout_slot); // connect timeout() signal from stateChangeTimer to timeout_slot() of simpleSwitch
+ stateChangeTimer->start(2000); // Start timer and set timout to 2 seconds
+ qDebug() << "Source Node Started";
+}
+
+//destructor
+SimpleSwitch::~SimpleSwitch()
+{
+ stateChangeTimer->stop();
+}
+
+void SimpleSwitch::server_slot(bool clientState)
+{
+ qDebug() << "Replica state is " << clientState; // print switch state echoed back by client
+}
+
+void SimpleSwitch::timeout_slot(void)
+{
+ // slot called on timer timeout
+ if (currState()) // check if current state is true, currState() is defined in repc generated rep_simpleswitch_source.h
+ setCurrState(false); // set state to false
+ else
+ setCurrState(true); // set state to true
+ qDebug() << "Source State is "<<currState();
+
+}
+
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef SIMPLESWITCH_H
+#define SIMPLESWITCH_H
+
+#include "rep_simpleswitch_source.h"
+
+class SimpleSwitch : public SimpleSwitchSimpleSource
+{
+ Q_OBJECT
+public:
+ SimpleSwitch(QObject *parent = nullptr);
+ ~SimpleSwitch() override;
+ void server_slot(bool clientState) override;
+public Q_SLOTS:
+ void timeout_slot();
+private:
+ QTimer *stateChangeTimer;
+};
+
+#endif
--- /dev/null
+#include <QtCore>
+
+class SimpleSwitch
+{
+ PROP(bool currState=false);
+ SLOT(void server_slot(bool clientState));
+};
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+//! [qtremoteobject_include]
+#include <QtRemoteObjects>
+//! [qtremoteobject_include]
+
+//! [implementing_source]
+#include "rep_TimeModel_source.h"
+
+class MinuteTimer : public MinuteTimerSource
+{
+Q_OBJECT
+public:
+ MinuteTimer(QObject *parent = nullptr);
+ virtual ~MinuteTimer();
+
+public slots:
+ virtual void SetTimeZone(int zn) { //this is a pure virtual method in MinuteTimerSource
+ qDebug()<<"SetTimeZone"<<zn;
+ if (zn != zone) {
+ zone = zn;
+ }
+ };
+
+protected:
+ void timerEvent(QTimerEvent *);
+
+private:
+ QTime time;
+ QBasicTimer timer;
+ int zone;
+};
+//! [implementing_source]
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+//! [simpleSwitch_rep]
+class SimpleSwitch
+{
+ PROP(bool currState=false);
+ SLOT(server_slot(bool clientState));
+};
+//! [simpleSwitch_rep]
+
+//! [simpleSwitch_repsource_example1]
+REPC_SOURCE = simpleswitch.rep
+//! [simpleSwitch_repsource_example1]
+
+//! [simpleSwitch_remoteobjectsadd_example1]
+QT += remoteobjects
+//! [simpleSwitch_remoteobjectsadd_example1]
+
+//! [simpleSwitch_serverheader_example1]
+#ifndef SIMPLESWITCH_H
+#define SIMPLESWITCH_H
+
+#include "rep_simpleswitch_source.h"
+
+class SimpleSwitch : public SimpleSwitchSimpleSource
+{
+ Q_OBJECT
+public:
+ SimpleSwitch(QObject *parent = nullptr);
+ ~SimpleSwitch();
+ virtual void server_slot(bool clientState);
+public Q_SLOTS:
+ void timeout_slot();
+private:
+ QTimer *stateChangeTimer;
+};
+
+#endif
+//! [simpleSwitch_serverheader_example1]
+
+//! [simpleSwitch_serversource_example1]
+#include "simpleswitch.h"
+
+// constructor
+SimpleSwitch::SimpleSwitch(QObject *parent) : SimpleSwitchSimpleSource(parent)
+{
+ stateChangeTimer = new QTimer(this); // Initialize timer
+ QObject::connect(stateChangeTimer, &SimpleSwitch::timeout, this, &SimpleSwitch::timeout_slot); // connect timeout() signal from stateChangeTimer to timeout_slot() of simpleSwitch
+ stateChangeTimer->start(2000); // Start timer and set timout to 2 seconds
+ qDebug() << "Source Node Started";
+}
+
+//destructor
+SimpleSwitch::~SimpleSwitch()
+{
+ stateChangeTimer->stop();
+}
+
+void SimpleSwitch::server_slot(bool clientState)
+{
+ qDebug() << "Replica state is " << clientState; // print switch state echoed back by client
+}
+
+void SimpleSwitch::timeout_slot()
+{
+ // slot called on timer timeout
+ if (currState()) // check if current state is true, currState() is defined in repc generated rep_simpleswitch_source.h
+ setCurrState(false); // set state to false
+ else
+ setCurrState(true); // set state to true
+ qDebug() << "Source State is "<<currState();
+
+}
+//! [simpleSwitch_serversource_example1]
+
+//! [simpleSwitch_serverhostnode_example1]
+QRemoteObjectHost srcNode(QUrl(QStringLiteral("local:switch")));
+//! [simpleSwitch_serverhostnode_example1]
+
+//! [simpleSwitch_enableremoting_example1]
+SimpleSwitch srcSwitch; // create simple switch
+srcNode.enableRemoting(&srcSwitch); // enable remoting
+//! [simpleSwitch_enableremoting_example1]
+
+//! [simpleSwitch_servermaincpp_example1]
+#include <QCoreApplication>
+#include "simpleswitch.h"
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication a(argc, argv);
+
+ SimpleSwitch srcSwitch; // create simple switch
+
+ QRemoteObjectHost srcNode(QUrl(QStringLiteral("local:switch"))); // create host node without Registry
+ srcNode.enableRemoting(&srcSwitch); // enable remoting/sharing
+
+ return a.exec();
+}
+//! [simpleSwitch_servermaincpp_example1]
+
+//! [simpleSwitch_clientrep_example1]
+REPC_REPLICA = simpleswitch.rep
+//! [simpleSwitch_clientrep_example1]
+
+//! [simpleSwitch_clientremotenode_example1]
+QRemoteObjectNode repNode; // create remote object node
+repNode.connectToNode(QUrl(QStringLiteral("local:switch"))); // connect with remote host node
+ //! [simpleSwitch_clientremotenode_example1]
+
+ //! [simpleSwitch_clientacquirereplica_example1]
+QSharedPointer<SimpleSwitchReplica> ptr;
+ptr.reset(repNode.acquire<SimpleSwitchReplica>()); // acquire replica of source from host node
+//! [simpleSwitch_clientacquirereplica_example1]
+
+//! [simpleSwitch_clientheader_example1]
+#ifndef _CLIENT_H
+#define _CLIENT_H
+
+#include <QObject>
+#include <QSharedPointer>
+
+#include "rep_simpleswitch_replica.h"
+
+class Client : public QObject
+{
+ Q_OBJECT
+public:
+ Client(QSharedPointer<SimpleSwitchReplica> ptr);
+ ~Client();
+ void initConnections();// Function to connect signals and slots of source and client
+
+Q_SIGNALS:
+ void echoSwitchState(bool switchState);// this signal is connected with server_slot(..) on the source object and echoes back switch state received from source
+
+public Q_SLOTS:
+ void recSwitchState_slot(); // slot to receive source state
+private:
+ bool clientSwitchState; // holds received server switch state
+ QSharedPointer<SimpleSwitchReplica> reptr;// holds reference to replica
+
+ };
+
+#endif
+//! [simpleSwitch_clientheader_example1]
+
+//! [simpleSwitch_clientcpp_example1]
+#include "client.h"
+
+// constructor
+Client::Client(QSharedPointer<SimpleSwitchReplica> ptr) :
+ QObject(nullptr),reptr(ptr)
+{
+ initConnections();
+ //We can connect to SimpleSwitchReplica Signals/Slots
+ //directly because our Replica was generated by repc.
+}
+
+//destructor
+Client::~Client()
+{
+}
+
+void Client::initConnections()
+{
+ // initialize connections between signals and slots
+
+ // connect source replica signal currStateChanged() with client's recSwitchState() slot to receive source's current state
+ QObject::connect(reptr.data(), &SimpleSwitchReplica::currStateChanged, this, &Client::recSwitchState_slot);
+ // connect client's echoSwitchState(..) signal with replica's server_slot(..) to echo back received state
+ QObject::connect(this, &Client::echoSwitchState, reptr.data(), &SimpleSwitchReplica::server_slot);
+}
+
+void Client::recSwitchState_slot(bool value)
+{
+ qDebug() << "Received source state "<< value << reptr.data()->currState();
+ clientSwitchState = reptr.data()->currState();
+ Q_EMIT echoSwitchState(clientSwitchState); // Emit signal to echo received state back to server
+}
+//! [simpleSwitch_clientcpp_example1]
+
+//! [simpleSwitch_clientmain_example1]
+#include <QCoreApplication>
+#include "client.h"
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication a(argc, argv);
+
+
+ QSharedPointer<SimpleSwitchReplica> ptr; // shared pointer to hold source replica
+
+ QRemoteObjectNode repNode; // create remote object node
+ repNode.connectToNode(QUrl(QStringLiteral("local:switch"))); // connect with remote host node
+
+ ptr.reset(repNode.acquire<SimpleSwitchReplica>()); // acquire replica of source from host node
+
+ Client rswitch(ptr); // create client switch object and pass reference of replica to it
+
+ return a.exec();
+}
+//! [simpleSwitch_clientmain_example1]
+
+//! [simpleSwitch_dynamicclientnode_example2]
+QRemoteObjectNode repNode; // create remote object node
+repNode.connectToNode(QUrl(QStringLiteral("local:switch"))); // connect with remote host node
+//! [simpleSwitch_dynamicclientnode_example2]
+
+//! [simpleSwitch_dynamicclientacquirereplica_example2]
+QSharedPointer<QRemoteObjectDynamicReplica> ptr; // shared pointer to hold replica
+ptr.reset(repNode.acquire("SimpleSwitch")); // acquire replica of source from host node
+//! [simpleSwitch_dynamicclientacquirereplica_example2]
+
+//! [simpleSwitch_dynamicclientheader_example2]
+#ifndef _DYNAMICCLIENT_H
+#define _DYNAMICCLIENT_H
+
+#include <QObject>
+#include <QSharedPointer>
+
+#include <QRemoteObjectNode>
+#include <qremoteobjectdynamicreplica.h>
+
+class DynamicClient : public QObject
+{
+ Q_OBJECT
+public:
+ DynamicClient(QSharedPointer<QRemoteObjectDynamicReplica> ptr);
+ ~DynamicClient();
+
+Q_SIGNALS:
+ void echoSwitchState(bool switchState);// this signal is connected with server_slot(..) slot of source object and echoes back switch state received from source
+
+public Q_SLOTS:
+ void recSwitchState_slot(); // Slot to receive source state
+ void initConnection_slot(); //Slot to connect signals/slot on replica initialization
+
+private:
+ bool clientSwitchState; // holds received server switch state
+ QSharedPointer<QRemoteObjectDynamicReplica> reptr;// holds reference to replica
+ };
+
+#endif
+//! [simpleSwitch_dynamicclientheader_example2]
+
+//! [simpleSwitch_dynamicclientcpp_example2]
+#include "dynamicclient.h"
+
+// constructor
+DynamicClient::DynamicClient(QSharedPointer<QRemoteObjectDynamicReplica> ptr) :
+ QObject(nullptr), reptr(ptr)
+{
+
+ //connect signal for replica valid changed with signal slot initialization
+ QObject::connect(reptr.data(), &QRemoteObjectDynamicReplica::initialized, this,
+ &DynamicClient::initConnection_slot);
+}
+
+//destructor
+DynamicClient::~DynamicClient()
+{
+}
+
+// Function to initialize connections between slots and signals
+void DynamicClient::initConnection_slot()
+{
+
+ // connect source replica signal currStateChanged() with client's recSwitchState() slot to receive source's current state
+ QObject::connect(reptr.data(), SIGNAL(currStateChanged()), this, SLOT(recSwitchState_slot()));
+ // connect client's echoSwitchState(..) signal with replica's server_slot(..) to echo back received state
+ QObject::connect(this, SIGNAL(echoSwitchState(bool)),reptr.data(), SLOT(server_slot(bool)));
+}
+
+void DynamicClient::recSwitchState_slot()
+{
+ clientSwitchState = reptr->property("currState").toBool(); // use replica property to get currState from source
+ qDebug() << "Received source state " << clientSwitchState;
+ Q_EMIT echoSwitchState(clientSwitchState); // Emit signal to echo received state back to server
+}
+
+//! [simpleSwitch_dynamicclientcpp_example2]
+
+//! [simpleSwitch_dynamicclientmaincpp_example2]
+#include <QCoreApplication>
+
+#include "dynamicclient.h"
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication a(argc, argv);
+
+ QSharedPointer<QRemoteObjectDynamicReplica> ptr; // shared pointer to hold replica
+
+ QRemoteObjectNode repNode;
+ repNode.connectToNode(QUrl(QStringLiteral("local:switch")));
+
+ ptr.reset(repNode.acquireDynamic("SimpleSwitch")); // acquire replica of source from host node
+
+ DynamicClient rswitch(ptr); // create client switch object and pass replica reference to it
+}
+//! [simpleSwitch_dynamicclientmaincpp_example2]
+
+//! [simpleSwitch_registrymaincpp_example3]
+#include <QCoreApplication>
+#include "simpleswitch.h"
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication a(argc, argv);
+
+ SimpleSwitch srcSwitch; // create SimpleSwitch
+
+ QRemoteObjectRegistryHost regNode(QUrl(QStringLiteral("local:registry"))); // create node that hosts registry
+ QRemoteObjectHost srcNode(QUrl(QStringLiteral("local:switch")), QUrl(QStringLiteral("local:registry"))); // create node that will host source and connect to registry
+ //Note, you can add srcSwitch directly to regNode if desired.
+ //We use two Nodes here, as the regNode could easily be in a third process.
+
+ srcNode.enableRemoting(&srcSwitch); // enable remoting of source object
+
+ return a.exec();
+}
+//! [simpleSwitch_registrymaincpp_example3]
+
+//! [simpleSwitch_registrydynamicclientmaincpp_example3]
+ QRemoteObjectNode repNode(QUrl(QStringLiteral("local:registry")));
+//! [simpleSwitch_registrydynamicclientmaincpp_example3]
--- /dev/null
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qtremoteobjects-cmake-qt-add-repc-replicas.html
+\ingroup cmake-macros-qtremoteobjects
+
+\title qt_add_repc_replicas
+\target qt6_add_repc_replicas
+
+\brief Creates C++ header files for replica types from the Qt Remote Objects .rep files.
+
+\cmakecommandsince 6.2
+
+\section1 Synopsis
+
+\badcode
+qt_add_repc_replicas(<TARGET> rep_files)
+
+qt6_add_repc_replicas(<TARGET> rep_files)
+\endcode
+
+\section1 Description
+
+Runs \l repc and generates \l {Qt Remote Objects Replica} header files based
+on the given \c rep_files. Adds the generated files to \c{<TARGET>}.
+
+\section1 Examples
+
+\snippet cmake-macros/CMakeLists.txt simpleSwitch_cmake_add_repc_replica
+
+The generated file(s) will be of the form \c {rep_<replica file name>_replica.h},
+in this case \c rep_simpleswitch_replica.h
+*/
+
+/*!
+\page qtremoteobjects-cmake-qt-add-repc-sources.html
+\ingroup cmake-macros-qtremoteobjects
+
+\title qt_add_repc_sources
+\target qt6_add_repc_sources
+
+\brief Creates C++ header files for source types from the Qt Remote Objects .rep files.
+
+\cmakecommandsince 6.2
+
+\section1 Synopsis
+
+\badcode
+qt_add_repc_sources(<TARGET> rep_files)
+
+qt6_add_repc_sources(<TARGET> rep_files)
+\endcode
+
+\section1 Description
+
+Runs \l repc and generates \l {Qt Remote Objects Source} header files based
+on the given \c rep_files. Adds the generated files to \c{<TARGET>}.
+
+\section1 Examples
+
+\snippet cmake-macros/CMakeLists.txt simpleSwitch_cmake_add_repc_source
+
+The generated file(s) will be of the form \c {rep_<replica file name>_source.h},
+in this case \c rep_simpleswitch_source.h
+*/
+
+/*!
+\page qtremoteobjects-cmake-qt-add-repc-merged.html
+\ingroup cmake-macros-qtremoteobjects
+
+\title qt_add_repc_merged
+\target qt6_add_repc_merged
+
+\brief Creates C++ header files for source and replica types from the Qt Remote Objects .rep files.
+
+\cmakecommandsince 6.2
+
+\section1 Synopsis
+
+\badcode
+qt_add_repc_merged(<TARGET> rep_files)
+
+qt6_add_repc_merged(<TARGET> rep_files)
+\endcode
+
+\section1 Description
+
+Runs \l repc and generates \l {Qt Remote Objects Replica} and \l {Qt Remote Objects Source}
+header files based on the given \c rep_files combined in a single header. Adds the
+generated files to \c{<TARGET>}.
+
+\section1 Examples
+
+\snippet cmake-macros/CMakeLists.txt simpleSwitch_cmake_add_repc_merged
+
+The generated file(s) will be of the form \c {rep_<replica file name>_merged.h},
+in this case \c rep_simpleswitch_merged.h
+
+\note Typically sources and replicas live in separate processes or devices, so
+this function is not commonly used.
+*/
+
+/*!
+\page qtremoteobjects-cmake-qt-rep-from-headers.html
+\ingroup cmake-macros-qtremoteobjects
+
+\title qt_reps_from_headers
+\target qt6_reps_from_headers
+
+\brief Creates .rep files from the QObject header files.
+
+\cmakecommandsince 6.2
+
+\section1 Synopsis
+
+\badcode
+qt_reps_from_headers(<TARGET> header_files)
+
+qt6_reps_from_headers(<TARGET> header_files)
+\endcode
+
+\section1 Description
+
+Generates corresponding \c .rep files from the existing QObject \c header_files.
+
+\section1 Examples
+
+\snippet cmake-macros/CMakeLists.txt simpleSwitch_cmake_rep_from_header
+
+This will generate an interface file called \c simpleswitch.rep in the build
+directory.
+*/
--- /dev/null
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \page remoteobjects-changes-qt6.html
+ \title Changes to Qt Remote Objects
+ \ingroup changes-qt-5-to-6
+ \brief Migrate Qt Remote Objects to Qt 6.
+
+ Qt 6 is a result of the conscious effort to make the framework more
+ efficient and easy to use.
+
+ We try to maintain binary and source compatibility for all the public
+ APIs in each release. But some changes were inevitable in an effort to
+ make Qt a better framework.
+
+ In this topic we summarize those changes in Qt Remote Objects, and provide
+ guidance to handle them.
+
+ \section1 API changes
+
+ \section2 Functions taking const-ref QString changing to QStringView
+
+ QRemoteObjectHostBase::proxy, QRemoteObjectHostBase::reverseProxy and
+ QRemoteObjectNode::instances now accept a QStringView instead
+ of \c{const QString &}. The largest difference caused by this is that it
+ no longer accepts implicit conversion from string literals
+ (i.e. \c{node.instances("abc");}). Instead, you could use a UTF-16
+ string literal (\c{node.instances(u"abc")}).
+
+ \section2 Changes to classes for custom transport backend support
+
+ The "semi-private" \c IoDeviceBase, \c ServerIoDevice, and \c ClientIoDevice
+ classes are now renamed to \c QtROIoDeviceBase, \c QtROServerIoDevice, and
+ \c QtROClientIoDevice respectively, to be consistent with naming in Qt.
+ They are also moved from the private \c qconnectionfactories_p.h header to
+ \c qconnectionfactories.h.
+
+ \note These classes are provided to give more flexibility for implementing
+ custom communication protocols for Qt Remote Objects, but there are no
+ source or binary compatibility guarantees for them. We recommend using the
+ QRemoteObjectNode::addClientSideConnection() and
+ QRemoteObjectHostBase::addHostSideConnection() methods, if you need
+ support for \l {External QIODevices} {external} communication channels.
+
+ \section1 CMake changes
+
+ The \c cmake instructions for calling \l repc and adding the generated
+ \c .rep files to a CMake project have slightly changed. Instead of the
+ \c qt5_generate_repc macro, you should now use \c qt6_add_repc_sources,
+ \c qt6_add_repc_replicas and \c qt6_add_repc_merged functions.
+ For example, the following code:
+
+ \code
+ set(SOURCES
+ main.cpp
+ simpleswitch.cpp
+ )
+
+ qt5_generate_repc(SOURCES simpleswitch.rep SOURCE)
+ add_executable(directconnectserver ${SOURCES})
+ \endcode
+
+ Should change to:
+
+ \code
+ set(SOURCES
+ main.cpp
+ simpleswitch.cpp
+ )
+ add_executable(directconnectserver ${SOURCES})
+ qt6_add_repc_sources(directconnectserver simpleswitch.rep)
+ \endcode
+
+ More detailed descriptions for these CMake functions can be found
+ \l {CMake functions} {here}.
+*/
--- /dev/null
+// Copyright (C) 2019 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qtremoteobjects-compatibility.html
+\title Qt Remote Objects Protocol Versioning
+
+Qt Remote Objects uses an internal protocol to pass data between processes
+and/or devices. The same protocol version needs to be used by all parties: if
+there is a mismatch, the connecting node will output a warning and the host
+node will not send any data.
+
+Currently released versions:
+
+\table
+\header
+ \li Protocol Version
+ \li Qt Version
+\row
+ \li <1.2
+ \li Used during QtRO's tech preview phase.
+\row
+ \li 1.2
+ \li 5.12.0
+\row
+ \li 1.3
+ \li 5.12.4
+\row
+ \li 2.0
+ \li 6.2.0
+\endtable
+*/
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\module QtRemoteObjects
+\title Qt Remote Objects C++ Classes
+\brief Provides an easy to use mechanism for sharing a QObject's properties, signals, or slots, between processes.
+\ingroup modules
+\qtcmakepackage RemoteObjects
+\qtvariable remoteobjects
+
+ To link against the module using CMake, add the following lines to your \c cmake file:
+
+ \snippet cmake-macros/CMakeLists.txt remote_objects_cmake
+
+ \snippet cmake-macros/CMakeLists.txt remote_objects_cmake_link
+
+ To link against the module using \c qmake, add this line to your \c{.pro} file:
+
+ \snippet doc_src_simpleswitch.cpp simpleSwitch_remoteobjectsadd_example1
+
+ For more information, see \l{Getting Started with Qt Remote Objects}.
+*/
--- /dev/null
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qtremoteobjects-custom-transport.html
+\title APIs for Implementing Custom Transport Backends
+\brief Gives an overview of abstract APIs for implementing custom transport
+backends for Qt Remote Objects.
+
+Qt Remote Objects provides several abstract interfaces for implementing custom
+transport backends. The concept behind these classes is that there needs to be
+a host node, which has an address that can be connected to. Then there is a
+client object, which can be publicly constructed, and can connect to the server.
+When the server gets a connection request, it creates the server side of the
+connection, which communicates directly with the client. There are thus three
+abstractions, one for the server (\c QConnectionAbstractServer), one for the
+client-side of the connection (\c QtROClientIoDevice), and the third for the
+server-side of the connection (\c QtROServerIoDevice). The latter two inherit
+from \c QtROIoDeviceBase.
+
+\section1 API Overview
+
+\list
+\li \c QtROIoDeviceBase
+\li \c QtROClientIoDevice
+\li \c QtROServerIoDevice
+\li \c QConnectionAbstractServer
+\endlist
+
+After implementing these interfaces, you can register your custom protocol
+using the qRegisterRemoteObjectsServer() and qRegisterRemoteObjectsClient()
+methods.
+
+\note These APIs are provided to give more flexibility for implementing custom
+communication protocols for Qt Remote Objects. There are no source or binary
+compatibility guarantees for them, meaning that the API is only guaranteed to work
+with the Qt version it was developed against. API changes will however only be made
+in minor releases. (6.1, 6.2, and so on.)
+
+*/
--- /dev/null
+// Copyright (C) 2019 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page remoteobjects-example-dynamic-replica.html
+\title Example 2: Direct Connection with a Dynamic Replica
+\brief Describes how the Qt Remote Objects establishes a direct connection with a dynamic replica.
+\target qtro-example2
+
+Initially, a dynamic replica is created as a "bare" QObject - without
+properties, signals or slots. Then, during initialization, QtRO returns the
+API for the object, after the connection to the source is made. Thus, the API
+is added to the object at runtime.
+
+There are no changes to be made on the source side, as a dynamic \l Replica
+only impacts how the requestor node acquires the replica. So, we use the
+source-side code shown in \l {qtro-example1}{Example 1}.
+
+\list 1
+ \li Add replica generation to the project.
+
+ Because the replica is dynamically acquired, no \c .rep file is
+ required unlike in \l {qtro-example1}{Example 1}.
+
+ \li Create the remote node and connect it to the source host node.
+
+ The code for this step is unchanged from \l {qtro-example1}{Example 1}.
+ \snippet doc_src_simpleswitch.cpp simpleSwitch_dynamicclientnode_example2
+
+ \li Acquire a replica of the remote source object.
+
+ In \c main.cpp, we use a QSharedPointer to hold a replica of the
+ remote object, and then instantiate a replica requestor object:
+
+ \snippet doc_src_simpleswitch.cpp simpleSwitch_dynamicclientmaincpp_example2
+\endlist
+
+The complete declaration and definition of the requestor class,
+\c DynamicClient, is as follows:
+
+\c dynamicclient.h
+\snippet doc_src_simpleswitch.cpp simpleSwitch_dynamicclientheader_example2
+
+\c dynamicclient.cpp
+\snippet doc_src_simpleswitch.cpp simpleSwitch_dynamicclientcpp_example2
+
+When run together with the source-side example, the output is identical
+to \l {qtro-example1}{Example 1}.
+
+*/
--- /dev/null
+// Copyright (C) 2019 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+
+\page remoteobjects-example-registry.html
+\title Example 3: Connections to Remote Nodes using a Registry
+\brief Describes how the Qt Remote Objects registry establishes connections between nodes.
+\target qtro-example3
+
+This example illustrates the use of a \l {Registry} to build the node topology.
+For simple networks, we use a QUrl to create a direct connection between two
+nodes. For complex networks, we use a registry, where you use a different QUrl
+to point both the host and replica nodes to the registry. For only two nodes,
+the benefits of using a registry are minimal. But, as the network grows, using
+a registry means that all nodes only need to connect to the registry via a single
+QUrl. In comparison, with direct connections, nodes would have to maintain a
+list of QUrls for every single node that they link to.
+
+\section2 Set up the Source
+
+The \c simpleswitch.h and \c simpleswitch.cpp sources from \l {qtro-example1}
+{Example} can be used without modification. The difference is in the way a host
+node is created and connected to the registry:
+
+\c main.cpp
+\snippet doc_src_simpleswitch.cpp simpleSwitch_registrymaincpp_example3
+
+\section2 Set up the Replica
+
+The requestor object used for this example is the dynamic replica client
+discussed in \l {qtro-example2}{Example 2}.
+
+The only modification is in \c main.cpp: a \l {Registry} node is created
+to acquire a \l {Replica}:
+
+\snippet doc_src_simpleswitch.cpp simpleSwitch_registrydynamicclientmaincpp_example3
+
+When run together with the source-side example, the output is identical
+to \l {qtro-example1}{Example 1}.
+
+*/
--- /dev/null
+// Copyright (C) 2019 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page remoteobjects-example-static-source
+\title Example 1: Direct Connection using a Static Source
+\brief Describes how the Qt Remote Objects establishes a direct connection using a static source.
+\target qtro-example1
+
+In this example, the source object is a simple binary switch that toggles its
+state based on a timer. When the state changes, a signal is emitted by the
+source which QtRO propagates to all replicas. Since the replicas have the same
+properties, signals, and slots as were exposed from the source, any slots
+connected to the replica's signal will be called when the replica receives that
+signal. The client process then echoes back the received switch state to the
+source by emitting its own signal which is connected to a slot on the replica.
+
+\list 1
+\li \b {Create a source object}
+
+To create this \l Source object, first we create the definition file,
+\c simpleswitch.rep. This file describes the properties and methods for the
+object and is input to the Qt Remote Objects Compiler \l{repc}. This file only
+defines interfaces that are necessary to expose to the \l{Replica}{Replicas}.
+
+\c simpleswitch.rep
+\snippet doc_src_simpleswitch.cpp simpleSwitch_rep
+
+In \c simpleswitch.rep,
+\list
+ \li \c currState holds the current state of the switch.
+ \li \c server_slot() allows us to interact with the source - it will be
+ connected to the \c echoSwitchState(bool newstate) signal.
+\endlist
+
+For repc to process this file, add the following line to your \c cmake file:
+
+\snippet cmake-macros/CMakeLists.txt simpleSwitch_cmake_add_repc_source
+
+If you're using \c qmake:
+
+\snippet doc_src_simpleswitch.cpp simpleSwitch_repsource_example1
+
+These instructions are only relevant for the Qt Remote Object module,
+so you need to add it to your project as well. If you're using CMake,
+add:
+
+\snippet cmake-macros/CMakeLists.txt remote_objects_cmake
+
+\snippet cmake-macros/CMakeLists.txt remote_objects_cmake_link
+
+If you're using \c qmake:
+
+\snippet doc_src_simpleswitch.cpp simpleSwitch_remoteobjectsadd_example1
+
+repc creates the \c rep_SimpleSwitch_source.h header in the build directory
+that you specify. For more information, see \l{Source}.
+
+repc creates three helper classes for use with QtRO. For this example, we
+use the basic: \c SimpleSwitchSimpleSource. It's an abstract class, defined in
+\c rep_SimpleSwitch_source.h. We derive from it to define our SimpleSwitch
+implementation class as shown below:
+
+\c simpleswitch.h
+
+\snippet doc_src_simpleswitch.cpp simpleSwitch_serverheader_example1
+
+In \c simpleswitch.h,
+\list
+ \li \c stateChangeTimer is a QTimer that is used to toggle the state of our
+ SimpleSwitch.
+ \li \c timeout_slot() is connected to \c stateChangeTimer's timeout() signal.
+ \li \c server_slot() -- which is called automatically on the source whenever
+ any replica calls their version of the slot -- outputs the received value.
+ \li \c currStateChanged(bool), defined in the \l{repc}-generated
+ \c rep_SimpleSwitch_source.h, is emitted whenever \c currState toggles.
+ In this example, we ignore the signal on the source side, and handle it
+ later on the replica side.
+\endlist
+
+The definition of our \c SwitchState class is shown below:
+
+\c simpleswitch.cpp
+
+\snippet doc_src_simpleswitch.cpp simpleSwitch_serversource_example1
+
+\li \b {Create a registry}
+
+Because this example uses a direct connection between nodes, we can omit this
+step.
+
+\li \b {Create a host node}
+
+The host node is created as shown below:
+
+\snippet doc_src_simpleswitch.cpp simpleSwitch_serverhostnode_example1
+
+\li \b {Host source object and remoting}
+
+The following statements instantiate the \l {Source} object and pass it on to
+the host to enable "remoting", which is the process of making an object visible
+to the QtRO network:
+
+\snippet doc_src_simpleswitch.cpp simpleSwitch_enableremoting_example1
+
+The contents of \c main.cpp file that implements the steps described above are
+as follows:
+
+\c main.cpp
+
+\snippet doc_src_simpleswitch.cpp simpleSwitch_servermaincpp_example1
+
+Compile and run this source-side project. The output, without any replicas
+created, should look as shown below with the switch state toggling between
+\c true and \c false every two seconds.
+
+\image DirectConnectServerOutput.png "Example 1: Server Output"
+
+\endlist
+
+The subsequent steps are for creating the replica side of the network, which in
+this example gets the state of switch from the \l {Source} and echoes it back.
+
+\section2 Replica Code
+
+\list 1
+\li \b {Use repc to add a replica to your project}
+
+We use the same API definition file as we did on the source side,
+\c SimpleSwitch.rep, to create a \l {Replica} header file using the \l {repc}.
+If you're using \c cmake, include the following line in your client side
+\c cmake file, specifying a \c .rep file input:
+
+\snippet cmake-macros/CMakeLists.txt simpleSwitch_cmake_add_repc_replica
+
+If you're using \c qmake, add the following line to your client side \c .pro
+file:
+
+\snippet doc_src_simpleswitch.cpp simpleSwitch_clientrep_example1
+
+The \l {repc} tool generates a \c rep_SimpleSwitch_replica.h file in the build
+directory. For more information, see \l{Replica}.
+
+\li \b {Create a node to connect with the source's host node}
+
+The following code instantiates the second node on the network and connects it
+with the source host node:
+
+\snippet doc_src_simpleswitch.cpp simpleSwitch_clientremotenode_example1
+
+\li \b {Call the node's \l{QRemoteObjectNode::}{acquire()} to create a pointer
+ to a replica}
+
+First, we instantiate a replica:
+
+\snippet doc_src_simpleswitch.cpp simpleSwitch_clientacquirereplica_example1
+
+\note \l{QRemoteObjectNode::}{acquire()} returns a pointer to the replica, but
+doesn't manage its lifetime. This example shows the recommended process of
+wrapping the returned pointer in a QSharedPointer or QScopedPointer to ensure
+that the pointer is always deleted properly.
+
+\c main.cpp implements the steps described above and instantiates our object:
+
+\c main.cpp
+\snippet doc_src_simpleswitch.cpp simpleSwitch_clientmain_example1
+
+The complete declaration and definition for the \c Client class is as follows:
+
+\c client.h
+\snippet doc_src_simpleswitch.cpp simpleSwitch_clientheader_example1
+
+\c client.cpp
+\snippet doc_src_simpleswitch.cpp simpleSwitch_clientcpp_example1
+
+Compiling and running this example together with the source-side example
+generates the following output:
+
+\image DirectConnectClientServerOutput.png "Direct Connect Server Client Communication output"
+\endlist
+
+*/
--- /dev/null
+// Copyright (C) 2018 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qtremoteobjects-external-schemas.html
+\title Qt Remote Objects - External QIODevices
+\brief Describes how Qt Remote Objects supports custom QIODevice channels.
+
+\section1 External QIODevices
+
+Qt Remote Objects supports several communications channels out-of-the-box, such
+as the \l QTcpServer and \l QTcpSocket pair. Given the desired \l QUrl for tcp,
+or the desired name (for the \l QLocalServer and \l QLocalSocket pair), the
+code needed to listen and connect are boilerplate and handled internally by Qt.
+Qt Remote Objects supports other types of \l QIODevice as well, and the \l
+QRemoteObjectNode classes provide additional methods to support cases where
+custom code is needed.
+
+A contrived example with TCP/IP is shown below. A more realistic example would
+use an SSL connection, which would require configuration of certificates, etc.
+
+\code
+ // Create the server and listen outside of QtRO
+ QTcpServer tcpServer;
+ tcpServer.listen(QHostAddress(QStringLiteral("127.0.0.1")), 65213);
+
+ // Create the host node. We don't need a hostUrl unless we want to take
+ // advantage of external schemas (see next example).
+ QRemoteObjectHost srcNode;
+
+ // Make sure any connections are handed to QtRO
+ QObject::connect(&tcpServer, &QTcpServer::newConnection, &srcNode,
+ [&srcNode, &tcpServer]() {
+ srcNode.addHostSideConnection(tcpServer.nextPendingConnection());
+ });
+\endcode
+
+The Replica side code needs to manually connect to the Host
+\code
+ QRemoteObjectNode repNode;
+ QTcpSocket *socket = new QTcpSocket(&repNode);
+ QObject::connect(socket, &QTcpSocket::connected, &repNode,
+ [socket, &repNode]() {
+ repNode.addClientSideConnection(socket);
+ });
+ socket->connectToHost(QHostAddress(QStringLiteral("127.0.0.1")), 65213);
+\endcode
+
+\section1 External Schemas
+
+It is possible to create each side of the QIODevice and call \l
+{QRemoteObjectNode::addClientSideConnection(QIODevice *ioDevice)} and \l
+{QRemoteObjectHostBase::addHostSideConnection(QIODevice *ioDevice)} as shown
+above. This is fully supported, but requires the client know how to establish
+the connection or have a way to discover that information. This is exactly the
+problem the registry was designed to solve.
+
+Qt Remote Objects also allows "External Schemas" to be used with the registry,
+which helps with connection setup. On the \l QRemoteObjectHost side, the
+user must set the hostUrl with the desired schema.
+
+\code
+ // Use standard tcp url for the registry
+ const QUrl registryUrl = QUrl(QStringLiteral("tcp://127.0.0.1:65212"));
+ // Use "exttcp" for the "external" interface
+ const QUrl extUrl = QUrl(QStringLiteral("exttcp://127.0.0.1:65213"));
+
+ // Create the server and listen outside of QtRO
+ QTcpServer tcpServer;
+ tcpServer.listen(QHostAddress(extUrl.host()), extUrl.port());
+
+ // We need a registry for everyone to connect to
+ QRemoteObjectRegistryHost registry(registryUrl);
+
+ // Finally, we create our host node and register "exttcp" as our schema.
+ // We need the AllowExternalRegistration parameter to prevent the node from
+ // setting a hostUrlInvalid error.
+ QRemoteObjectHost srcNode(extUrl, registryUrl, QRemoteObjectHost::AllowExternalRegistration);
+ // From now on, when we call enableRemoting() from this node, the registry
+ // will be updated to show the Source object at extUrl.
+\endcode
+
+On the \l Replica side, the \l QRemoteObjectNode needs to register a callback
+to be used when the external schema is detected. The callback must be a
+\l {QRemoteObjectNode::}{RemoteObjectSchemaHandler}.
+
+\code
+ // Use standard tcp url for the registry
+ const QUrl registryUrl = QUrl(QStringLiteral("tcp://127.0.0.1:65212"));
+
+ // This time create the node connected to the registry
+ QRemoteObjectNode repNode(registryUrl);
+
+ // Create the RemoteObjectSchemaHandler callback
+ QRemoteObjectNode::RemoteObjectSchemaHandler setupTcp = [&repNode](QUrl url) {
+ QTcpSocket *socket = new QTcpSocket(&repNode);
+ connect(socket, &QTcpSocket::connected,
+ [socket, &repNode]() {
+ repNode.addClientSideConnection(socket);
+ });
+ connect(socket, &QSslSocket::errorOccurred,
+ [socket](QAbstractSocket::SocketError error) {
+ delete socket;
+ });
+ socket->connectToHost(url.host(), url.port());
+ };
+
+ // Once we call registerExternalSchema, the above method will be called
+ // whenever the registry sees an object we are interested in on "exttcp"
+ repNode.registerExternalSchema(QStringLiteral("exttcp"), setupTcp);
+\endcode
+*/
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qtremoteobjects-gettingstarted.html
+\title Getting Started with Qt Remote Objects
+\brief Detailed information on how to use Qt Remote Objects.
+
+\section1 Introduction
+
+The Qt Remote Objects module provides an easy way to share Qt APIs between
+processes and devices. For this to work, we require a data channel between
+processes and devices. To establish this data channel, first, you need a
+QRemoteObjectNode.
+
+In QtRO, a node is an endpoint for communication. In a remote objects network,
+each participant, be it a process or a device, needs its own node. QtRO is a
+peer-to-peer network, with connected nodes being the links in the network.
+
+Nodes, by themselves, don’t provide much use. But their value comes when you
+add QObject classes to a node to share. Then, any peer node can request a copy
+or instance of the shared object from the \e{host node}, the node that shares
+it.
+
+Unlike when using normal class instances (with independent properties and
+signal emissions), QtRO automatically synchronizes changes to the shared object
+across all of its copies. With a few exceptions, these copies have the
+identical Qt API as the original object, and are meant to be used exactly as if
+the original object were available.
+
+In QtRO, the original object is called the \l{Source}. It's a fully-implemented
+C++ class, with the necessary business logic to provide the required
+functionality. Copies of this object are called \l{Replica}s. You don’t need
+to write any C++ code for a replica; you request an instance from a node
+instead. While you do need some code to use the replica, such as connecting
+signals to your own slots, you don’t need to implement the internal behavior –
+that's already done for you in the source.
+
+Because the source can be in a different process or even on a different device,
+there are factors in QtRO that you need to consider, which you typically
+wouldn't run into when developing without Inter-Process Communication (IPC).
+Specifically, what happens if the process or device isn't there? This is where
+the additions to the Replica API come in:
+
+\list
+ \li The \l{QRemoteObjectReplica::}{initialized()} signal is emitted once the
+ replica has received the \l{Source}{source} state from the QtRO
+ network.
+ \li Both the \l{QRemoteObjectReplica::}{isReplicaValid} property and the
+ \l{QRemoteObjectReplica::}{stateChanged()} signal alert you if the
+ connection is lost.
+\endlist
+
+Objects shared over QtRO use the links (conduits) between nodes for all
+communication. If you want to share a QObject, you must create a \e{host node}
+with a URL other nodes can connect to. You can also use the \l{Registry} to
+facilitate connections, but your nodes that share \l{Source}{sources} still need
+to be host nodes. Each shared object is given a name (a QString), used to
+identify it on the QtRO network.
+
+\section1 Implementation
+
+To illustrate the use of remote objects, on the source side, we need to:
+
+\list 1
+ \li Create the \l {Source} object that is replicated to other nodes, with or
+ without using \l repc, the Qt Remote Objects Compiler.
+ \li Optionally, create the \l{Registry}. Otherwise, use direct connections.
+ \li Create a host node so that the source object can be shared.
+ \li Call the node's \l{QRemoteObjectHostBase::}{enableRemoting()} function
+ to share the source object.
+\endlist
+
+On the replica side, we need to:
+
+\list 1
+ \li Optionally, use \l repc to generate a \l{Replica} header for your project.
+ \li Create the node that will connect with the \l{Source} host node.
+ \li Call the node's \l{QRemoteObjectNode::}{acquire()} function to create a
+ pointer to a replica.
+\endlist
+
+The following examples illustrate both \l{repc}-compiled static objects and dynamic
+source objects. Additionally, they also show direct connections as well as
+connections that use a \l{Registry} between nodes.
+
+\list
+ \li \l{Example 1: Direct Connection using a Static Source}
+ \li \l{Example 2: Direct Connection with a Dynamic Replica}
+ \li \l{Example 3: Connections to Remote Nodes using a Registry}
+\endlist
+
+*/
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qtremoteobjects-index.html
+\title Qt Remote Objects
+\ingroup overviews
+\brief Provides APIs for inter-process communication.
+
+\target Qt Remote Objects
+\section1 Remote Object Concepts
+
+Qt Remote Objects (QtRO) is an Inter-Process Communication (IPC) module
+developed for Qt. This module extends Qt's existing functionalities to enable
+information exchange between processes or computers, easily.
+
+One of Qt's key features, to enable this information exchange, is the
+distinction between an object's API (defined by its properties, signals, and
+slots) and the implementation of that API. QtRO's purpose is to meet that
+expected API, even if the true QObject is in a different process. A slot
+called on a copy of an object (the \l {Replica} in QtRO) is forwarded to the
+true object (the \l {Source} in QtRO) for handling. Every Replica receives
+updates to the Source, either property changes or emitted signals.
+
+A \l {Replica} is a light-weight proxy for the \l {Source} object, but a
+Replica supports the same connections and behavior of QObjects, which makes it
+usable in the same way as any other QObject that Qt provides. Behind the
+scenes, QtRO handles everything that's necessary for the Replica to look like
+its Source.
+
+Note that Remote Objects behave differently from traditional Remote Procedure
+Call (RPC) implementations, for example:
+
+\list
+ \li In RPC, the client makes a request and waits for the response.
+ \li In RPC, the server doesn't push anything to the client unless it's in
+ response to a request.
+ \li Often, the design of RPC is such that different clients are independent
+ of each other: for instance, two clients can ask a mapping service for
+ directions and get different results.
+\endlist
+
+While it is possible to implement this RPC-style behavior in QtRO, as Sources
+without properties, and slots that have return values, QtRO hides the fact that
+the processing is really remote. You let a node give you the Replica instead of
+creating it yourself, possibly use the status signals
+(\l {QRemoteObjectReplica::} {isReplicaValid()}), but then interact with the
+object like you would with any other QObject-based type.
+
+\section1 Use Case: GPS
+
+Consider a sensor such as a Global Positioning System (GPS) receiver. In
+QtRO terms:
+
+\list
+ \li The \l{Source} would be the process that directly interacts with the
+ GPS hardware and derives your current location.
+ \li The location would be exposed as QObject properties; the periodic
+ updates to the location would update these properties and emit property
+ changed signals.
+ \li \l{Replica}{Replicas} would be created in other processes and would
+ always know your current location, but wouldn't need any of the logic
+ to compute the location from the sensor data.
+ \li Connecting to the location changed signal on the Replica would work as
+ expected: the signal emitted from the Source would trigger the signal
+ emission on every Replica.
+\endlist
+
+\section1 Use Case: Printer Access
+
+Consider a service that provides access to a printer. In QtRO terms:
+
+\list
+ \li The \l{Source} would be the process controlling the printer directly.
+ \li The ink levels and printer status would be monitored by QObject
+ properties. Updates to these properties would emit property changed
+ signals.
+ \li The key feature -- being able to print something -- needs to be passed
+ back to the printer. Incidentally, this aligns with the Qt slot mechanism,
+ which QtRO uses as the way for \l{Replica}{Replicas} to make calls on the
+ Source. In effect, properties and signals go from Source to Replicas;
+ slots go from Replica to Source.
+ \li When a print request is accepted, the printer status would change,
+ triggering a change in the status property. This would then be reported
+ to all Replicas.
+\endlist
+
+\include module-use.qdocinc using qt module
+\snippet cmake-macros/CMakeLists.txt remote_objects_cmake
+
+See also the \l {Build with CMake} overview.
+
+\include module-use.qdocinc building with qmake
+\snippet doc_src_simpleswitch.cpp simpleSwitch_remoteobjectsadd_example1
+
+\section1 Articles and Guides
+ \list
+ \li \l {Getting Started with Qt Remote Objects}
+ \li \l {Qt Remote Objects Nodes}{Nodes}
+ \li \l {Qt Remote Objects Source}{Sources}
+ \li \l {Qt Remote Objects Replica}{Replicas}
+ \li \l {Qt Remote Objects Registry}{Registry}
+ \li Using Custom Transports
+ \list
+ \li \l {Qt Remote Objects - External QIODevices}{External QIODevices}
+ \li \l {APIs for Implementing Custom Transport Backends}
+ {Implementing Custom Transport Backends}
+ \endlist
+ \li \l {Qt Remote Objects Compiler}{Compiler}
+ \li \l {Remote Object Interaction}
+ \li \l {Troubleshooting Qt Remote Objects}{Troubleshooting}
+ \li \l {Qt Remote Objects Protocol Versioning}{Protocol Versioning}
+ \endlist
+
+\section2 API Reference
+
+\list
+ \li \l {Qt Remote Objects C++ Classes}
+ \li \l {Qt Remote Objects QML Types}
+\endlist
+
+\section1 Licenses
+
+Qt Remote Objects is available under commercial licenses from \l{The Qt Company}.
+In addition, it is available under the
+\l{GNU Lesser General Public License, version 3}, or the
+\l{GNU General Public License, version 2}.
+See \l{Qt Licensing} for further details.
+
+*/
--- /dev/null
+// Copyright (C) 2018 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+/*!
+\page qtremoteobjects-interaction.html
+\brief Describes how a source and replicas interact with one another.
+\section1 Remote Object Interaction
+
+The interaction between source and replicas is directional. Property changes
+and signal emission happen on the source, and are propagated to all replicas.
+If a property is writable, you can call the setter function on a replica, which
+is then forwarded to the source. Afterwards, if this call results in a new
+property value, that value is first changed at the source and then subsequently
+forwarded to all replicas. To the replica, it is then an asynchronous call,
+with latency before the change takes effect.
+
+While technically you can emit a signal on a replica, this is discouraged as it
+may have unexpected results. It will only trigger slots connected to the
+replica itself; not slots connected to the source or other replicas. Like
+property setters, slot invocations on a replica are forwarded to the source to
+run.
+
+Qt Remote Objects automatically implements the behavior described above. There
+is no need to write any replica implementation code. For dynamic replicas,
+this is handled automatically at runtime. For \l{repc} generated headers, this
+is handled at compile time.
+*/
--- /dev/null
+// Copyright (C) 2018 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+/*!
+\page qtremoteobjects-node.html
+\title Qt Remote Objects Nodes
+\brief Describes how Qt Remote Objects pass data between nodes.
+
+In a QtRO network, information is passed between processes via
+QRemoteObjectNodes ("nodes"). This peer-to-peer functionality uses a small
+number of distinct packets passing the necessary data between nodes.
+
+Each process that participates in the network instantiates a Node-based type,
+such as QRemoteObjectNode, QRemoteObjectHost, or QRemoteObjectRegistryHost.
+The host types of Nodes provide additional functionality. Both
+QRemoteObjectHost and QRemoteObjectRegistryHost support the
+\l{QRemoteObjectHostBase::}{enableRemoting()} and the corresponding
+\l{QRemoteObjectHostBase::}{disableRemoting()} methods, which are the key
+methods to expose source objects to the network. To use the \l Registry
+functionality, you need to have a QRemoteObjectRegistryHost on the network.
+Then, all other nodes can pass the RegistryHost's URL to the Node's
+\c registryAddress constructor parameter, or pass the URL to the
+\l {QRemoteObjectNode::}{setRegistryUrl()} method.
+
+Since QtRO is a peer-to-peer network, to
+\l{QRemoteObjectNode::acquire()}{acquire()} a valid \l{Replica}, the replica's
+node needs to be connected to the node that hosts its \l{Source}. A host node is a
+node that allows other nodes to connect to it, which is accomplished by giving
+hosts unique addresses. This address is provided to the QRemoteObjectHost
+constructor or set by the setHostUrl method. The node from which a replica is
+requested must establish the connection to the host node, to initialize the
+replica and keep it up to date.
+
+\section1 Connecting Nodes using QtRO URLs
+
+Host Nodes use custom URLs to simplify connections. Currently, QtRO supports
+two types of connections:
+
+\list 1
+ \li A TCP connection using the standard TCP/IP protocol - supports
+ connections between devices as well as between processes on the same
+ device.
+ \li A local connection - supports connections between processes on the same
+ device. This type of connection can have less overhead, depending on
+ the underlying Operating System features.
+\endlist
+
+For local connections, you must use a unique name. For TCP connections, you
+must provide a unique address and port number combination.
+
+Currently, QtRO does not include a \l {http://www.zeroconf.org/} {zeroconf}
+facility. Therefore, all processes or devices must know beforehand, how to
+connect to each other. A \l {QRemoteObjectRegistry} can be used to simplify
+the connection process for a network with multiple Host Nodes.
+
+The table below summarizes the connection types available:
+
+ \table 90%
+ \header
+ \li URL
+ \li Notes
+ \row
+ \li {QUrl}("local:service")
+ \li Uses (internally) {QLocalServer}/{QLocalSocket} classes to
+ communicate between nodes.
+ \row
+ \li {QUrl}("tcp://192.168.1.1:9999")
+ \li Uses (internally) {QTcpServer}/{QTcpSocket} classes to
+ communicate between nodes.
+ \row
+ \li {QUrl}("qnx:service")
+ \li QNX OS only. Uses a custom (named) channel for native
+ communication between nodes.
+ \row
+ \li {QUrl}("localabstract:service")
+ \li Since 6.2. Linux/Android OSes only. Uses an abstract namespace
+ for Unix domain sockets. This allows QLocalSocket behavior to work on
+ non-writable devices.
+ \endtable
+
+Nodes have a few \l{QRemoteObjectHostBase::enableRemoting()}
+{enableRemoting()} methods that are used to share objects on the network.
+However, if the node is not a host node, an error is returned.
+
+Other processes or devices that want to interact with a shared object can
+use one of the \l{QRemoteObjectNode::acquire()} {node's acquire()} methods,
+to instantiate a replica.
+
+*/
--- /dev/null
+// Copyright (C) 2018 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\qmlmodule QtRemoteObjects 6.\QtMinorVersion
+\title Qt Remote Objects QML Types
+\ingroup qmlmodules
+\brief Provides QML types for remote objects support.
+
+The QML types for \l{Qt Remote Objects} provide the helper pieces needed to build a remote objects network.
+They are typically used in conjunction with custom-registered replica types that make up a specific
+network.
+
+As an example, consider the following .rep file:
+\code
+class MyType {
+ PROP(QString myProp="Hello World")
+};
+\endcode
+
+The generated replica can be registered as a QML type:
+\code
+qmlRegisterType<MyTypeReplica>("custom",1,0,"MyTypeReplica")
+\endcode
+
+And then used from QML in conjunction with the base type Node:
+\qml
+import QtQuick
+import QtRemoteObjects
+import custom 1.0
+
+Item {
+ MyTypeReplica {
+ id: myType
+ node: Node { registryUrl: "local:registry" }
+ }
+
+ Text { text: myType.myProp }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: myType.pushMyProp("Updated Text")
+ }
+}
+\endqml
+
+Note that by default you cannot directly assign to a replica property, but rather use a \c push function.
+This is due to the potential problems that arise from the mix of declarative programming and asynchronous updates.
+Specifically, we want to avoid issues like the following:
+\badcode
+myType.myProp = "Updated Text"
+console.log(myType.myProp) // logs "Hello World", as the new text has not yet been round-tripped
+\endcode
+
+The QML types in this module can be imported into your application using the following import
+statement in your .qml file:
+\qml
+import QtRemoteObjects
+\endqml
+
+\section1 QML Types
+*/
+
+/*!
+\qmltype QtRemoteObjects
+\inqmlmodule QtRemoteObjects
+\since 5.14
+\brief The QtRemoteObjects global object provides useful functions for working with remote
+ types in QML.
+*/
+
+/*!
+ \qmlmethod Promise QtRemoteObjects::QtRemoteObjects::watch(QRemoteObjectPendingCall reply, int timeout = 30000)
+ Encapsulates the return value, represented by \a reply, from a replica in a JavaScript Promise.
+ A timeout can be set with \a timeout. The default timeout value is 30 seconds.
+
+ \qml
+ QtRemoteObjects.watch(replica.fetchDetails(identifier))
+ .then(function(value) { details = value },
+ function(error) { console.log("error fetching details:", error) })
+ \endqml
+*/
--- /dev/null
+// Copyright (C) 2018 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qtremoteobjects-registry.html
+\title Qt Remote Objects Registry
+\brief Describes how the Qt Remote Objects registry establishes connections
+ between nodes.
+\target Registry
+
+When you \l {QRemoteObjectNode::acquire()} a replica, the node URL is not passed
+as an argument. This means you do not need to specify the host node, but it does
+require you to have some other means of connecting to that host. Without the
+registry, it is necessary to manually call \l {QRemoteObjectNode::connectToNode()},
+from each node, to every host node that has \l {Source} objects it should
+replicate. This is fine for small or static networks, but does not scale.
+
+The registry provides a simpler way to establish these connections. Every node
+that wants to be part of the registry's network connects to the registry. The
+registry is itself a specialized source object, and thus is hosted by a node.
+Connecting to the registry is simply a matter of passing the registry's URL to
+the QRemoteObjectNode or QRemoteObjectHost constructor, or passing the URL to
+the setRegistryUrl method.
+
+The registry is tightly integrated with QtRO. Whenever a \l {Source} is added
+or removed, the name/URL is updated in the registry automatically. So once
+your node is connected to the registry, it is not necessary to connect to any
+other nodes manually. If you request an object on the network and you aren't
+connected to the hosting node, the registry will know what URL to connect to
+and will initiate the connection. Once connected (and the list of available
+objects is passed along, including the desired \l {Source}), the initialization
+process for the requested \l Replica will start automatically.
+*/
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \externalpage https://en.wikipedia.org/wiki/Domain-specific_language
+ \title Domain Specific Language (DSL)
+*/
+
+/*!
+\page qtremoteobjects-repc.html
+\title Qt Remote Objects Compiler
+\brief The Qt Remote Objects Compiler creates \l {Source} and \l {Replica} header files
+\ingroup overviews
+\keyword repc
+
+ \section1 REPC Overview
+
+ The \underline {Rep}lica \underline {C}ompiler (repc) generates QObject
+ header files based on an API definition file. The file (called a "rep"
+ file) uses a specific (text) syntax to describe the API. By convention,
+ these files are given a .rep file extension, short for Replica. When these
+ files are processed by repc, repc generates both \l {Source} and \l
+ {Replica} header files.
+
+ The Qt Remote Objects module also includes \l {CMake functions} and
+ \l {qmake variables} that can be added to your project file to automatically run
+ repc, and add the resulting files to the list of files processed by
+ \l{moc}{Meta Object Compiler} during the build process, making use of Qt
+ Remote Objects in your projects simple.
+
+ While Qt Remote Objects supports sharing any QObject over the network (using
+ enableRemoting on the Source side and acquireDynamic on the Replica side),
+ there are a couple of advantages to letting repc define your objects. First
+ of all, while \l {QRemoteObjectDynamicReplica} {DynamicReplicas} are
+ useful, they are more cumbersome to work with. The API is not known until
+ the object is initialized, and using the API from C++ requires string
+ lookups through QMetaObject's methods. Secondly, having the interface known
+ at compile time finds any issues at compile vs. at runtime. Thirdly, the rep
+ format supports default values, which can be handy if you are unable to
+ ensure the Source is available when the Replica is instantiated.
+
+ See the documentation \l {Source} {here} for information on using
+ the generated files in your code. Here we will focus on the repc format and
+ options.
+
+ \section1 The rep file format
+
+ The rep file format is a simple \l {Domain Specific Language (DSL)} for
+ describing an interface supported over Qt Remote Objects (QtRO). Since QtRO
+ is an object based system, these interfaces are defined by APIs available
+ through objects, that is, classes with properties, signals, and slots.
+
+ \section2 The class type
+
+ Each class defined in a rep file becomes a QObject in the generated header
+ files, with the described API generated for you.
+
+ To define a class use the \c class keyword, followed by the name you
+ want for your type, and then enclose your API in brackets like so
+ \code
+ class MyType
+ {
+ //PROP/CLASS/MODEL/SIGNAL/SLOT/ENUM declarations to define your API
+ };
+ \endcode
+
+ \section3 PROP
+
+ Q_PROPERTY elements are created by using the PROP keyword in the rep
+ file. The syntax is the \c PROP keyword followed by the definition enclosed
+ in parentheses, where the definition is the type, the name, and (optionally) a
+ default value or attributes.
+ \code
+ PROP(bool simpleBool) // boolean named simpleBool
+ PROP(bool defaultFalseBool=false) // boolean named defaultFalseBool, with false
+ // as the default value
+
+ PROP(int lifeUniverseEverything=42) // int value that defaults to 42
+ PROP(QByteArray myBinaryInfo) // Qt types are fine, may need #include
+ // additional headers in your rep file
+
+ PROP(QString name CONSTANT) // Property with the CONSTANT attribute
+ PROP(QString setable READWRITE) // Property with the READWRITE attribute
+ // note: Properties default to READPUSH
+ // (see description below)
+
+ PROP(SomeOtherType myCustomType) // Custom types work. Needs #include for the
+ // appropriate header for your type, make
+ // sure your type is known to the metabject
+ // system, and make sure it supports Queued
+ // Connections (see Q_DECLARE_METATYPE and
+ // qRegisterMetaType)
+ \endcode
+ More information about creating custom types can be found \l {Creating
+ Custom Qt Types} {here}.
+
+ By default, properties will have getters and a "push" slot defined, as well
+ as a notify signal emitted when the value is changed. Qt Remote Objects
+ requires the notify signal on the Source object to trigger sending updates
+ to the attached Replicas. In earlier versions of QtRO, properties defaulted
+ to being read/write, that is, having getters and setters. However, due to the
+ asynchronous nature of QtRO, this led to unintuitive behavior at times.
+ Setting the READWRITE attribute on the PROP will provide the old (getter
+ and setter) behavior.
+ \code
+ // In .rep file, old (setter) behavior
+ PROP(int myVal READWRITE) // Old behavior with setMyVal(int myVal) method
+
+ // In code... Assume myVal is initially set to 0 in Source
+ int originalValue = rep->myVal(); // Will be 0
+ rep->setMyVal(10); // Call setter, expecting a blocking/
+ // non-asynchronous return
+
+ if (rep->myVal() == 10) ... // Test will usually fail
+ \endcode
+
+ If it is necessary to block until the value is changed, something like the
+ following is necessary.
+ \code
+ // In .rep file, old (setter) behavior
+ PROP(int myVal READWRITE) // Old behavior with setMyVal(int myVal) method
+
+ // In code... Assume myVal is initially set to 0 in Source
+ bool originalValue = rep->myVal(); // Will be 0
+
+ // We can wait for the change using \l QSignalSpy
+ QSignalSpy spy(rep, SIGNAL(myValChanged(int)));
+
+ rep->setMyVal(10); // Call setter, expecting a blocking/
+ // non-asynchronous return
+
+ spy.wait(); // spy.wait() blocks until changed signal
+ // is received
+ if (rep->myVal() == 10) ... // Test will succeed assuming
+ // 1. Source object is connected
+ // 2. Nobody else (Source or other Replica)
+ // sets the myVal to something else (race
+ // condition)
+ // Rather than use QSignalSpy, the event-driven practice would be to connect the
+ // myValChanged notify signal to a slot that responds to the changes.
+ \endcode
+
+ QtRO now defaults to READPUSH, which provides an automatically generated
+ slot for requesting a property change.
+ \code
+ // In .rep file, defaults to READPUSH
+ PROP(bool myVal) // No setMyVal(int myVal) on Replica, has
+ // pushMyVal(int myVal) instead
+
+ // In code... Assume myVal is initially set to 0 in Source
+ bool originalValue = rep->myVal(); // Will be 0
+
+ // We can wait for the change using \l QSignalSpy
+ QSignalSpy spy(rep, SIGNAL(myValChanged(int)));
+
+ rep->pushMyVal(10); // Call push method, no expectation that change
+ // is applied upon method completion.
+
+ // Some way of waiting for change to be received by the Replica is still necessary,
+ // but hopefully not a surprise with the new pushMyVal() Slot.
+ spy.wait(); // spy.wait() blocks until changed signal
+ // is received
+ if (rep->myVal() == 10) ... // Test will succeed assuming
+ // 1. Source object is connected
+ // 2. Nobody else (Source or other Replica)
+ // set the myVal to something else (race
+ // condition)
+ \endcode
+
+ You can also use the \c CONSTANT, \c READONLY, \c PERSISTED, \c READWRITE,
+ \c READPUSH, or \c SOURCEONLYSETTER keywords in the PROP declaration, which
+ affects how the property is implemented. READPUSH is the default value if no value
+ used.
+
+ \code
+ PROP(int lifeUniverseEverything=42 CONSTANT)
+ PROP(QString name READONLY)
+ \endcode
+
+ Please note there are some subtleties here. A CONSTANT PROP has a
+ Q_PROPERTY declared as CONSTANT on the SOURCE side. However, replicas
+ cannot know the correct value until they are initialized, which means the
+ property value has to be allowed to change during initialization. For
+ READONLY, the source will have neither a setter nor a push slot, and the
+ replica side will not have a push slot generated. Adding the PERSISTED
+ trait to a PROP will have the PROP use the \l QRemoteObjectAbstractPersistedStore
+ instance set on a Node (if any) to save/restore PROP values.
+
+ Another nuanced value is SOURCEONLYSETTER, which provides another way of
+ specifying asymmetric behavior, where the \l Source (specifically the helper
+ class, \c SimpleSource) will have a public getter and setter for the
+ property, but it will be ReadOnly (with a notify signal) on the \l Replica
+ side. Thus the property can be fully controlled from the \l Source side,
+ but only observed from the \l Replica side. SOURCEONLYSETTER is the mode
+ used by repc for MODEL and CLASS instances, meaning the \l Source can
+ change the pointed to object, but the \l Replica can not provide a new
+ object, as no set<Prop> or push<Prop> method is generated. Note, this does
+ not impact the behavior of the pointed to type's properties, just the
+ ability to change the pointer itself.
+
+ \sa QRemoteObjectAbstractPersistedStore
+
+ \section3 CLASS
+
+ The CLASS keyword generates special Q_PROPERTY elements for objects derived from QObject.
+ These properties have the same semantics as SOURCEONLYSETTER. The syntax is the \c CLASS
+ keyword followed by the property name and then the type of subobject enclosed in parentheses.
+ \code
+ // In .rep file
+ class OtherClass
+ {
+ PROP(int value)
+ }
+
+ class MainClass
+ {
+ CLASS subObject(OtherClass)
+ }
+ \endcode
+
+ \section3 MODEL
+
+ The MODEL keyword generates special Q_PROPERTY elements for objects derived from QAbstractItemModel.
+ These properties have the same semantics as SOURCEONLYSETTER. The syntax is the \c MODEL
+ keyword followed by the property name and then parentheses enclosing the (comma-separated) roles
+ that should be exposed to the replica.
+ \code
+ // In .rep file
+ class CdClass
+ {
+ PROP(QString title READONLY)
+ MODEL tracks(title, artist, length)
+ }
+ \endcode
+
+ \section3 SIGNAL
+
+ \l [DOC QtCore] {Signals} {Signal} methods are created by using the SIGNAL keyword in the rep file.
+
+ Usage is to declare \c SIGNAL followed by the desired signature wrapped in
+ parentheses. The void return value should be skipped.
+
+ \code
+ SIGNAL(test())
+ SIGNAL(test(QString foo, int bar))
+ SIGNAL(test(QMap<QString,int> foo))
+ SIGNAL(test(const QString &foo))
+ SIGNAL(test(QString &foo))
+ \endcode
+
+ Just like in Qt \l {Qt::ConnectionType} {queued connections}, parameters in signals that are
+ references will be copied when being passed to replicas.
+
+ \section3 SLOT
+
+ \l [DOC QtCore] {Slots} {Slot} methods are created by using the SLOT keyword in the rep file.
+
+ Usage is to declare \c SLOT followed by the desired signature wrapped in
+ parentheses. The return value can be included in the declaration. If the
+ return value is skipped, void will be used in the generated files.
+
+ \code
+ SLOT(test())
+ SLOT(void test(QString foo, int bar))
+ SLOT(test(QMap<QString,int> foo))
+ SLOT(test(QMap<QString,int> foo, QMap<QString,int> bar))
+ SLOT(test(QMap<QList<QString>,int> foo))
+ SLOT(test(const QString &foo))
+ SLOT(test(QString &foo))
+ SLOT(test(const QMap<QList<QString>,int> &foo))
+ SLOT(test(const QString &foo, int bar))
+ \endcode
+
+ Just like in Qt \l {Qt::ConnectionType} {queued connections} and QtRO
+ SIGNALS, parameters in slots that are references will be copied when being
+ passed to Replicas.
+
+ \section3 ENUM
+
+ Enumerations (which use a combination of C++ enum and Qt's Q_ENUM in QtRO)
+ are described using the ENUM keyword.
+
+ \code
+ ENUM MyEnum {Foo}
+ ENUM MyEnum {Foo, Bar}
+ ENUM MyEnum {Foo, Bar = -1}
+ ENUM MyEnum {Foo=-1, Bar}
+ ENUM MyEnum {Foo=0xf, Bar}
+ ENUM MyEnum {Foo=1, Bar=3, Bas=5}
+ \endcode
+
+ Related topics: \l {The ENUM type}, \l {USE_ENUM keyword}
+
+ \section2 The POD type
+
+ Plain Old Data (POD) is a term to describe a simple data collection, along
+ the lines of a C++ struct. For example, if you have an API for a phonebook,
+ you may want to use the concept of an "address" in its interface (where
+ address might include street, city, state, country, and postal code). You
+ can use the POD keyword to define objects like this, which can then be used
+ by in PROP/SIGNAL/SLOT definitions in your class definitions.
+
+ Usage is to declare \c POD followed by the name for the generated type,
+ followed by type and name pairs separated by commas, where the type/name
+ pairs are wrapped in parentheses.
+
+ \code
+ POD Foo(int bar)
+ POD Foo(int bar, double bas)
+ POD Foo(QMap<QString,int> bar)
+ POD Foo(QList<QString> bar)
+ POD Foo(QMap<QString,int> bar, QMap<double,int> bas)
+ \endcode
+
+ A full example would look like this
+ \code
+ POD Foo(QList<QString> bar)
+ class MyType
+ {
+ SIGNAL(sendCustom(Foo foo));
+ };
+ \endcode
+
+ The code generated by repc creates a Q_GADGET class for each POD, with
+ corresponding Q_PROPERTY members for each type defined for the POD.
+
+ \section2 The ENUM type
+
+ It is often easier and cleaner to define an ENUM inside a class (see \l ENUM),
+ but if you need a standalone enum type, using the ENUM keyword outside of a
+ class definition can be helpful. This will generate a new class in your
+ header files that handles marshalling, etc.. The syntax is identical to \l
+ ENUM, with the exception that the declaration in this case is not contained
+ in a \c class declaration.
+
+ Related topics: \l {ENUM}, \l {USE_ENUM keyword}
+
+ \section2 USE_ENUM keyword
+
+ The USE_ENUM keyword was implemented before autogeneration via the ENUM
+ keyword was added. It is kept for backwards compatibility.
+
+ Related topics: \l {ENUM}, \l {The ENUM type}
+
+ \section2 Directives
+
+ The rep file defines an interface, but interfaces often require external
+ elements. In order to support this, repc will include any (single line)
+ directives at the top of the generated files. This allows you to, for
+ instance, use #include or #define directives that support the logic or
+ datatypes needed.
+
+ The repc tool currently ignores everything from the "#" symbol to the
+ end-of-line and adds that to the generated files. So multi-line
+ #if/#else/#endif statements and multi-line macros are not supported.
+
+ \section1 CMake functions
+
+ The CMake functions for generating source and replica types are listed below.
+
+ \annotatedlist cmake-macros-qtremoteobjects
+
+ \section1 qmake variables
+ \section2 REPC_REPLICA
+
+ Specifies the names of all rep files in the project that should be used to generate
+ replica header files.
+
+ For example:
+ \code
+ REPC_REPLICA = media.rep \
+ location.rep
+ \endcode
+
+ The generated file(s) will be of the form \c {rep_<replica file base>_replica.h}.
+
+ \section2 REPC_SOURCE
+
+ Specifies the names of all rep files in the project that should be used to generate
+ source header files.
+
+ For example:
+ \code
+ REPC_SOURCE = media.rep \
+ location.rep
+ \endcode
+
+ The generated file(s) will be of the form \c {rep_<replica file base>_source.h}.
+
+ \section2 REPC_MERGED
+
+ Specifies the names of all rep files in the project that should be used to generate
+ combined (source and replica) header files.
+
+ For example:
+ \code
+ REPC_MERGED = media.rep \
+ location.rep
+ \endcode
+
+ The generated file(s) will be of the form \c {rep_<replica file base>_merged.h}.
+
+ \note Typically sources and replicas live in separate processes or devices, so this variable
+ is not commonly used.
+
+ \section2 QOBJECT_REP
+
+ Specifies the names of existing QObject header files that should be used to generate corresponding
+ .rep files.
+*/
--- /dev/null
+// Copyright (C) 2018 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+/*!
+\page qtremoteobjects-replica.html
+\title Qt Remote Objects Replica
+\brief Describes the concept of a replica and how it works as a surrogate object.
+\target Replica
+
+A QRemoteObjectReplica ("replica") is a surrogate object that has approximately
+the same API as the \l {Source} QObject it replicates. Additionally, there are
+a few properties and signals to make it possible to detect when the replica is
+initialized or if it loses its connection to the source object. There
+are a few other differences, notably, a constant property on the source cannot
+be constant on the replica. The value will not be known at the time when the
+replica is instantiated; it will only be known once the replica is initialized.
+For more information, see \l {Remote Object Interaction}.
+
+A compiled replica is a \l {QRemoteObjectReplica} based type, where the derived
+class definition is automatically generated by the \l {repc} compiler. When you
+use \l {CMake functions} or \l {qmake variables} for running the \l repc compiler,
+this makes the generation part of the build process. Although only a header is
+generated, it's a complete type. There is no public constructor, so you need to
+use the \l {QRemoteObjectNode::acquire} template function to create the Replica
+instance.
+
+A \l {QRemoteObjectDynamicReplica} can be generated at runtime. To do so, you
+call \l {QRemoteObjectNode::acquireDynamic()}, passing in the source name
+(a QString) as an argument. Dynamic replicas are a bit more verbose to use from
+C++, but they do not require compilation. Dynamic replicas do not support
+initial property values, or introspection until they have been initialized.
+
+An important difference between these two ways of creating replicas is the
+behavior before the replica is initialized. Since a dynamic replica only gets
+a metaObject after initialization, it has basically no API before
+initialization -- no properties, and no signals to connect slots to.
+
+Because metaObjects for compiled replicas are created at compile-time, their
+API is available when the replica is instantiated. You can even provide default
+values for properties in the template file, which are used until the replica is
+initialized with current values from the source.
+
+\section1 Replica Initialization
+
+A host node will share the list of sources that it hosts with every other node
+that connects to it. This host sends updates when sources are added to or
+removed from the list. In this way, a connected node will always know what
+sources it can attach itself to. Changes to a specific source are only
+propagated to nodes that have a replica of that source. Consequently, this
+avoids any unnecessary network traffic.
+
+When a node acquires a replica for a known source, it sends a request for that
+source to the host node. Upon receiving this request, the host creates a reply
+packet with the current values for all properties of that source. If the
+requested replica is \l{QRemoteObjectDynamicReplica}{dynamic}, the reply packet
+includes the API definition for the source. From then on, the replica's node
+will be included in the list of connections that receive changes to that
+source.
+
+If a replica is instantiated but its node is not connected to the node that
+hosts the requested source -- or that object lives in a host node process, but
+sharing/remoting has not been enabled for the QObject -- the Replica will still
+be created, but remain uninitialized.
+
+If, at a later time, the replica's node gets notified that the requested source
+is available from a connected node, at that point it will request the source
+and start the initialization process.
+
+If the connection to a host node is lost, the replica will transition to the
+invalid state. It will attempt to reconnect and will re-initialize if the
+connection is restored; this makes sure all properties are current.
+
+\section1 Replica Ownership
+
+The acquire methods return a pointer to the replica QObject instantiated by the
+node. The node has no way of knowing the replica's intended lifetime.
+Consequently, when the replica is not longer needed, it is the calling
+program's responsibility to delete it.
+
+You can instantiate multiple copies of the same replica. All replicas of the
+same source from a single node will share a private data member which handles
+the network communication. This means multiple instances of a replica do not
+introduce additional network traffic, although there will be some additional
+processing overhead. Failing to delete replicas will prevent the reference
+count on this private object from reaching 0, and cause unnecessary network
+communication until the calling process exits. For this reason, it is
+recommended to use \l {QScopedPointer} or \l {QSharedPointer} to help track
+the lifetime of replicas.
+
+*/
--- /dev/null
+// Copyright (C) 2018 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+/*!
+\page qtremoteobjects-source.html
+\title Qt Remote Objects Source
+\brief Describes the concept of a source and how to use the source header that the repc generates.
+\target Source
+
+A QRemoteObjectSource ("source") is the QObject that is responsible for
+implementing the exposed API.
+
+At a high level, you can choose to use a QObject type directly as a source; or
+you can define the desired API in a \c{.rep} template for use with the
+\l {repc} compiler.
+
+If you already have a fully defined QObject, you can use it as a source by
+passing it to \l {QRemoteObjectHostBase::enableRemoting()}. This way, other
+processes or devices can then create
+\l{QRemoteObjectDynamicReplica}{dynamics replicas} of the object to interact
+with.
+
+For more information, see \l{Remote Object Interaction}.
+
+Letting repc generate a source header file for your project, using the
+\l {REPC_SOURCE} variable, provides three different options to implement the
+required API.
+
+Suppose your class name is Foo, you then have the following options:
+
+\list
+ \li \l {TypeSimpleSource} {FooSimpleSource} inheritance
+ \li \l {TypeSource} {FooSource} inheritance
+ \li \l {TypeAPI} {FooSourceAPI} usage with your own QObject
+\endlist
+
+For more details on how to create a \c{.rep} file, see \l{The rep file format}.
+
+\target TypeSimpleSource
+There is a <Type>SimpleSource class defined in the header, which provides the
+basic getter/setter methods for each property and implements data members of
+the correct property type in the header. "<Type>" here represents the class
+name from the \c{.rep} file, so if your class is of type "MyType" in the
+\c{.rep} file, then the generated header file will have a MyTypeSimpleSource
+class declared. This is a quick way to get started using the API. To use this
+class, you need to write a class that inherits from it and implement any slots
+defined, which are marked as pure virtual in the generated header file.
+You need to add the necessary logic to manage the exposed properties and emit
+signals, to your class as well.
+
+\target TypeSource
+If you need to hide your implementation details, use the second class declared
+in the header file, the <Type>Source class. This class' definition does not
+provide data members, and also makes the getter/setter functions pure virtual.
+While you may need to write more code, using this class gives you more
+flexibility in your implementation.
+
+\target TypeAPI
+The third class generated in the header is <Type>SourceAPI. This is a templated
+class, for use specifically by the templated version of
+\l {QRemoteObjectHostBase::enableRemoting()}, which allows you to use any
+QObject that supports the required API as the source. Use this class to hide or
+convert properties or signal/slot parameters. If your implementation
+does not provide the correct API, there will be compile-time warnings.
+
+\note Replicas and sources both derive from QObject; but their QObject API is
+\b never exposed. For instance, while a replica has a \l{QObject::}{destroyed}
+signal, the source's \l{QObject::}{destroyed} signal is not propagated. The
+source and each of its replica are unique QObjects with their own connections
+and lifetimes. Instead, the API that is exposed is defined by the \c{.rep}
+template used by repc. In the case of raw QObjects, all API elements are defined
+in the inheritance chain from a specific ancestor. The QObject's parent is always
+used, unless you define \c{Q_CLASSINFO("RemoteObject Type")} in an ancestor. If
+you use \c{Q_CLASSINFO("RemoteObject Type")}, that class's API is the lowest
+level of API used.
+
+\section1 Identifying Sources
+
+Because a host node can share more than one source, each source requires a name.
+All repc-generated headers include a way for the node to determine the class name:
+Q_CLASSINFO for replica, simplesource, or source types; or a static \c name()
+function for the SourceAPI type. If you pass your own QObject type to
+\l {QRemoteObjectHostBase::enableRemoting()}, the name is determined using the
+following logic:
+\list
+ \li A name can optionally be passed to \l {QRemoteObjectHostBase::enableRemoting()}.
+ If provided, that name is used.
+ \li If the object or any of its ancestors has Q_CLASSINFO of type
+ "RemoteObject Type" defined, that type name is used.
+ \li If the QObject's objectName is set, then it is used.
+ \li If none of the above are available, the call to
+ \l {QRemoteObjectHostBase::enableRemoting()} fails, returning false.
+\endlist
+*/
--- /dev/null
+// Copyright (C) 2018 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+/*!
+\page qtremoteobjects-troubleshooting.html
+\title Troubleshooting Qt Remote Objects
+\section1 Troubleshooting
+
+QtRO includes a number of internal logging categories that can be used for further debugging:
+
+\code
+qt.remoteobjects
+qt.remoteobjects.io
+qt.remoteobjects.models
+\endcode
+
+*/
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qconnection_local_backend_p.h"
+
+QT_BEGIN_NAMESPACE
+
+LocalClientIo::LocalClientIo(QObject *parent)
+ : QtROClientIoDevice(parent)
+ , m_socket(new QLocalSocket(this))
+{
+ connect(m_socket, &QLocalSocket::readyRead, this, &QtROClientIoDevice::readyRead);
+ connect(m_socket, &QLocalSocket::errorOccurred, this, &LocalClientIo::onError);
+ connect(m_socket, &QLocalSocket::stateChanged, this, &LocalClientIo::onStateChanged);
+}
+
+LocalClientIo::~LocalClientIo()
+{
+ close();
+}
+
+QIODevice *LocalClientIo::connection() const
+{
+ return m_socket;
+}
+
+void LocalClientIo::doClose()
+{
+ if (m_socket->isOpen()) {
+ connect(m_socket, &QLocalSocket::disconnected, this, &QObject::deleteLater);
+ m_socket->disconnectFromServer();
+ } else {
+ this->deleteLater();
+ }
+}
+
+void LocalClientIo::doDisconnectFromServer()
+{
+ m_socket->disconnectFromServer();
+}
+
+void LocalClientIo::connectToServer()
+{
+#ifdef Q_OS_ANDROID
+ if (!m_socket->socketOptions().testFlag(QLocalSocket::AbstractNamespaceOption))
+ qWarning() << "It is recommended to use 'localabstract' over 'local' on Android.";
+#endif
+ if (!isOpen())
+ m_socket->connectToServer(url().path());
+}
+
+bool LocalClientIo::isOpen() const
+{
+ return !isClosing() && (m_socket->state() == QLocalSocket::ConnectedState
+ || m_socket->state() == QLocalSocket::ConnectingState);
+}
+
+void LocalClientIo::onError(QLocalSocket::LocalSocketError error)
+{
+ qCDebug(QT_REMOTEOBJECT) << "onError" << error << m_socket->serverName();
+
+ switch (error) {
+ case QLocalSocket::ServerNotFoundError:
+ case QLocalSocket::UnknownSocketError:
+ case QLocalSocket::PeerClosedError:
+ //Host not there, wait and try again
+ emit shouldReconnect(this);
+ break;
+ case QLocalSocket::ConnectionError:
+ case QLocalSocket::ConnectionRefusedError:
+ //... TODO error reporting
+#ifdef Q_OS_UNIX
+ emit shouldReconnect(this);
+#endif
+ break;
+ default:
+ break;
+ }
+}
+
+void LocalClientIo::onStateChanged(QLocalSocket::LocalSocketState state)
+{
+ if (state == QLocalSocket::ClosingState && !isClosing()) {
+ m_socket->abort();
+ emit shouldReconnect(this);
+ }
+ if (state == QLocalSocket::ConnectedState)
+ initializeDataStream();
+}
+
+LocalServerIo::LocalServerIo(QLocalSocket *conn, QObject *parent)
+ : QtROServerIoDevice(parent), m_connection(conn)
+{
+ m_connection->setParent(this);
+ connect(conn, &QIODevice::readyRead, this, &QtROServerIoDevice::readyRead);
+ connect(conn, &QLocalSocket::disconnected, this, &QtROServerIoDevice::disconnected);
+}
+
+QIODevice *LocalServerIo::connection() const
+{
+ return m_connection;
+}
+
+void LocalServerIo::doClose()
+{
+ m_connection->disconnectFromServer();
+}
+
+LocalServerImpl::LocalServerImpl(QObject *parent)
+ : QConnectionAbstractServer(parent)
+{
+ connect(&m_server, &QLocalServer::newConnection, this, &QConnectionAbstractServer::newConnection);
+}
+
+LocalServerImpl::~LocalServerImpl()
+{
+ m_server.close();
+}
+
+QtROServerIoDevice *LocalServerImpl::configureNewConnection()
+{
+ if (!m_server.isListening())
+ return nullptr;
+
+ return new LocalServerIo(m_server.nextPendingConnection(), this);
+}
+
+bool LocalServerImpl::hasPendingConnections() const
+{
+ return m_server.hasPendingConnections();
+}
+
+QUrl LocalServerImpl::address() const
+{
+ QUrl result;
+ result.setPath(m_server.serverName());
+ result.setScheme(QRemoteObjectStringLiterals::local());
+
+ return result;
+}
+
+bool LocalServerImpl::listen(const QUrl &address)
+{
+#ifdef Q_OS_ANDROID
+ if (!m_server.socketOptions().testFlag(QLocalServer::AbstractNamespaceOption))
+ qWarning() << "It is recommended to use 'localabstract' over 'local' on Android.";
+#endif
+#ifdef Q_OS_UNIX
+ bool res = m_server.listen(address.path());
+ if (!res) {
+ QLocalServer::removeServer(address.path());
+ res = m_server.listen(address.path());
+ }
+ return res;
+#else
+ return m_server.listen(address.path());
+#endif
+}
+
+QAbstractSocket::SocketError LocalServerImpl::serverError() const
+{
+ return m_server.serverError();
+}
+
+void LocalServerImpl::close()
+{
+ m_server.close();
+}
+
+#ifdef Q_OS_LINUX
+
+AbstractLocalClientIo::AbstractLocalClientIo(QObject *parent)
+ : LocalClientIo(parent)
+{
+ m_socket->setSocketOptions(QLocalSocket::AbstractNamespaceOption);
+}
+
+AbstractLocalServerImpl::AbstractLocalServerImpl(QObject *parent)
+ : LocalServerImpl(parent)
+{
+ m_server.setSocketOptions(QLocalServer::AbstractNamespaceOption);
+}
+
+QUrl AbstractLocalServerImpl::address() const
+{
+ QUrl result;
+ result.setPath(m_server.serverName());
+ result.setScheme(QRemoteObjectStringLiterals::localabstract());
+
+ return result;
+}
+
+#endif // Q_OS_LINUX
+
+QT_END_NAMESPACE
--- /dev/null
+// Copyright (C) 2017-2015 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QCONNECTIONCLIENTFACTORY_P_H
+#define QCONNECTIONCLIENTFACTORY_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qconnectionfactories_p.h"
+
+#include <QtNetwork/qlocalserver.h>
+#include <QtNetwork/qlocalsocket.h>
+
+QT_BEGIN_NAMESPACE
+
+class LocalClientIo : public QtROClientIoDevice
+{
+ Q_OBJECT
+
+public:
+ explicit LocalClientIo(QObject *parent = nullptr);
+ ~LocalClientIo() override;
+
+ QIODevice *connection() const override;
+ void connectToServer() override;
+ bool isOpen() const override;
+
+public Q_SLOTS:
+ void onError(QLocalSocket::LocalSocketError error);
+ void onStateChanged(QLocalSocket::LocalSocketState state);
+
+protected:
+ void doClose() override;
+ void doDisconnectFromServer() override;
+ QLocalSocket* m_socket;
+};
+
+#ifdef Q_OS_LINUX
+
+class AbstractLocalClientIo final : public LocalClientIo
+{
+ Q_OBJECT
+
+public:
+ explicit AbstractLocalClientIo(QObject *parent = nullptr);
+};
+
+#endif // Q_OS_LINUX
+
+class LocalServerIo final : public QtROServerIoDevice
+{
+ Q_OBJECT
+public:
+ explicit LocalServerIo(QLocalSocket *conn, QObject *parent = nullptr);
+
+ QIODevice *connection() const override;
+protected:
+ void doClose() override;
+
+private:
+ QLocalSocket *m_connection;
+};
+
+class LocalServerImpl : public QConnectionAbstractServer
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(LocalServerImpl)
+
+public:
+ explicit LocalServerImpl(QObject *parent);
+ ~LocalServerImpl() override;
+
+ bool hasPendingConnections() const override;
+ QtROServerIoDevice *configureNewConnection() override;
+ QUrl address() const override;
+ bool listen(const QUrl &address) override;
+ QAbstractSocket::SocketError serverError() const override;
+ void close() override;
+
+protected:
+ QLocalServer m_server;
+};
+
+#ifdef Q_OS_LINUX
+
+class AbstractLocalServerImpl final : public LocalServerImpl
+{
+ Q_OBJECT
+
+public:
+ explicit AbstractLocalServerImpl(QObject *parent);
+ QUrl address() const override;
+};
+
+#endif // Q_OS_LINUX
+
+QT_END_NAMESPACE
+
+#endif
--- /dev/null
+// Copyright (C) 2017-2016 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qconnection_qnx_backend_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QnxClientIo::QnxClientIo(QObject *parent)
+ : QtROClientIoDevice(parent)
+ , m_socket(new QQnxNativeIo(this))
+{
+ connect(m_socket, &QQnxNativeIo::readyRead, this, &QtROClientIoDevice::readyRead);
+ connect(m_socket,
+ static_cast<void(QQnxNativeIo::*)(QAbstractSocket::SocketError)>(&QQnxNativeIo::error),
+ this,
+ &QnxClientIo::onError);
+ connect(m_socket, &QQnxNativeIo::stateChanged, this, &QnxClientIo::onStateChanged);
+}
+
+QnxClientIo::~QnxClientIo()
+{
+ close();
+}
+
+QIODevice *QnxClientIo::connection() const
+{
+ return m_socket;
+}
+
+void QnxClientIo::doClose()
+{
+ if (m_socket->isOpen()) {
+ connect(m_socket, &QQnxNativeIo::disconnected, this, &QObject::deleteLater);
+ m_socket->disconnectFromServer();
+ } else {
+ deleteLater();
+ }
+}
+
+void QnxClientIo::doDisconnectFromServer()
+{
+ m_socket->disconnectFromServer();
+}
+
+void QnxClientIo::connectToServer()
+{
+ if (!isOpen())
+ m_socket->connectToServer(url().path());
+}
+
+bool QnxClientIo::isOpen() const
+{
+ return !isClosing() && (m_socket->state() == QAbstractSocket::ConnectedState
+ || m_socket->state() == QAbstractSocket::ConnectingState);
+}
+
+void QnxClientIo::onError(QAbstractSocket::SocketError error)
+{
+ qCDebug(QT_REMOTEOBJECT) << "onError" << error << m_socket->serverName();
+
+ switch (error) {
+ case QAbstractSocket::RemoteHostClosedError:
+ m_socket->close();
+ qCWarning(QT_REMOTEOBJECT) << "RemoteHostClosedError";
+ Q_FALLTHROUGH();
+ case QAbstractSocket::HostNotFoundError: //Host not there, wait and try again
+ case QAbstractSocket::AddressInUseError:
+ case QAbstractSocket::ConnectionRefusedError:
+ //... TODO error reporting
+ emit shouldReconnect(this);
+ break;
+ default:
+ break;
+ }
+}
+
+void QnxClientIo::onStateChanged(QAbstractSocket::SocketState state)
+{
+ if (state == QAbstractSocket::ClosingState && !isClosing()) {
+ m_socket->abort();
+ emit shouldReconnect(this);
+ } else if (state == QAbstractSocket::ConnectedState)
+ initializeDataStream();
+}
+
+QnxServerIo::QnxServerIo(QSharedPointer<QIOQnxSource> conn, QObject *parent)
+ : QtROServerIoDevice(parent), m_connection(conn)
+{
+ connect(conn.data(), &QIODevice::readyRead, this, &QtROServerIoDevice::readyRead);
+ connect(conn.data(), &QIOQnxSource::disconnected, this, &QtROServerIoDevice::disconnected);
+}
+
+QIODevice *QnxServerIo::connection() const
+{
+ return m_connection.data();
+}
+
+void QnxServerIo::doClose()
+{
+ m_connection->close();
+}
+
+QnxServerImpl::QnxServerImpl(QObject *parent)
+ : QConnectionAbstractServer(parent)
+{
+ connect(&m_server,
+ &QQnxNativeServer::newConnection,
+ this,
+ &QConnectionAbstractServer::newConnection);
+}
+
+QnxServerImpl::~QnxServerImpl()
+{
+ m_server.close();
+}
+
+QtROServerIoDevice *QnxServerImpl::configureNewConnection()
+{
+ if (!m_server.isListening())
+ return nullptr;
+
+ return new QnxServerIo(m_server.nextPendingConnection(), this);
+}
+
+bool QnxServerImpl::hasPendingConnections() const
+{
+ return m_server.hasPendingConnections();
+}
+
+QUrl QnxServerImpl::address() const
+{
+ QUrl result;
+ result.setPath(m_server.serverName());
+ result.setScheme(QStringLiteral("qnx"));
+
+ return result;
+}
+
+bool QnxServerImpl::listen(const QUrl &address)
+{
+ return m_server.listen(address.path());
+}
+
+QAbstractSocket::SocketError QnxServerImpl::serverError() const
+{
+ //TODO implement on QQnxNativeServer and here
+ //return m_server.serverError();
+ return QAbstractSocket::AddressInUseError;
+}
+
+void QnxServerImpl::close()
+{
+ close();
+}
+
+QT_END_NAMESPACE
--- /dev/null
+// Copyright (C) 2017-2016 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QCONNECTIONQNXBACKEND_P_H
+#define QCONNECTIONQNXBACKEND_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qconnectionfactories_p.h"
+#include "qconnection_qnx_qiodevices.h"
+#include "qconnection_qnx_server.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ QtRO provides QtROClientIoDevice, QtROServerIoDevice and QConnectionAbstractServer
+ as abstract interfaces to allow different backends to be used by QtRO. The
+ concept behind these classes is that there needs to be a Host node, which
+ has an address that can be connected to. Then there is a client object,
+ which can be publicly constructed, and can connect to the server. When the
+ server gets a connection request, it creates the server side of the
+ connection, which communicates directly with the client. There are thus
+ three abstractions, one for the server, one for the client-side of the
+ connection, and the third for the server-side of the connection. The later
+ two need to inherit from QIODevice.
+
+ Creating a backend for something that is already implemented in Qt is a
+ matter of creating the three needed abstractions. In the case of creating a
+ QNX backend using QNX's Native Messaging, the backend needs to create the
+ Server (which has an address for accepting connections), the client
+ QIODevice, and the server side QIODevice. Since Native Messaging is one
+ way, and recommends using pulses to support two-way communication, the
+ logic for the client-side and server-side QIODevice are very different.
+ Thus, three additional backend classes are needed as well.
+
+ QnxClientIo implements the QtRO QtROClientIoDevice wrapper around the QNX
+ specific QQnxNativeIo QIODevice (the client-side QIODevice).
+
+ QnxServerIo implements the QtRO QtROServerIoDevice wrapper around the QNX
+ specific QIOQnxSource QIODevice (the server-side QIODevice).
+
+ QnxServerImpl implements the QtRO QConnectionAbstractServer wrapper around
+ the QNX specific QQnxNativeServer, which is the server object listening for
+ connections.
+
+ Not sure if it is of interest to the Qt community, but it seems like
+ QQnxNativeIo, QIOQnxSource and QQnxNativeServer could used as an optimized
+ QLocalServer/QLocalSocket QPA for QNX.
+*/
+
+class QnxClientIo final : public QtROClientIoDevice
+{
+ Q_OBJECT
+
+public:
+ explicit QnxClientIo(QObject *parent = nullptr);
+ ~QnxClientIo() override;
+
+ QIODevice *connection() const override;
+ void connectToServer() override;
+ bool isOpen() const override;
+
+public Q_SLOTS:
+ void onError(QAbstractSocket::SocketError error);
+ void onStateChanged(QAbstractSocket::SocketState state);
+
+protected:
+ void doClose() override;
+ void doDisconnectFromServer() override;
+private:
+ QQnxNativeIo *m_socket;
+};
+
+class QnxServerIo final : public QtROServerIoDevice
+{
+public:
+ explicit QnxServerIo(QSharedPointer<QIOQnxSource> conn, QObject *parent = nullptr);
+
+ QIODevice *connection() const override;
+protected:
+ void doClose() override;
+
+private:
+ //TODO Source or Replica
+ QSharedPointer<QIOQnxSource> m_connection;
+};
+
+class QnxServerImpl final : public QConnectionAbstractServer
+{
+ Q_OBJECT
+
+public:
+ explicit QnxServerImpl(QObject *parent);
+ ~QnxServerImpl() override;
+
+ bool hasPendingConnections() const override;
+ QtROServerIoDevice *configureNewConnection() override;
+ QUrl address() const override;
+ bool listen(const QUrl &address) override;
+ QAbstractSocket::SocketError serverError() const override;
+ void close() override;
+
+private:
+ QQnxNativeServer m_server;
+};
+
+QT_END_NAMESPACE
+
+#endif // QCONNECTIONQNXBACKEND_P_H
+
--- /dev/null
+// Copyright (C) 2017-2016 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QNXIPCPRIVATE_GLOBAL_H
+#define QNXIPCPRIVATE_GLOBAL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <sys/neutrino.h>
+#include <sys/dispatch.h>
+#include <sys/siginfo.h>
+#include <unistd.h> // provides SETIOV
+#include <sys/netmgr.h> //FOR ND_LOCAL_NODE
+#include <errno.h>
+#include <QtCore/qthread.h>
+#include <QtCore/private/qglobal_p.h>
+#ifdef USE_HAM
+# include <ha/ham.h>
+#endif
+
+#define WARNING(cmd) qCWarning(QT_REMOTEOBJECT) << "Warning " #cmd << strerror(errno) \
+ << Q_FUNC_INFO << __FILE__ << __LINE__;
+
+#define WARN_ON_ERROR(cmd, ...) if (cmd(__VA_ARGS__) == -1) qCWarning(QT_REMOTEOBJECT) \
+ << "Error " #cmd << strerror(errno) << Q_FUNC_INFO << __FILE__ << __LINE__;
+
+#define WARN_AND_RETURN_ON_ERROR(cmd, retval, ...) if (cmd(__VA_ARGS__) == -1) \
+ { qCWarning(QT_REMOTEOBJECT) << "Error " #cmd << strerror(errno) \
+ << Q_FUNC_INFO << __FILE__ << __LINE__; return (retval); }
+
+#define FATAL_ON_ERROR(cmd, ...) if (cmd(__VA_ARGS__) == -1) qFatal("Error %s: %s %s %s %d", \
+ #cmd, strerror(errno), Q_FUNC_INFO, __FILE__, __LINE__);
+
+QT_BEGIN_NAMESPACE
+
+const int MAX_RETRIES = 3;
+
+enum MsgType : uint16_t { REPLICA_INIT = _IO_MAX+100,
+ REPLICA_TX_RECV,
+ SOURCE_TX_RESP,
+ SOURCE_TX_RESP_REPEAT,
+ };
+enum PulseType : uint8_t { SOURCE_TX_RQ = _PULSE_CODE_MINAVAIL+42,
+ REPLICA_WRITE,
+ TERMINATE,
+ NODE_DEATH
+ };
+union recv_msgs
+{
+ struct _pulse pulse;
+ uint16_t type;
+};
+
+template <typename T>
+class Thread : public QThread
+{
+public:
+ Thread(T *obj, const QString &name = QString()) : QThread(), m_obj(obj)
+ {
+ if (!name.isEmpty())
+ setObjectName(name);
+ }
+ void run() override
+ {
+ m_obj->thread_func();
+ }
+private:
+ T *m_obj;
+};
+
+QT_END_NAMESPACE
+
+#endif // QNXIPCPRIVATE_GLOBAL_H
+
--- /dev/null
+// Copyright (C) 2017-2016 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qconnection_qnx_global_p.h"
+#include "qconnection_qnx_qiodevices.h"
+#include "qconnection_qnx_server.h"
+#include "qconnection_qnx_qiodevices_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QQnxNativeIoPrivate::QQnxNativeIoPrivate()
+ : QIODevicePrivate()
+ , serverId(-1)
+ , channelId(-1)
+ , connectionId(-1)
+ , state(QAbstractSocket::UnconnectedState)
+ , obuffer(new QRingBuffer)
+ , thread(this, QStringLiteral("NativeIo"))
+{
+ //This lets us set msgType before any MsgSend
+ //and have the value sent as the header/type.
+ SETIOV(tx_iov + 0, &msgType, sizeof(msgType));
+ SIGEV_NONE_INIT(&tx_pulse);
+}
+
+QQnxNativeIoPrivate::~QQnxNativeIoPrivate()
+{
+ teardownConnection();
+}
+
+bool QQnxNativeIoPrivate::establishConnection()
+{
+ //On the client side, we need to create the channel/connection
+ //to listen for the server's send pulse.
+ if (channelId == -1) {
+ const int channel = ChannelCreate(0);
+ if (channel == -1) {
+ WARNING(ChannelCreate)
+ return false;
+ }
+ channelId = channel;
+ }
+
+ const int connection = ConnectAttach(ND_LOCAL_NODE, 0, channelId, _NTO_SIDE_CHANNEL, 0);
+ if (connection == -1) {
+ WARNING(ConnectAttach)
+ teardownConnection();
+ return false;
+ }
+ connectionId = connection;
+
+ SIGEV_PULSE_INIT(&tx_pulse, connection, SIGEV_PULSE_PRIO_INHERIT, SOURCE_TX_RQ, 0);
+ SIGEV_MAKE_UPDATEABLE(&tx_pulse);
+ qCDebug(QT_REMOTEOBJECT) << "in establish" << tx_pulse.sigev_code << SOURCE_TX_RQ;
+
+ if (MsgRegisterEvent(&tx_pulse, connectionId) == -1) {
+ qCWarning(QT_REMOTEOBJECT) << "Unable to register event for server" << serverName;
+ teardownConnection();
+ return false;
+ }
+
+ const int id = name_open(qPrintable(serverName), 0);
+ if (id == -1) {
+ qCWarning(QT_REMOTEOBJECT) << "Unable to connect to server" << serverName;
+ teardownConnection();
+ return false;
+ }
+ serverId = id;
+
+ thread.start();
+
+ Q_Q(QQnxNativeIo);
+ qCDebug(QT_REMOTEOBJECT) << "Before INIT: ServerId" << id << "name" << serverName << q;
+ SETIOV(tx_iov + 1, &tx_pulse, sizeof(tx_pulse));
+ SETIOV(tx_iov + 2, &channelId, sizeof(channelId));
+
+ //Send our registration message to the server
+ msgType = MsgType::REPLICA_INIT;
+ //We send tx_pulse along. When the server want to
+ //transmit data, it sends the pulse back to us.
+ //When we see that (in receive_thread) we send a
+ //message and get the server's tx data in the reply.
+ //
+ //We transmit the channelId as well - it is only used
+ //if HAM is enabled, but this will prevent a possible
+ //mismatch between code compiled with vs. without HAM
+ //(which could happen if we ever use QCONN between
+ //devices)
+ if (MsgSendv(serverId, tx_iov, 3, nullptr, 0) == -1) {
+ WARNING(MsgSendv)
+ teardownConnection();
+ return false;
+ }
+
+ state = QAbstractSocket::ConnectedState;
+ emit q->stateChanged(state);
+
+ return true;
+}
+
+void QQnxNativeIoPrivate::teardownConnection()
+{
+ Q_Q(QQnxNativeIo);
+ state = QAbstractSocket::ClosingState;
+ emit q->stateChanged(state);
+
+ stopThread();
+
+ state = QAbstractSocket::UnconnectedState;
+ emit q->stateChanged(state);
+}
+
+void QQnxNativeIoPrivate::stopThread()
+{
+ if (thread.isRunning()) {
+ for (int count = 0; count < MAX_RETRIES; ++count) {
+ if (MsgSendPulse(connectionId, -1, TERMINATE, 0) == -1) {
+ if (errno == EAGAIN) {
+ usleep(5000 + (rand() % 10) * 10000); //5 to 95 msec
+ qCWarning(QT_REMOTEOBJECT) << "Retrying terminate pulse";
+ continue;
+ }
+ qFatal("MsgSendPulse failed on terminate pulse. Error = %s", strerror(errno));
+ }
+ thread.wait();
+ return;
+ }
+ if (errno == EAGAIN)
+ qFatal("MsgSendPulse failed on terminate pulse (max retries)");
+ }
+}
+
+// method (run in a thread) to watch for connections and handle receiving data
+void QQnxNativeIoPrivate::thread_func()
+{
+ Q_Q(QQnxNativeIo);
+
+ _pulse pulse;
+
+ qCDebug(QT_REMOTEOBJECT) << "Client thread_func running";
+
+ bool running = true;
+ int nTxRequestToIgnore = 0;
+ while (running) {
+ int rcvid = MsgReceivePulse(channelId, &pulse, sizeof(pulse), nullptr);
+ if (rcvid == -1)
+ continue;
+
+ qCDebug(QT_REMOTEOBJECT) << "MsgReceivePulse unblocked, code =" << pulse.code;
+
+ switch (pulse.code) {
+ case SOURCE_TX_RQ: //The Source object wants to send us data
+ {
+ const int len = pulse.value.sival_int;
+ qCDebug(QT_REMOTEOBJECT, "TX request with len = %d, tx ignore = %d", len, nTxRequestToIgnore);
+ if (nTxRequestToIgnore) {
+ --nTxRequestToIgnore;
+ break;
+ }
+
+ int bytesLeft;
+
+ msgType = MsgType::SOURCE_TX_RESP;
+ ibLock.lockForWrite();
+ iov_t reply_vector[2];
+ SETIOV(reply_vector, &bytesLeft, sizeof(bytesLeft));
+ SETIOV(reply_vector+1, buffer.reserve(len), size_t(len));
+ const int res = MsgSendv(serverId, tx_iov, 1, reply_vector, 2);
+
+ if (res == -1) {
+ buffer.chop(len);
+ ibLock.unlock();
+ WARNING(MsgSendv);
+ break;
+ }
+ ibLock.unlock();
+
+ qCDebug(QT_REMOTEOBJECT) << "Reply said bytesLeft =" << bytesLeft;
+
+ if (bytesLeft) {
+ msgType = MsgType::SOURCE_TX_RESP_REPEAT;
+ ibLock.lockForWrite();
+ SETIOV(reply_vector, &nTxRequestToIgnore, sizeof(nTxRequestToIgnore));
+ SETIOV(reply_vector+1, buffer.reserve(bytesLeft), size_t(bytesLeft));
+ const int res = MsgSendv(serverId, tx_iov, 1, reply_vector, 2);
+ if (res == -1) {
+ buffer.chop(bytesLeft);
+ ibLock.unlock();
+ WARNING(MsgSendv);
+ break;
+ }
+ ibLock.unlock();
+ qCDebug(QT_REMOTEOBJECT) << "Reply2 said nTxRequestToIgnore =" << nTxRequestToIgnore;
+ }
+
+ QMetaObject::invokeMethod(q, "readyRead", Qt::QueuedConnection);
+ }
+ break;
+ case REPLICA_WRITE: //Our node has data to send
+ {
+ const int len = pulse.value.sival_int;
+ obLock.lockForWrite(); //NAR (Not-An-Error)
+ const QByteArray payload = obuffer->read();
+ obLock.unlock();
+ Q_ASSERT(len == payload.length());
+
+ msgType = MsgType::REPLICA_TX_RECV;
+ SETIOV(tx_iov + 1, payload.constData(), size_t(len));
+ if (MsgSendvs(serverId, tx_iov, 2, nullptr, 0) == -1) {
+ WARNING(MsgSendvs);
+ obLock.lockForWrite();
+ if (obuffer->isEmpty()) {
+ obuffer->append(payload);
+ } else {
+ //Since QRingBuffer just holds a QList of
+ //QByteArray, copying the QByteArrays to
+ //another container is cheap.
+ QRingBuffer *newBuffer = new QRingBuffer;
+ newBuffer->append(payload);
+ while (!obuffer->isEmpty())
+ newBuffer->append(obuffer->read());
+ obuffer.reset(newBuffer);
+ }
+ obLock.unlock();
+ WARNING(MsgSendvs);
+ }
+ }
+ break;
+ case TERMINATE:
+ running = false;
+ continue;
+ case NODE_DEATH:
+ qCWarning(QT_REMOTEOBJECT) << "Host node died";
+ running = false;
+ emit q->error(QAbstractSocket::RemoteHostClosedError);
+ continue;
+ default:
+ /* some other unexpected message */
+ qCWarning(QT_REMOTEOBJECT) << "unexpected pulse type:" << pulse.type << __FILE__ << __LINE__;
+ WARN_ON_ERROR(MsgError, rcvid, ENOSYS)
+ break;
+ }
+ }
+
+ if (serverId >= 0) {
+ WARN_ON_ERROR(name_close, serverId)
+ serverId = -1;
+ }
+
+ if (tx_pulse.sigev_notify & SIGEV_FLAG_HANDLE) {
+ WARN_ON_ERROR(MsgUnregisterEvent, &tx_pulse);
+ SIGEV_NONE_INIT(&tx_pulse);
+ }
+
+ if (connectionId >= 0) {
+ WARN_ON_ERROR(ConnectDetach, connectionId)
+ connectionId = -1;
+ }
+
+ if (channelId >= 0) {
+ WARN_ON_ERROR(ChannelDestroy, channelId)
+ channelId = -1;
+ }
+
+ qCDebug(QT_REMOTEOBJECT) << "Client thread_func stopped";
+}
+
+QQnxNativeIo::QQnxNativeIo(QObject *parent)
+ : QIODevice(*new QQnxNativeIoPrivate, parent)
+{
+}
+
+QQnxNativeIo::~QQnxNativeIo()
+{
+ close();
+}
+
+bool QQnxNativeIo::connectToServer(QIODevice::OpenMode openMode)
+{
+ Q_D(QQnxNativeIo);
+ Q_UNUSED(openMode)
+
+ if (state() == QAbstractSocket::ConnectedState ||
+ state() == QAbstractSocket::ConnectingState) {
+ setErrorString(QStringLiteral("Already connected"));
+ emit error(QAbstractSocket::OperationError);
+ return false;
+ }
+
+ const int omMask = QIODevice::Append | QIODevice::Truncate | QIODevice::Text;
+ if (openMode & omMask)
+ qCWarning(QT_REMOTEOBJECT, "Tried to open using unsupported open mode flags.");
+
+ d->errorString.clear();
+ d->state = QAbstractSocket::ConnectingState;
+ emit stateChanged(d->state);
+
+ if (d->serverName.isEmpty()) {
+ setErrorString(QStringLiteral("serverName not set"));
+ emit error(QAbstractSocket::HostNotFoundError);
+ return false;
+ }
+
+ QIODevice::open(openMode & (~omMask));
+
+ if (!d->establishConnection()) {
+ QIODevice::close();
+ qCWarning(QT_REMOTEOBJECT, "Failed to connect to server");
+ emit error(QAbstractSocket::UnknownSocketError);
+ return false;
+ }
+
+ emit stateChanged(d->state);
+
+ return true;
+}
+
+bool QQnxNativeIo::connectToServer(const QString &name, QIODevice::OpenMode openMode)
+{
+ setServerName(name);
+ return connectToServer(openMode);
+}
+
+void QQnxNativeIo::disconnectFromServer()
+{
+ close();
+}
+
+void QQnxNativeIo::setServerName(const QString &name)
+{
+ Q_D(QQnxNativeIo);
+
+ if (d->state != QAbstractSocket::UnconnectedState) {
+ qCWarning(QT_REMOTEOBJECT) << "QQnxNativeIo::setServerName() called while not unconnected";
+ return;
+ }
+
+ d->serverName = name;
+}
+
+QString QQnxNativeIo::serverName() const
+{
+ Q_D(const QQnxNativeIo);
+ return d->serverName;
+}
+
+void QQnxNativeIo::abort()
+{
+ Q_D(QQnxNativeIo);
+
+ d->stopThread();
+ //Don't need mutex since thread is stopped
+ d->obuffer->clear();
+ d->buffer.clear();
+
+ d->state = QAbstractSocket::UnconnectedState;
+ emit stateChanged(d->state);
+ QIODevice::close();
+}
+
+bool QQnxNativeIo::isSequential() const
+{
+ return true;
+}
+
+qint64 QQnxNativeIo::bytesAvailable() const
+{
+ Q_D(const QQnxNativeIo);
+
+ d->ibLock.lockForRead();
+ qint64 size = d->buffer.size();
+ d->ibLock.unlock();
+
+ return size;
+}
+
+qint64 QQnxNativeIo::bytesToWrite() const
+{
+ Q_D(const QQnxNativeIo);
+
+ d->obLock.lockForRead();
+ qint64 size = d->obuffer->size();
+ d->obLock.unlock();
+
+ return size;
+}
+
+bool QQnxNativeIo::open(QIODevice::OpenMode openMode)
+{
+ const int omMask = QIODevice::Append | QIODevice::Truncate | QIODevice::Text;
+ if (openMode & omMask)
+ qCWarning(QT_REMOTEOBJECT, "Tried to open using unsupported open mode flags.");
+
+ return connectToServer(openMode & (~omMask));
+}
+
+void QQnxNativeIo::close()
+{
+ Q_D(QQnxNativeIo);
+
+ if (!isOpen())
+ return;
+
+ d->teardownConnection();
+
+ d->obuffer->clear();
+ d->buffer.clear();
+ QIODevice::close();
+}
+
+QAbstractSocket::SocketState QQnxNativeIo::state() const
+{
+ Q_D(const QQnxNativeIo);
+ return d->state;
+}
+
+bool QQnxNativeIo::waitForBytesWritten(int msecs)
+{
+ //TODO - This method isn't used by Qt Remote Objects, but would
+ //need to be implemented before this class could be used as a
+ //generic QIODevice.
+ Q_UNUSED(msecs)
+ Q_ASSERT(false);
+ return false;
+}
+
+bool QQnxNativeIo::waitForReadyRead(int msecs)
+{
+ //TODO - This method isn't used by Qt Remote Objects, but would
+ //need to be implemented before this class could be used as a
+ //generic QIODevice.
+ Q_UNUSED(msecs)
+ Q_ASSERT(false);
+ return false;
+}
+
+qint64 QQnxNativeIo::readData(char *data, qint64 size)
+{
+ Q_D(QQnxNativeIo);
+ qint64 read;
+
+ if (!isReadable())
+ return 0;
+
+ d->ibLock.lockForWrite(); //NAR (Not-An-Error)
+ read = d->buffer.read(data, size);
+ d->ibLock.unlock();
+
+ return read;
+}
+
+qint64 QQnxNativeIo::writeData(const char *data, qint64 size)
+{
+ Q_D(QQnxNativeIo);
+
+ if (!isWritable())
+ return 0;
+
+ if (size < 0 || size > INT_MAX) {
+ qCWarning(QT_REMOTEOBJECT) << "Invalid size (" << size << ") passed to QtRO QNX backend writeData().";
+ return -1;
+ }
+
+ int isize = static_cast<int>(size);
+
+ d->obLock.lockForWrite();
+ d->obuffer->append(QByteArray(data, isize));
+ d->obLock.unlock();
+
+ WARN_AND_RETURN_ON_ERROR(MsgSendPulse, -1, d->connectionId, -1, PulseType::REPLICA_WRITE, isize)
+
+ return size;
+}
+
+/* QIOQnxSource ***************************************************************/
+
+QIOQnxSourcePrivate::QIOQnxSourcePrivate(int _rcvid)
+ : QIODevicePrivate()
+ , rcvid(_rcvid)
+ , state(QAbstractSocket::ConnectedState)
+{
+}
+
+QIOQnxSource::QIOQnxSource(int rcvid, QObject *parent)
+ : QIODevice(*new QIOQnxSourcePrivate(rcvid), parent)
+{
+ setOpenMode(QIODevice::ReadWrite);
+}
+
+QIOQnxSource::~QIOQnxSource()
+{
+ close();
+}
+
+bool QIOQnxSource::isSequential() const
+{
+ return true;
+}
+
+qint64 QIOQnxSource::bytesAvailable() const
+{
+ Q_D(const QIOQnxSource);
+
+ d->ibLock.lockForRead();
+ qint64 size = d->buffer.size();
+ d->ibLock.unlock();
+
+ return size;
+}
+
+qint64 QIOQnxSource::bytesToWrite() const
+{
+ Q_D(const QIOQnxSource);
+
+ d->obLock.lockForRead();
+ qint64 size = d->obuffer.size();
+ d->obLock.unlock();
+
+ return size;
+}
+
+bool QIOQnxSource::open(QIODevice::OpenMode openMode)
+{
+ Q_UNUSED(openMode)
+ return false;
+}
+
+void QIOQnxSource::onDisconnected()
+{
+ close();
+ emit disconnected();
+}
+
+void QIOQnxSource::close()
+{
+ Q_D(QIOQnxSource);
+
+ if (!isOpen())
+ return;
+
+ d->state = QAbstractSocket::ClosingState;
+ emit stateChanged(d->state);
+
+ d->state = QAbstractSocket::UnconnectedState;
+ emit stateChanged(d->state);
+
+ d->obuffer.clear();
+ d->buffer.clear();
+ QIODevice::close();
+}
+
+QAbstractSocket::SocketState QIOQnxSource::state() const
+{
+ Q_D(const QIOQnxSource);
+ return d->state;
+}
+
+bool QIOQnxSource::waitForBytesWritten(int msecs)
+{
+ //TODO - This method isn't used by Qt Remote Objects, but would
+ //need to be implemented before this class could be used as a
+ //generic QIODevice.
+ Q_UNUSED(msecs)
+ Q_ASSERT(false);
+ return false;
+}
+
+bool QIOQnxSource::waitForReadyRead(int msecs)
+{
+ //TODO - This method isn't used by Qt Remote Objects, but would
+ //need to be implemented before this class could be used as a
+ //generic QIODevice.
+ Q_UNUSED(msecs)
+ Q_ASSERT(false);
+ return false;
+}
+
+qint64 QIOQnxSource::readData(char *data, qint64 size)
+{
+ Q_D(QIOQnxSource);
+ qint64 read;
+
+ if (!isReadable())
+ return 0;
+
+ d->ibLock.lockForWrite(); //NAR (Not-An-Error)
+ read = d->buffer.read(data, size);
+ d->ibLock.unlock();
+
+ return read;
+}
+
+qint64 QIOQnxSource::writeData(const char *data, qint64 size)
+{
+ Q_D(QIOQnxSource);
+
+ if (!isWritable())
+ return 0;
+
+ if (size < 0 || size > INT_MAX) {
+ qCWarning(QT_REMOTEOBJECT) << "Invalid size (" << size << ") passed to QtRO QNX backend writeData().";
+ return -1;
+ }
+
+ int isize = static_cast<int>(size);
+
+ d->obLock.lockForWrite();
+ d->obuffer.append(QByteArray(data, isize));
+ d->obLock.unlock();
+
+ if (!d->m_serverClosing.loadRelaxed()) {
+ d->m_event.sigev_value.sival_int = isize;
+ WARN_ON_ERROR(MsgDeliverEvent, d->rcvid, &(d->m_event))
+ }
+
+ return size;
+}
+
+QT_END_NAMESPACE
--- /dev/null
+// Copyright (C) 2017-2016 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQNXNATIVEIO_H
+#define QQNXNATIVEIO_H
+
+#include <QtNetwork/qabstractsocket.h>
+#include <QtRemoteObjects/qtremoteobjectglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+/*
+ * The implementation of the Source and Replica
+ * side QIODevice look like they will be fairly
+ * different, including different APIs. So
+ * creating a 2nd derived type for the source.
+ *
+ * TODO: revisit if these can be combined into a
+ * single type.
+ *
+ * With two types, QQnxNativeIo will need to get
+ * Source or Replica added. Not sure what intuitive
+ * names are yet. So for now, QQnxNativeIo is the
+ * Replica side, QIOQnxSourcePrivate is the Source
+ * side. Revisit the name as this matures.
+ *
+*/
+class QQnxNativeIoPrivate;
+class QIOQnxSourcePrivate;
+
+class Q_REMOTEOBJECTS_EXPORT QQnxNativeIo : public QIODevice
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QQnxNativeIo)
+
+public:
+ explicit QQnxNativeIo(QObject *parent = nullptr);
+ ~QQnxNativeIo() override;
+
+ bool connectToServer(OpenMode openMode = ReadWrite);
+ bool connectToServer(const QString &name, OpenMode openMode = ReadWrite);
+ void disconnectFromServer();
+
+ void setServerName(const QString &name);
+ QString serverName() const;
+
+ void abort();
+ bool isSequential() const override;
+ qint64 bytesAvailable() const override;
+ qint64 bytesToWrite() const override;
+ bool open(OpenMode openMode = ReadWrite) override;
+ void close() override;
+ QAbstractSocket::SocketError error() const;
+ bool flush();
+ bool isValid() const;
+
+ QAbstractSocket::SocketState state() const;
+ bool waitForBytesWritten(int msecs = 30000) override;
+ bool waitForConnected(int msecs = 30000);
+ bool waitForDisconnected(int msecs = 30000);
+ bool waitForReadyRead(int msecs = 30000) override;
+
+Q_SIGNALS:
+ void connected();
+ void disconnected();
+ void error(QAbstractSocket::SocketError socketError);
+ void stateChanged(QAbstractSocket::SocketState socketState);
+
+protected:
+ qint64 readData(char*, qint64) override;
+ qint64 writeData(const char*, qint64) override;
+
+private:
+ Q_DISABLE_COPY(QQnxNativeIo)
+};
+Q_DECLARE_TYPEINFO(QQnxNativeIo, Q_RELOCATABLE_TYPE);
+
+class Q_REMOTEOBJECTS_EXPORT QIOQnxSource : public QIODevice
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QIOQnxSource)
+
+public:
+ explicit QIOQnxSource(int rcvid, QObject *parent = nullptr);
+ ~QIOQnxSource() override;
+
+ bool isSequential() const override;
+ qint64 bytesAvailable() const override;
+ qint64 bytesToWrite() const override;
+ void close() override;
+ QAbstractSocket::SocketError error() const;
+ bool isValid() const;
+
+ QAbstractSocket::SocketState state() const;
+ bool waitForBytesWritten(int msecs = 30000) override;
+ bool waitForConnected(int msecs = 30000);
+ bool waitForDisconnected(int msecs = 30000);
+ bool waitForReadyRead(int msecs = 30000) override;
+
+Q_SIGNALS:
+ void disconnected();
+ void error(QAbstractSocket::SocketError socketError);
+ void stateChanged(QAbstractSocket::SocketState socketState);
+
+protected:
+ qint64 readData(char*, qint64) override;
+ qint64 writeData(const char*, qint64) override;
+ bool open(OpenMode openMode) override;
+
+private Q_SLOTS:
+ void onDisconnected();
+
+private:
+ Q_DISABLE_COPY(QIOQnxSource)
+ friend class QQnxNativeServerPrivate;
+ friend class QnxServerIo;
+};
+Q_DECLARE_TYPEINFO(QIOQnxSource, Q_RELOCATABLE_TYPE);
+
+QT_END_NAMESPACE
+
+#endif // QQNXNATIVEIO_H
--- /dev/null
+// Copyright (C) 2017-2016 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQNXNATIVEIO_P_H
+#define QQNXNATIVEIO_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qconnection_qnx_qiodevices.h"
+#include "qconnection_qnx_global_p.h"
+
+#include <QtCore/qreadwritelock.h>
+#include <QtCore/qscopedpointer.h>
+
+#include "private/qiodevice_p.h"
+#include "private/qringbuffer_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQnxNativeIoPrivate : public QIODevicePrivate
+{
+ Q_DECLARE_PUBLIC(QQnxNativeIo)
+
+ mutable QReadWriteLock ibLock;
+ mutable QReadWriteLock obLock;
+
+public:
+ QQnxNativeIoPrivate();
+ ~QQnxNativeIoPrivate();
+ void thread_func();
+ bool establishConnection();
+ void teardownConnection();
+ void stopThread();
+ QString serverName;
+ int serverId, channelId, connectionId;
+ sigevent tx_pulse;
+ QAbstractSocket::SocketState state;
+ QScopedPointer<QRingBuffer> obuffer;
+ MsgType msgType;
+ iov_t tx_iov[3], rx_iov[2];
+ Thread<QQnxNativeIoPrivate> thread;
+};
+
+class QIOQnxSourcePrivate : public QIODevicePrivate
+{
+ Q_DECLARE_PUBLIC(QIOQnxSource)
+
+ friend class QQnxNativeServerPrivate;
+
+ mutable QReadWriteLock ibLock;
+ mutable QReadWriteLock obLock;
+
+public:
+ QIOQnxSourcePrivate(int _rcvid);
+ int rcvid;
+ QAbstractSocket::SocketState state;
+ MsgType msgType;
+ sigevent m_event;
+ QRingBuffer obuffer;
+ QAtomicInt m_serverClosing;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQNXNATIVEIO_P_H
+
--- /dev/null
+// Copyright (C) 2017-2016 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qconnection_qnx_global_p.h"
+#include "qconnection_qnx_qiodevices_p.h"
+#include "qconnection_qnx_server_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QQnxNativeServer::QQnxNativeServer(QObject *parent)
+ : QObject(*new QQnxNativeServerPrivate, parent)
+{
+}
+
+QQnxNativeServer::~QQnxNativeServer()
+{
+}
+
+void QQnxNativeServer::close()
+{
+ Q_D(QQnxNativeServer);
+ d->teardownServer();
+}
+
+bool QQnxNativeServer::hasPendingConnections() const
+{
+ Q_D(const QQnxNativeServer);
+ d->mutex.lock();
+ const int len = d->pending.length();
+ d->mutex.unlock();
+ return len > 0;
+}
+
+bool QQnxNativeServer::isListening() const
+{
+ Q_D(const QQnxNativeServer);
+ return !(d->serverName.isEmpty());
+}
+
+bool QQnxNativeServer::listen(const QString &name)
+{
+ Q_D(QQnxNativeServer);
+ if (isListening()) {
+ qCWarning(QT_REMOTEOBJECT) << "QQnxNativeServer::listen() called when already listening";
+ return false;
+ }
+
+ if (name.isEmpty()) {
+ d->error = QAbstractSocket::HostNotFoundError;
+ QString function = QLatin1String("QQnxNativeServer::listen");
+ d->errorString = tr("%1: Name error").arg(function);
+ return false;
+ }
+
+ if (!d->listen(name)) {
+ d->serverName.clear();
+ return false;
+ }
+
+ d->serverName = name;
+ return true;
+}
+
+QSharedPointer<QIOQnxSource> QQnxNativeServer::nextPendingConnection()
+{
+ Q_D(QQnxNativeServer);
+ d->mutex.lock();
+ Q_ASSERT(d->pending.length() > 0);
+ auto io = d->pending.takeFirst();
+ d->mutex.unlock();
+ return io;
+}
+
+QString QQnxNativeServer::serverName() const
+{
+ Q_D(const QQnxNativeServer);
+ return d->serverName;
+}
+
+bool QQnxNativeServer::waitForNewConnection(int msec, bool *timedOut)
+{
+ //TODO - This method isn't used by Qt Remote Objects, but would
+ //need to be implemented before this class could be used as a
+ //connection server (like QTcpServer or QLocalServer).
+ Q_UNUSED(msec)
+ Q_UNUSED(timedOut)
+ Q_ASSERT(false);
+ return false;
+}
+
+void QQnxNativeServer::onSourceClosed()
+{
+ Q_D(QQnxNativeServer);
+ QIOQnxSource *conn = qobject_cast<QIOQnxSource *>(sender());
+ Q_ASSERT(conn);
+
+ d->cleanupIOSource(conn);
+}
+
+QQnxNativeServerPrivate::QQnxNativeServerPrivate()
+ : error(QAbstractSocket::UnknownSocketError)
+ , thread(this, QStringLiteral("NativeServer"))
+{
+}
+
+QQnxNativeServerPrivate::~QQnxNativeServerPrivate()
+{
+ if (thread.isRunning())
+ teardownServer();
+}
+
+// method (run in a thread) to watch for connections and handle receiving data
+void QQnxNativeServerPrivate::thread_func()
+{
+ struct _msg_info msg_info;
+ recv_msgs recv_buf;
+ QList<iov_t> resp_repeat_iov(5);
+
+ qCDebug(QT_REMOTEOBJECT) << "Server thread_func running";
+
+ while (running.loadRelaxed()) {
+ // wait for messages and pulses
+ int rcvid = MsgReceive(attachStruct->chid, &recv_buf, sizeof(_pulse), &msg_info);
+ qCDebug(QT_REMOTEOBJECT) << "MsgReceive unblocked. Rcvid" << rcvid << " Scoid" << msg_info.scoid;
+ if (rcvid == -1) {
+ if (errno != ESRCH) // ESRCH means the channel closed
+ WARNING(MsgReceive)
+ break;
+ }
+
+ if (0 == rcvid) {
+ qCDebug(QT_REMOTEOBJECT) << "Pulse" << recv_buf.pulse.code;
+ /* we received a pulse
+ */
+ switch (recv_buf.pulse.code) {
+ case _PULSE_CODE_DISCONNECT:
+ {
+ /* a client has disconnected. Verify that it is
+ * our client, and if so, clean up our saved state
+ */
+ const int scoid = recv_buf.pulse.scoid;
+ const QSet<int> coids = connections.take(scoid);
+ for (int coid : coids)
+ {
+ const uint64_t uid = static_cast<uint64_t>(scoid) << 32 | static_cast<uint32_t>(coid);
+ QSharedPointer<QIOQnxSource> io;
+ mutex.lock();
+ if (sources.contains(uid))
+ io = sources.take(uid);
+ mutex.unlock();
+#ifdef USE_HAM
+ ham_action_t *action = hamActions.take(uid);
+ ham_action_remove(action, 0);
+ ham_action_handle_free(action);
+#endif
+
+ if (!io.isNull()) {
+ io->d_func()->m_serverClosing.ref();
+ QMetaObject::invokeMethod(io.data(),"onDisconnected",Qt::QueuedConnection);
+ }
+ }
+
+ /* always do the ConnectDetach() */
+ //Don't care about return value
+ ConnectDetach(recv_buf.pulse.scoid);
+ qCDebug(QT_REMOTEOBJECT) << "disconnect from client" << recv_buf.pulse.scoid;
+ }
+ break;
+ case _PULSE_CODE_COIDDEATH:
+ {
+ /* a connection has gone away. Verify that it is
+ * our client, and if so, clean up our saved state
+ */
+ const int coid = recv_buf.pulse.value.sival_int;
+
+ if (ConnectServerInfo(0, coid, nullptr) != coid) {
+ const int scoid = recv_buf.pulse.scoid;
+ if (connections.value(scoid).contains(coid))
+ connections[scoid].remove(coid);
+ QSharedPointer<QIOQnxSource> io;
+ const uint64_t uid = static_cast<uint64_t>(scoid) << 32 | static_cast<uint32_t>(coid);
+ mutex.lock();
+ if (sources.contains(uid))
+ io = sources.take(uid);
+ mutex.unlock();
+#ifdef USE_HAM
+ ham_action_t *action = hamActions.take(uid);
+ ham_action_remove(action, 0);
+ ham_action_handle_free(action);
+#endif
+
+ if (!io.isNull()) {
+ io->d_func()->m_serverClosing.ref();
+ QMetaObject::invokeMethod(io.data(),"onDisconnected",Qt::QueuedConnection);
+ }
+ qCDebug(QT_REMOTEOBJECT) << "Connection dropped" << coid;
+ }
+ break;
+ }
+ break;
+ case _PULSE_CODE_UNBLOCK:
+ if (running.loadRelaxed()) {
+ // did we forget to Reply to our client?
+ qCWarning(QT_REMOTEOBJECT) << "got an unblock pulse, did you forget to reply to your client?";
+ WARN_ON_ERROR(MsgError, recv_buf.pulse.value.sival_int, EINTR)
+ }
+ break;
+ default:
+ qCWarning(QT_REMOTEOBJECT) << "unexpected pulse code: " << recv_buf.pulse.code << __FILE__ << __LINE__;
+ break;
+ }
+ continue;
+ }
+
+ /* not an error, not a pulse, therefore a message */
+ switch (recv_buf.type)
+ {
+ qCDebug(QT_REMOTEOBJECT) << "Msg Received" << recv_buf.type;
+
+ case _IO_CONNECT:
+ /* _IO_CONNECT because someone did a name_open() to us and we are
+ * in the network case (gns running). We must EOK it. */
+ if (connections.contains(msg_info.scoid)
+ && connections.value(msg_info.scoid).contains(msg_info.coid))
+ {
+ qCWarning(QT_REMOTEOBJECT) << "Already connected rcvid seen" << connections << rcvid;
+ FATAL_ON_ERROR(MsgError, rcvid, EADDRINUSE)
+ } else {
+ if (!connections.contains(msg_info.scoid))
+ connections.insert(msg_info.scoid, QSet<int>());
+ connections[msg_info.scoid] << msg_info.coid;
+ qCDebug(QT_REMOTEOBJECT) << "New connection (qns)" << msg_info.coid << msg_info.scoid;
+ const uint64_t uid = static_cast<uint64_t>(msg_info.scoid) << 32 | static_cast<uint32_t>(msg_info.coid);
+ createSource(rcvid, uid, msg_info.pid); // Reads more and then calls MsgReply
+ }
+ break;
+
+ case MsgType::REPLICA_INIT:
+ qCDebug(QT_REMOTEOBJECT) << "MsgType::INIT received" << rcvid << msg_info.scoid << msg_info.tid << msg_info.chid << msg_info.coid;
+ //Check if this is a new connection (not gns)
+ if (connections.contains(msg_info.scoid)
+ && connections.value(msg_info.scoid).contains(msg_info.coid))
+ {
+ qCWarning(QT_REMOTEOBJECT) << "Already connected rcvid seen" << connections << rcvid;
+ FATAL_ON_ERROR(MsgError, rcvid, EADDRINUSE)
+ } else {
+ if (!connections.contains(msg_info.scoid))
+ connections.insert(msg_info.scoid, QSet<int>());
+ connections[msg_info.scoid] << msg_info.coid;
+ qCDebug(QT_REMOTEOBJECT) << "New connection (non-gns)" << rcvid << msg_info.coid << msg_info.scoid;
+ const uint64_t uid = static_cast<uint64_t>(msg_info.scoid) << 32 | static_cast<uint32_t>(msg_info.coid);
+ createSource(rcvid, uid, msg_info.pid); // Reads more and then calls MsgReply
+ }
+
+ break;
+ case MsgType::SOURCE_TX_RESP:
+ {
+ WARN_ON_ERROR(MsgInfo, rcvid, &msg_info);
+ const uint64_t uid = static_cast<uint64_t>(msg_info.scoid) << 32 | static_cast<uint32_t>(msg_info.coid);
+ mutex.lock();
+ Q_ASSERT(sources.contains(uid));
+ auto io = sources.value(uid);
+ mutex.unlock();
+
+ io->d_func()->obLock.lockForWrite(); //NAR (Not-An-Error)
+ const QByteArray data = io->d_func()->obuffer.read();
+ const int bytesLeft = io->d_func()->obuffer.size();
+ io->d_func()->obLock.unlock();
+
+ qCDebug(QT_REMOTEOBJECT) << "server received SOURCE_TX_RESP with length" << msg_info.dstmsglen << "/" << data.length() << "Available:" << bytesLeft;
+ Q_ASSERT(data.length() == static_cast<int>(msg_info.dstmsglen - sizeof(bytesLeft)));
+
+ iov_t reply_vector[2];
+ SETIOV(reply_vector, &bytesLeft, sizeof(bytesLeft));
+ SETIOV(reply_vector+1, data.data(), size_t(data.length()));
+
+ FATAL_ON_ERROR(MsgReplyv, rcvid, EOK, reply_vector, 2)
+ }
+ break;
+ case MsgType::SOURCE_TX_RESP_REPEAT:
+ {
+ WARN_ON_ERROR(MsgInfo, rcvid, &msg_info);
+ const uint64_t uid = static_cast<uint64_t>(msg_info.scoid) << 32 | static_cast<uint32_t>(msg_info.coid);
+ mutex.lock();
+ Q_ASSERT(sources.contains(uid));
+ auto io = sources.value(uid);
+ mutex.unlock();
+
+ int len_taken = 0;
+ const int to_send = msg_info.dstmsglen - sizeof(int); //Exclude the buffer count
+ QByteArrayList qba_array;
+ io->d_func()->obLock.lockForWrite(); //NAR (Not-An-Error)
+ qCDebug(QT_REMOTEOBJECT) << "server received SOURCE_TX_RESP_REPEAT with length" << msg_info.dstmsglen << "Available:" << io->d_func()->obuffer.size();
+ while (len_taken != to_send)
+ {
+ QByteArray data = io->d_func()->obuffer.read();
+ qba_array << data;
+ len_taken += data.length();
+ if (data.length() == 0) { // We somehow reached the end
+ qCWarning(QT_REMOTEOBJECT) << "Reached the end of the buffer before getting the requested amount of data." << Q_FUNC_INFO << __FILE__ << __LINE__;
+ break;
+ }
+ }
+ qCDebug(QT_REMOTEOBJECT) << "grabbing more data" << len_taken << to_send << qba_array.length();
+ io->d_func()->obLock.unlock();
+
+ Q_ASSERT(len_taken == to_send);
+ const int buffers_taken = qba_array.length();
+
+ resp_repeat_iov.resize(buffers_taken+1);
+ SETIOV(&resp_repeat_iov[0], &buffers_taken, sizeof(buffers_taken));
+ for (int i = 1; i <= buffers_taken; ++i)
+ SETIOV(&resp_repeat_iov[i], qba_array.at(i-1).constData(), size_t(qba_array.at(i-1).length()));
+ FATAL_ON_ERROR(MsgReplyv, rcvid, EOK, resp_repeat_iov.data(), buffers_taken+1)
+ }
+ break;
+ case MsgType::REPLICA_TX_RECV:
+ {
+ FATAL_ON_ERROR(MsgInfo,rcvid, &msg_info)
+ const uint64_t uid = static_cast<uint64_t>(msg_info.scoid) << 32 | static_cast<uint32_t>(msg_info.coid);
+ mutex.lock();
+ Q_ASSERT(sources.contains(uid));
+ auto io = sources.value(uid);
+ mutex.unlock();
+
+ //Long-lock, use buffer+memcpy if we run into trouble
+ io->d_func()->ibLock.lockForWrite();
+ qint32 toRead = msg_info.msglen-sizeof(MsgType);
+ char *payload = io->d_func()->buffer.reserve(toRead);
+
+ const int res = MsgRead(rcvid, payload, toRead, sizeof(MsgType));
+ if (-1 == res) {
+ io->d_func()->buffer.chop(toRead);
+ io->d_func()->ibLock.unlock();
+ qFatal("MsgRead");
+ }
+ Q_ASSERT(res == toRead);
+
+ if (res < toRead)
+ io->d_func()->buffer.chop(toRead - res);
+ io->d_func()->ibLock.unlock();
+
+ FATAL_ON_ERROR(MsgReply, rcvid, EOK, nullptr, 0)
+
+ qCDebug(QT_REMOTEOBJECT) << "server received REPLICA_TX_RECV" << payload << toRead;
+
+ emit io->readyRead();
+ }
+ break;
+ default:
+ /* some other unexpected message */
+ qCWarning(QT_REMOTEOBJECT) << "unexpected message type" << recv_buf.type << __FILE__ << __LINE__;
+ WARN_ON_ERROR(MsgError, rcvid, ENOSYS)
+ break;
+ }
+ }
+ mutex.lock();
+ for (auto io: sources)
+ io->d_func()->m_serverClosing.ref();
+ mutex.unlock();
+ name_detach(attachStruct, 0);
+ attachStruct = nullptr;
+ qCDebug(QT_REMOTEOBJECT) << "Server thread_func stopped";
+}
+
+bool QQnxNativeServerPrivate::listen(const QString &name)
+{
+ attachStruct = name_attach(nullptr, qPrintable(name), 0);
+ if (attachStruct == nullptr) {
+ qCDebug(QT_REMOTEOBJECT, "name_attach call failed");
+ return false;
+ }
+ terminateCoid = ConnectAttach(ND_LOCAL_NODE, 0, attachStruct->chid, _NTO_SIDE_CHANNEL, 0);
+ if (terminateCoid == -1) {
+ qCDebug(QT_REMOTEOBJECT, "ConnectAttach failed");
+ return false;
+ }
+
+ running.ref();
+ thread.start();
+
+ return true;
+}
+
+void QQnxNativeServerPrivate::cleanupIOSource(QIOQnxSource *conn)
+{
+ QSharedPointer<QIOQnxSource> io;
+ mutex.lock();
+ for (auto i = sources.begin(), end = sources.end(); i != end; ++i) {
+ if (i.value().data() == conn) {
+ io = std::move(i.value());
+ sources.erase(i);
+ break;
+ }
+ }
+ mutex.unlock();
+ if (!io.isNull()) {
+ io->d_func()->m_serverClosing.ref();
+ io->close();
+ }
+}
+
+void QQnxNativeServerPrivate::teardownServer()
+{
+ if (attachStruct == nullptr)
+ return;
+
+ running.deref();
+ MsgSendPulse(terminateCoid, SIGEV_PULSE_PRIO_INHERIT, _PULSE_CODE_UNBLOCK, 0);
+ ConnectDetach(terminateCoid);
+ thread.wait();
+
+ //Existing QIOQnxSources will be deleted along with object
+ //threads gone, don't need to use mutex
+ sources.clear();
+#ifdef USE_HAM
+ if (hamAvailable)
+ closeHamResources();
+#endif
+}
+
+void QQnxNativeServerPrivate::createSource(int rcvid, uint64_t uid, pid_t toPid)
+{
+ Q_Q(QQnxNativeServer);
+#ifndef USE_HAM
+ Q_UNUSED(toPid)
+#endif
+ auto io = QSharedPointer<QIOQnxSource>(new QIOQnxSource(rcvid));
+ io->moveToThread(q->thread());
+ QObject::connect(io.data(), &QIOQnxSource::aboutToClose,
+ q, &QQnxNativeServer::onSourceClosed);
+
+ QIOQnxSourcePrivate *iop = io->d_func();
+ FATAL_ON_ERROR(MsgRead, rcvid, &(iop->m_event), sizeof(sigevent), sizeof(MsgType))
+ int sentChannelId;
+ FATAL_ON_ERROR(MsgRead, rcvid, &sentChannelId, sizeof(int), sizeof(MsgType)+sizeof(sigevent))
+ FATAL_ON_ERROR(MsgReply, rcvid, EOK, nullptr, 0)
+
+ mutex.lock();
+ sources.insert(uid, io);
+ pending.append(io);
+ mutex.unlock();
+
+ //push an event into the main threads eventloop to emit newConnection.
+ QMetaObject::invokeMethod(q,"newConnection",Qt::QueuedConnection);
+#ifdef USE_HAM
+ if (!hamInitialized) {
+ hamInitialized = true;
+ hamAvailable = initializeHam();
+ }
+ if (hamAvailable)
+ configureHamDeath(sentChannelId, toPid, uid);
+#endif
+}
+
+#ifdef USE_HAM
+bool QQnxNativeServerPrivate::initializeHam()
+{
+ if (access("/proc/ham",F_OK) != 0) {
+ WARNING(access(/proc/ham))
+ return false;
+ }
+ ham_connect(0);
+ pid_t pid = getpid();
+ hamEntityHandle = ham_attach(qPrintable(serverName), ND_LOCAL_NODE, pid, NULL, 0);
+ if (!hamEntityHandle) {
+ WARNING(ham_attach)
+ ham_disconnect(0);
+ return false;
+ }
+ hamConditionHandle = ham_condition(hamEntityHandle, CONDDEATH, "death", 0);
+ if (!hamConditionHandle) {
+ WARNING(ham_condition)
+ ham_detach(hamEntityHandle, 0);
+ ham_entity_handle_free(hamEntityHandle);
+ ham_disconnect(0);
+ return false;
+ }
+ qCDebug(QT_REMOTEOBJECT, "HAM initialized for %s (pid = %d)", qPrintable(serverName), pid);
+ return true;
+}
+
+void QQnxNativeServerPrivate::configureHamDeath(int sentChannelId, pid_t toPid, uint64_t uid)
+{
+ char identifier[25];
+ snprintf(identifier, 25, "%d_%d", toPid, sentChannelId);
+ ham_action_t *a = ham_action_notify_pulse(hamConditionHandle, identifier,
+ ND_LOCAL_NODE, toPid,
+ sentChannelId, NODE_DEATH, 0, 0);
+ if (!a)
+ WARNING(ham_action_notify_pulse)
+ else
+ hamActions.insert(uid, a);
+}
+
+void QQnxNativeServerPrivate::closeHamResources()
+{
+ ham_entity_handle_free(hamEntityHandle);
+ ham_condition_handle_free(hamConditionHandle);
+ ham_disconnect(0);
+}
+#endif
+
+QT_END_NAMESPACE
--- /dev/null
+// Copyright (C) 2017-2016 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQNXNATIVESERVER_H
+#define QQNXNATIVESERVER_H
+
+#include <QtNetwork/qabstractsocket.h>
+#include <QtRemoteObjects/qtremoteobjectglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQnxNativeServerPrivate;
+class QQnxNativeIo;
+class QIOQnxSource;
+
+class Q_REMOTEOBJECTS_EXPORT QQnxNativeServer : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QQnxNativeServer)
+
+Q_SIGNALS:
+ void newConnection();
+
+public:
+ explicit QQnxNativeServer(QObject *parent = nullptr);
+ ~QQnxNativeServer();
+
+ void close();
+ bool hasPendingConnections() const;
+ bool isListening() const;
+ bool listen(const QString &name);
+ QSharedPointer<QIOQnxSource> nextPendingConnection();
+ QString serverName() const;
+ bool waitForNewConnection(int msec = 0, bool *timedOut = nullptr);
+
+private Q_SLOTS:
+ void onSourceClosed();
+
+private:
+ Q_DISABLE_COPY(QQnxNativeServer)
+ friend class QIOQnxSource;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQNXNATIVESERVER_H
--- /dev/null
+// Copyright (C) 2017-2016 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQNXNATIVESERVER_P_H
+#define QQNXNATIVESERVER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qobject_p.h"
+#include "qconnection_qnx_server.h"
+#include "qconnection_qnx_global_p.h"
+
+#include <QtCore/qatomic.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qsharedpointer.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQnxNativeServerPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QQnxNativeServer)
+
+public:
+ QQnxNativeServerPrivate();
+
+ ~QQnxNativeServerPrivate();
+
+ void thread_func();
+
+ void cleanupIOSource(QIOQnxSource *conn);
+ void teardownServer();
+ void createSource(int rcvid, uint64_t uid, pid_t toPid);
+#ifdef USE_HAM
+ bool initializeHam();
+ void configureHamDeath(int sentChannelId, pid_t toPid, uint64_t uid);
+ void closeHamResources();
+#endif
+
+ bool listen(const QString &name);
+ QString errorString;
+ QAbstractSocket::SocketError error;
+ QString serverName;
+ name_attach_t *attachStruct;
+ QHash<int, QSet<int> > connections;
+ QHash<uint64_t, QSharedPointer<QIOQnxSource>> sources;
+ QList<QSharedPointer<QIOQnxSource>> pending;
+ QAtomicInt running;
+ Thread<QQnxNativeServerPrivate> thread;
+ mutable QMutex mutex;
+ int terminateCoid;
+#ifdef USE_HAM
+ ham_entity_t *hamEntityHandle;
+ ham_condition_t *hamConditionHandle;
+ QHash<uint64_t, ham_action_t*> hamActions;
+ bool hamAvailable = false;
+ bool hamInitialized = false;
+#endif
+};
+
+QT_END_NAMESPACE
+
+#endif // QQNXNATIVESERVER_P_H
+
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qconnection_tcpip_backend_p.h"
+
+#include <QtNetwork/qhostinfo.h>
+
+QT_BEGIN_NAMESPACE
+
+TcpClientIo::TcpClientIo(QObject *parent)
+ : QtROClientIoDevice(parent)
+ , m_socket(new QTcpSocket(this))
+{
+ connect(m_socket, &QTcpSocket::readyRead, this, &QtROClientIoDevice::readyRead);
+ connect(m_socket, &QAbstractSocket::errorOccurred, this, &TcpClientIo::onError);
+ connect(m_socket, &QTcpSocket::stateChanged, this, &TcpClientIo::onStateChanged);
+}
+
+TcpClientIo::~TcpClientIo()
+{
+ close();
+}
+
+QIODevice *TcpClientIo::connection() const
+{
+ return m_socket;
+}
+
+void TcpClientIo::doClose()
+{
+ if (m_socket->isOpen()) {
+ connect(m_socket, &QTcpSocket::disconnected, this, &QObject::deleteLater);
+ m_socket->disconnectFromHost();
+ } else {
+ this->deleteLater();
+ }
+}
+
+void TcpClientIo::doDisconnectFromServer()
+{
+ m_socket->disconnectFromHost();
+}
+
+void TcpClientIo::connectToServer()
+{
+ if (isOpen())
+ return;
+ QHostAddress address(url().host());
+ if (address.isNull()) {
+ const QList<QHostAddress> addresses = QHostInfo::fromName(url().host()).addresses();
+ Q_ASSERT_X(addresses.size() >= 1, Q_FUNC_INFO, url().toString().toLatin1().data());
+ address = addresses.first();
+ }
+
+ m_socket->connectToHost(address, quint16(url().port()));
+}
+
+bool TcpClientIo::isOpen() const
+{
+ return (!isClosing() && (m_socket->state() == QAbstractSocket::ConnectedState
+ || m_socket->state() == QAbstractSocket::ConnectingState));
+}
+
+void TcpClientIo::onError(QAbstractSocket::SocketError error)
+{
+ qCDebug(QT_REMOTEOBJECT) << "onError" << error;
+
+ switch (error) {
+ case QAbstractSocket::HostNotFoundError: //Host not there, wait and try again
+ case QAbstractSocket::ConnectionRefusedError:
+ case QAbstractSocket::NetworkError:
+ emit shouldReconnect(this);
+ break;
+ case QAbstractSocket::AddressInUseError:
+ //... TODO error reporting
+ break;
+ default:
+ break;
+ }
+}
+
+void TcpClientIo::onStateChanged(QAbstractSocket::SocketState state)
+{
+ if (state == QAbstractSocket::ClosingState && !isClosing()) {
+ m_socket->abort();
+ emit shouldReconnect(this);
+ }
+ if (state == QAbstractSocket::ConnectedState)
+ initializeDataStream();
+}
+
+
+TcpServerIo::TcpServerIo(QTcpSocket *conn, QObject *parent)
+ : QtROServerIoDevice(parent), m_connection(conn)
+{
+ m_connection->setParent(this);
+ connect(conn, &QIODevice::readyRead, this, &QtROServerIoDevice::readyRead);
+ connect(conn, &QAbstractSocket::disconnected, this, &QtROServerIoDevice::disconnected);
+}
+
+QIODevice *TcpServerIo::connection() const
+{
+ return m_connection;
+}
+
+void TcpServerIo::doClose()
+{
+ m_connection->disconnectFromHost();
+}
+
+
+
+TcpServerImpl::TcpServerImpl(QObject *parent)
+ : QConnectionAbstractServer(parent)
+{
+ connect(&m_server, &QTcpServer::newConnection, this, &QConnectionAbstractServer::newConnection);
+}
+
+TcpServerImpl::~TcpServerImpl()
+{
+ close();
+}
+
+QtROServerIoDevice *TcpServerImpl::configureNewConnection()
+{
+ if (!m_server.isListening())
+ return nullptr;
+
+ return new TcpServerIo(m_server.nextPendingConnection(), this);
+}
+
+bool TcpServerImpl::hasPendingConnections() const
+{
+ return m_server.hasPendingConnections();
+}
+
+QUrl TcpServerImpl::address() const
+{
+ return m_originalUrl;
+}
+
+bool TcpServerImpl::listen(const QUrl &address)
+{
+ QHostAddress host(address.host());
+ if (host.isNull()) {
+ if (address.host().isEmpty()) {
+ host = QHostAddress::Any;
+ } else {
+ qCWarning(QT_REMOTEOBJECT) << address.host() << " is not an IP address, trying to resolve it";
+ QHostInfo info = QHostInfo::fromName(address.host());
+ if (info.addresses().isEmpty())
+ host = QHostAddress::Any;
+ else
+ host = info.addresses().constFirst();
+ }
+ }
+
+ bool ret = m_server.listen(host, quint16(address.port()));
+ if (ret) {
+ m_originalUrl.setScheme(QLatin1String("tcp"));
+ m_originalUrl.setHost(m_server.serverAddress().toString());
+ m_originalUrl.setPort(m_server.serverPort());
+ }
+ return ret;
+}
+
+QAbstractSocket::SocketError TcpServerImpl::serverError() const
+{
+ return m_server.serverError();
+}
+
+void TcpServerImpl::close()
+{
+ m_server.close();
+}
+
+QT_END_NAMESPACE
--- /dev/null
+// Copyright (C) 2017-2015 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QCONNECTIONTCPIPBACKEND_P_H
+#define QCONNECTIONTCPIPBACKEND_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qconnectionfactories_p.h"
+
+#include <QtNetwork/qtcpserver.h>
+#include <QtNetwork/qtcpsocket.h>
+
+QT_BEGIN_NAMESPACE
+
+class TcpClientIo final : public QtROClientIoDevice
+{
+ Q_OBJECT
+
+public:
+ explicit TcpClientIo(QObject *parent = nullptr);
+ ~TcpClientIo() override;
+
+ QIODevice *connection() const override;
+ void connectToServer() override;
+ bool isOpen() const override;
+
+public Q_SLOTS:
+ void onError(QAbstractSocket::SocketError error);
+ void onStateChanged(QAbstractSocket::SocketState state);
+
+protected:
+ void doClose() override;
+ void doDisconnectFromServer() override;
+
+private:
+ QTcpSocket *m_socket;
+};
+
+class TcpServerIo final : public QtROServerIoDevice
+{
+ Q_OBJECT
+public:
+ explicit TcpServerIo(QTcpSocket *conn, QObject *parent = nullptr);
+
+ QIODevice *connection() const override;
+protected:
+ void doClose() override;
+
+private:
+ QTcpSocket *m_connection;
+};
+
+class TcpServerImpl final : public QConnectionAbstractServer
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(TcpServerImpl)
+
+public:
+ explicit TcpServerImpl(QObject *parent);
+ ~TcpServerImpl() override;
+
+ bool hasPendingConnections() const override;
+ QtROServerIoDevice *configureNewConnection() override;
+ QUrl address() const override;
+ bool listen(const QUrl &address) override;
+ QAbstractSocket::SocketError serverError() const override;
+ void close() override;
+
+private:
+ QTcpServer m_server;
+ QUrl m_originalUrl; // necessary because of a QHostAddress bug
+};
+
+QT_END_NAMESPACE
+#endif // QCONNECTIONTCPIPBACKEND_P_H
--- /dev/null
+// Copyright (C) 2017-2015 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qconnectionfactories_p.h"
+#include "qremoteobjectpacket_p.h"
+
+// BEGIN: Backends
+#if defined(Q_OS_QNX)
+#include "qconnection_qnx_backend_p.h"
+#endif
+#include "qconnection_local_backend_p.h"
+#include "qconnection_tcpip_backend_p.h"
+// END: Backends
+
+QT_BEGIN_NAMESPACE
+
+using namespace QtRemoteObjects;
+
+class QtROFactoryLoader
+{
+public:
+ QtROClientFactory clientFactory;
+ QtROServerFactory serverFactory;
+};
+
+Q_GLOBAL_STATIC(QtROFactoryLoader, loader)
+
+inline bool fromDataStream(QDataStream &in, QRemoteObjectPacketTypeEnum &type, QString &name)
+{
+ quint16 _type;
+ in >> _type;
+ type = Invalid;
+ switch (_type) {
+ case Handshake: type = Handshake; break;
+ case InitPacket: type = InitPacket; break;
+ case InitDynamicPacket: type = InitDynamicPacket; break;
+ case AddObject: type = AddObject; break;
+ case RemoveObject: type = RemoveObject; break;
+ case InvokePacket: type = InvokePacket; break;
+ case InvokeReplyPacket: type = InvokeReplyPacket; break;
+ case PropertyChangePacket: type = PropertyChangePacket; break;
+ case ObjectList: type = ObjectList; break;
+ case Ping: type = Ping; break;
+ case Pong: type = Pong; break;
+ default:
+ qCWarning(QT_REMOTEOBJECT_IO) << "Invalid packet received" << _type;
+ }
+ if (type == Invalid)
+ return false;
+ if (type == ObjectList)
+ return true;
+ in >> name;
+ qCDebug(QT_REMOTEOBJECT_IO) << "Packet received of type" << type << "for object" << name;
+ return true;
+}
+
+/*!
+ All communication between nodes happens through some form of QIODevice with
+ an associated QDataStream to handle marshalling of Qt types. QtROIoDeviceBase
+ is an abstract base class that provides a consistent interface to QtRO, yet
+ can be extended to support different types of QIODevice.
+ */
+QtROIoDeviceBase::QtROIoDeviceBase(QObject *parent) : QObject(*new QtROIoDeviceBasePrivate, parent) { }
+
+QtROIoDeviceBase::QtROIoDeviceBase(QtROIoDeviceBasePrivate &dptr, QObject *parent) : QObject(dptr, parent) { }
+
+QtROIoDeviceBase::~QtROIoDeviceBase()
+{
+}
+
+bool QtROIoDeviceBase::read(QRemoteObjectPacketTypeEnum &type, QString &name)
+{
+ Q_D(QtROIoDeviceBase);
+ qCDebug(QT_REMOTEOBJECT_IO) << deviceType() << "read()" << d->m_curReadSize << bytesAvailable();
+
+ if (d->m_curReadSize == 0) {
+ if (bytesAvailable() < static_cast<int>(sizeof(quint32)))
+ return false;
+
+ d->m_dataStream >> d->m_curReadSize;
+ }
+
+ qCDebug(QT_REMOTEOBJECT_IO) << deviceType() << "read()-looking for map" << d->m_curReadSize
+ << bytesAvailable();
+
+ if (bytesAvailable() < d->m_curReadSize)
+ return false;
+
+ d->m_curReadSize = 0;
+ return fromDataStream(d->m_dataStream, type, name);
+}
+
+void QtROIoDeviceBase::write(const QByteArray &data)
+{
+ Q_D(QtROIoDeviceBase);
+ if (connection()->isOpen() && !d->m_isClosing)
+ connection()->write(data);
+}
+
+void QtROIoDeviceBase::write(const QByteArray &data, qint64 size)
+{
+ Q_D(QtROIoDeviceBase);
+ if (connection()->isOpen() && !d->m_isClosing)
+ connection()->write(data.data(), size);
+}
+
+bool QtROIoDeviceBase::isOpen() const
+{
+ return !isClosing();
+}
+
+void QtROIoDeviceBase::close()
+{
+ Q_D(QtROIoDeviceBase);
+ d->m_isClosing = true;
+ doClose();
+}
+
+qint64 QtROIoDeviceBase::bytesAvailable() const
+{
+ return connection()->bytesAvailable();
+}
+
+void QtROIoDeviceBase::initializeDataStream()
+{
+ Q_D(QtROIoDeviceBase);
+ d->m_dataStream.setDevice(connection());
+ d->m_dataStream.resetStatus();
+}
+
+bool QtROIoDeviceBase::isClosing() const
+{
+ Q_D(const QtROIoDeviceBase);
+ return d->m_isClosing;
+}
+
+void QtROIoDeviceBase::addSource(const QString &name)
+{
+ Q_D(QtROIoDeviceBase);
+ d->m_remoteObjects.insert(name);
+}
+
+void QtROIoDeviceBase::removeSource(const QString &name)
+{
+ Q_D(QtROIoDeviceBase);
+ d->m_remoteObjects.remove(name);
+}
+
+QSet<QString> QtROIoDeviceBase::remoteObjects() const
+{
+ Q_D(const QtROIoDeviceBase);
+ return d->m_remoteObjects;
+}
+
+QtROClientIoDevice::QtROClientIoDevice(QObject *parent) : QtROIoDeviceBase(*new QtROClientIoDevicePrivate, parent)
+{
+}
+
+QtROClientIoDevice::~QtROClientIoDevice()
+{
+ if (!isClosing())
+ close();
+}
+
+void QtROClientIoDevice::disconnectFromServer()
+{
+ doDisconnectFromServer();
+ emit shouldReconnect(this);
+}
+
+QUrl QtROClientIoDevice::url() const
+{
+ Q_D(const QtROClientIoDevice);
+ return d->m_url;
+}
+
+QString QtROClientIoDevice::deviceType() const
+{
+ return QStringLiteral("QtROClientIoDevice");
+}
+
+void QtROClientIoDevice::setUrl(const QUrl &url)
+{
+ Q_D(QtROClientIoDevice);
+ d->m_url = url;
+}
+
+/*!
+ The Qt servers create QIODevice derived classes from handleConnection. The
+ problem is that they behave differently, so this class adds some
+ consistency.
+ */
+QtROServerIoDevice::QtROServerIoDevice(QObject *parent) : QtROIoDeviceBase(parent)
+{
+}
+
+QString QtROServerIoDevice::deviceType() const
+{
+ return QStringLiteral("QtROServerIoDevice");
+}
+
+QConnectionAbstractServer::QConnectionAbstractServer(QObject *parent)
+ : QObject(parent)
+{
+}
+
+QConnectionAbstractServer::~QConnectionAbstractServer()
+{
+}
+
+QtROServerIoDevice *QConnectionAbstractServer::nextPendingConnection()
+{
+ QtROServerIoDevice *iodevice = configureNewConnection();
+ iodevice->initializeDataStream();
+ return iodevice;
+}
+
+QtROExternalIoDevice::QtROExternalIoDevice(QIODevice *device, QObject *parent)
+ : QtROIoDeviceBase(*new QtROExternalIoDevicePrivate(device), parent)
+{
+ Q_D(QtROExternalIoDevice);
+ initializeDataStream();
+ connect(device, &QIODevice::aboutToClose, this, [d]() { d->m_isClosing = true; });
+ connect(device, &QIODevice::readyRead, this, &QtROExternalIoDevice::readyRead);
+ auto meta = device->metaObject();
+ if (-1 != meta->indexOfSignal(SIGNAL(disconnected())))
+ connect(device, SIGNAL(disconnected()), this, SIGNAL(disconnected()));
+}
+
+QIODevice *QtROExternalIoDevice::connection() const
+{
+ Q_D(const QtROExternalIoDevice);
+ return d->m_device;
+}
+
+bool QtROExternalIoDevice::isOpen() const
+{
+ Q_D(const QtROExternalIoDevice);
+ if (!d->m_device)
+ return false;
+ return d->m_device->isOpen() && QtROIoDeviceBase::isOpen();
+}
+
+void QtROExternalIoDevice::doClose()
+{
+ Q_D(QtROExternalIoDevice);
+ if (isOpen())
+ d->m_device->close();
+}
+
+QString QtROExternalIoDevice::deviceType() const
+{
+ return QStringLiteral("QtROExternalIoDevice");
+}
+
+/*!
+ \class QtROServerFactory
+ \inmodule QtRemoteObjects
+ \brief A class that holds information about server backends available on the Qt Remote Objects network.
+*/
+QtROServerFactory::QtROServerFactory()
+{
+#ifdef Q_OS_QNX
+ registerType<QnxServerImpl>(QStringLiteral("qnx"));
+#endif
+#ifdef Q_OS_LINUX
+ registerType<AbstractLocalServerImpl>(QStringLiteral("localabstract"));
+#endif
+ registerType<LocalServerImpl>(QStringLiteral("local"));
+ registerType<TcpServerImpl>(QStringLiteral("tcp"));
+}
+
+QtROServerFactory *QtROServerFactory::instance()
+{
+ return &loader->serverFactory;
+}
+
+/*!
+ \class QtROClientFactory
+ \inmodule QtRemoteObjects
+ \brief A class that holds information about client backends available on the Qt Remote Objects network.
+*/
+QtROClientFactory::QtROClientFactory()
+{
+#ifdef Q_OS_QNX
+ registerType<QnxClientIo>(QStringLiteral("qnx"));
+#endif
+#ifdef Q_OS_LINUX
+ registerType<AbstractLocalClientIo>(QStringLiteral("localabstract"));
+#endif
+ registerType<LocalClientIo>(QStringLiteral("local"));
+ registerType<TcpClientIo>(QStringLiteral("tcp"));
+}
+
+QtROClientFactory *QtROClientFactory::instance()
+{
+ return &loader->clientFactory;
+}
+
+/*!
+ \fn void qRegisterRemoteObjectsClient(const QString &id)
+ \relates QtROClientFactory
+
+ Registers the Remote Objects client \a id for the type \c{T}.
+
+ If you need a custom transport protocol for Qt Remote Objects, you need to
+ register the client & server implementation here.
+
+ \note This function requires that \c{T} is a fully defined type at the point
+ where the function is called.
+
+ This example registers the class \c{CustomClientIo} as \c{"myprotocol"}:
+
+ \code
+ qRegisterRemoteObjectsClient<CustomClientIo>(QStringLiteral("myprotocol"));
+ \endcode
+
+ With this in place, you can now instantiate nodes using this new custom protocol:
+
+ \code
+ QRemoteObjectNode client(QUrl(QStringLiteral("myprotocol:registry")));
+ \endcode
+
+ \sa {qRegisterRemoteObjectsServer}
+*/
+
+/*!
+ \fn void qRegisterRemoteObjectsServer(const QString &id)
+ \relates QtROServerFactory
+
+ Registers the Remote Objects server \a id for the type \c{T}.
+
+ If you need a custom transport protocol for Qt Remote Objects, you need to
+ register the client & server implementation here.
+
+ \note This function requires that \c{T} is a fully defined type at the point
+ where the function is called.
+
+ This example registers the class \c{CustomServerImpl} as \c{"myprotocol"}:
+
+ \code
+ qRegisterRemoteObjectsServer<CustomServerImpl>(QStringLiteral("myprotocol"));
+ \endcode
+
+ With this in place, you can now instantiate nodes using this new custom protocol:
+
+ \code
+ QRemoteObjectNode client(QUrl(QStringLiteral("myprotocol:registry")));
+ \endcode
+
+ \sa {qRegisterRemoteObjectsServer}
+*/
+
+QtROIoDeviceBasePrivate::QtROIoDeviceBasePrivate() : QObjectPrivate()
+{
+ m_dataStream.setVersion(dataStreamVersion);
+ m_dataStream.setByteOrder(QDataStream::LittleEndian);
+}
+
+QT_END_NAMESPACE
--- /dev/null
+// Copyright (C) 2021 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QCONNECTIONFACTORIES_H
+#define QCONNECTIONFACTORIES_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is part of the QtRO internal API and is not meant to be used
+// in applications. It contains "internal" components, which are exported
+// to allow new QIODevice channels to be added to extend QtRO. Usage of these
+// APIs may make your code source and binary incompatible with future versions
+// of Qt.
+//
+
+#include <QtNetwork/qabstractsocket.h>
+
+#include <QtRemoteObjects/qtremoteobjectglobal.h>
+
+
+QT_BEGIN_NAMESPACE
+
+class QtROIoDeviceBasePrivate;
+class QtROClientIoDevicePrivate;
+class QtROExternalIoDevicePrivate;
+
+class Q_REMOTEOBJECTS_EXPORT QtROIoDeviceBase : public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(QtROIoDeviceBase)
+
+public:
+ explicit QtROIoDeviceBase(QObject *parent = nullptr);
+ ~QtROIoDeviceBase() override;
+
+ bool read(QtRemoteObjects::QRemoteObjectPacketTypeEnum &, QString &);
+
+ virtual void write(const QByteArray &data);
+ virtual void write(const QByteArray &data, qint64);
+ virtual bool isOpen() const;
+ virtual void close();
+ virtual qint64 bytesAvailable() const;
+ virtual QIODevice *connection() const = 0;
+ void initializeDataStream();
+ bool isClosing() const;
+ void addSource(const QString &);
+ void removeSource(const QString &);
+ QSet<QString> remoteObjects() const;
+
+Q_SIGNALS:
+ void readyRead();
+ void disconnected();
+
+protected:
+ explicit QtROIoDeviceBase(QtROIoDeviceBasePrivate &, QObject *parent);
+ virtual QString deviceType() const = 0;
+ virtual void doClose() = 0;
+
+private:
+ Q_DECLARE_PRIVATE(QtROIoDeviceBase)
+ friend class QRemoteObjectNodePrivate;
+ friend class QConnectedReplicaImplementation;
+ friend class QRemoteObjectSourceIo;
+};
+
+class Q_REMOTEOBJECTS_EXPORT QtROServerIoDevice : public QtROIoDeviceBase
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(QtROServerIoDevice)
+
+public:
+ explicit QtROServerIoDevice(QObject *parent = nullptr);
+
+protected:
+ QString deviceType() const override;
+};
+
+class Q_REMOTEOBJECTS_EXPORT QConnectionAbstractServer : public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(QConnectionAbstractServer)
+
+public:
+ explicit QConnectionAbstractServer(QObject *parent = nullptr);
+ ~QConnectionAbstractServer() override;
+
+ virtual bool hasPendingConnections() const = 0;
+ QtROServerIoDevice* nextPendingConnection();
+ virtual QUrl address() const = 0;
+ virtual bool listen(const QUrl &address) = 0;
+ virtual QAbstractSocket::SocketError serverError() const = 0;
+ virtual void close() = 0;
+
+protected:
+ virtual QtROServerIoDevice* configureNewConnection() = 0;
+
+Q_SIGNALS:
+ void newConnection();
+};
+
+class Q_REMOTEOBJECTS_EXPORT QtROClientIoDevice : public QtROIoDeviceBase
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(QtROClientIoDevice)
+
+public:
+ explicit QtROClientIoDevice(QObject *parent = nullptr);
+ ~QtROClientIoDevice() override;
+
+ void disconnectFromServer();
+ virtual void connectToServer() = 0;
+
+ QUrl url() const;
+
+Q_SIGNALS:
+ void shouldReconnect(QtROClientIoDevice*);
+
+protected:
+ virtual void doDisconnectFromServer() = 0;
+ QString deviceType() const override;
+ void setUrl(const QUrl &url);
+
+private:
+ Q_DECLARE_PRIVATE(QtROClientIoDevice)
+ friend class QtROClientFactory;
+};
+
+class QtROServerFactory
+{
+public:
+ Q_REMOTEOBJECTS_EXPORT static QtROServerFactory *instance();
+
+ QConnectionAbstractServer *create(const QUrl &url, QObject *parent = nullptr)
+ {
+ auto creatorFunc = m_creatorFuncs.value(url.scheme());
+ return creatorFunc ? (*creatorFunc)(parent) : nullptr;
+ }
+
+ template<typename T>
+ void registerType(const QString &id)
+ {
+ m_creatorFuncs[id] = [](QObject *parent) -> QConnectionAbstractServer * {
+ return new T(parent);
+ };
+ }
+
+ bool isValid(const QUrl &url)
+ {
+ return m_creatorFuncs.contains(url.scheme());
+ }
+
+private:
+ friend class QtROFactoryLoader;
+ QtROServerFactory();
+
+ using CreatorFunc = QConnectionAbstractServer * (*)(QObject *);
+ QHash<QString, CreatorFunc> m_creatorFuncs;
+};
+
+class QtROClientFactory
+{
+public:
+ Q_REMOTEOBJECTS_EXPORT static QtROClientFactory *instance();
+
+ /// creates an object from a string
+ QtROClientIoDevice *create(const QUrl &url, QObject *parent = nullptr)
+ {
+ auto creatorFunc = m_creatorFuncs.value(url.scheme());
+ if (!creatorFunc)
+ return nullptr;
+
+ QtROClientIoDevice *res = (*creatorFunc)(parent);
+ if (res)
+ res->setUrl(url);
+ return res;
+ }
+
+ template<typename T>
+ void registerType(const QString &id)
+ {
+ m_creatorFuncs[id] = [](QObject *parent) -> QtROClientIoDevice * {
+ return new T(parent);
+ };
+ }
+
+ bool isValid(const QUrl &url)
+ {
+ return m_creatorFuncs.contains(url.scheme());
+ }
+
+private:
+ friend class QtROFactoryLoader;
+ QtROClientFactory();
+
+ using CreatorFunc = QtROClientIoDevice * (*)(QObject *);
+ QHash<QString, CreatorFunc> m_creatorFuncs;
+};
+
+template <typename T>
+inline void qRegisterRemoteObjectsClient(const QString &id)
+{
+ QtROClientFactory::instance()->registerType<T>(id);
+}
+
+template <typename T>
+inline void qRegisterRemoteObjectsServer(const QString &id)
+{
+ QtROServerFactory::instance()->registerType<T>(id);
+}
+
+QT_END_NAMESPACE
+
+#endif // QCONNECTIONFACTORIES_H
--- /dev/null
+// Copyright (C) 2017-2015 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QCONNECTIONFACTORIES_P_H
+#define QCONNECTIONFACTORIES_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qdatastream.h>
+#include <QtCore/qiodevice.h>
+#include <QtCore/qpointer.h>
+#include <QtCore/private/qobject_p.h>
+
+#include <QtRemoteObjects/qtremoteobjectglobal.h>
+#include <QtRemoteObjects/qconnectionfactories.h>
+#include "qremoteobjectpacket_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QtRemoteObjects {
+
+static const int dataStreamVersion = QDataStream::Qt_6_2;
+static const QLatin1String protocolVersion("QtRO 2.0");
+
+}
+
+class QtROExternalIoDevice : public QtROIoDeviceBase
+{
+ Q_OBJECT
+
+public:
+ explicit QtROExternalIoDevice(QIODevice *device, QObject *parent=nullptr);
+ QIODevice *connection() const override;
+ bool isOpen() const override;
+
+protected:
+ void doClose() override;
+ QString deviceType() const override;
+private:
+ Q_DECLARE_PRIVATE(QtROExternalIoDevice)
+};
+
+class QtROIoDeviceBasePrivate : public QObjectPrivate
+{
+public:
+ QtROIoDeviceBasePrivate();
+
+ // TODO Remove stream()
+ QDataStream &stream() { return m_dataStream; }
+
+ bool m_isClosing = false;
+ quint32 m_curReadSize = 0;
+ QDataStream m_dataStream;
+ QSet<QString> m_remoteObjects;
+ QRemoteObjectPackets::CodecBase *m_codec { nullptr };
+ Q_DECLARE_PUBLIC(QtROIoDeviceBase)
+};
+
+class QtROClientIoDevicePrivate : public QtROIoDeviceBasePrivate
+{
+public:
+ QtROClientIoDevicePrivate() : QtROIoDeviceBasePrivate() { }
+ QUrl m_url;
+ Q_DECLARE_PUBLIC(QtROClientIoDevice)
+};
+
+class QtROExternalIoDevicePrivate : public QtROIoDeviceBasePrivate
+{
+public:
+ QtROExternalIoDevicePrivate(QIODevice *device) : QtROIoDeviceBasePrivate(), m_device(device) { }
+ QPointer<QIODevice> m_device;
+ Q_DECLARE_PUBLIC(QtROExternalIoDevice)
+};
+
+QT_END_NAMESPACE
+
+#endif
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qremoteobjectabstractitemmodeladapter_p.h"
+
+#include <QtCore/qitemselectionmodel.h>
+
+inline QList<QModelRoleData> createModelRoleData(const QList<int> &roles)
+{
+ QList<QModelRoleData> roleData;
+ roleData.reserve(roles.size());
+ for (int role : roles)
+ roleData.emplace_back(role);
+ return roleData;
+}
+
+// consider evaluating performance difference with item data
+inline QVariantList collectData(const QModelIndex &index, const QAbstractItemModel *model,
+ QModelRoleDataSpan roleDataSpan)
+{
+ model->multiData(index, roleDataSpan);
+
+ QVariantList result;
+ result.reserve(roleDataSpan.size());
+ for (auto &roleData : roleDataSpan)
+ result.push_back(std::move(roleData.data()));
+
+ return result;
+}
+
+inline QList<int> filterRoles(const QList<int> &roles, const QList<int> &availableRoles)
+{
+ if (roles.isEmpty())
+ return availableRoles;
+
+ QList<int> neededRoles;
+ for (int inRole : roles) {
+ for (int availableRole : availableRoles)
+ if (inRole == availableRole) {
+ neededRoles << inRole;
+ continue;
+ }
+ }
+ return neededRoles;
+}
+
+QAbstractItemModelSourceAdapter::QAbstractItemModelSourceAdapter(QAbstractItemModel *obj, QItemSelectionModel *sel, const QList<int> &roles)
+ : QObject(obj),
+ m_model(obj),
+ m_availableRoles(roles)
+{
+ QAbstractItemModelSourceAdapter::registerTypes();
+ m_selectionModel = sel;
+ connect(m_model, &QAbstractItemModel::dataChanged, this, &QAbstractItemModelSourceAdapter::sourceDataChanged);
+ connect(m_model, &QAbstractItemModel::rowsInserted, this, &QAbstractItemModelSourceAdapter::sourceRowsInserted);
+ connect(m_model, &QAbstractItemModel::columnsInserted, this, &QAbstractItemModelSourceAdapter::sourceColumnsInserted);
+ connect(m_model, &QAbstractItemModel::rowsRemoved, this, &QAbstractItemModelSourceAdapter::sourceRowsRemoved);
+ connect(m_model, &QAbstractItemModel::rowsMoved, this, &QAbstractItemModelSourceAdapter::sourceRowsMoved);
+ connect(m_model, &QAbstractItemModel::layoutChanged, this, &QAbstractItemModelSourceAdapter::sourceLayoutChanged);
+ if (m_selectionModel)
+ connect(m_selectionModel, &QItemSelectionModel::currentChanged, this, &QAbstractItemModelSourceAdapter::sourceCurrentChanged);
+}
+
+void QAbstractItemModelSourceAdapter::registerTypes()
+{
+ static bool alreadyRegistered = false;
+ if (alreadyRegistered)
+ return;
+
+ alreadyRegistered = true;
+ qRegisterMetaType<QAbstractItemModel*>();
+ qRegisterMetaType<Qt::Orientation>();
+ qRegisterMetaType<QList<Qt::Orientation>>();
+ qRegisterMetaType<QtPrivate::ModelIndex>();
+ qRegisterMetaType<QtPrivate::IndexList>();
+ qRegisterMetaType<QtPrivate::DataEntries>();
+ qRegisterMetaType<QtPrivate::MetaAndDataEntries>();
+ qRegisterMetaType<QItemSelectionModel::SelectionFlags>();
+ qRegisterMetaType<QSize>();
+ qRegisterMetaType<QIntHash>();
+}
+
+QItemSelectionModel* QAbstractItemModelSourceAdapter::selectionModel() const
+{
+ return m_selectionModel;
+}
+
+QSize QAbstractItemModelSourceAdapter::replicaSizeRequest(QtPrivate::IndexList parentList)
+{
+ QModelIndex parent = toQModelIndex(parentList, m_model);
+ const int rowCount = m_model->rowCount(parent);
+ const int columnCount = m_model->columnCount(parent);
+ const QSize size(columnCount, rowCount);
+ qCDebug(QT_REMOTEOBJECT_MODELS) << "parent" << parentList << "size=" << size;
+ return size;
+}
+
+void QAbstractItemModelSourceAdapter::replicaSetData(const QtPrivate::IndexList &index, const QVariant &value, int role)
+{
+ const QModelIndex modelIndex = toQModelIndex(index, m_model);
+ Q_ASSERT(modelIndex.isValid());
+ const bool result = m_model->setData(modelIndex, value, role);
+ Q_ASSERT(result);
+ Q_UNUSED(result)
+}
+
+QtPrivate::DataEntries QAbstractItemModelSourceAdapter::replicaRowRequest(QtPrivate::IndexList start, QtPrivate::IndexList end, QList<int> roles)
+{
+ qCDebug(QT_REMOTEOBJECT_MODELS) << "Requested rows" << "start=" << start << "end=" << end << "roles=" << roles;
+
+ Q_ASSERT(start.size() == end.size());
+ Q_ASSERT(!start.isEmpty());
+
+ if (roles.isEmpty())
+ roles << m_availableRoles;
+
+ QtPrivate::IndexList parentList = start;
+ Q_ASSERT(!parentList.isEmpty());
+ parentList.pop_back();
+ QModelIndex parent = toQModelIndex(parentList, m_model);
+
+ const int startRow = start.last().row;
+ const int startColumn = start.last().column;
+ const int rowCount = m_model->rowCount(parent);
+ const int columnCount = m_model->columnCount(parent);
+
+ QtPrivate::DataEntries entries;
+ if (rowCount <= 0)
+ return entries;
+ const int endRow = std::min(end.last().row, rowCount - 1);
+ const int endColumn = std::min(end.last().column, columnCount - 1);
+ Q_ASSERT_X(endRow >= 0 && endRow < rowCount, __FUNCTION__, qPrintable(QString(QLatin1String("0 <= %1 < %2")).arg(endRow).arg(rowCount)));
+ Q_ASSERT_X(endColumn >= 0 && endColumn < columnCount, __FUNCTION__, qPrintable(QString(QLatin1String("0 <= %1 < %2")).arg(endColumn).arg(columnCount)));
+
+ auto roleData = createModelRoleData(roles);
+ for (int row = startRow; row <= endRow; ++row) {
+ for (int column = startColumn; column <= endColumn; ++column) {
+ const QModelIndex current = m_model->index(row, column, parent);
+ Q_ASSERT(current.isValid());
+ const QtPrivate::IndexList currentList = QtPrivate::toModelIndexList(current, m_model);
+ const QVariantList data = collectData(current, m_model, roleData);
+ const bool hasChildren = m_model->hasChildren(current);
+ const Qt::ItemFlags flags = m_model->flags(current);
+ qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "current=" << currentList << "data=" << data;
+ entries.data << QtPrivate::IndexValuePair(currentList, data, hasChildren, flags);
+ }
+ }
+ return entries;
+}
+
+QtPrivate::MetaAndDataEntries QAbstractItemModelSourceAdapter::replicaCacheRequest(size_t size, const QList<int> &roles)
+{
+ QtPrivate::MetaAndDataEntries res;
+ res.roles = roles.isEmpty() ? m_availableRoles : roles;
+ res.data = fetchTree(QModelIndex {}, size, res.roles);
+ const int rowCount = m_model->rowCount(QModelIndex{});
+ const int columnCount = m_model->columnCount(QModelIndex{});
+ res.size = QSize{columnCount, rowCount};
+ return res;
+}
+
+QVariantList QAbstractItemModelSourceAdapter::replicaHeaderRequest(QList<Qt::Orientation> orientations, QList<int> sections, QList<int> roles)
+{
+ qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "orientations=" << orientations << "sections=" << sections << "roles=" << roles;
+ QVariantList data;
+ Q_ASSERT(roles.size() == sections.size());
+ Q_ASSERT(roles.size() == orientations.size());
+ for (int i = 0; i < roles.size(); ++i) {
+ data << m_model->headerData(sections[i], orientations[i], roles[i]);
+ }
+ return data;
+}
+
+void QAbstractItemModelSourceAdapter::replicaSetCurrentIndex(QtPrivate::IndexList index, QItemSelectionModel::SelectionFlags command)
+{
+ if (m_selectionModel)
+ m_selectionModel->setCurrentIndex(toQModelIndex(index, m_model), command);
+}
+
+void QAbstractItemModelSourceAdapter::sourceDataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight, const QList<int> & roles) const
+{
+ QList<int> neededRoles = filterRoles(roles, availableRoles());
+ if (neededRoles.isEmpty()) {
+ qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "Needed roles is empty!";
+ return;
+ }
+ Q_ASSERT(topLeft.isValid());
+ Q_ASSERT(bottomRight.isValid());
+ QtPrivate::IndexList start = QtPrivate::toModelIndexList(topLeft, m_model);
+ QtPrivate::IndexList end = QtPrivate::toModelIndexList(bottomRight, m_model);
+ qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "start=" << start << "end=" << end << "neededRoles=" << neededRoles;
+ emit dataChanged(start, end, neededRoles);
+}
+
+void QAbstractItemModelSourceAdapter::sourceRowsInserted(const QModelIndex & parent, int start, int end)
+{
+ QtPrivate::IndexList parentList = QtPrivate::toModelIndexList(parent, m_model);
+ emit rowsInserted(parentList, start, end);
+}
+
+void QAbstractItemModelSourceAdapter::sourceColumnsInserted(const QModelIndex & parent, int start, int end)
+{
+ QtPrivate::IndexList parentList = QtPrivate::toModelIndexList(parent, m_model);
+ emit columnsInserted(parentList, start, end);
+}
+
+void QAbstractItemModelSourceAdapter::sourceRowsRemoved(const QModelIndex & parent, int start, int end)
+{
+ QtPrivate::IndexList parentList = QtPrivate::toModelIndexList(parent, m_model);
+ emit rowsRemoved(parentList, start, end);
+}
+
+void QAbstractItemModelSourceAdapter::sourceRowsMoved(const QModelIndex & sourceParent, int sourceRow, int count, const QModelIndex & destinationParent, int destinationChild) const
+{
+ emit rowsMoved(QtPrivate::toModelIndexList(sourceParent, m_model), sourceRow, count, QtPrivate::toModelIndexList(destinationParent, m_model), destinationChild);
+}
+
+void QAbstractItemModelSourceAdapter::sourceCurrentChanged(const QModelIndex & current, const QModelIndex & previous)
+{
+ QtPrivate::IndexList currentIndex = QtPrivate::toModelIndexList(current, m_model);
+ QtPrivate::IndexList previousIndex = QtPrivate::toModelIndexList(previous, m_model);
+ qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "current=" << currentIndex << "previous=" << previousIndex;
+ emit currentChanged(currentIndex, previousIndex);
+}
+
+void QAbstractItemModelSourceAdapter::sourceLayoutChanged(const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint)
+{
+ QtPrivate::IndexList indexes;
+ for (const QPersistentModelIndex &idx : parents)
+ indexes << QtPrivate::toModelIndexList((QModelIndex)idx, m_model);
+ emit layoutChanged(indexes, hint);
+}
+
+QList<QtPrivate::IndexValuePair> QAbstractItemModelSourceAdapter::fetchTree(const QModelIndex &parent, size_t &size, const QList<int> &roles)
+{
+ QList<QtPrivate::IndexValuePair> entries;
+ const int rowCount = m_model->rowCount(parent);
+ const int columnCount = m_model->columnCount(parent);
+ if (!columnCount || !rowCount)
+ return entries;
+ entries.reserve(std::min(rowCount * columnCount, int(size)));
+ auto roleData = createModelRoleData(roles);
+ for (int row = 0; row < rowCount && size > 0; ++row)
+ for (int column = 0; column < columnCount && size > 0; ++column) {
+ const auto index = m_model->index(row, column, parent);
+ const QtPrivate::IndexList currentList = QtPrivate::toModelIndexList(index, m_model);
+ const QVariantList data = collectData(index, m_model, roleData);
+ const bool hasChildren = m_model->hasChildren(index);
+ const Qt::ItemFlags flags = m_model->flags(index);
+ int rc = m_model->rowCount(index);
+ int cc = m_model->columnCount(index);
+ QtPrivate::IndexValuePair rowData(currentList, data, hasChildren, flags, QSize{cc, rc});
+ --size;
+ if (hasChildren)
+ rowData.children = fetchTree(index, size, roles);
+ entries.push_back(rowData);
+ }
+ return entries;
+}
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QREMOTEOBJECTS_ABSTRACT_ITEM_ADAPTER_P_H
+#define QREMOTEOBJECTS_ABSTRACT_ITEM_ADAPTER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qremoteobjectabstractitemmodeltypes_p.h"
+#include "qremoteobjectsource.h"
+
+#include <QtCore/qsize.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAbstractItemModel;
+class QItemSelectionModel;
+
+class QAbstractItemModelSourceAdapter : public QObject
+{
+ Q_OBJECT
+public:
+ Q_INVOKABLE explicit QAbstractItemModelSourceAdapter(QAbstractItemModel *object, QItemSelectionModel *sel, const QList<int> &roles = QList<int>());
+ Q_PROPERTY(QList<int> availableRoles READ availableRoles WRITE setAvailableRoles NOTIFY availableRolesChanged)
+ Q_PROPERTY(QIntHash roleNames READ roleNames)
+ static void registerTypes();
+ QItemSelectionModel* selectionModel() const;
+
+public Q_SLOTS:
+ QList<int> availableRoles() const { return m_availableRoles; }
+ void setAvailableRoles(QList<int> availableRoles)
+ {
+ if (availableRoles != m_availableRoles)
+ {
+ m_availableRoles = availableRoles;
+ Q_EMIT availableRolesChanged();
+ }
+ }
+
+ QIntHash roleNames() const {return m_model->roleNames();}
+
+ QSize replicaSizeRequest(QtPrivate::IndexList parentList);
+ QtPrivate::DataEntries replicaRowRequest(QtPrivate::IndexList start, QtPrivate::IndexList end, QList<int> roles);
+ QVariantList replicaHeaderRequest(QList<Qt::Orientation> orientations, QList<int> sections, QList<int> roles);
+ void replicaSetCurrentIndex(QtPrivate::IndexList index, QItemSelectionModel::SelectionFlags command);
+ void replicaSetData(const QtPrivate::IndexList &index, const QVariant &value, int role);
+ QtPrivate::MetaAndDataEntries replicaCacheRequest(size_t size, const QList<int> &roles);
+
+ void sourceDataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight, const QList<int> & roles = QList<int> ()) const;
+ void sourceRowsInserted(const QModelIndex & parent, int start, int end);
+ void sourceColumnsInserted(const QModelIndex & parent, int start, int end);
+ void sourceRowsRemoved(const QModelIndex & parent, int start, int end);
+ void sourceRowsMoved(const QModelIndex & sourceParent, int sourceRow, int count, const QModelIndex & destinationParent, int destinationChild) const;
+ void sourceCurrentChanged(const QModelIndex & current, const QModelIndex & previous);
+ void sourceLayoutChanged(const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint);
+Q_SIGNALS:
+ void availableRolesChanged();
+ void dataChanged(QtPrivate::IndexList topLeft, QtPrivate::IndexList bottomRight, QList<int> roles) const;
+ void rowsInserted(QtPrivate::IndexList parent, int start, int end) const;
+ void rowsRemoved(QtPrivate::IndexList parent, int start, int end) const;
+ void rowsMoved(QtPrivate::IndexList sourceParent, int sourceRow, int count, QtPrivate::IndexList destinationParent, int destinationChild) const;
+ void currentChanged(QtPrivate::IndexList current, QtPrivate::IndexList previous);
+ void columnsInserted(QtPrivate::IndexList parent, int start, int end) const;
+ void layoutChanged(QtPrivate::IndexList parents, QAbstractItemModel::LayoutChangeHint hint);
+
+private:
+ QAbstractItemModelSourceAdapter();
+ QList<QtPrivate::IndexValuePair> fetchTree(const QModelIndex &parent, size_t &size, const QList<int> &roles);
+
+ QAbstractItemModel *m_model;
+ QItemSelectionModel *m_selectionModel;
+ QList<int> m_availableRoles;
+};
+
+template <class ObjectType, class AdapterType>
+struct QAbstractItemAdapterSourceAPI : public SourceApiMap
+{
+ QAbstractItemAdapterSourceAPI(const QString &name)
+ : SourceApiMap()
+ , m_signalArgTypes {}
+ , m_methodArgTypes {}
+ , m_name(name)
+ {
+ m_properties[0] = 2;
+ m_properties[1] = QtPrivate::qtro_property_index<AdapterType>(&AdapterType::availableRoles, static_cast<QList<int> (QObject::*)()>(nullptr),"availableRoles");
+ m_properties[2] = QtPrivate::qtro_property_index<AdapterType>(&AdapterType::roleNames, static_cast<QIntHash (QObject::*)()>(nullptr),"roleNames");
+ m_signals[0] = 10;
+ m_signals[1] = QtPrivate::qtro_signal_index<AdapterType>(&AdapterType::availableRolesChanged, static_cast<void (QObject::*)()>(nullptr),m_signalArgCount+0,&m_signalArgTypes[0]);
+ m_signals[2] = QtPrivate::qtro_signal_index<AdapterType>(&AdapterType::dataChanged, static_cast<void (QObject::*)(QtPrivate::IndexList,QtPrivate::IndexList,QList<int>)>(nullptr),m_signalArgCount+1,&m_signalArgTypes[1]);
+ m_signals[3] = QtPrivate::qtro_signal_index<AdapterType>(&AdapterType::rowsInserted, static_cast<void (QObject::*)(QtPrivate::IndexList,int,int)>(nullptr),m_signalArgCount+2,&m_signalArgTypes[2]);
+ m_signals[4] = QtPrivate::qtro_signal_index<AdapterType>(&AdapterType::rowsRemoved, static_cast<void (QObject::*)(QtPrivate::IndexList,int,int)>(nullptr),m_signalArgCount+3,&m_signalArgTypes[3]);
+ m_signals[5] = QtPrivate::qtro_signal_index<AdapterType>(&AdapterType::rowsMoved, static_cast<void (QObject::*)(QtPrivate::IndexList,int,int,QtPrivate::IndexList,int)>(nullptr),m_signalArgCount+4,&m_signalArgTypes[4]);
+ m_signals[6] = QtPrivate::qtro_signal_index<AdapterType>(&AdapterType::currentChanged, static_cast<void (QObject::*)(QtPrivate::IndexList,QtPrivate::IndexList)>(nullptr),m_signalArgCount+5,&m_signalArgTypes[5]);
+ m_signals[7] = QtPrivate::qtro_signal_index<ObjectType>(&ObjectType::modelReset, static_cast<void (QObject::*)()>(nullptr),m_signalArgCount+6,&m_signalArgTypes[6]);
+ m_signals[8] = QtPrivate::qtro_signal_index<ObjectType>(&ObjectType::headerDataChanged, static_cast<void (QObject::*)(Qt::Orientation,int,int)>(nullptr),m_signalArgCount+7,&m_signalArgTypes[7]);
+ m_signals[9] = QtPrivate::qtro_signal_index<AdapterType>(&AdapterType::columnsInserted, static_cast<void (QObject::*)(QtPrivate::IndexList,int,int)>(nullptr),m_signalArgCount+8,&m_signalArgTypes[8]);
+ m_signals[10] = QtPrivate::qtro_signal_index<AdapterType>(&AdapterType::layoutChanged, static_cast<void (QObject::*)(QtPrivate::IndexList,QAbstractItemModel::LayoutChangeHint)>(nullptr),m_signalArgCount+9,&m_signalArgTypes[9]);
+ m_methods[0] = 6;
+ m_methods[1] = QtPrivate::qtro_method_index<AdapterType>(&AdapterType::replicaSizeRequest, static_cast<void (QObject::*)(QtPrivate::IndexList)>(nullptr),"replicaSizeRequest(QtPrivate::IndexList)",m_methodArgCount+0,&m_methodArgTypes[0]);
+ m_methods[2] = QtPrivate::qtro_method_index<AdapterType>(&AdapterType::replicaRowRequest, static_cast<void (QObject::*)(QtPrivate::IndexList,QtPrivate::IndexList,QList<int>)>(nullptr),"replicaRowRequest(QtPrivate::IndexList,QtPrivate::IndexList,QList<int>)",m_methodArgCount+1,&m_methodArgTypes[1]);
+ m_methods[3] = QtPrivate::qtro_method_index<AdapterType>(&AdapterType::replicaHeaderRequest, static_cast<void (QObject::*)(QList<Qt::Orientation>,QList<int>,QList<int>)>(nullptr),"replicaHeaderRequest(QList<Qt::Orientation>,QList<int>,QList<int>)",m_methodArgCount+2,&m_methodArgTypes[2]);
+ m_methods[4] = QtPrivate::qtro_method_index<AdapterType>(&AdapterType::replicaSetCurrentIndex, static_cast<void (QObject::*)(QtPrivate::IndexList,QItemSelectionModel::SelectionFlags)>(nullptr),"replicaSetCurrentIndex(QtPrivate::IndexList,QItemSelectionModel::SelectionFlags)",m_methodArgCount+3,&m_methodArgTypes[3]);
+ m_methods[5] = QtPrivate::qtro_method_index<AdapterType>(&AdapterType::replicaSetData, static_cast<void (QObject::*)(QtPrivate::IndexList,QVariant,int)>(nullptr),"replicaSetData(QtPrivate::IndexList,QVariant,int)",m_methodArgCount+4,&m_methodArgTypes[4]);
+ m_methods[6] = QtPrivate::qtro_method_index<AdapterType>(&AdapterType::replicaCacheRequest, static_cast<void (QObject::*)(size_t,QList<int>)>(nullptr),"replicaCacheRequest(size_t,QList<int>)",m_methodArgCount+5,&m_methodArgTypes[5]);
+ }
+
+ QString name() const override { return m_name; }
+ QString typeName() const override { return QStringLiteral("QAbstractItemModelAdapter"); }
+ int enumCount() const override { return 0; }
+ int propertyCount() const override { return m_properties[0]; }
+ int signalCount() const override { return m_signals[0]; }
+ int methodCount() const override { return m_methods[0]; }
+ int sourceEnumIndex(int /*index*/) const override
+ {
+ return -1;
+ }
+ int sourcePropertyIndex(int index) const override
+ {
+ if (index < 0 || index >= m_properties[0])
+ return -1;
+ return m_properties[index+1];
+ }
+ int sourceSignalIndex(int index) const override
+ {
+ if (index < 0 || index >= m_signals[0])
+ return -1;
+ return m_signals[index+1];
+ }
+ int sourceMethodIndex(int index) const override
+ {
+ if (index < 0 || index >= m_methods[0])
+ return -1;
+ return m_methods[index+1];
+ }
+ int signalParameterCount(int index) const override { return m_signalArgCount[index]; }
+ int signalParameterType(int sigIndex, int paramIndex) const override { return m_signalArgTypes[sigIndex][paramIndex]; }
+ int methodParameterCount(int index) const override { return m_methodArgCount[index]; }
+ int methodParameterType(int methodIndex, int paramIndex) const override { return m_methodArgTypes[methodIndex][paramIndex]; }
+ QByteArrayList signalParameterNames(int index) const override
+ {
+ QByteArrayList res;
+ int count = signalParameterCount(index);
+ while (count--)
+ res << QByteArray{};
+ return res;
+ }
+ int propertyIndexFromSignal(int index) const override
+ {
+ switch (index) {
+ case 0: return m_properties[1];
+ }
+ return -1;
+ }
+ int propertyRawIndexFromSignal(int index) const override
+ {
+ switch (index) {
+ case 0: return 0;
+ }
+ return -1;
+ }
+ const QByteArray signalSignature(int index) const override
+ {
+ switch (index) {
+ case 0: return QByteArrayLiteral("availableRolesChanged()");
+ case 1: return QByteArrayLiteral("dataChanged(QtPrivate::IndexList,QtPrivate::IndexList,QList<int>)");
+ case 2: return QByteArrayLiteral("rowsInserted(QtPrivate::IndexList,int,int)");
+ case 3: return QByteArrayLiteral("rowsRemoved(QtPrivate::IndexList,int,int)");
+ case 4: return QByteArrayLiteral("rowsMoved(QtPrivate::IndexList,int,int,QtPrivate::IndexList,int)");
+ case 5: return QByteArrayLiteral("currentChanged(QtPrivate::IndexList,QtPrivate::IndexList)");
+ case 6: return QByteArrayLiteral("resetModel()");
+ case 7: return QByteArrayLiteral("headerDataChanged(Qt::Orientation,int,int)");
+ case 8: return QByteArrayLiteral("columnsInserted(QtPrivate::IndexList,int,int)");
+ case 9: return QByteArrayLiteral("layoutChanged(QtPrivate::IndexList,QAbstractItemModel::LayoutChangeHint)");
+ }
+ return QByteArrayLiteral("");
+ }
+ const QByteArray methodSignature(int index) const override
+ {
+ switch (index) {
+ case 0: return QByteArrayLiteral("replicaSizeRequest(QtPrivate::IndexList)");
+ case 1: return QByteArrayLiteral("replicaRowRequest(QtPrivate::IndexList,QtPrivate::IndexList,QList<int>)");
+ case 2: return QByteArrayLiteral("replicaHeaderRequest(QList<Qt::Orientation>,QList<int>,QList<int>)");
+ case 3: return QByteArrayLiteral("replicaSetCurrentIndex(QtPrivate::IndexList,QItemSelectionModel::SelectionFlags)");
+ case 4: return QByteArrayLiteral("replicaSetData(QtPrivate::IndexList,QVariant,int)");
+ case 5: return QByteArrayLiteral("replicaCacheRequest(size_t,QList<int>)");
+ }
+ return QByteArrayLiteral("");
+ }
+ QMetaMethod::MethodType methodType(int) const override
+ {
+ return QMetaMethod::Slot;
+ }
+ const QByteArray typeName(int index) const override
+ {
+ switch (index) {
+ case 0: return QByteArrayLiteral("QSize");
+ case 1: return QByteArrayLiteral("QtPrivate::DataEntries");
+ case 2: return QByteArrayLiteral("QVariantList");
+ case 3: return QByteArrayLiteral("");
+ case 5: return QByteArrayLiteral("QtPrivate::MetaAndDataEntries");
+ }
+ return QByteArrayLiteral("");
+ }
+
+ QByteArrayList methodParameterNames(int index) const override
+ {
+ QByteArrayList res;
+ int count = methodParameterCount(index);
+ while (count--)
+ res << QByteArray{};
+ return res;
+ }
+
+ QByteArray objectSignature() const override { return QByteArray{}; }
+ bool isAdapterSignal(int index) const override
+ {
+ switch (index) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 8:
+ case 9:
+ return true;
+ }
+ return false;
+ }
+ bool isAdapterMethod(int index) const override
+ {
+ switch (index) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ return true;
+ }
+ return false;
+ }
+ bool isAdapterProperty(int index) const override
+ {
+ switch (index) {
+ case 0:
+ case 1:
+ return true;
+ }
+ return false;
+ }
+
+ int m_properties[3];
+ int m_signals[11];
+ int m_methods[7];
+ int m_signalArgCount[10];
+ const int* m_signalArgTypes[10];
+ int m_methodArgCount[6];
+ const int* m_methodArgTypes[6];
+ QString m_name;
+};
+
+QT_END_NAMESPACE
+
+#endif //QREMOTEOBJECTS_ABSTRACT_ITEM_ADAPTER_P_H
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qremoteobjectabstractitemmodelreplica.h"
+#include "qremoteobjectabstractitemmodelreplica_p.h"
+
+#include "qremoteobjectnode.h"
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qrect.h>
+#include <QtCore/qpoint.h>
+
+QT_BEGIN_NAMESPACE
+
+QT_IMPL_METATYPE_EXTERN_TAGGED(QtPrivate::ModelIndex, QtPrivate__ModelIndex)
+QT_IMPL_METATYPE_EXTERN_TAGGED(QtPrivate::IndexList, QtPrivate__IndexList)
+QT_IMPL_METATYPE_EXTERN_TAGGED(QtPrivate::DataEntries, QtPrivate__DataEntries)
+QT_IMPL_METATYPE_EXTERN_TAGGED(QtPrivate::MetaAndDataEntries, QtPrivate__MetaAndDataEntries)
+QT_IMPL_METATYPE_EXTERN_TAGGED(QtPrivate::IndexValuePair, QtPrivate__IndexValuePair)
+QT_IMPL_METATYPE_EXTERN_TAGGED(Qt::Orientation, Qt__Orientation)
+QT_IMPL_METATYPE_EXTERN_TAGGED(QItemSelectionModel::SelectionFlags,
+ QItemSelectionModel__SelectionFlags)
+
+inline QDebug operator<<(QDebug stream, const RequestedData &data)
+{
+ return stream.nospace() << "RequestedData[start=" << data.start << ", end=" << data.end << ", roles=" << data.roles << "]";
+}
+
+CacheData::CacheData(QAbstractItemModelReplicaImplementation *model, CacheData *parentItem)
+ : replicaModel(model)
+ , parent(parentItem)
+ , hasChildren(false)
+ , columnCount(0)
+ , rowCount(0)
+{
+ if (parent)
+ replicaModel->m_activeParents.insert(parent);
+}
+
+CacheData::~CacheData() {
+ if (parent && !replicaModel->m_activeParents.empty())
+ replicaModel->m_activeParents.erase(this);
+}
+
+QAbstractItemModelReplicaImplementation::QAbstractItemModelReplicaImplementation()
+ : QRemoteObjectReplica()
+ , m_selectionModel(nullptr)
+ , m_rootItem(this)
+{
+ QAbstractItemModelReplicaImplementation::registerMetatypes();
+ initializeModelConnections();
+ connect(this, &QAbstractItemModelReplicaImplementation::availableRolesChanged, this, [this]{
+ m_availableRoles.clear();
+ });
+}
+
+QAbstractItemModelReplicaImplementation::QAbstractItemModelReplicaImplementation(QRemoteObjectNode *node, const QString &name)
+ : QRemoteObjectReplica(ConstructWithNode)
+ , m_selectionModel(nullptr)
+ , m_rootItem(this)
+{
+ QAbstractItemModelReplicaImplementation::registerMetatypes();
+ initializeModelConnections();
+ initializeNode(node, name);
+ connect(this, &QAbstractItemModelReplicaImplementation::availableRolesChanged, this, [this]{
+ m_availableRoles.clear();
+ });
+}
+
+QAbstractItemModelReplicaImplementation::~QAbstractItemModelReplicaImplementation()
+{
+ m_rootItem.clear();
+ qDeleteAll(m_pendingRequests);
+}
+
+void QAbstractItemModelReplicaImplementation::initialize()
+{
+ QVariantList properties;
+ properties << QVariant::fromValue(QList<int>());
+ properties << QVariant::fromValue(QIntHash());
+ setProperties(std::move(properties));
+}
+
+void QAbstractItemModelReplicaImplementation::registerMetatypes()
+{
+ static bool alreadyRegistered = false;
+ if (alreadyRegistered)
+ return;
+
+ alreadyRegistered = true;
+ qRegisterMetaType<QAbstractItemModel*>();
+ qRegisterMetaType<Qt::Orientation>();
+ qRegisterMetaType<QList<Qt::Orientation>>();
+ qRegisterMetaType<QtPrivate::ModelIndex>();
+ qRegisterMetaType<QtPrivate::IndexList>();
+ qRegisterMetaType<QtPrivate::DataEntries>();
+ qRegisterMetaType<QtPrivate::MetaAndDataEntries>();
+ qRegisterMetaType<QItemSelectionModel::SelectionFlags>();
+ qRegisterMetaType<QSize>();
+ qRegisterMetaType<QIntHash>();
+}
+
+void QAbstractItemModelReplicaImplementation::initializeModelConnections()
+{
+ connect(this, &QAbstractItemModelReplicaImplementation::dataChanged, this, &QAbstractItemModelReplicaImplementation::onDataChanged);
+ connect(this, &QAbstractItemModelReplicaImplementation::rowsInserted, this, &QAbstractItemModelReplicaImplementation::onRowsInserted);
+ connect(this, &QAbstractItemModelReplicaImplementation::columnsInserted, this, &QAbstractItemModelReplicaImplementation::onColumnsInserted);
+ connect(this, &QAbstractItemModelReplicaImplementation::rowsRemoved, this, &QAbstractItemModelReplicaImplementation::onRowsRemoved);
+ connect(this, &QAbstractItemModelReplicaImplementation::rowsMoved, this, &QAbstractItemModelReplicaImplementation::onRowsMoved);
+ connect(this, &QAbstractItemModelReplicaImplementation::currentChanged, this, &QAbstractItemModelReplicaImplementation::onCurrentChanged);
+ connect(this, &QAbstractItemModelReplicaImplementation::modelReset, this, &QAbstractItemModelReplicaImplementation::onModelReset);
+ connect(this, &QAbstractItemModelReplicaImplementation::headerDataChanged, this, &QAbstractItemModelReplicaImplementation::onHeaderDataChanged);
+ connect(this, &QAbstractItemModelReplicaImplementation::layoutChanged, this, &QAbstractItemModelReplicaImplementation::onLayoutChanged);
+
+}
+
+inline void removeIndexFromRow(const QModelIndex &index, const QList<int> &roles, CachedRowEntry *entry)
+{
+ CachedRowEntry &entryRef = *entry;
+ if (index.column() < entryRef.size()) {
+ CacheEntry &entry = entryRef[index.column()];
+ if (roles.isEmpty()) {
+ entry.data.clear();
+ } else {
+ for (int role : roles)
+ entry.data.remove(role);
+ }
+ }
+}
+
+void QAbstractItemModelReplicaImplementation::onReplicaCurrentChanged(const QModelIndex ¤t, const QModelIndex &previous)
+{
+ Q_UNUSED(previous)
+ QtPrivate::IndexList currentIndex = QtPrivate::toModelIndexList(current, q);
+ qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "current=" << currentIndex;
+ replicaSetCurrentIndex(currentIndex, QItemSelectionModel::Clear|QItemSelectionModel::Select|QItemSelectionModel::Current);
+}
+
+void QAbstractItemModelReplicaImplementation::setModel(QAbstractItemModelReplica *model)
+{
+ q = model;
+ setParent(model);
+ m_selectionModel.reset(new QItemSelectionModel(model));
+ connect(m_selectionModel.data(), &QItemSelectionModel::currentChanged, this, &QAbstractItemModelReplicaImplementation::onReplicaCurrentChanged);
+}
+
+bool QAbstractItemModelReplicaImplementation::clearCache(const QtPrivate::IndexList &start, const QtPrivate::IndexList &end, const QList<int> &roles = QList<int>())
+{
+ Q_ASSERT(start.size() == end.size());
+
+ bool ok = true;
+ const QModelIndex startIndex = toQModelIndex(start, q, &ok);
+ if (!ok)
+ return false;
+ const QModelIndex endIndex = toQModelIndex(end, q, &ok);
+ if (!ok)
+ return false;
+ Q_ASSERT(startIndex.isValid());
+ Q_ASSERT(endIndex.isValid());
+ Q_ASSERT(startIndex.parent() == endIndex.parent());
+ Q_UNUSED(endIndex)
+ QModelIndex parentIndex = startIndex.parent();
+ auto parentItem = cacheData(parentIndex);
+
+ const int startRow = start.last().row;
+ const int lastRow = end.last().row;
+ const int startColumn = start.last().column;
+ const int lastColumn = end.last().column;
+ for (int row = startRow; row <= lastRow; ++row) {
+ Q_ASSERT_X(row >= 0 && row < parentItem->rowCount, __FUNCTION__, qPrintable(QString(QLatin1String("0 <= %1 < %2")).arg(row).arg(parentItem->rowCount)));
+ auto item = parentItem->children.get(row);
+ if (item) {
+ CachedRowEntry *entry = &(item->cachedRowEntry);
+ for (int column = startColumn; column <= lastColumn; ++column)
+ removeIndexFromRow(q->index(row, column, parentIndex), roles, entry);
+ }
+ }
+ return true;
+}
+
+void QAbstractItemModelReplicaImplementation::onDataChanged(const QtPrivate::IndexList &start, const QtPrivate::IndexList &end, const QList<int> &roles)
+{
+ qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "start=" << start << "end=" << end << "roles=" << roles;
+
+ // we need to clear the cache to make sure the new remote data is fetched if the new data call is happening
+ if (clearCache(start, end, roles)) {
+ bool ok = true;
+ const QModelIndex startIndex = toQModelIndex(start, q, &ok);
+ if (!ok)
+ return;
+ const QModelIndex endIndex = toQModelIndex(end, q, &ok);
+ if (!ok)
+ return;
+ Q_ASSERT(startIndex.parent() == endIndex.parent());
+ auto parentItem = cacheData(startIndex.parent());
+ int startRow = start.last().row;
+ int endRow = end.last().row;
+ bool dataChanged = false;
+ while (startRow <= endRow) {
+ for (;startRow <= endRow; startRow++) {
+ if (parentItem->children.exists(startRow))
+ break;
+ }
+
+ if (startRow > endRow)
+ break;
+
+ RequestedData data;
+ data.roles = roles;
+ data.start = start;
+ data.start.last().row = startRow;
+
+ while (startRow <= endRow && parentItem->children.exists(startRow))
+ ++startRow;
+
+ data.end = end;
+ data.end.last().row = startRow -1;
+
+ m_requestedData.append(data);
+ dataChanged = true;
+ }
+
+ if (dataChanged)
+ QMetaObject::invokeMethod(this, "fetchPendingData", Qt::QueuedConnection);
+ }
+}
+
+void QAbstractItemModelReplicaImplementation::onRowsInserted(const QtPrivate::IndexList &parent, int start, int end)
+{
+ qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "start=" << start << "end=" << end << "parent=" << parent;
+
+ bool treeFullyLazyLoaded = true;
+ const QModelIndex parentIndex = toQModelIndex(parent, q, &treeFullyLazyLoaded, true);
+ if (!treeFullyLazyLoaded)
+ return;
+
+ auto parentItem = cacheData(parentIndex);
+ q->beginInsertRows(parentIndex, start, end);
+ parentItem->insertChildren(start, end);
+ for (int i = start; i <= end; ++i)
+ m_headerData[1].append(CacheEntry());
+ q->endInsertRows();
+ if (!parentItem->hasChildren && parentItem->columnCount > 0) {
+ parentItem->hasChildren = true;
+ emit q->dataChanged(parentIndex, parentIndex);
+ }
+}
+
+void QAbstractItemModelReplicaImplementation::onColumnsInserted(const QtPrivate::IndexList &parent, int start, int end)
+{
+ qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "start=" << start << "end=" << end << "parent=" << parent;
+
+ bool treeFullyLazyLoaded = true;
+ const QModelIndex parentIndex = toQModelIndex(parent, q, &treeFullyLazyLoaded);
+ if (!treeFullyLazyLoaded)
+ return;
+
+ //Since we need to support QAIM and models that don't emit columnCountChanged
+ //check if we have a constant columnCount everywhere if thats the case don't insert
+ //more columns
+ auto parentItem = cacheData(parentIndex);
+ auto parentOfParent = parentItem->parent;
+ if (parentOfParent && parentItem != &m_rootItem)
+ if (parentOfParent->columnCount == parentItem->columnCount)
+ return;
+ q->beginInsertColumns(parentIndex, start, end);
+ parentItem->columnCount += end - start + 1;
+ for (int i = start; i <= end; ++i)
+ m_headerData[0].append(CacheEntry());
+ q->endInsertColumns();
+ if (!parentItem->hasChildren && parentItem->children.size() > 0) {
+ parentItem->hasChildren = true;
+ emit q->dataChanged(parentIndex, parentIndex);
+ }
+
+}
+
+void QAbstractItemModelReplicaImplementation::onRowsRemoved(const QtPrivate::IndexList &parent, int start, int end)
+{
+ qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "start=" << start << "end=" << end << "parent=" << parent;
+
+ bool treeFullyLazyLoaded = true;
+ const QModelIndex parentIndex = toQModelIndex(parent, q, &treeFullyLazyLoaded);
+ if (!treeFullyLazyLoaded)
+ return;
+
+ auto parentItem = cacheData(parentIndex);
+ q->beginRemoveRows(parentIndex, start, end);
+ if (parentItem)
+ parentItem->removeChildren(start, end);
+ m_headerData[1].erase(m_headerData[1].begin() + start, m_headerData[1].begin() + end + 1);
+ q->endRemoveRows();
+}
+
+void QAbstractItemModelReplicaImplementation::onRowsMoved(QtPrivate::IndexList srcParent, int srcRow, int count, QtPrivate::IndexList destParent, int destRow)
+{
+ qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO;
+
+ const QModelIndex sourceParent = toQModelIndex(srcParent, q);
+ const QModelIndex destinationParent = toQModelIndex(destParent, q);
+ Q_ASSERT(!sourceParent.isValid());
+ Q_ASSERT(!destinationParent.isValid());
+ q->beginMoveRows(sourceParent, srcRow, count, destinationParent, destRow);
+//TODO misses parents...
+ QtPrivate::IndexList start, end;
+ start << QtPrivate::ModelIndex(srcRow, 0);
+ end << QtPrivate::ModelIndex(srcRow + count, q->columnCount(sourceParent)-1);
+ clearCache(start, end);
+ QtPrivate::IndexList start2, end2;
+ start2 << QtPrivate::ModelIndex(destRow, 0);
+ end2 << QtPrivate::ModelIndex(destRow + count, q->columnCount(destinationParent)-1);
+ clearCache(start2, end2);
+ q->endMoveRows();
+}
+
+void QAbstractItemModelReplicaImplementation::onCurrentChanged(QtPrivate::IndexList current, QtPrivate::IndexList previous)
+{
+ qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "current=" << current << "previous=" << previous;
+ Q_UNUSED(previous)
+ Q_ASSERT(m_selectionModel);
+ bool ok;
+ // If we have several tree models sharing a selection model, we
+ // can't guarantee that all Replicas have the selected cell
+ // available.
+ const QModelIndex currentIndex = toQModelIndex(current, q, &ok);
+ // Ignore selection if we can't find the desired cell.
+ if (ok)
+ m_selectionModel->setCurrentIndex(currentIndex, QItemSelectionModel::Clear|QItemSelectionModel::Select|QItemSelectionModel::Current);
+}
+
+void QAbstractItemModelReplicaImplementation::handleInitDone(QRemoteObjectPendingCallWatcher *watcher)
+{
+ qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO;
+
+ handleModelResetDone(watcher);
+ m_initDone = true;
+ emit q->initialized();
+}
+
+void QAbstractItemModelReplicaImplementation::handleModelResetDone(QRemoteObjectPendingCallWatcher *watcher)
+{
+ QSize size;
+ if (m_initialAction == QtRemoteObjects::FetchRootSize)
+ size = watcher->returnValue().toSize();
+ else {
+ Q_ASSERT(watcher->returnValue().canConvert<QtPrivate::MetaAndDataEntries>());
+ size = watcher->returnValue().value<QtPrivate::MetaAndDataEntries>().size;
+ }
+
+ qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "size=" << size;
+
+ q->beginResetModel();
+ m_rootItem.clear();
+ if (size.height() > 0) {
+ m_rootItem.rowCount = size.height();
+ m_rootItem.hasChildren = true;
+ }
+
+ m_rootItem.columnCount = size.width();
+ m_headerData[0].resize(size.width());
+ m_headerData[1].resize(size.height());
+ {
+ QList<CacheEntry> &headerEntries = m_headerData[0];
+ for (int i = 0; i < size.width(); ++i )
+ headerEntries[i].data.clear();
+ }
+ {
+ QList<CacheEntry> &headerEntries = m_headerData[1];
+ for (int i = 0; i < size.height(); ++i )
+ headerEntries[i].data.clear();
+ }
+ if (m_initialAction == QtRemoteObjects::PrefetchData) {
+ auto entries = watcher->returnValue().value<QtPrivate::MetaAndDataEntries>();
+ for (int i = 0; i < entries.data.size(); ++i)
+ fillCache(entries.data[i], entries.roles);
+ }
+ q->endResetModel();
+ m_pendingRequests.removeAll(watcher);
+ delete watcher;
+}
+
+void QAbstractItemModelReplicaImplementation::handleSizeDone(QRemoteObjectPendingCallWatcher *watcher)
+{
+ SizeWatcher *sizeWatcher = static_cast<SizeWatcher*>(watcher);
+ const QSize size = sizeWatcher->returnValue().toSize();
+ auto parentItem = cacheData(sizeWatcher->parentList);
+ const QModelIndex parent = toQModelIndex(sizeWatcher->parentList, q);
+
+ if (size.width() != parentItem->columnCount) {
+ const int columnCount = std::max(0, parentItem->columnCount);
+ Q_ASSERT_X(size.width() >= parentItem->columnCount, __FUNCTION__, "The column count should only shrink in columnsRemoved!!");
+ parentItem->columnCount = size.width();
+ if (size.width() > columnCount) {
+ Q_ASSERT(size.width() > 0);
+ q->beginInsertColumns(parent, columnCount, size.width() - 1);
+ q->endInsertColumns();
+ } else {
+ Q_ASSERT_X(size.width() == columnCount, __FUNCTION__, qPrintable(QString(QLatin1String("%1 != %2")).arg(size.width()).arg(columnCount)));
+ }
+ }
+
+ Q_ASSERT_X(size.height() >= parentItem->rowCount, __FUNCTION__, "The new size and the current size should match!!");
+ if (!parentItem->rowCount) {
+ if (size.height() > 0) {
+ q->beginInsertRows(parent, 0, size.height() - 1);
+ parentItem->rowCount = size.height();
+ q->endInsertRows();
+ }
+ } else {
+ Q_ASSERT_X(parentItem->rowCount == size.height(), __FUNCTION__, qPrintable(QString(QLatin1String("%1 != %2")).arg(parentItem->rowCount).arg(size.height())));
+ }
+ m_pendingRequests.removeAll(watcher);
+ delete watcher;
+}
+
+void QAbstractItemModelReplicaImplementation::init()
+{
+ qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << this->node()->objectName();
+ QRemoteObjectPendingCallWatcher *watcher = doModelReset();
+ connect(watcher, &QRemoteObjectPendingCallWatcher::finished, this, &QAbstractItemModelReplicaImplementation::handleInitDone);
+}
+
+QRemoteObjectPendingCallWatcher* QAbstractItemModelReplicaImplementation::doModelReset()
+{
+ qDeleteAll(m_pendingRequests);
+ m_pendingRequests.clear();
+ QtPrivate::IndexList parentList;
+ QRemoteObjectPendingCallWatcher *watcher;
+ if (m_initialAction == QtRemoteObjects::FetchRootSize) {
+ auto call = replicaSizeRequest(parentList);
+ watcher = new SizeWatcher(parentList, call);
+ } else {
+ auto call = replicaCacheRequest(m_rootItem.children.cacheSize, m_initialFetchRolesHint);
+ watcher = new QRemoteObjectPendingCallWatcher(call);
+ }
+ m_pendingRequests.push_back(watcher);
+ return watcher;
+}
+
+inline void fillCacheEntry(CacheEntry *entry, const QtPrivate::IndexValuePair &pair, const QList<int> &roles)
+{
+ Q_ASSERT(entry);
+
+ const QVariantList &data = pair.data;
+ Q_ASSERT(roles.size() == data.size());
+
+ entry->flags = pair.flags;
+
+ qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "data.size=" << data.size();
+ for (int i = 0; i < data.size(); ++i) {
+ const int role = roles[i];
+ const QVariant dataVal = data[i];
+ qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "role=" << role << "data=" << dataVal;
+ entry->data[role] = dataVal;
+ }
+}
+
+inline void fillRow(CacheData *item, const QtPrivate::IndexValuePair &pair, const QAbstractItemModel *model, const QList<int> &roles)
+{
+ CachedRowEntry &rowRef = item->cachedRowEntry;
+ const QModelIndex index = toQModelIndex(pair.index, model);
+ Q_ASSERT(index.isValid());
+ qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "row=" << index.row() << "column=" << index.column();
+ if (index.column() == 0)
+ item->hasChildren = pair.hasChildren;
+ bool existed = false;
+ for (int i = 0; i < rowRef.size(); ++i) {
+ if (i == index.column()) {
+ fillCacheEntry(&rowRef[i], pair, roles);
+ existed = true;
+ }
+ }
+ qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "existed=" << existed;
+ if (!existed) {
+ CacheEntry entries;
+ fillCacheEntry(&entries, pair, roles);
+ rowRef.append(entries);
+ }
+}
+
+int collectEntriesForRow(QtPrivate::DataEntries* filteredEntries, int row, const QtPrivate::DataEntries &entries, int startIndex)
+{
+ Q_ASSERT(filteredEntries);
+ const int size = int(entries.data.size());
+ for (int i = startIndex; i < size; ++i)
+ {
+ const QtPrivate::IndexValuePair &pair = entries.data[i];
+ if (pair.index.last().row == row)
+ filteredEntries->data << pair;
+ else
+ return i;
+ }
+ return size;
+}
+
+void QAbstractItemModelReplicaImplementation::fillCache(const QtPrivate::IndexValuePair &pair, const QList<int> &roles)
+{
+ if (auto item = createCacheData(pair.index)) {
+ fillRow(item, pair, q, roles);
+ item->rowCount = pair.size.height();
+ item->columnCount = pair.size.width();
+ }
+ for (const auto &it : pair.children)
+ fillCache(it, roles);
+}
+
+void QAbstractItemModelReplicaImplementation::requestedData(QRemoteObjectPendingCallWatcher *qobject)
+{
+ RowWatcher *watcher = static_cast<RowWatcher *>(qobject);
+ Q_ASSERT(watcher);
+ Q_ASSERT(watcher->start.size() == watcher->end.size());
+
+ qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "start=" << watcher->start << "end=" << watcher->end;
+
+ QtPrivate::IndexList parentList = watcher->start;
+ Q_ASSERT(!parentList.isEmpty());
+ parentList.pop_back();
+ auto parentItem = cacheData(parentList);
+ QtPrivate::DataEntries entries = watcher->returnValue().value<QtPrivate::DataEntries>();
+
+ const int rowCount = parentItem->rowCount;
+ const int columnCount = parentItem->columnCount;
+
+ if (rowCount < 1 || columnCount < 1)
+ return;
+
+ const int startRow = std::min(watcher->start.last().row, rowCount - 1);
+ const int endRow = std::min(watcher->end.last().row, rowCount - 1);
+ const int startColumn = std::min(watcher->start.last().column, columnCount - 1);
+ const int endColumn = std::min(watcher->end.last().column, columnCount - 1);
+ Q_ASSERT_X(startRow >= 0 && startRow < parentItem->rowCount, __FUNCTION__, qPrintable(QString(QLatin1String("0 <= %1 < %2")).arg(startRow).arg(parentItem->rowCount)));
+ Q_ASSERT_X(endRow >= 0 && endRow < parentItem->rowCount, __FUNCTION__, qPrintable(QString(QLatin1String("0 <= %1 < %2")).arg(endRow).arg(parentItem->rowCount)));
+
+ for (int i = 0; i < entries.data.size(); ++i) {
+ QtPrivate::IndexValuePair pair = entries.data[i];
+ if (auto item = createCacheData(pair.index))
+ fillRow(item, pair, q, watcher->roles);
+ }
+
+ const QModelIndex parentIndex = toQModelIndex(parentList, q);
+ const QModelIndex startIndex = q->index(startRow, startColumn, parentIndex);
+ const QModelIndex endIndex = q->index(endRow, endColumn, parentIndex);
+ Q_ASSERT(startIndex.isValid());
+ Q_ASSERT(endIndex.isValid());
+ emit q->dataChanged(startIndex, endIndex, watcher->roles);
+ m_pendingRequests.removeAll(watcher);
+ delete watcher;
+}
+
+void QAbstractItemModelReplicaImplementation::fetchPendingData()
+{
+ if (m_requestedData.isEmpty())
+ return;
+
+ qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "m_requestedData.size=" << m_requestedData.size();
+
+ std::vector<RequestedData> finalRequests;
+ RequestedData curData;
+ for (const RequestedData &data : qExchange(m_requestedData, {})) {
+ qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "REQUESTED start=" << data.start << "end=" << data.end << "roles=" << data.roles;
+
+ Q_ASSERT(!data.start.isEmpty());
+ Q_ASSERT(!data.end.isEmpty());
+ Q_ASSERT(data.start.size() == data.end.size());
+ if (curData.start.isEmpty() || curData.start.last().row == -1 || curData.start.last().column == -1)
+ curData = data;
+ if (curData.start.size() != data.start.size()) {
+ finalRequests.push_back(curData);
+ curData = data;
+ } else {
+ if (data.start.size() > 1) {
+ for (int i = 0; i < data.start.size() - 1; ++i) {
+ if (curData.start[i].row != data.start[i].row ||
+ curData.start[i].column != data.start[i].column) {
+ finalRequests.push_back(curData);
+ curData = data;
+ }
+ }
+ }
+
+ const QtPrivate::ModelIndex curIndStart = curData.start.last();
+ const QtPrivate::ModelIndex curIndEnd = curData.end.last();
+ const QtPrivate::ModelIndex dataIndStart = data.start.last();
+ const QtPrivate::ModelIndex dataIndEnd = data.end.last();
+ const QtPrivate::ModelIndex resStart(std::min(curIndStart.row, dataIndStart.row), std::min(curIndStart.column, dataIndStart.column));
+ const QtPrivate::ModelIndex resEnd(std::max(curIndEnd.row, dataIndEnd.row), std::max(curIndEnd.column, dataIndEnd.column));
+ QList<int> roles = curData.roles;
+ if (!curData.roles.isEmpty()) {
+ for (int role : data.roles) {
+ if (!curData.roles.contains(role))
+ roles.append(role);
+ }
+ }
+ QRect firstRect( QPoint(curIndStart.row, curIndStart.column), QPoint(curIndEnd.row, curIndEnd.column));
+ QRect secondRect( QPoint(dataIndStart.row, dataIndStart.column), QPoint(dataIndEnd.row, dataIndEnd.column));
+
+ const bool borders = (qAbs(curIndStart.row - dataIndStart.row) == 1) ||
+ (qAbs(curIndStart.column - dataIndStart.column) == 1) ||
+ (qAbs(curIndEnd.row - dataIndEnd.row) == 1) ||
+ (qAbs(curIndEnd.column - dataIndEnd.column) == 1);
+
+ if ((resEnd.row - resStart.row < 100) && (firstRect.intersects(secondRect) || borders)) {
+ QtPrivate::IndexList start = curData.start;
+ start.pop_back();
+ start.push_back(resStart);
+ QtPrivate::IndexList end = curData.end;
+ end.pop_back();
+ end.push_back(resEnd);
+ curData.start = start;
+ curData.end = end;
+ curData.roles = roles;
+ Q_ASSERT(!start.isEmpty());
+ Q_ASSERT(!end.isEmpty());
+ } else {
+ finalRequests.push_back(curData);
+ curData = data;
+ }
+ }
+ }
+ finalRequests.push_back(curData);
+ //qCDebug(QT_REMOTEOBJECT_MODELS) << "Final requests" << finalRequests;
+ int rows = 0;
+ // There is no point to eat more than can chew
+ for (auto it = finalRequests.rbegin(); it != finalRequests.rend() && size_t(rows) < m_rootItem.children.cacheSize; ++it) {
+ qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "FINAL start=" << it->start << "end=" << it->end << "roles=" << it->roles;
+
+ QRemoteObjectPendingReply<QtPrivate::DataEntries> reply = replicaRowRequest(it->start, it->end, it->roles);
+ RowWatcher *watcher = new RowWatcher(it->start, it->end, it->roles, reply);
+ rows += 1 + it->end.first().row - it->start.first().row;
+ m_pendingRequests.push_back(watcher);
+ connect(watcher, &RowWatcher::finished, this, &QAbstractItemModelReplicaImplementation::requestedData);
+ }
+}
+
+void QAbstractItemModelReplicaImplementation::onModelReset()
+{
+ if (!m_initDone)
+ return;
+
+ qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO;
+ QRemoteObjectPendingCallWatcher *watcher = doModelReset();
+ connect(watcher, &QRemoteObjectPendingCallWatcher::finished, this, &QAbstractItemModelReplicaImplementation::handleModelResetDone);
+}
+
+void QAbstractItemModelReplicaImplementation::onHeaderDataChanged(Qt::Orientation orientation, int first, int last)
+{
+ // TODO clean cache
+ const int index = orientation == Qt::Horizontal ? 0 : 1;
+ QList<CacheEntry> &entries = m_headerData[index];
+ for (int i = first; i <= last && i < entries.size(); ++i )
+ entries[i].data.clear();
+ emit q->headerDataChanged(orientation, first, last);
+}
+
+void QAbstractItemModelReplicaImplementation::fetchPendingHeaderData()
+{
+ QList<int> roles;
+ QList<int> sections;
+ QList<Qt::Orientation> orientations;
+ for (const RequestedHeaderData &data : std::as_const(m_requestedHeaderData)) {
+ roles.push_back(data.role);
+ sections.push_back(data.section);
+ orientations.push_back(data.orientation);
+ }
+ QRemoteObjectPendingReply<QVariantList> reply = replicaHeaderRequest(orientations, sections, roles);
+ HeaderWatcher *watcher = new HeaderWatcher(orientations, sections, roles, reply);
+ connect(watcher, &HeaderWatcher::finished, this, &QAbstractItemModelReplicaImplementation::requestedHeaderData);
+ m_requestedHeaderData.clear();
+ m_pendingRequests.push_back(watcher);
+}
+
+void QAbstractItemModelReplicaImplementation::onLayoutChanged(const QtPrivate::IndexList &parents,
+ QAbstractItemModel::LayoutChangeHint hint)
+{
+ QList<QPersistentModelIndex> indexes;
+ for (const QtPrivate::ModelIndex &parent : std::as_const(parents)) {
+ const QModelIndex parentIndex = toQModelIndex(QtPrivate::IndexList{parent}, q);
+ indexes << QPersistentModelIndex(parentIndex);
+ }
+ QRemoteObjectPendingCallWatcher *watcher;
+ auto call = replicaCacheRequest(m_rootItem.children.cacheSize, m_initialFetchRolesHint);
+ watcher = new QRemoteObjectPendingCallWatcher(call);
+ m_pendingRequests.push_back(watcher);
+ connect(watcher, &QRemoteObjectPendingCallWatcher::finished, this, [this, watcher, indexes, hint]() {
+ Q_ASSERT(watcher->returnValue().canConvert<QtPrivate::MetaAndDataEntries>());
+ const QSize size = watcher->returnValue().value<QtPrivate::MetaAndDataEntries>().size;
+
+ q->layoutAboutToBeChanged(indexes, hint);
+ m_rootItem.clear();
+ if (size.height() > 0) {
+ m_rootItem.rowCount = size.height();
+ m_rootItem.hasChildren = true;
+ }
+
+ m_rootItem.columnCount = size.width();
+ if (m_initialAction == QtRemoteObjects::PrefetchData) {
+ auto entries = watcher->returnValue().value<QtPrivate::MetaAndDataEntries>();
+ for (int i = 0; i < entries.data.size(); ++i)
+ fillCache(entries.data[i], entries.roles);
+ }
+ m_pendingRequests.removeAll(watcher);
+ watcher->deleteLater();
+ emit q->layoutChanged(indexes, hint);
+ });
+}
+
+static inline QList<QPair<int, int>> listRanges(const QList<int> &list)
+{
+ QList<QPair<int, int>> result;
+ if (!list.isEmpty()) {
+ QPair<int, int> currentElem = qMakePair(list.first(), list.first());
+ const auto end = list.constEnd();
+ for (auto it = list.constBegin() + 1; it != end; ++it) {
+ if (currentElem.first == *it + 1)
+ currentElem.first = *it;
+ else if ( currentElem.second == *it -1)
+ currentElem.second = *it;
+ else if (currentElem.first <= *it && currentElem.second >= *it)
+ continue;
+ else {
+ result.push_back(currentElem);
+ currentElem.first = *it;
+ currentElem.second = *it;
+ }
+ }
+ result.push_back(currentElem);
+ }
+ return result;
+}
+
+void QAbstractItemModelReplicaImplementation::requestedHeaderData(QRemoteObjectPendingCallWatcher *qobject)
+{
+ HeaderWatcher *watcher = static_cast<HeaderWatcher *>(qobject);
+ Q_ASSERT(watcher);
+
+ QVariantList data = watcher->returnValue().value<QVariantList>();
+ Q_ASSERT(watcher->orientations.size() == data.size());
+ Q_ASSERT(watcher->sections.size() == data.size());
+ Q_ASSERT(watcher->roles.size() == data.size());
+ QList<int> horizontalSections;
+ QList<int> verticalSections;
+
+ for (int i = 0; i < data.size(); ++i) {
+ if (watcher->orientations[i] == Qt::Horizontal)
+ horizontalSections.append(watcher->sections[i]);
+ else
+ verticalSections.append(watcher->sections[i]);
+ const int index = watcher->orientations[i] == Qt::Horizontal ? 0 : 1;
+ const int role = watcher->roles[i];
+ QHash<int, QVariant> &dat = m_headerData[index][watcher->sections[i]].data;
+ dat[role] = data[i];
+ }
+ QList<QPair<int, int>> horRanges = listRanges(horizontalSections);
+ QList<QPair<int, int>> verRanges = listRanges(verticalSections);
+
+ for (int i = 0; i < horRanges.size(); ++i)
+ emit q->headerDataChanged(Qt::Horizontal, horRanges[i].first, horRanges[i].second);
+ for (int i = 0; i < verRanges.size(); ++i)
+ emit q->headerDataChanged(Qt::Vertical, verRanges[i].first, verRanges[i].second);
+ m_pendingRequests.removeAll(watcher);
+ delete watcher;
+}
+
+/*!
+ \class QAbstractItemModelReplica
+ \inmodule QtRemoteObjects
+ \brief The QAbstractItemModelReplica class serves as a convenience class for
+ Replicas of Sources based on QAbstractItemModel.
+
+ QAbstractItemModelReplica makes replicating QAbstractItemModels more
+ efficient by employing caching and pre-fetching.
+
+ \sa QAbstractItemModel
+*/
+
+/*!
+ \internal
+*/
+QAbstractItemModelReplica::QAbstractItemModelReplica(QAbstractItemModelReplicaImplementation *rep, QtRemoteObjects::InitialAction action, const QList<int> &rolesHint)
+ : QAbstractItemModel()
+ , d(rep)
+{
+ d->m_initialAction = action;
+ d->m_initialFetchRolesHint = rolesHint;
+
+ rep->setModel(this);
+ connect(rep, &QAbstractItemModelReplicaImplementation::initialized, d.data(), &QAbstractItemModelReplicaImplementation::init);
+}
+
+/*!
+ Destroys the instance of QAbstractItemModelReplica.
+*/
+QAbstractItemModelReplica::~QAbstractItemModelReplica()
+{
+}
+
+static QVariant findData(const CachedRowEntry &row, const QModelIndex &index, int role, bool *cached = nullptr)
+{
+ if (index.column() < row.size()) {
+ const CacheEntry &entry = row[index.column()];
+ QHash<int, QVariant>::ConstIterator it = entry.data.constFind(role);
+ if (it != entry.data.constEnd()) {
+ if (cached)
+ *cached = true;
+ return it.value();
+ }
+ }
+ if (cached)
+ *cached = false;
+ return QVariant();
+}
+
+/*!
+ Returns a pointer to the QItemSelectionModel for the current
+ QAbstractItemModelReplica.
+*/
+QItemSelectionModel* QAbstractItemModelReplica::selectionModel() const
+{
+ return d->m_selectionModel.data();
+}
+
+/*!
+ \reimp
+*/
+bool QAbstractItemModelReplica::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ if (role == Qt::UserRole - 1) {
+ auto parent = d->cacheData(index);
+ if (!parent)
+ return false;
+ bool ok = true;
+ auto row = value.toInt(&ok);
+ if (ok)
+ parent->ensureChildren(row, row);
+ return ok;
+ }
+ if (!index.isValid())
+ return false;
+ if (index.row() < 0 || index.row() >= rowCount(index.parent()))
+ return false;
+ if (index.column() < 0 || index.column() >= columnCount(index.parent()))
+ return false;
+
+ const QList<int> &availRoles = availableRoles();
+ const auto res = std::find(availRoles.begin(), availRoles.end(), role);
+ if (res == availRoles.end()) {
+ qCWarning(QT_REMOTEOBJECT_MODELS) << "Tried to setData for index" << index << "on a not supported role" << role;
+ return false;
+ }
+ // sendInvocationRequest to change server side data;
+ d->replicaSetData(QtPrivate::toModelIndexList(index, this), value, role);
+ return true;
+}
+
+/*!
+ Returns the \a role data for the item at \a index if available in cache.
+ A default-constructed QVariant is returned if the index is invalid, the role
+ is not one of the available roles, the \l {Replica} is uninitialized or
+ the data was not available.
+ If the data was not available in cache it will be requested from the
+ \l {Source}.
+
+ \sa QAbstractItemModel::data(), hasData(), setData(), isInitialized()
+*/
+QVariant QAbstractItemModelReplica::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ QModelRoleData roleData(role);
+ multiData(index, roleData);
+ return roleData.data();
+}
+
+/*!
+ \reimp
+*/
+void QAbstractItemModelReplica::multiData(const QModelIndex &index,
+ QModelRoleDataSpan roleDataSpan) const
+{
+ Q_ASSERT(checkIndex(index, CheckIndexOption::IndexIsValid));
+
+ if (!d->isInitialized()) {
+ qCDebug(QT_REMOTEOBJECT_MODELS)<<"Data not initialized yet";
+
+ for (auto &roleData : roleDataSpan)
+ roleData.clearData();
+ return;
+ }
+
+ QList<int> rolesToFetch;
+ const auto roles = availableRoles();
+ if (auto item = d->cacheData(index); item) {
+ // If the index is found in cache, try to find the data for each role
+ for (auto &roleData : roleDataSpan) {
+ const auto role = roleData.role();
+ if (roles.contains(role)) {
+ bool cached = false;
+ QVariant result = findData(item->cachedRowEntry, index, role, &cached);
+ if (cached) {
+ roleData.setData(std::move(result));
+ } else {
+ roleData.clearData();
+ rolesToFetch.push_back(role);
+ }
+ } else {
+ roleData.clearData();
+ }
+ }
+ } else {
+ // If the index is not found in cache, schedule all roles for fetching
+ for (auto &roleData : roleDataSpan) {
+ const auto role = roleData.role();
+ if (roles.contains(role))
+ rolesToFetch.push_back(role);
+ roleData.clearData();
+ }
+ }
+
+ if (rolesToFetch.empty())
+ return;
+
+ auto parentItem = d->cacheData(index.parent());
+ Q_ASSERT(parentItem);
+ Q_ASSERT(index.row() < parentItem->rowCount);
+ const int row = index.row();
+ QtPrivate::IndexList parentList = QtPrivate::toModelIndexList(index.parent(), this);
+ QtPrivate::IndexList start = QtPrivate::IndexList() << parentList << QtPrivate::ModelIndex(row, 0);
+ QtPrivate::IndexList end = QtPrivate::IndexList() << parentList << QtPrivate::ModelIndex(row, std::max(0, parentItem->columnCount - 1));
+ Q_ASSERT(toQModelIndex(start, this).isValid());
+
+ RequestedData data;
+ data.start = start;
+ data.end = end;
+ data.roles = rolesToFetch;
+ d->m_requestedData.push_back(data);
+ qCDebug(QT_REMOTEOBJECT_MODELS) << "FETCH PENDING DATA" << start << end << rolesToFetch;
+ QMetaObject::invokeMethod(d.data(), "fetchPendingData", Qt::QueuedConnection);
+}
+
+/*!
+ \reimp
+*/
+QModelIndex QAbstractItemModelReplica::parent(const QModelIndex &index) const
+{
+ if (!index.isValid() || !index.internalPointer())
+ return QModelIndex();
+ auto parent = static_cast<CacheData*>(index.internalPointer());
+ Q_ASSERT(parent);
+ if (parent == &d->m_rootItem)
+ return QModelIndex();
+ if (d->m_activeParents.find(parent) == d->m_activeParents.end() || d->m_activeParents.find(parent->parent) == d->m_activeParents.end())
+ return QModelIndex();
+ int row = parent->parent->children.find(parent);
+ Q_ASSERT(row >= 0);
+ return createIndex(row, 0, parent->parent);
+}
+
+/*!
+ \reimp
+*/
+QModelIndex QAbstractItemModelReplica::index(int row, int column, const QModelIndex &parent) const
+{
+ auto parentItem = d->cacheData(parent);
+ if (!parentItem)
+ return QModelIndex();
+
+ Q_ASSERT_X((parent.isValid() && parentItem && parentItem != &d->m_rootItem) || (!parent.isValid() && parentItem == &d->m_rootItem), __FUNCTION__, qPrintable(QString(QLatin1String("isValid=%1 equals=%2")).arg(parent.isValid()).arg(parentItem == &d->m_rootItem)));
+
+ // hmpf, following works around a Q_ASSERT-bug in QAbstractItemView::setModel which does just call
+ // d->model->index(0,0) without checking the range before-hand what triggers our assert in case the
+ // model is empty when view::setModel is called :-/ So, work around with the following;
+ if (row < 0 || row >= parentItem->rowCount)
+ return QModelIndex();
+
+ if (column < 0 || column >= parentItem->columnCount)
+ return QModelIndex();
+
+ if (parentItem != &d->m_rootItem)
+ parentItem->ensureChildren(row, row);
+ return createIndex(row, column, reinterpret_cast<void*>(parentItem));
+}
+
+/*!
+ \reimp
+*/
+bool QAbstractItemModelReplica::hasChildren(const QModelIndex &parent) const
+{
+ auto parentItem = d->cacheData(parent);
+ if (parent.isValid() && parent.column() != 0)
+ return false;
+ else
+ return parentItem ? parentItem->hasChildren : false;
+}
+
+/*!
+ \reimp
+*/
+int QAbstractItemModelReplica::rowCount(const QModelIndex &parent) const
+{
+ auto parentItem = d->cacheData(parent);
+ const bool canHaveChildren = parentItem && parentItem->hasChildren && !parentItem->rowCount && parent.column() == 0;
+ if (canHaveChildren) {
+ QtPrivate::IndexList parentList = QtPrivate::toModelIndexList(parent, this);
+ QRemoteObjectPendingReply<QSize> reply = d->replicaSizeRequest(parentList);
+ SizeWatcher *watcher = new SizeWatcher(parentList, reply);
+ connect(watcher, &SizeWatcher::finished, d.data(), &QAbstractItemModelReplicaImplementation::handleSizeDone);
+ } else if (parent.column() > 0) {
+ return 0;
+ }
+
+ return parentItem ? parentItem->rowCount : 0;
+}
+
+/*!
+ \reimp
+*/
+int QAbstractItemModelReplica::columnCount(const QModelIndex &parent) const
+{
+ if (parent.isValid() && parent.column() > 0)
+ return 0;
+ auto parentItem = d->cacheData(parent);
+ if (!parentItem)
+ return 0;
+ while (parentItem->columnCount < 0 && parentItem->parent)
+ parentItem = parentItem->parent;
+ return std::max(0, parentItem->columnCount);
+}
+
+/*!
+ Returns the data for the given \a role and \a section in the header with the
+ specified \a orientation.
+
+ If the data is not available it will be requested from the \l {Source}.
+
+ \sa QAbstractItemModel::headerData()
+*/
+QVariant QAbstractItemModelReplica::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ const int index = orientation == Qt::Horizontal ? 0 : 1;
+ const QList<CacheEntry> elem = d->m_headerData[index];
+ if (section >= elem.size())
+ return QVariant();
+
+ const QHash<int, QVariant> &dat = elem.at(section).data;
+ QHash<int, QVariant>::ConstIterator it = dat.constFind(role);
+ if (it != dat.constEnd())
+ return it.value();
+
+ RequestedHeaderData data;
+ data.role = role;
+ data.section = section;
+ data.orientation = orientation;
+ d->m_requestedHeaderData.push_back(data);
+ QMetaObject::invokeMethod(d.data(), "fetchPendingHeaderData", Qt::QueuedConnection);
+ return QVariant();
+}
+
+/*!
+ \reimp
+*/
+Qt::ItemFlags QAbstractItemModelReplica::flags(const QModelIndex &index) const
+{
+ CacheEntry *entry = d->cacheEntry(index);
+ return entry ? entry->flags : Qt::NoItemFlags;
+}
+
+/*!
+ \fn void QAbstractItemModelReplica::initialized()
+
+ The initialized signal is emitted the first time we receive data
+ from the \l {Source}.
+
+ \sa isInitialized()
+*/
+
+/*!
+ Returns \c true if this replica has been initialized with data from the
+ \l {Source} object. Returns \c false otherwise.
+
+ \sa initialized()
+*/
+bool QAbstractItemModelReplica::isInitialized() const
+{
+ return d->isInitialized();
+}
+
+/*!
+ Returns \c true if there exists \a role data for the item at \a index.
+ Returns \c false in any other case.
+*/
+bool QAbstractItemModelReplica::hasData(const QModelIndex &index, int role) const
+{
+ if (!d->isInitialized() || !index.isValid())
+ return false;
+ auto item = d->cacheData(index);
+ if (!item)
+ return false;
+ bool cached = false;
+ const CachedRowEntry &entry = item->cachedRowEntry;
+ QVariant result = findData(entry, index, role, &cached);
+ Q_UNUSED(result)
+ return cached;
+}
+
+/*!
+ Returns the current size of the internal cache.
+ By default this is set to the value of the \c QTRO_NODES_CACHE_SIZE
+ environment variable, or a default of \c 1000 if it is invalid or doesn't
+ exist.
+
+ \sa setRootCacheSize()
+*/
+size_t QAbstractItemModelReplica::rootCacheSize() const
+{
+ return d->m_rootItem.children.cacheSize;
+}
+
+/*!
+ Sets the size of the internal cache to \a rootCacheSize.
+
+ \sa rootCacheSize()
+*/
+void QAbstractItemModelReplica::setRootCacheSize(size_t rootCacheSize)
+{
+ d->m_rootItem.children.setCacheSize(rootCacheSize);
+}
+
+/*!
+ Returns a list of available roles.
+
+ \sa QAbstractItemModel
+*/
+QList<int> QAbstractItemModelReplica::availableRoles() const
+{
+ return d->availableRoles();
+}
+
+/*!
+ \reimp
+*/
+QHash<int, QByteArray> QAbstractItemModelReplica::roleNames() const
+{
+ return d->roleNames();
+}
+
+QT_END_NAMESPACE
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QREMOTEOBJECTS_ABSTRACTITEMMODELREPLICA_H
+#define QREMOTEOBJECTS_ABSTRACTITEMMODELREPLICA_H
+
+#include <QtRemoteObjects/qtremoteobjectglobal.h>
+
+#include <QtCore/qabstractitemmodel.h>
+#include <QtCore/qitemselectionmodel.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAbstractItemModelReplicaImplementation;
+
+class Q_REMOTEOBJECTS_EXPORT QAbstractItemModelReplica : public QAbstractItemModel
+{
+ Q_OBJECT
+public:
+ ~QAbstractItemModelReplica() override;
+
+ QItemSelectionModel* selectionModel() const;
+
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const final;
+ void multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const override;
+ bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
+ QModelIndex parent(const QModelIndex & index) const override;
+ QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const override;
+ bool hasChildren(const QModelIndex & parent = QModelIndex()) const override;
+ int rowCount(const QModelIndex & parent = QModelIndex()) const override;
+ int columnCount(const QModelIndex & parent = QModelIndex()) const override;
+ QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
+ Qt::ItemFlags flags(const QModelIndex &index) const override;
+ QList<int> availableRoles() const;
+ QHash<int, QByteArray> roleNames() const override;
+
+ bool isInitialized() const;
+ bool hasData(const QModelIndex &index, int role) const;
+
+ size_t rootCacheSize() const;
+ void setRootCacheSize(size_t rootCacheSize);
+
+Q_SIGNALS:
+ void initialized();
+
+private:
+ explicit QAbstractItemModelReplica(QAbstractItemModelReplicaImplementation *rep, QtRemoteObjects::InitialAction action, const QList<int> &rolesHint);
+ QScopedPointer<QAbstractItemModelReplicaImplementation> d;
+ friend class QAbstractItemModelReplicaImplementation;
+ friend class QRemoteObjectNode;
+};
+
+QT_END_NAMESPACE
+
+#endif // QREMOTEOBJECTS_ABSTRACTITEMMODELREPLICA_H
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QREMOTEOBJECTS_ABSTRACT_ITEM_REPLICA_P_H
+#define QREMOTEOBJECTS_ABSTRACT_ITEM_REPLICA_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qremoteobjectabstractitemmodeltypes_p.h"
+#include "qremoteobjectabstractitemmodelreplica.h"
+#include "qremoteobjectreplica.h"
+#include "qremoteobjectpendingcall.h"
+#include <list>
+#include <unordered_map>
+#include <unordered_set>
+
+QT_BEGIN_NAMESPACE
+
+namespace {
+ const int DefaultNodesCacheSize = 1000;
+}
+
+struct CacheEntry
+{
+ QHash<int, QVariant> data;
+ Qt::ItemFlags flags;
+
+ explicit CacheEntry()
+ : flags(Qt::NoItemFlags)
+ {}
+};
+
+using CachedRowEntry = QList<CacheEntry>;
+
+template <class Key, class Value>
+struct LRUCache
+{
+ typedef std::pair<Key, Value*> Pair;
+ std::list<Pair> cachedItems;
+ typedef typename std::list<Pair>::iterator CacheIterator;
+ std::unordered_map<Key, CacheIterator> cachedItemsMap;
+ size_t cacheSize;
+
+ explicit LRUCache()
+ {
+ bool ok;
+ cacheSize = qEnvironmentVariableIntValue("QTRO_NODES_CACHE_SIZE" , &ok);
+ if (!ok)
+ cacheSize = DefaultNodesCacheSize;
+ }
+
+ ~LRUCache()
+ {
+ clear();
+ }
+
+ inline void cleanCache()
+ {
+ Q_ASSERT(cachedItems.size() == cachedItemsMap.size());
+
+ auto it = cachedItems.rbegin();
+ while (cachedItemsMap.size() > cacheSize) {
+ // Do not trash elements with children
+ // Workaround QTreeView bugs which caches the children indexes for very long time
+ while (it->second->hasChildren && it != cachedItems.rend())
+ ++it;
+
+ if (it == cachedItems.rend())
+ break;
+
+ cachedItemsMap.erase(it->first);
+ delete it->second;
+ cachedItems.erase((++it).base());
+ }
+ Q_ASSERT(cachedItems.size() == cachedItemsMap.size());
+ }
+
+ void setCacheSize(size_t rootCacheSize)
+ {
+ cacheSize = rootCacheSize;
+ cleanCache();
+ cachedItemsMap.reserve(rootCacheSize);
+ }
+
+ void changeKeys(Key key, Key delta) {
+ std::vector<std::pair<Key, CacheIterator>> changed;
+ auto it = cachedItemsMap.begin();
+ while (it != cachedItemsMap.end()) {
+ if (it->first >= key) {
+ changed.emplace_back(it->first + delta, it->second);
+ it->second->first += delta;
+ it = cachedItemsMap.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ for (auto pair : changed)
+ cachedItemsMap[pair.first] = pair.second;
+ }
+
+ void insert(Key key, Value *value)
+ {
+ changeKeys(key, 1);
+ ensure(key, value);
+ Q_ASSERT(cachedItems.size() == cachedItemsMap.size());
+ }
+
+ void ensure(Key key, Value *value)
+ {
+ cachedItems.emplace_front(key, value);
+ cachedItemsMap[key] = cachedItems.begin();
+ cleanCache();
+ }
+
+ void remove(Key key)
+ {
+ auto it = cachedItemsMap.find(key);
+ if (it != cachedItemsMap.end()) {
+ delete it->second->second;
+ cachedItems.erase(it->second);
+ cachedItemsMap.erase(it);
+ }
+ changeKeys(key, -1);
+ Q_ASSERT(cachedItems.size() == cachedItemsMap.size());
+ }
+
+ Value *get(Key key)
+ {
+ auto it = cachedItemsMap.find(key);
+ if (it == cachedItemsMap.end())
+ return nullptr;
+
+ // Move the accessed item to front
+ cachedItems.splice(cachedItems.begin(), cachedItems, it->second);
+ Q_ASSERT(it->second->first == key);
+ return it->second->second;
+ }
+
+ Key find(Value *val)
+ {
+ for (auto it = cachedItemsMap.begin(); it != cachedItemsMap.end(); ++it) {
+ if (it->second->second == val)
+ return it->first;
+ }
+ Q_ASSERT_X(false, __FUNCTION__, "Value not found");
+ return Key{};
+ }
+
+ bool exists(Value *val)
+ {
+ for (const auto &pair : cachedItems)
+ if (pair.second == val)
+ return true;
+
+ return false;
+ }
+
+ bool exists(Key key)
+ {
+ return cachedItemsMap.find(key) != cachedItemsMap.end();
+ }
+
+ size_t size()
+ {
+ return cachedItemsMap.size();
+ }
+
+ void clear()
+ {
+ for (const auto &pair : cachedItems)
+ delete pair.second;
+ cachedItems.clear();
+ cachedItemsMap.clear();
+ }
+};
+
+class QAbstractItemModelReplicaImplementation;
+struct CacheData
+{
+ QAbstractItemModelReplicaImplementation *replicaModel;
+ CacheData *parent;
+ CachedRowEntry cachedRowEntry;
+
+ bool hasChildren;
+ LRUCache<int, CacheData> children;
+ int columnCount;
+ int rowCount;
+
+ explicit CacheData(QAbstractItemModelReplicaImplementation *model, CacheData *parentItem = nullptr);
+
+ ~CacheData();
+
+ void ensureChildren(int start, int end)
+ {
+ for (int i = start; i <= end; ++i)
+ if (!children.exists(i))
+ children.ensure(i, new CacheData(replicaModel, this));
+ }
+
+ void insertChildren(int start, int end) {
+ Q_ASSERT_X(start >= 0 && start <= end, __FUNCTION__, qPrintable(QString(QLatin1String("0 <= %1 <= %2")).arg(start).arg(end)));
+ for (int i = start; i <= end; ++i) {
+ auto cacheData = new CacheData(replicaModel, this);
+ cacheData->columnCount = columnCount;
+ children.insert(i, cacheData);
+ ++rowCount;
+ }
+ if (rowCount)
+ hasChildren = true;
+ }
+ void removeChildren(int start, int end) {
+ Q_ASSERT_X(start >= 0 && start <= end && end < rowCount, __FUNCTION__, qPrintable(QString(QLatin1String("0 <= %1 <= %2 < %3")).arg(start).arg(end).arg(rowCount)));
+ for (int i = end; i >= start; --i) {
+ children.remove(i);
+ --rowCount;
+ }
+ hasChildren = rowCount;
+ }
+ void clear() {
+ cachedRowEntry.clear();
+ children.clear();
+ hasChildren = false;
+ columnCount = 0;
+ rowCount = 0;
+ }
+};
+
+struct RequestedData
+{
+ QtPrivate::IndexList start;
+ QtPrivate::IndexList end;
+ QList<int> roles;
+};
+
+struct RequestedHeaderData
+{
+ int role;
+ int section;
+ Qt::Orientation orientation;
+};
+
+class SizeWatcher : public QRemoteObjectPendingCallWatcher
+{
+ Q_OBJECT
+public:
+ SizeWatcher(QtPrivate::IndexList _parentList, const QRemoteObjectPendingReply<QSize> &reply)
+ : QRemoteObjectPendingCallWatcher(reply),
+ parentList(_parentList) {}
+ QtPrivate::IndexList parentList;
+};
+
+class RowWatcher : public QRemoteObjectPendingCallWatcher
+{
+ Q_OBJECT
+public:
+ RowWatcher(QtPrivate::IndexList _start, QtPrivate::IndexList _end, QList<int> _roles, const QRemoteObjectPendingReply<QtPrivate::DataEntries> &reply)
+ : QRemoteObjectPendingCallWatcher(reply),
+ start(_start),
+ end(_end),
+ roles(_roles) {}
+ QtPrivate::IndexList start, end;
+ QList<int> roles;
+};
+
+class HeaderWatcher : public QRemoteObjectPendingCallWatcher
+{
+ Q_OBJECT
+public:
+ HeaderWatcher(QList<Qt::Orientation> _orientations, QList<int> _sections, QList<int> _roles, const QRemoteObjectPendingReply<QVariantList> &reply)
+ : QRemoteObjectPendingCallWatcher(reply),
+ orientations(_orientations),
+ sections(_sections),
+ roles(_roles) {}
+ QList<Qt::Orientation> orientations;
+ QList<int> sections, roles;
+};
+
+class QAbstractItemModelReplicaImplementation : public QRemoteObjectReplica
+{
+ Q_OBJECT
+ //TODO Use an input name for the model on the Replica side
+ Q_CLASSINFO(QCLASSINFO_REMOTEOBJECT_TYPE, "ServerModelAdapter")
+ Q_PROPERTY(QList<int> availableRoles READ availableRoles NOTIFY availableRolesChanged)
+ Q_PROPERTY(QIntHash roleNames READ roleNames)
+public:
+ QAbstractItemModelReplicaImplementation();
+ QAbstractItemModelReplicaImplementation(QRemoteObjectNode *node, const QString &name);
+ ~QAbstractItemModelReplicaImplementation() override;
+ void initialize() override;
+ static void registerMetatypes();
+
+ inline const QList<int> &availableRoles() const
+ {
+ if (m_availableRoles.isEmpty())
+ m_availableRoles = propAsVariant(0).value<QList<int>>();
+ return m_availableRoles;
+ }
+
+ QHash<int, QByteArray> roleNames() const
+ {
+ QIntHash roles = propAsVariant(1).value<QIntHash>();
+ return roles;
+ }
+
+ void setModel(QAbstractItemModelReplica *model);
+ bool clearCache(const QtPrivate::IndexList &start, const QtPrivate::IndexList &end, const QList<int> &roles);
+
+Q_SIGNALS:
+ void availableRolesChanged();
+ void dataChanged(QtPrivate::IndexList topLeft, QtPrivate::IndexList bottomRight, QList<int> roles);
+ void rowsInserted(QtPrivate::IndexList parent, int first, int last);
+ void rowsRemoved(QtPrivate::IndexList parent, int first, int last);
+ void rowsMoved(QtPrivate::IndexList parent, int start, int end, QtPrivate::IndexList destination, int row);
+ void currentChanged(QtPrivate::IndexList current, QtPrivate::IndexList previous);
+ void modelReset();
+ void headerDataChanged(Qt::Orientation,int,int);
+ void columnsInserted(QtPrivate::IndexList parent, int first, int last);
+ void layoutChanged(QtPrivate::IndexList parents, QAbstractItemModel::LayoutChangeHint hint);
+public Q_SLOTS:
+ QRemoteObjectPendingReply<QSize> replicaSizeRequest(QtPrivate::IndexList parentList)
+ {
+ static int __repc_index = QAbstractItemModelReplicaImplementation::staticMetaObject.indexOfSlot("replicaSizeRequest(QtPrivate::IndexList)");
+ QVariantList __repc_args;
+ __repc_args << QVariant::fromValue(parentList);
+ return QRemoteObjectPendingReply<QSize>(sendWithReply(QMetaObject::InvokeMetaMethod, __repc_index, __repc_args));
+ }
+ QRemoteObjectPendingReply<QtPrivate::DataEntries> replicaRowRequest(QtPrivate::IndexList start, QtPrivate::IndexList end, QList<int> roles)
+ {
+ static int __repc_index = QAbstractItemModelReplicaImplementation::staticMetaObject.indexOfSlot("replicaRowRequest(QtPrivate::IndexList,QtPrivate::IndexList,QList<int>)");
+ QVariantList __repc_args;
+ __repc_args << QVariant::fromValue(start) << QVariant::fromValue(end) << QVariant::fromValue(roles);
+ return QRemoteObjectPendingReply<QtPrivate::DataEntries>(sendWithReply(QMetaObject::InvokeMetaMethod, __repc_index, __repc_args));
+ }
+ QRemoteObjectPendingReply<QVariantList> replicaHeaderRequest(QList<Qt::Orientation> orientations, QList<int> sections, QList<int> roles)
+ {
+ static int __repc_index = QAbstractItemModelReplicaImplementation::staticMetaObject.indexOfSlot("replicaHeaderRequest(QList<Qt::Orientation>,QList<int>,QList<int>)");
+ QVariantList __repc_args;
+ __repc_args << QVariant::fromValue(orientations) << QVariant::fromValue(sections) << QVariant::fromValue(roles);
+ return QRemoteObjectPendingReply<QVariantList>(sendWithReply(QMetaObject::InvokeMetaMethod, __repc_index, __repc_args));
+ }
+ void replicaSetCurrentIndex(QtPrivate::IndexList index, QItemSelectionModel::SelectionFlags command)
+ {
+ static int __repc_index = QAbstractItemModelReplicaImplementation::staticMetaObject.indexOfSlot("replicaSetCurrentIndex(QtPrivate::IndexList,QItemSelectionModel::SelectionFlags)");
+ QVariantList __repc_args;
+ __repc_args << QVariant::fromValue(index) << QVariant::fromValue(command);
+ send(QMetaObject::InvokeMetaMethod, __repc_index, __repc_args);
+ }
+ void replicaSetData(QtPrivate::IndexList index, const QVariant &value, int role)
+ {
+ static int __repc_index = QAbstractItemModelReplicaImplementation::staticMetaObject.indexOfSlot("replicaSetData(QtPrivate::IndexList,QVariant,int)");
+ QVariantList __repc_args;
+ __repc_args << QVariant::fromValue(index) << QVariant::fromValue(value) << QVariant::fromValue(role);
+ send(QMetaObject::InvokeMetaMethod, __repc_index, __repc_args);
+ }
+ QRemoteObjectPendingReply<QtPrivate::MetaAndDataEntries> replicaCacheRequest(size_t size, QList<int> roles)
+ {
+ static int __repc_index = QAbstractItemModelReplicaImplementation::staticMetaObject.indexOfSlot("replicaCacheRequest(size_t,QList<int>)");
+ QVariantList __repc_args;
+ __repc_args << QVariant::fromValue(size) << QVariant::fromValue(roles);
+ return QRemoteObjectPendingReply<QtPrivate::MetaAndDataEntries>(sendWithReply(QMetaObject::InvokeMetaMethod, __repc_index, __repc_args));
+ }
+ void onHeaderDataChanged(Qt::Orientation orientation, int first, int last);
+ void onDataChanged(const QtPrivate::IndexList &start, const QtPrivate::IndexList &end, const QList<int> &roles);
+ void onRowsInserted(const QtPrivate::IndexList &parent, int start, int end);
+ void onRowsRemoved(const QtPrivate::IndexList &parent, int start, int end);
+ void onColumnsInserted(const QtPrivate::IndexList &parent, int start, int end);
+ void onRowsMoved(QtPrivate::IndexList srcParent, int srcRow, int count, QtPrivate::IndexList destParent, int destRow);
+ void onCurrentChanged(QtPrivate::IndexList current, QtPrivate::IndexList previous);
+ void onModelReset();
+ void requestedData(QRemoteObjectPendingCallWatcher *);
+ void requestedHeaderData(QRemoteObjectPendingCallWatcher *);
+ void init();
+ void fetchPendingData();
+ void fetchPendingHeaderData();
+ void handleInitDone(QRemoteObjectPendingCallWatcher *watcher);
+ void handleModelResetDone(QRemoteObjectPendingCallWatcher *watcher);
+ void handleSizeDone(QRemoteObjectPendingCallWatcher *watcher);
+ void onReplicaCurrentChanged(const QModelIndex ¤t, const QModelIndex &previous);
+ void fillCache(const QtPrivate::IndexValuePair &pair,const QList<int> &roles);
+ void onLayoutChanged(const QtPrivate::IndexList &parents, QAbstractItemModel::LayoutChangeHint hint);
+public:
+ QScopedPointer<QItemSelectionModel> m_selectionModel;
+ QList<CacheEntry> m_headerData[2];
+
+ CacheData m_rootItem;
+ inline CacheData* cacheData(const QModelIndex &index) const {
+ if (!index.isValid())
+ return const_cast<CacheData*>(&m_rootItem);
+ if (index.internalPointer()) {
+ auto parent = static_cast<CacheData*>(index.internalPointer());
+ if (m_activeParents.find(parent) != m_activeParents.end())
+ return parent->children.get(index.row());
+ }
+ return nullptr;
+ }
+ inline CacheData* cacheData(const QtPrivate::IndexList &index) const {
+ return cacheData(toQModelIndex(index, q));
+ }
+ inline CacheData* createCacheData(const QtPrivate::IndexList &index) const {
+ bool ok = false;
+ auto modelIndex = toQModelIndex(index, q, &ok);
+ if (!ok)
+ return nullptr;
+ cacheData(modelIndex.parent())->ensureChildren(modelIndex.row() , modelIndex.row());
+ return cacheData(modelIndex);
+ }
+ inline CacheEntry* cacheEntry(const QModelIndex &index) const {
+ auto data = cacheData(index);
+ if (!data || index.column() < 0 || index.column() >= data->cachedRowEntry.size())
+ return nullptr;
+ CacheEntry &entry = data->cachedRowEntry[index.column()];
+ return &entry;
+ }
+ inline CacheEntry* cacheEntry(const QtPrivate::IndexList &index) const {
+ return cacheEntry(toQModelIndex(index, q));
+ }
+
+ QRemoteObjectPendingCallWatcher *doModelReset();
+ void initializeModelConnections();
+
+ bool m_initDone = false;
+ QList<RequestedData> m_requestedData;
+ QList<RequestedHeaderData> m_requestedHeaderData;
+ QList<QRemoteObjectPendingCallWatcher*> m_pendingRequests;
+ QAbstractItemModelReplica *q;
+ mutable QList<int> m_availableRoles;
+ std::unordered_set<CacheData*> m_activeParents;
+ QtRemoteObjects::InitialAction m_initialAction;
+ QList<int> m_initialFetchRolesHint;
+};
+
+QT_END_NAMESPACE
+
+#endif // QREMOTEOBJECTS_ABSTRACT_ITEM_REPLICA_P_H
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QREMOTEOBJECTS_ABSTRACT_ITEM_MODEL_TYPES_P_H
+#define QREMOTEOBJECTS_ABSTRACT_ITEM_MODEL_TYPES_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qabstractitemmodel.h>
+#include <QtCore/qdatastream.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qitemselectionmodel.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qnamespace.h>
+#include <QtCore/qpair.h>
+#include <QtCore/qsize.h>
+#include <QtCore/qvariant.h>
+#include <QtRemoteObjects/qtremoteobjectglobal.h>
+#include <QtCore/private/qglobal_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QtPrivate {
+
+struct ModelIndex
+{
+ ModelIndex() : row(-1), column(-1) {}
+ ModelIndex(int row_, int column_)
+ : row(row_)
+ , column(column_)
+ {}
+
+ inline bool operator==(const ModelIndex &other) const { return row == other.row && column == other.column; }
+ inline bool operator!=(const ModelIndex &other) const { return !(*this == other); }
+ int row;
+ int column;
+};
+
+typedef QList<ModelIndex> IndexList;
+
+struct IndexValuePair
+{
+ explicit IndexValuePair(const IndexList index_ = IndexList(), const QVariantList &data_ = QVariantList(),
+ bool hasChildren_ = false, const Qt::ItemFlags &flags_ = Qt::ItemFlags(), const QSize &size_ = {})
+ : index(index_)
+ , data(data_)
+ , flags(flags_)
+ , hasChildren(hasChildren_)
+ , size(size_)
+ {}
+
+ inline bool operator==(const IndexValuePair &other) const { return index == other.index && data == other.data && hasChildren == other.hasChildren && flags == other.flags; }
+ inline bool operator!=(const IndexValuePair &other) const { return !(*this == other); }
+
+ IndexList index;
+ QVariantList data;
+ Qt::ItemFlags flags;
+ bool hasChildren;
+ QList<IndexValuePair> children;
+ QSize size;
+};
+
+struct DataEntries
+{
+ inline bool operator==(const DataEntries &other) const { return data == other.data; }
+ inline bool operator!=(const DataEntries &other) const { return !(*this == other); }
+
+ QList<IndexValuePair> data;
+};
+
+struct MetaAndDataEntries : DataEntries
+{
+ QList<int> roles;
+ QSize size;
+};
+
+inline QDebug operator<<(QDebug stream, const ModelIndex &index)
+{
+ return stream.nospace() << "ModelIndex[row=" << index.row << ", column=" << index.column << "]";
+}
+
+inline QDataStream& operator<<(QDataStream &stream, const ModelIndex &index)
+{
+ return stream << index.row << index.column;
+}
+
+inline QDataStream& operator>>(QDataStream &stream, ModelIndex &index)
+{
+ return stream >> index.row >> index.column;
+}
+
+inline QDataStream& operator<<(QDataStream &stream, Qt::Orientation orient)
+{
+ return stream << static_cast<int>(orient);
+}
+
+inline QDataStream& operator>>(QDataStream &stream, Qt::Orientation &orient)
+{
+ int val;
+ QDataStream &ret = stream >> val;
+ orient = static_cast<Qt::Orientation>(val);
+ return ret;
+}
+
+inline QDataStream& operator<<(QDataStream &stream, QItemSelectionModel::SelectionFlags command)
+{
+ return stream << static_cast<int>(command);
+}
+
+inline QDataStream& operator>>(QDataStream &stream, QItemSelectionModel::SelectionFlags &command)
+{
+ int val;
+ QDataStream &ret = stream >> val;
+ command = static_cast<QItemSelectionModel::SelectionFlags>(val);
+ return ret;
+}
+
+inline QDebug operator<<(QDebug stream, const IndexValuePair &pair)
+{
+ return stream.nospace() << "IndexValuePair[index=" << pair.index << ", data=" << pair.data << ", hasChildren=" << pair.hasChildren << ", flags=" << pair.flags << "]";
+}
+
+inline QDataStream& operator<<(QDataStream &stream, const IndexValuePair &pair)
+{
+ return stream << pair.index << pair.data << pair.hasChildren << static_cast<int>(pair.flags) << pair.children << pair.size;
+}
+
+inline QDataStream& operator>>(QDataStream &stream, IndexValuePair &pair)
+{
+ int flags;
+ QDataStream &ret = stream >> pair.index >> pair.data >> pair.hasChildren >> flags >> pair.children >> pair.size;
+ pair.flags = static_cast<Qt::ItemFlags>(flags);
+ return ret;
+}
+
+inline QDebug operator<<(QDebug stream, const DataEntries &entries)
+{
+ return stream.nospace() << "DataEntries[" << entries.data << "]";
+}
+
+inline QDataStream& operator<<(QDataStream &stream, const DataEntries &entries)
+{
+ return stream << entries.data;
+}
+
+inline QDataStream& operator>>(QDataStream &stream, DataEntries &entries)
+{
+ return stream >> entries.data;
+}
+
+inline QDataStream& operator<<(QDataStream &stream, const MetaAndDataEntries &entries)
+{
+ return stream << entries.data << entries.roles << entries.size;
+}
+
+inline QDataStream& operator>>(QDataStream &stream, MetaAndDataEntries &entries)
+{
+ return stream >> entries.data >> entries.roles >> entries.size;
+}
+
+inline QString modelIndexToString(const IndexList &list)
+{
+ QString s;
+ QDebug(&s) << list;
+ return s;
+}
+
+inline QString modelIndexToString(const ModelIndex &index)
+{
+ QString s;
+ QDebug(&s) << index;
+ return s;
+}
+
+inline QModelIndex toQModelIndex(const IndexList &list, const QAbstractItemModel *model, bool *ok = nullptr, bool ensureItem = false)
+{
+ if (ok)
+ *ok = true;
+ QModelIndex result;
+ for (int i = 0; i < list.size(); ++i) {
+ const ModelIndex &index = list[i];
+ if (ensureItem)
+ const_cast<QAbstractItemModel *>(model)->setData(result, index.row, Qt::UserRole - 1);
+
+ result = model->index(index.row, index.column, result);
+ if (!result.isValid()) {
+ if (ok) {
+ *ok = false;
+ } else {
+ qFatal("Internal error: invalid index=%s in indexList=%s",
+ qPrintable(modelIndexToString(list[i])), qPrintable(modelIndexToString(list)));
+ }
+ return QModelIndex();
+ }
+ }
+ return result;
+}
+
+inline IndexList toModelIndexList(const QModelIndex &index, const QAbstractItemModel *model)
+{
+ IndexList list;
+ if (index.isValid()) {
+ list << ModelIndex(index.row(), index.column());
+ for (QModelIndex curIndex = model->parent(index); curIndex.isValid(); curIndex = model->parent(curIndex))
+ list.prepend(ModelIndex(curIndex.row(), curIndex.column()));
+ }
+ return list;
+}
+
+} // namespace QtPrivate
+
+QT_END_NAMESPACE
+
+QT_DECL_METATYPE_EXTERN_TAGGED(QtPrivate::ModelIndex, QtPrivate__ModelIndex, /* not exported */)
+QT_DECL_METATYPE_EXTERN_TAGGED(QtPrivate::IndexList, QtPrivate__IndexList, /* not exported */)
+QT_DECL_METATYPE_EXTERN_TAGGED(QtPrivate::DataEntries, QtPrivate__DataEntries, /* not exported */)
+QT_DECL_METATYPE_EXTERN_TAGGED(QtPrivate::MetaAndDataEntries, QtPrivate__MetaAndDataEntries,
+ /* not exported */)
+QT_DECL_METATYPE_EXTERN_TAGGED(QtPrivate::IndexValuePair, QtPrivate__IndexValuePair,
+ /* not exported */)
+QT_DECL_METATYPE_EXTERN_TAGGED(Qt::Orientation, Qt__Orientation, /* not exported */)
+QT_DECL_METATYPE_EXTERN_TAGGED(QItemSelectionModel::SelectionFlags,
+ QItemSelectionModel__SelectionFlags,
+ /* not exported */)
+
+#endif // QREMOTEOBJECTS_ABSTRACT_ITEM_MODEL_TYPES_P_H
--- /dev/null
+// Copyright (C) 2021 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <QtCore/qiodevice.h>
+
+#include "qremoteobjectcontainers_p.h"
+#include "qremoteobjectpacket_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QDataStream &operator>>(QDataStream &ds, QtROSequentialContainer &p)
+{
+ QByteArray typeName;
+ quint32 count;
+ ds >> typeName;
+ p.setValueType(typeName);
+ ds >> count;
+ p.reserve(count);
+ QVariant value{p.m_valueType, nullptr};
+ for (quint32 i = 0; i < count; i++) {
+ if (!p.m_valueType.load(ds, value.data())) {
+ qWarning("QSQ_: unable to load type '%s', returning an empty list.", p.m_valueTypeName.constData());
+ p.clear();
+ break;
+ }
+ p.append(value);
+ }
+ return ds;
+}
+
+QDataStream &operator<<(QDataStream &ds, const QtROSequentialContainer &p)
+{
+ ds << p.m_valueTypeName;
+ auto pos = ds.device()->pos();
+ quint32 count = p.size();
+ ds << count;
+ for (quint32 i = 0; i < count; i++) {
+ if (!p.m_valueType.save(ds, p.at(i).data())) {
+ ds.device()->seek(pos);
+ ds.resetStatus();
+ ds << quint32(0);
+ qWarning("QSQ_: unable to save type '%s'.", p.m_valueTypeName.constData());
+ break;
+ }
+ }
+ return ds;
+}
+
+const char *descopedName(QMetaType type) {
+ auto name = QByteArray::fromRawData(type.name(), qstrlen(type.name()));
+ int index = name.lastIndexOf(':'); // Returns -1 if not found
+ return type.name() + index + 1;
+}
+
+QDataStream &operator>>(QDataStream &ds, QtROAssociativeContainer &p)
+{
+ QByteArray keyTypeName, valueTypeName;
+ quint32 count;
+ ds >> keyTypeName;
+ ds >> valueTypeName;
+ p.setTypes(keyTypeName, valueTypeName);
+ ds >> count;
+ p.m_keys.reserve(count);
+ auto transferType = p.m_keyType;
+ if (p.m_keyType.flags().testFlag(QMetaType::IsEnumeration))
+ transferType = QRemoteObjectPackets::transferTypeForEnum(p.m_keyType);
+ QVariant key{transferType, nullptr};
+ QVariant value{p.m_valueType, nullptr};
+ for (quint32 i = 0; i < count; i++) {
+ if (!transferType.load(ds, key.data())) {
+ qWarning("QAS_: unable to load key '%s', returning an empty map.", p.m_keyTypeName.constData());
+ p.clear();
+ break;
+ }
+ if (!p.m_valueType.load(ds, value.data())) {
+ qWarning("QAS_: unable to load value '%s', returning an empty map.", p.m_valueTypeName.constData());
+ p.clear();
+ break;
+ }
+ if (transferType != p.m_keyType) {
+ bool isFlag = false;
+ QVariant enumKey(key);
+ enumKey.convert(p.m_keyType);
+ p.m_keys.append(enumKey);
+ if (auto meta = p.m_keyType.metaObject()) {
+ int index = meta->indexOfEnumerator(descopedName(p.m_keyType));
+ isFlag = meta->enumerator(index).isFlag();
+ }
+ // If multiple flag values are set, toString() returns an empty string
+ // Thus, for flags, we convert the integer value to a string
+ if (isFlag)
+ p.insert(key.toString(), value);
+ else
+ p.insert(enumKey.toString(), value);
+ } else {
+ p.insert(key.toString(), value);
+ p.m_keys.append(key);
+ }
+ }
+ return ds;
+}
+
+QDataStream &operator<<(QDataStream &ds, const QtROAssociativeContainer &p)
+{
+ ds << p.m_keyTypeName;
+ ds << p.m_valueTypeName;
+ auto pos = ds.device()->pos();
+ quint32 count = p.size();
+ ds << count;
+ QAssociativeIterable map(&p);
+ QAssociativeIterable::const_iterator iter = map.begin();
+ auto transferType = p.m_keyType;
+ if (p.m_keyType.flags().testFlag(QMetaType::IsEnumeration))
+ transferType = QRemoteObjectPackets::transferTypeForEnum(p.m_keyType);
+ bool keySaved;
+ for (quint32 i = 0; i < count; i++) {
+ if (transferType != p.m_keyType) {
+ QVariant intKey(iter.key());
+ intKey.convert(transferType);
+ keySaved = transferType.save(ds, intKey.data());
+ } else {
+ keySaved = transferType.save(ds, iter.key().data());
+ }
+ if (!keySaved) {
+ ds.device()->seek(pos);
+ ds.resetStatus();
+ ds << quint32(0);
+ qWarning("QAS_: unable to save type '%s'.", p.m_valueTypeName.constData());
+ break;
+ }
+ if (!p.m_valueType.save(ds, iter.value().data())) {
+ ds.device()->seek(pos);
+ ds.resetStatus();
+ ds << quint32(0);
+ qWarning("QAS_: unable to save type '%s'.", p.m_valueTypeName.constData());
+ break;
+ }
+ iter++;
+ }
+ return ds;
+}
+
+QT_END_NAMESPACE
--- /dev/null
+// Copyright (C) 2021 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#pragma once
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qassociativeiterable.h>
+#include <QtCore/qsequentialiterable.h>
+#include <QtCore/qvariant.h>
+#include <QtCore/private/qglobal_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QtROSequentialContainer : public QVariantList
+{
+public:
+ QtROSequentialContainer() = default;
+ QtROSequentialContainer(const QSequentialIterable &lst)
+ {
+ m_valueType = lst.metaContainer().valueMetaType();
+ reserve(lst.size());
+ for (const auto v : lst)
+ append(v);
+ }
+ void setValueType(const QByteArray &valueTypeName)
+ {
+ m_valueTypeName = valueTypeName;
+ m_valueType = QMetaType::fromName(valueTypeName.constData());
+ clear();
+ }
+
+ QMetaType m_valueType;
+ QByteArray m_typeName, m_valueTypeName;
+};
+
+QDataStream &operator>>(QDataStream &ds, QtROSequentialContainer &p);
+
+QDataStream &operator<<(QDataStream &ds, const QtROSequentialContainer &p);
+
+class QtROAssociativeContainer : public QVariantMap
+{
+public:
+ QtROAssociativeContainer() = default;
+ QtROAssociativeContainer(const QAssociativeIterable &map)
+ {
+ m_keyType = map.metaContainer().keyMetaType();
+ m_valueType = map.metaContainer().mappedMetaType();
+ m_keys.reserve(map.size());
+ QAssociativeIterable::const_iterator iter = map.begin();
+ const QAssociativeIterable::const_iterator end = map.end();
+ for ( ; iter != end; ++iter) {
+ m_keys.append(iter.key());
+ insert(iter.key().toString(), iter.value());
+ }
+ }
+ void setTypes(const QByteArray &keyTypeName, const QByteArray &valueTypeName)
+ {
+ m_keyTypeName = keyTypeName;
+ m_keyType = QMetaType::fromName(keyTypeName.constData());
+ m_valueTypeName = valueTypeName;
+ m_valueType = QMetaType::fromName(valueTypeName.constData());
+ clear();
+ m_keys.clear();
+ }
+
+ QMetaType m_keyType, m_valueType;
+ QByteArray m_typeName, m_keyTypeName, m_valueTypeName;
+ QVariantList m_keys; // Map uses QString for key, this doesn't lose information for proxy
+};
+
+QDataStream &operator>>(QDataStream &ds, QtROAssociativeContainer &p);
+
+QDataStream &operator<<(QDataStream &ds, const QtROAssociativeContainer &p);
+
+QT_END_NAMESPACE
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qremoteobjectdynamicreplica.h"
+#include "qremoteobjectreplica_p.h"
+
+#include <QtCore/qmetaobject.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QRemoteObjectDynamicReplica
+ \inmodule QtRemoteObjects
+ \brief A dynamically instantiated \l {Replica}.
+
+ There are generated replicas (replicas having the header files produced by
+ the \l {repc} {Replica Compiler}), and dynamic replicas, that are generated
+ on-the-fly. This is the class for the dynamic type of replica.
+
+ When the connection to the \l {Source} object is made, the initialization
+ step passes the current property values (see \l {Replica Initialization}).
+ In a DynamicReplica, the property/signal/slot details are also sent,
+ allowing the replica object to be created on-the-fly. This can be convenient
+ in QML or scripting, but has two primary disadvantages. First, the object is
+ in effect "empty" until it is successfully initialized by the \l {Source}.
+ Second, in C++, calls must be made using QMetaObject::invokeMethod(), as the
+ moc generated lookup will not be available.
+
+ This class does not have a public constructor. It can only be instantiated
+ by using the dynamic QRemoteObjectNode::acquire method.
+*/
+
+QRemoteObjectDynamicReplica::QRemoteObjectDynamicReplica()
+ : QRemoteObjectReplica()
+{
+}
+
+QRemoteObjectDynamicReplica::QRemoteObjectDynamicReplica(QRemoteObjectNode *node, const QString &name)
+ : QRemoteObjectReplica(ConstructWithNode)
+{
+ initializeNode(node, name);
+}
+
+/*!
+ Destroys the dynamic replica.
+
+ \sa {Replica Ownership}
+*/
+QRemoteObjectDynamicReplica::~QRemoteObjectDynamicReplica()
+{
+}
+
+/*!
+ \internal
+ Returns a pointer to the dynamically generated meta-object of this object, or
+ QRemoteObjectDynamicReplica's metaObject if the object is not initialized. This
+ function overrides the QObject::metaObject() virtual function to provide the same
+ functionality for dynamic replicas.
+
+ \sa QObject::metaObject(), {Replica Initialization}
+*/
+const QMetaObject* QRemoteObjectDynamicReplica::metaObject() const
+{
+ auto impl = qSharedPointerCast<QRemoteObjectReplicaImplementation>(d_impl);
+ // Returning nullptr will likely result in a crash if this type is used before the
+ // definition is received. Note: QRemoteObjectDynamicReplica doesn't include the
+ // QObject macro, so it's metaobject would resolve to QRemoteObjectReplica::metaObject()
+ // if we weren't overriding it.
+ if (!impl->m_metaObject) {
+ qWarning() << "Dynamic metaobject is not assigned, returning generic Replica metaObject.";
+ qWarning() << "This may cause issues if used for more than checking the Replica state.";
+ return QRemoteObjectReplica::metaObject();
+ }
+
+ return impl->m_metaObject;
+}
+
+/*!
+ \internal
+ This function overrides the QObject::qt_metacast() virtual function to provide the same functionality for dynamic replicas.
+
+ \sa QObject::qt_metacast()
+*/
+void *QRemoteObjectDynamicReplica::qt_metacast(const char *name)
+{
+ if (!name)
+ return nullptr;
+
+ if (!strcmp(name, "QRemoteObjectDynamicReplica"))
+ return static_cast<void*>(const_cast<QRemoteObjectDynamicReplica*>(this));
+
+ // not entirely sure that one is needed... TODO: check
+ auto impl = qSharedPointerCast<QRemoteObjectReplicaImplementation>(d_impl);
+ if (QString::fromLatin1(name) == impl->m_objectName)
+ return static_cast<void*>(const_cast<QRemoteObjectDynamicReplica*>(this));
+
+ return QObject::qt_metacast(name);
+}
+
+/*!
+ \internal
+ This function overrides the QObject::qt_metacall() virtual function to provide the same functionality for dynamic replicas.
+
+ \sa QObject::qt_metacall()
+*/
+int QRemoteObjectDynamicReplica::qt_metacall(QMetaObject::Call call, int id, void **argv)
+{
+ static const bool debugArgs = qEnvironmentVariableIsSet("QT_REMOTEOBJECT_DEBUG_ARGUMENTS");
+
+ auto impl = qSharedPointerCast<QConnectedReplicaImplementation>(d_impl);
+
+ int saved_id = id;
+ id = QRemoteObjectReplica::qt_metacall(call, id, argv);
+ if (id < 0 || impl->m_metaObject == nullptr)
+ return id;
+
+ if (call == QMetaObject::ReadProperty || call == QMetaObject::WriteProperty) {
+ QMetaProperty mp = metaObject()->property(saved_id);
+
+ if (call == QMetaObject::WriteProperty) {
+ QVariantList args;
+ if (mp.userType() == QMetaType::QVariant)
+ args << *reinterpret_cast<QVariant*>(argv[0]);
+ else
+ args << QVariant(mp.metaType(), argv[0]);
+ QRemoteObjectReplica::send(QMetaObject::WriteProperty, saved_id, args);
+ } else {
+ if (mp.userType() == QMetaType::QVariant)
+ *reinterpret_cast<QVariant*>(argv[0]) = impl->m_propertyStorage[id];
+ else {
+ const QVariant value = propAsVariant(id);
+ mp.metaType().destruct(argv[0]);
+ mp.metaType().construct(argv[0], value.data());
+ }
+ }
+
+ id = -1;
+ } else if (call == QMetaObject::InvokeMetaMethod) {
+ if (id < impl->m_numSignals) {
+ qCDebug(QT_REMOTEOBJECT) << "DynamicReplica Activate" << impl->m_metaObject->method(saved_id).methodSignature();
+ // signal relay from Source world to Replica
+ QMetaObject::activate(this, impl->m_metaObject, id, argv);
+
+ } else {
+ // method relay from Replica to Source
+ const QMetaMethod mm = impl->m_metaObject->method(saved_id);
+ const int nParam = mm.parameterCount();
+ QVariantList args;
+ args.reserve(nParam);
+ for (int i = 0; i < nParam; ++i) {
+ const auto metaType = mm.parameterMetaType(i);
+ if (metaType.flags().testFlag(QMetaType::IsEnumeration)) {
+ auto transferType = QRemoteObjectPackets::transferTypeForEnum(metaType);
+ args.push_back(QVariant(transferType, argv[i + 1]));
+ } else
+ args.push_back(QVariant(metaType, argv[i + 1]));
+ }
+
+ if (debugArgs) {
+ qCDebug(QT_REMOTEOBJECT) << "method" << mm.methodSignature() << "invoked - args:" << args;
+ } else {
+ qCDebug(QT_REMOTEOBJECT) << "method" << mm.methodSignature() << "invoked";
+ }
+
+ if (mm.returnType() == QMetaType::Void)
+ QRemoteObjectReplica::send(QMetaObject::InvokeMetaMethod, saved_id, args);
+ else {
+ QRemoteObjectPendingCall call = QRemoteObjectReplica::sendWithReply(QMetaObject::InvokeMetaMethod, saved_id, args);
+ if (argv[0])
+ *(static_cast<QRemoteObjectPendingCall*>(argv[0])) = call;
+ }
+ }
+
+ id = -1;
+ }
+
+ return id;
+}
+
+QT_END_NAMESPACE
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QDYNAMICREMOTEOBJECT_H
+#define QDYNAMICREMOTEOBJECT_H
+
+#include <QtRemoteObjects/qremoteobjectreplica.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_REMOTEOBJECTS_EXPORT QRemoteObjectDynamicReplica : public QRemoteObjectReplica
+{
+public:
+ ~QRemoteObjectDynamicReplica() override;
+
+ const QMetaObject *metaObject() const override;
+ void *qt_metacast(const char *name) override;
+ int qt_metacall(QMetaObject::Call call, int id, void **argv) override;
+
+private:
+ explicit QRemoteObjectDynamicReplica();
+ explicit QRemoteObjectDynamicReplica(QRemoteObjectNode *node, const QString &name);
+ friend class QRemoteObjectNodePrivate;
+ friend class QRemoteObjectNode;
+};
+
+QT_END_NAMESPACE
+
+#endif
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "private/qmetaobjectbuilder_p.h"
+
+#include "qremoteobjectcontainers_p.h"
+#include "qremoteobjectnode.h"
+#include "qremoteobjectnode_p.h"
+
+#include "qremoteobjectregistry.h"
+#include "qremoteobjectdynamicreplica.h"
+#include "qremoteobjectpacket_p.h"
+#include "qremoteobjectregistrysource_p.h"
+#include "qremoteobjectreplica_p.h"
+#include "qremoteobjectsource_p.h"
+#include "qremoteobjectabstractitemmodelreplica_p.h"
+#include "qremoteobjectabstractitemmodeladapter_p.h"
+#include <QtCore/qabstractitemmodel.h>
+#include <memory>
+#include <algorithm>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QtRemoteObjects;
+using namespace QRemoteObjectStringLiterals;
+
+using GadgetType = QVariantList;
+
+struct ManagedGadgetTypeEntry
+{
+ GadgetType gadgetType;
+ QMetaType gadgetMetaType;
+ QList<QMetaType> enumMetaTypes;
+ std::shared_ptr<QMetaObject> metaObject;
+
+ void unregisterMetaTypes()
+ {
+ QMetaType::unregisterMetaType(gadgetMetaType);
+ for (auto enumMetaType : enumMetaTypes)
+ QMetaType::unregisterMetaType(enumMetaType);
+ }
+};
+
+static QMutex s_managedTypesMutex;
+static QHash<int, ManagedGadgetTypeEntry> s_managedTypes;
+static QHash<int, QSet<QtROIoDeviceBase*>> s_trackedConnections;
+
+static void GadgetsStaticMetacallFunction(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
+{
+ if (_c == QMetaObject::ReadProperty) {
+ GadgetType *_t = reinterpret_cast<GadgetType *>(_o);
+ if (_id < _t->size()) {
+ const auto &prop = _t->at(_id);
+ prop.metaType().destruct(_a[0]);
+ prop.metaType().construct(_a[0], prop.constData());
+ }
+ } else if (_c == QMetaObject::WriteProperty) {
+ GadgetType *_t = reinterpret_cast<GadgetType *>(_o);
+ if (_id < _t->size()) {
+ auto & prop = (*_t)[_id];
+ prop = QVariant(prop.metaType(), _a[0]);
+ }
+ }
+}
+
+static void GadgetTypedDestructor(const QtPrivate::QMetaTypeInterface *, void *ptr)
+{
+ reinterpret_cast<GadgetType*>(ptr)->~GadgetType();
+}
+
+static void GadgetTypedConstructor(const QtPrivate::QMetaTypeInterface *interface, void *where)
+{
+ GadgetType *gadget = new(where) GadgetType;
+ QMutexLocker lock(&s_managedTypesMutex);
+ auto it = s_managedTypes.find(interface->typeId);
+ if (it == s_managedTypes.end()) {
+ delete gadget;
+ gadget = nullptr;
+ return;
+ }
+ *gadget = it->gadgetType;
+}
+
+static void GadgetTypedCopyConstructor(const QtPrivate::QMetaTypeInterface *, void *where, const void *copy)
+{
+ new(where) GadgetType(*reinterpret_cast<const GadgetType*>(copy));
+}
+
+static void GadgetTypedMoveConstructor(const QtPrivate::QMetaTypeInterface *, void *where, void *copy)
+{
+ new(where) GadgetType(std::move(*reinterpret_cast<GadgetType*>(copy)));
+}
+
+static bool GadgetEqualsFn(const QtPrivate::QMetaTypeInterface *, const void *a, const void *b)
+{
+ return *reinterpret_cast<const GadgetType*>(a) == *reinterpret_cast<const GadgetType*>(b);
+}
+
+static void GadgetDebugStreamFn(const QtPrivate::QMetaTypeInterface *, QDebug &dbg, const void *a)
+{
+ const GadgetType *gadgetProperties = reinterpret_cast<const GadgetType *>(a);
+ for (const auto &prop : *gadgetProperties)
+ dbg << prop;
+}
+
+static void GadgetDataStreamOutFn(const QtPrivate::QMetaTypeInterface *, QDataStream &ds, const void *a)
+{
+ const GadgetType *gadgetProperties = reinterpret_cast<const GadgetType *>(a);
+ for (const auto &prop : *gadgetProperties)
+ ds << prop;
+}
+
+static void GadgetDataStreamInFn(const QtPrivate::QMetaTypeInterface *, QDataStream &ds, void *a)
+{
+ GadgetType *gadgetProperties = reinterpret_cast<GadgetType *>(a);
+ for (auto &prop : *gadgetProperties)
+ ds >> prop;
+}
+
+// Like the Q_GADGET static methods above, we need constructor/destructor methods
+// in order to use dynamically defined enums with QVariant or as signal/slot
+// parameters (i.e., the queued connection mechanism, which QtRO leverages).
+//
+// We will need the enum methods to support different sizes when typed scope enum
+// support is added, so might as well use that now.
+template<typename T>
+static void EnumDestructor(const QtPrivate::QMetaTypeInterface *, void *ptr)
+{
+ static_cast<T*>(ptr)->~T();
+}
+
+template<typename T>
+static void EnumConstructor(const QtPrivate::QMetaTypeInterface *, void *where)
+{
+ new(where) T;
+}
+
+template<typename T>
+static void EnumCopyConstructor(const QtPrivate::QMetaTypeInterface *, void *where, const void *copy)
+{
+ new(where) T(*static_cast<const T*>(copy));
+}
+
+template<typename T>
+static void EnumMoveConstructor(const QtPrivate::QMetaTypeInterface *, void *where, void *copy)
+{
+ new(where) T(std::move(*static_cast<T*>(copy)));
+}
+
+// Not used, but keeping these in case we end up with a need for save/load.
+template<typename T>
+static void EnumSaveOperator(QDataStream & out, const void *data)
+{
+ const T value = *static_cast<const T *>(data);
+ out << value;
+}
+
+template<typename T>
+static void EnumLoadOperator(QDataStream &in, void *data)
+{
+ T value = *static_cast<T *>(data);
+ in >> value;
+}
+
+template<typename T>
+static bool EnumEqualsFn(const QtPrivate::QMetaTypeInterface *, const void *a, const void *b)
+{
+ return *static_cast<const T*>(a) == *static_cast<const T*>(b);
+}
+
+template<typename T>
+static bool EnumLessThanFn(const QtPrivate::QMetaTypeInterface *, const void *a, const void *b)
+{
+ return *static_cast<const T*>(a) < *static_cast<const T*>(b);
+}
+
+template<typename T>
+static void EnumDebugStreamFn(const QtPrivate::QMetaTypeInterface *, QDebug &dbg, const void *a)
+{
+ dbg << *static_cast<const T *>(a);
+}
+
+template<typename T>
+static void EnumDataStreamOutFn(const QtPrivate::QMetaTypeInterface *, QDataStream &ds, const void *a)
+{
+ ds << *static_cast<const T *>(a);
+}
+
+template<typename T>
+static void EnumDataStreamInFn(const QtPrivate::QMetaTypeInterface *, QDataStream &ds, void *a)
+{
+ ds >> *static_cast<T *>(a);
+}
+
+static QString name(const QMetaObject * const mobj)
+{
+ const int ind = mobj->indexOfClassInfo(QCLASSINFO_REMOTEOBJECT_TYPE);
+ return ind >= 0 ? QString::fromLatin1(mobj->classInfo(ind).value()) : QString();
+}
+
+QString QtRemoteObjects::getTypeNameAndMetaobjectFromClassInfo(const QMetaObject *& meta) {
+ QString typeName;
+ const int ind = meta->indexOfClassInfo(QCLASSINFO_REMOTEOBJECT_TYPE);
+ if (ind != -1) { //We have an object created from repc or at least with QCLASSINFO defined
+ typeName = QString::fromLatin1(meta->classInfo(ind).value());
+ while (true) {
+ Q_ASSERT(meta->superClass());//This recurses to QObject, which doesn't have QCLASSINFO_REMOTEOBJECT_TYPE
+ //At the point superclass doesn't have the same QCLASSINFO_REMOTEOBJECT_TYPE,
+ //we have the metaobject we should work from
+ if (ind != meta->superClass()->indexOfClassInfo(QCLASSINFO_REMOTEOBJECT_TYPE))
+ break;
+ meta = meta->superClass();
+ }
+ }
+ return typeName;
+}
+
+template <typename K, typename V, typename Query>
+bool map_contains(const QMap<K,V> &map, const Query &key, typename QMap<K,V>::const_iterator &result)
+{
+ const typename QMap<K,V>::const_iterator it = map.find(key);
+ if (it == map.end())
+ return false;
+ result = it;
+ return true;
+}
+
+/*!
+ \qmltype Node
+ \instantiates QRemoteObjectNode
+ \inqmlmodule QtRemoteObjects
+ \brief A node on a Qt Remote Objects network.
+
+ The Node type provides an entry point to a Qt Remote Objects network. A network
+ can be as simple as two nodes, or an arbitrarily complex set of processes and devices.
+
+ A Node does not have a url that other nodes can connect to, and thus is able to acquire
+ replicas only. It is not able to share source objects.
+
+*/
+
+QRemoteObjectNodePrivate::QRemoteObjectNodePrivate()
+ : QObjectPrivate()
+ , registry(nullptr)
+ , retryInterval(250)
+ , lastError(QRemoteObjectNode::NoError)
+ , persistedStore(nullptr)
+{ }
+
+QRemoteObjectNodePrivate::~QRemoteObjectNodePrivate()
+{
+}
+
+QRemoteObjectSourceLocations QRemoteObjectNodePrivate::remoteObjectAddresses() const
+{
+ if (registry)
+ return registry->sourceLocations();
+ return QRemoteObjectSourceLocations();
+}
+
+QRemoteObjectSourceLocations QRemoteObjectRegistryHostPrivate::remoteObjectAddresses() const
+{
+ if (registrySource)
+ return registrySource->sourceLocations();
+ return QRemoteObjectSourceLocations();
+}
+
+/*!
+ \reimp
+*/
+void QRemoteObjectNode::timerEvent(QTimerEvent*)
+{
+ Q_D(QRemoteObjectNode);
+
+ for (auto it = d->pendingReconnect.begin(), end = d->pendingReconnect.end(); it != end; /*erasing*/) {
+ const auto &conn = *it;
+ if (conn->isOpen()) {
+ it = d->pendingReconnect.erase(it);
+ } else {
+ conn->connectToServer();
+ ++it;
+ }
+ }
+
+ if (d->pendingReconnect.isEmpty())
+ d->reconnectTimer.stop();
+
+ qRODebug(this) << "timerEvent" << d->pendingReconnect.size();
+}
+
+/*!
+ \qmlproperty int Node::heartbeatInterval
+
+ Heartbeat interval in ms.
+
+ The heartbeat (only helpful for socket connections) will periodically send a
+ message to connected nodes to detect whether the connection was disrupted.
+ Qt Remote Objects will try to reconnect automatically if it detects a dropped
+ connection. This function can help with that detection since the client will
+ only detect that the server is unavailable when it tries to send data.
+
+ A value of \c 0 (the default) will disable the heartbeat.
+*/
+
+
+/*!
+ \property QRemoteObjectNode::heartbeatInterval
+ \brief Heartbeat interval in ms.
+
+ The heartbeat (only helpful for socket connections) will periodically send a
+ message to connected nodes to detect whether the connection was disrupted.
+ Qt Remote Objects will try to reconnect automatically if it detects a dropped
+ connection. This function can help with that detection since the client will
+ only detect that the server is unavailable when it tries to send data.
+
+ A value of \c 0 (the default) will disable the heartbeat.
+*/
+int QRemoteObjectNode::heartbeatInterval() const
+{
+ Q_D(const QRemoteObjectNode);
+ return d->m_heartbeatInterval;
+}
+
+void QRemoteObjectNode::setHeartbeatInterval(int interval)
+{
+ Q_D(QRemoteObjectNode);
+ if (d->m_heartbeatInterval == interval)
+ return;
+ d->m_heartbeatInterval = interval;
+ emit heartbeatIntervalChanged(interval);
+}
+
+/*!
+ \since 5.12
+ \typedef QRemoteObjectNode::RemoteObjectSchemaHandler
+
+ Typedef for a std::function method that can take a QUrl input and is
+ responsible for creating the communications channel between this node and
+ the node hosting the desired \l Source. As some types of QIODevices (e.g.,
+ QSslSocket) require additional steps before the device is ready for use,
+ the method is responsible for calling \l addClientSideConnection once the
+ connection is fully established.
+*/
+
+/*!
+ \since 5.12
+ \brief Provide a custom method to handle externally provided schemas
+
+ This method is tied to the \l Registry and \l {External Schemas}. By
+ registering a std::function handler for an external schema, the registered
+ method will be called when the registry is notified of a \l Source you've
+ acquired being available. Without this registration, QtRO would only be
+ able to handle the "built-in" schemas.
+
+ The provided method, \a handler, will be called when the registry sees a \l
+ Source object on a new (not yet connected) Node with a {QUrl::schema()} of
+ \a schema. The \a handler, of type \l
+ QRemoteObjectNode::RemoteObjectSchemaHandler will get the \l QUrl of the
+ Node providing the \l Source as an input parameter, and is responsible for
+ establishing the communications channel (a \l QIODevice of some sort) and
+ calling \l addClientSideConnection with it.
+
+ \sa RemoteObjectSchemaHandler
+*/
+void QRemoteObjectNode::registerExternalSchema(const QString &schema, QRemoteObjectNode::RemoteObjectSchemaHandler handler)
+{
+ Q_D(QRemoteObjectNode);
+ d->schemaHandlers.insert(schema, handler);
+}
+
+/*!
+ \since 5.11
+ \brief Forward Remote Objects from another network
+
+ The proxy functionality is useful when you want to share \l Source objects
+ over multiple networks. For instance, if you have an embedded target using
+ target-only connections (like local) and you want to make some of those
+ same objects available externally.
+
+ As a concrete example, say you have a set of processes talking to each
+ other on your target hardware using a registry, with the \l Registry at
+ "local:registry" and separate processes using a node at "local:MyHost" that
+ holds \l Source objects. If you wanted to access these objects, but over
+ tcp, you could create a new proxyNode like so:
+
+\code
+ // myInternalHost is a node only visible on the device...
+ QRemoteObjectHost myInternalHost("local:MyHost");
+ myInternalHost.enableRemoting<SomeObject>(&someObject);
+
+ // Regular host node, listening on port 12123, so visible to other
+ // devices
+ QRemoteObjectHost proxyNode("tcp://localhost:12123");
+
+ // Enable proxying objects from nodes on the local machine's internal
+ // QtRO bus
+ proxyNode.proxy("local:registry");
+\endcode
+
+ And from another device you create another node:
+
+\code
+ // NB: localhost resolves to a different ip address than proxyNode
+ QRemoteObjectHost nodeOnRemoteDevice("tcp://localhost:23234");
+
+ // Connect to the target's proxyNode directly, or use a tcp registry...
+ nodeOnRemoteDevice.connectToNode("tcp://<target device>:12123");
+
+ // Because of the proxy, we can get the object over tcp/ip port 12123,
+ // even though we can't connect directly to "local:MyHost"
+ SomeObject *so = nodeOnRemoteDevice.acquire<SomeObject>();
+\endcode
+
+ This would (internally) create a node in proxyNode, which (again
+ internally/automatically) connects to the provided registry (given by the
+ \a registryUrl parameter, "local:registry" in this example). Whenever
+ local:registry emits the \l remoteObjectAdded signal, the
+ \c QRemoteObjectSourceLocation is passed to the \a filter given to the proxy
+ call. If this method returns true (the default filter simply returns true
+ without any filtering), the object is acquired() from the internal node and
+ enableRemoting() (once the replica is initialized) is called on proxyNode.
+
+ If a \a hostUrl is provided (which is required to enable reverseProxy, but
+ not needed otherwise), the internal node will be a \l QRemoteObjectHost node
+ configured with the provided address. If no \a hostUrl is provided, the
+ internal node will be a QRemoteObjectNode (not HostNode).
+
+ Returns \c true if the object is acquired from the internal node.
+
+ \sa reverseProxy()
+*/
+bool QRemoteObjectHostBase::proxy(const QUrl ®istryUrl, const QUrl &hostUrl, RemoteObjectNameFilter filter)
+{
+ Q_D(QRemoteObjectHostBase);
+ if (!registryUrl.isValid() || !QtROClientFactory::instance()->isValid(registryUrl)) {
+ qROWarning(this) << "Can't proxy to registryUrl (invalid url or schema)" << registryUrl;
+ return false;
+ }
+
+ if (!hostUrl.isEmpty() && !QtROClientFactory::instance()->isValid(hostUrl)) {
+ qROWarning(this) << "Can't proxy using hostUrl (invalid schema)" << hostUrl;
+ return false;
+ }
+
+ if (d->proxyInfo) {
+ qROWarning(this) << "Proxying from multiple objects is currently not supported.";
+ return false;
+ }
+
+ QRemoteObjectNode *node;
+ if (hostUrl.isEmpty()) {
+ node = new QRemoteObjectNode(registryUrl);
+ } else {
+ node = new QRemoteObjectHost(hostUrl, registryUrl);
+ }
+ d->proxyInfo = new ProxyInfo(node, this, filter);
+ return true;
+}
+
+/*!
+ \since 5.11
+ \brief Forwards remote objects to another network.
+
+ The reverseProxy() function allows the \l proxy() functionality to be
+ extended, in effect mirroring the proxy functionality in the "reverse"
+ direction. These are distinct, because node communication is not symmetric,
+ one side calls enableRemoting() with a \l Source object, the other side
+ calls acquire() to get a \l Replica. Using \l proxy() allows you to
+ "observe" objects on a target device remotely via acquire, but it does not
+ allow off-target \l Source objects to be acquired from the device's local:*
+ network. That is where \l reverseProxy() comes in. If a proxyNode is
+ created like so:
+
+\code
+ // myInternalHost is a node only visible on the device...
+ QRemoteObjectHost myInternalHost("local:MyHost", "local:registry");
+
+ // RegistryHost node, listening on port 12123, so visible to other
+ // devices. The node must be a RegistryHost, so the Sources on
+ // the "outside" network can be forwarded to the inner network.
+ QRemoteObjectRegistryHost proxyNode("tcp://0.0.0.0:12123");
+
+ // Enable proxying objects from nodes on the local machine's internal
+ // QtRO bus. Note the hostUrl parameter is now needed.
+ proxyNode.proxy("local:registry", "local:fromProxy");
+ proxyNode.reverseProxy();
+\endcode
+
+ And from another device you create another node:
+
+\code
+ // Listen on a local port, and connect to "proxyNode" as the registry.
+ // NB: localhost resolves to a different ip address than proxyNode
+ QRemoteObjectHost nodeOnRemoteDevice("tcp://localhost:23234",
+ "tcp://<target device>:12123");
+
+ // Because of the reverseProxy, we can expose objects on this device
+ // and they will make their way to proxyNode...
+ nodeOnRemoteDevice.enableRemoting<OtherObject>(&otherObject);
+\endcode
+
+\code
+ // Acquire() can now see the objects on other devices through proxyNode,
+ // due to the reverseProxy call.
+ OtherObject *oo = myInternalHost.acquire<OtherObject>();
+\endcode
+
+ While the \l proxy() functionality allows \l Source objects on another
+ network to be acquired(), reverseProxy() allows \l Source objects to be
+ "pushed" to an otherwise inaccessible network.
+
+ \note proxy() needs to be called before \l reverseProxy(), and a
+ hostUrl needs to be provided to \l proxy for \l reverseProxy() to work. The
+ \l reverseProxy() method allows a separate \a filter to be applied. This
+ reverseProxy specific filter will receive notifications of new \l Source
+ objects on \c proxyNode and acquire them on the internal node if they pass the
+ \a filter.
+
+ Returns \c true on success, \c false otherwise.
+
+ \note Currently the reverse proxy functionality is supported only for
+ QRemoteObjectRegistryHost. Calling this method on a QRemoteObjectHost node
+ will always return \c false.
+
+ \sa proxy()
+*/
+bool QRemoteObjectHostBase::reverseProxy(QRemoteObjectHostBase::RemoteObjectNameFilter filter)
+{
+ Q_D(QRemoteObjectHostBase);
+
+ if (!d->proxyInfo) {
+ qROWarning(this) << "proxy() needs to be called before setting up reverse proxy.";
+ return false;
+ }
+
+ QRemoteObjectHost *host = qobject_cast<QRemoteObjectHost *>(d->proxyInfo->proxyNode);
+ if (!host) {
+ qROWarning(this) << "proxy() needs called with host-url to enable reverse proxy.";
+ return false;
+ }
+
+ return d->proxyInfo->setReverseProxy(filter);
+}
+
+/*!
+ \internal The replica needs to have a default constructor to be able
+ to create a replica from QML. In order for it to be properly
+ constructed, there needs to be a way to associate the replica with a
+ node and start the replica initialization. Thus we need a public
+ method on node to facilitate that. That's initializeReplica.
+*/
+void QRemoteObjectNode::initializeReplica(QRemoteObjectReplica *instance, const QString &name)
+{
+ Q_D(QRemoteObjectNode);
+ if (instance->inherits("QRemoteObjectDynamicReplica")) {
+ d->setReplicaImplementation(nullptr, instance, name);
+ } else {
+ const QMetaObject *meta = instance->metaObject();
+ // This is a templated acquire, so we tell the Source we don't need
+ // them to send the class definition. Thus we need to store the
+ // metaObject for this class - if this is a nested class, the QObject
+ // could be a nullptr or updated from the source,
+ d->dynamicTypeManager.addFromMetaObject(meta);
+ d->setReplicaImplementation(meta, instance, name.isEmpty() ? ::name(meta) : name);
+ }
+}
+
+void QRemoteObjectNodePrivate::setLastError(QRemoteObjectNode::ErrorCode errorCode)
+{
+ Q_Q(QRemoteObjectNode);
+ lastError = errorCode;
+ emit q->error(lastError);
+}
+
+void QRemoteObjectNodePrivate::setReplicaImplementation(const QMetaObject *meta, QRemoteObjectReplica *instance, const QString &name)
+{
+ qROPrivDebug() << "Starting setReplicaImplementation for" << name;
+ openConnectionIfNeeded(name);
+ QMutexLocker locker(&mutex);
+ if (hasInstance(name)) {
+ qCDebug(QT_REMOTEOBJECT)<<"setReplicaImplementation - using existing instance";
+ QSharedPointer<QRemoteObjectReplicaImplementation> rep = qSharedPointerCast<QRemoteObjectReplicaImplementation>(replicas.value(name).toStrongRef());
+ Q_ASSERT(rep);
+ instance->d_impl = rep;
+ rep->configurePrivate(instance);
+ } else {
+ instance->d_impl.reset(handleNewAcquire(meta, instance, name));
+ instance->initialize();
+ replicas.insert(name, instance->d_impl.toWeakRef());
+ qROPrivDebug() << "setReplicaImplementation - Created new instance" << name<<remoteObjectAddresses();
+ }
+}
+
+/*!
+ Returns a pointer to the Node's \l {QRemoteObjectRegistry}, if the Node
+ is using the Registry feature; otherwise it returns \nullptr.
+*/
+const QRemoteObjectRegistry *QRemoteObjectNode::registry() const
+{
+ Q_D(const QRemoteObjectNode);
+ return d->registry;
+}
+
+/*!
+ \class QRemoteObjectAbstractPersistedStore
+ \inmodule QtRemoteObjects
+ \brief A class which provides the methods for setting PROP values of a
+ replica to value they had the last time the replica was used.
+
+ This can be used to provide a "reasonable" value to be displayed until the
+ connection to the source is established and current values are available.
+
+ This class must be overridden to provide an implementation for saving (\l
+ QRemoteObjectAbstractPersistedStore::saveProperties) and restoring (\l
+ QRemoteObjectAbstractPersistedStore::restoreProperties) PROP values. The derived
+ type can then be set for a node, and any replica acquired from that node
+ will then automatically store PERSISTED properties when the replica
+ destructor is called, and retrieve the values when the replica is
+ instantiated.
+*/
+
+/*!
+ Constructs a QRemoteObjectAbstractPersistedStore with the given \a parent.
+ The default value of \a parent is \nullptr.
+*/
+QRemoteObjectAbstractPersistedStore::QRemoteObjectAbstractPersistedStore(QObject *parent)
+ : QObject(parent)
+{
+}
+
+QRemoteObjectAbstractPersistedStore::~QRemoteObjectAbstractPersistedStore()
+{
+}
+
+/*!
+ \fn virtual void QRemoteObjectAbstractPersistedStore::saveProperties(const QString &repName, const QByteArray &repSig, const QVariantList &values)
+
+ This method will be provided the replica class's \a repName, \a repSig and the list of
+ \a values that PERSISTED properties have when the replica destructor was
+ called. It is the responsibility of the inheriting class to store the
+ information in a manner consistent for \l
+ QRemoteObjectAbstractPersistedStore::restoreProperties to retrieve.
+
+ \sa QRemoteObjectAbstractPersistedStore::restoreProperties
+*/
+
+/*!
+ \fn virtual QVariantList QRemoteObjectAbstractPersistedStore::restoreProperties(const QString &repName, const QByteArray &repSig)
+
+ This method will be provided the replica class's \a repName and \a repSig when the
+ replica is being initialized. It is the responsibility of the inheriting
+ class to get the last values persisted by \l
+ QRemoteObjectAbstractPersistedStore::saveProperties and return them. An empty
+ QVariantList should be returned if no values are available.
+
+ \sa QRemoteObjectAbstractPersistedStore::saveProperties
+*/
+
+
+QRemoteObjectAbstractPersistedStore *QRemoteObjectNode::persistedStore() const
+{
+ Q_D(const QRemoteObjectNode);
+ return d->persistedStore;
+}
+
+/*!
+ \qmlproperty QRemoteObjectAbstractPersistedStore Node::persistedStore
+
+ Allows setting a \l QRemoteObjectAbstractPersistedStore instance for the node.
+
+ Allows replica \l PROP members with the PERSISTED trait to save their current value when the
+ replica is deleted and restore a stored value the next time the replica is started.
+
+ Requires a \l QRemoteObjectAbstractPersistedStore class implementation to control where and how
+ persistence is handled. A default QSettings-based implementation is provided by SettingsStore.
+*/
+
+/*!
+ \since 5.11
+ \property QRemoteObjectNode::persistedStore
+ \brief Allows setting a \l QRemoteObjectAbstractPersistedStore instance for the node.
+
+ Allows replica \l PROP members with the PERSISTED trait to save their current value when the
+ replica is deleted and restore a stored value the next time the replica is started.
+
+ Requires a \l QRemoteObjectAbstractPersistedStore class implementation to control where and how
+ persistence is handled.
+*/
+void QRemoteObjectNode::setPersistedStore(QRemoteObjectAbstractPersistedStore *persistedStore)
+{
+ Q_D(QRemoteObjectNode);
+ d->persistedStore = persistedStore;
+}
+
+QRemoteObjectAbstractPersistedStore::QRemoteObjectAbstractPersistedStore(QRemoteObjectAbstractPersistedStorePrivate &dptr, QObject *parent)
+ : QObject(dptr, parent)
+{
+}
+
+QRemoteObjectAbstractPersistedStorePrivate::QRemoteObjectAbstractPersistedStorePrivate()
+{
+}
+
+QRemoteObjectAbstractPersistedStorePrivate::~QRemoteObjectAbstractPersistedStorePrivate()
+{
+}
+
+QRemoteObjectMetaObjectManager::~QRemoteObjectMetaObjectManager()
+{
+ for (QMetaObject *mo : dynamicTypes) {
+ for (auto metaType : enumTypes[mo])
+ QMetaType::unregisterMetaType(metaType);
+ enumTypes.remove(mo);
+ free(mo); //QMetaObjectBuilder uses malloc, not new
+ }
+}
+
+const QMetaObject *QRemoteObjectMetaObjectManager::metaObjectForType(const QString &type)
+{
+ qCDebug(QT_REMOTEOBJECT) << "metaObjectForType: looking for" << type << "static keys:" << staticTypes.keys() << "dynamic keys:" << dynamicTypes.keys();
+ Q_ASSERT(staticTypes.contains(type) || dynamicTypes.contains(type));
+ auto it = staticTypes.constFind(type);
+ if (it != staticTypes.constEnd())
+ return it.value();
+ return dynamicTypes.value(type);
+}
+
+static void trackConnection(int typeId, QtROIoDeviceBase *connection)
+{
+ QMutexLocker lock(&s_managedTypesMutex);
+ if (s_trackedConnections[typeId].contains(connection))
+ return;
+ s_trackedConnections[typeId].insert(connection);
+ auto unregisterIfNotUsed = [typeId, connection]{
+ QMutexLocker lock(&s_managedTypesMutex);
+ Q_ASSERT(s_trackedConnections.contains(typeId));
+ Q_ASSERT(s_trackedConnections[typeId].contains(connection));
+ s_trackedConnections[typeId].remove(connection);
+ if (s_trackedConnections[typeId].isEmpty()) {
+ s_trackedConnections.remove(typeId);
+ s_managedTypes[typeId].unregisterMetaTypes();
+ s_managedTypes.remove(typeId); // Destroys the meta types
+ }
+ };
+
+ // Unregister the type only when the connection is destroyed
+ // Do not unregister types when the connections is discconected, because
+ // if it gets reconnected it will not register the types again
+ QObject::connect(connection, &QtROIoDeviceBase::destroyed, unregisterIfNotUsed);
+}
+
+struct EnumPair {
+ QByteArray name;
+ int value;
+};
+
+struct EnumData {
+ QByteArray name;
+ bool isFlag, isScoped;
+ quint32 keyCount, size;
+ QList<EnumPair> values;
+};
+
+struct GadgetProperty {
+ QByteArray name;
+ QByteArray type;
+};
+
+struct GadgetData {
+ QList<GadgetProperty> properties;
+ QList<EnumData> enums;
+};
+
+static const char *strDup(const QByteArray &s)
+{
+ auto result = new char[uint(s.size()) + 1];
+ auto end = std::copy(s.cbegin(), s.cend(), result);
+ *end = 0;
+ return result;
+}
+
+using Gadgets = QHash<QByteArray, GadgetData>;
+struct TypeInfo : public QtPrivate::QMetaTypeInterface
+{
+ const QMetaObject *metaObject;
+};
+static const QMetaObject *metaObjectFn(const QtPrivate::QMetaTypeInterface *self)
+{
+ return static_cast<const TypeInfo *>(self)->metaObject;
+}
+
+template <class Int>
+static TypeInfo *enumMetaType(const QByteArray &name, uint size, const QMetaObject *meta=nullptr)
+{
+ static const auto flags = QMetaType::IsEnumeration | QMetaType::NeedsConstruction
+ | QMetaType::NeedsDestruction;
+
+ auto typeInfo = new TypeInfo {
+ {
+ 0, alignof(Int), size, uint(flags), 0, metaObjectFn, strDup(name),
+ EnumConstructor<Int>,
+ EnumCopyConstructor<Int>,
+ EnumMoveConstructor<Int>,
+ EnumDestructor<Int>,
+ EnumEqualsFn<Int>,
+ EnumLessThanFn<Int>,
+ EnumDebugStreamFn<Int>,
+ EnumDataStreamOutFn<Int>,
+ EnumDataStreamInFn<Int>,
+ nullptr
+ }, meta
+ };
+ return typeInfo;
+}
+
+static TypeInfo *registerEnum(const QByteArray &name, uint size=4u)
+{
+ TypeInfo *result = nullptr;
+ if (QMetaType::fromName(name).isValid())
+ return result;
+ switch (size) {
+ case 1:
+ result = enumMetaType<qint8>(name, size);
+ break;
+ case 2:
+ result = enumMetaType<qint16>(name, size);
+ break;
+ case 4:
+ result = enumMetaType<qint32>(name, size);
+ break;
+ // Qt currently only supports enum values of 4 or less bytes (QMetaEnum value(index) returns int)
+// case 8: id = QMetaType::registerType(name.constData(), nullptr, nullptr, &EnumDestructor<qint64>,
+// &EnumConstructor<qint64>, size, flags, meta);
+// break;
+ default:
+ qWarning() << "Invalid enum detected" << name << "with size" << size << ". Defaulting to register as int.";
+ size = 4;
+ result = enumMetaType<qint32>(name, size);
+ break;
+ }
+#ifdef QTRO_VERBOSE_PROTOCOL
+ qDebug() << "Registering new enum" << name << "size:" << size;
+#endif
+ return result;
+}
+
+static int registerGadgets(QtROIoDeviceBase *connection, Gadgets &gadgets, QByteArray typeName)
+{
+ const auto &gadget = gadgets.take(typeName);
+ // TODO Look at having registerGadgets return QMetaType index of the id of the type
+ int typeId = QMetaType::fromName(typeName).id();
+ if (typeId != QMetaType::UnknownType) {
+ trackConnection(typeId, connection);
+ return typeId;
+ }
+
+ ManagedGadgetTypeEntry entry;
+
+ QMetaObjectBuilder gadgetBuilder;
+ gadgetBuilder.setClassName(typeName);
+ gadgetBuilder.setFlags(DynamicMetaObject | PropertyAccessInStaticMetaCall);
+ for (const auto &prop : gadget.properties) {
+ int propertyType = QMetaType::fromName(prop.type).id();
+ if (!propertyType && gadgets.contains(prop.type))
+ propertyType = registerGadgets(connection, gadgets, prop.type);
+ entry.gadgetType.push_back(QVariant(QMetaType(propertyType)));
+ auto dynamicProperty = gadgetBuilder.addProperty(prop.name, prop.type);
+ dynamicProperty.setWritable(true);
+ dynamicProperty.setReadable(true);
+ }
+ QList<TypeInfo *> enumsToBeAssignedMetaObject;
+ enumsToBeAssignedMetaObject.reserve(gadget.enums.size());
+ for (const auto &enumData: gadget.enums) {
+ auto enumBuilder = gadgetBuilder.addEnumerator(enumData.name);
+ enumBuilder.setIsFlag(enumData.isFlag);
+ enumBuilder.setIsScoped(enumData.isScoped);
+
+ for (quint32 k = 0; k < enumData.keyCount; ++k) {
+ const auto pair = enumData.values.at(k);
+ enumBuilder.addKey(pair.name, pair.value);
+ }
+ const QByteArray registeredName = QByteArray(typeName).append("::").append(enumData.name);
+ auto typeInfo = registerEnum(registeredName, enumData.size);
+ if (typeInfo)
+ enumsToBeAssignedMetaObject.append(typeInfo);
+ }
+ auto meta = gadgetBuilder.toMetaObject();
+ entry.metaObject = std::shared_ptr<QMetaObject>{meta, [](QMetaObject *ptr){ ::free(ptr); }};
+ for (auto typeInfo : enumsToBeAssignedMetaObject) {
+ typeInfo->metaObject = meta;
+ auto metaType = QMetaType(typeInfo);
+ entry.enumMetaTypes.append(metaType);
+ auto id = metaType.id();
+ qCDebug(QT_REMOTEOBJECT) << "Registering new gadget enum with id" << id << typeInfo->name << "size:" << typeInfo->size;
+ }
+
+ QMetaType::TypeFlags flags = QMetaType::IsGadget;
+ if (meta->propertyCount()) {
+ meta->d.static_metacall = &GadgetsStaticMetacallFunction;
+ meta->d.superdata = nullptr;
+ flags |= QMetaType::NeedsConstruction | QMetaType::NeedsDestruction;
+ auto typeInfo = new TypeInfo {
+ {
+ 0, alignof(GadgetType), sizeof(GadgetType), uint(flags), 0, metaObjectFn,
+ strDup(typeName),
+ GadgetTypedConstructor,
+ GadgetTypedCopyConstructor,
+ GadgetTypedMoveConstructor,
+ GadgetTypedDestructor,
+ GadgetEqualsFn,
+ nullptr, /* LessThanFn */
+ GadgetDebugStreamFn,
+ GadgetDataStreamOutFn,
+ GadgetDataStreamInFn,
+ nullptr /* LegacyRegisterOp */
+ },
+ meta
+ };
+ entry.gadgetMetaType = QMetaType(typeInfo);
+ } else {
+ auto typeInfo = new TypeInfo {
+ {
+ 0, alignof(GadgetType), sizeof(GadgetType), uint(flags), 0, metaObjectFn,
+ strDup(typeName),
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr
+ },
+ meta
+ };
+ entry.gadgetMetaType = QMetaType(typeInfo);
+ }
+ const int gadgetTypeId = entry.gadgetMetaType.id();
+ trackConnection(gadgetTypeId, connection);
+ QMutexLocker lock(&s_managedTypesMutex);
+ s_managedTypes.insert(gadgetTypeId, entry);
+ return gadgetTypeId;
+}
+
+static void registerAllGadgets(QtROIoDeviceBase *connection, Gadgets &gadgets)
+{
+ while (!gadgets.isEmpty())
+ registerGadgets(connection, gadgets, gadgets.constBegin().key());
+}
+
+static void deserializeEnum(QDataStream &ds, EnumData &enumData)
+{
+ ds >> enumData.name;
+ ds >> enumData.isFlag;
+ ds >> enumData.isScoped;
+ ds >> enumData.size;
+ ds >> enumData.keyCount;
+ for (quint32 i = 0; i < enumData.keyCount; i++) {
+ EnumPair pair;
+ ds >> pair.name;
+ ds >> pair.value;
+ enumData.values.push_back(pair);
+ }
+}
+
+static void parseGadgets(QtROIoDeviceBase *connection, QDataStream &in)
+{
+ quint32 qtEnums, numGadgets;
+ in >> qtEnums; // Qt enums - just need registration
+ for (quint32 i = 0; i < qtEnums; ++i) {
+ QByteArray enumName;
+ in >> enumName;
+ QMetaType type(enumMetaType<qint32>(enumName, 4, &Qt::staticMetaObject));
+ type.id();
+ }
+ in >> numGadgets;
+ if (numGadgets == 0)
+ return;
+ Gadgets gadgets;
+ for (quint32 i = 0; i < numGadgets; ++i) {
+ QByteArray type;
+ in >> type;
+ quint32 numProperties, numEnums;
+ in >> numProperties;
+ auto &properties = gadgets[type].properties;
+ for (quint32 p = 0; p < numProperties; ++p) {
+ GadgetProperty prop;
+ in >> prop.name;
+ in >> prop.type;
+ properties.push_back(prop);
+ }
+ in >> numEnums;
+ auto &enums = gadgets[type].enums;
+ for (quint32 e = 0; e < numEnums; ++e) {
+ EnumData enumData;
+ deserializeEnum(in, enumData);
+ enums.push_back(enumData);
+ }
+ }
+ registerAllGadgets(connection, gadgets);
+}
+
+QMetaObject *QRemoteObjectMetaObjectManager::addDynamicType(QtROIoDeviceBase *connection, QDataStream &in)
+{
+ QMetaObjectBuilder builder;
+ builder.setSuperClass(&QRemoteObjectReplica::staticMetaObject);
+ builder.setFlags(DynamicMetaObject);
+
+ QString typeString;
+ QByteArray type;
+ quint32 numEnums = 0;
+ quint32 numSignals = 0;
+ quint32 numMethods = 0;
+ quint32 numProperties = 0;
+ QHash<QByteArray, QByteArray> classEnums;
+
+ in >> typeString;
+ type = typeString.toLatin1();
+ builder.addClassInfo(QCLASSINFO_REMOTEOBJECT_TYPE, type);
+ builder.setClassName(type);
+
+ in >> numEnums;
+ QList<quint32> enumSizes(numEnums);
+ enumsToBeAssignedMetaObject.reserve(numEnums);
+ for (quint32 i = 0; i < numEnums; ++i) {
+ EnumData enumData;
+ deserializeEnum(in, enumData);
+ auto enumBuilder = builder.addEnumerator(enumData.name);
+ enumBuilder.setIsFlag(enumData.isFlag);
+ enumBuilder.setIsScoped(enumData.isScoped);
+ enumSizes[i] = enumData.size;
+
+ for (quint32 k = 0; k < enumData.keyCount; ++k) {
+ const auto pair = enumData.values.at(k);
+ enumBuilder.addKey(pair.name, pair.value);
+ }
+ const QByteArray registeredName = QByteArray(type).append("::").append(enumData.name);
+ classEnums[enumData.name] = registeredName;
+ auto typeInfo = registerEnum(registeredName, enumData.size);
+ if (typeInfo) {
+ enumsToBeAssignedMetaObject[typeInfo] = QMetaType(typeInfo);
+ int id = enumsToBeAssignedMetaObject[typeInfo].id();
+ qCDebug(QT_REMOTEOBJECT) << "Registering new class enum with id" << id << typeInfo->name << "size:" << typeInfo->size;
+ }
+ }
+ parseGadgets(connection, in);
+
+ int curIndex = 0;
+
+ in >> numSignals;
+ for (quint32 i = 0; i < numSignals; ++i) {
+ QByteArray signature;
+ QByteArrayList paramNames;
+ in >> signature;
+ in >> paramNames;
+ ++curIndex;
+ auto mmb = builder.addSignal(signature);
+ mmb.setParameterNames(paramNames);
+ }
+
+ in >> numMethods;
+ for (quint32 i = 0; i < numMethods; ++i) {
+ QByteArray signature, returnType;
+ QByteArrayList paramNames;
+ in >> signature;
+ in >> returnType;
+ in >> paramNames;
+ ++curIndex;
+ const bool isVoid = returnType.isEmpty() || returnType == QByteArrayLiteral("void");
+ QMetaMethodBuilder mmb;
+ if (isVoid)
+ mmb = builder.addMethod(signature);
+ else
+ mmb = builder.addMethod(signature, QByteArrayLiteral("QRemoteObjectPendingCall"));
+ mmb.setParameterNames(paramNames);
+ }
+
+ in >> numProperties;
+
+ for (quint32 i = 0; i < numProperties; ++i) {
+ QByteArray name;
+ QByteArray typeName;
+ QByteArray signalName;
+ in >> name;
+ in >> typeName;
+ in >> signalName;
+
+ auto choppedName = QByteArray::fromRawData(typeName.constData(),
+ typeName.size() - 1); // Remove trailing null
+ // The typeName for class enums is qualified with the class name.
+ // Need to remove the class name before checking if it's a class enum.
+ if (auto idx = choppedName.indexOf("::"); idx >= 0) {
+ choppedName = choppedName.sliced(idx + 2);
+ if (classEnums.contains(choppedName))
+ typeName = classEnums[choppedName] + '\0'; // Update to the enum's registered name
+ }
+
+ if (signalName.isEmpty())
+ builder.addProperty(name, typeName);
+ else
+ builder.addProperty(name, typeName, builder.indexOfSignal(signalName));
+ }
+
+ auto meta = builder.toMetaObject();
+ for (auto typeInfo : enumsToBeAssignedMetaObject.keys()) {
+ auto typeInfoWithMetaObject = static_cast<TypeInfo *>(typeInfo);
+ typeInfoWithMetaObject->metaObject = meta;
+ enumTypes[meta].append(enumsToBeAssignedMetaObject.take(typeInfo));
+ }
+ dynamicTypes.insert(typeString, meta);
+ return meta;
+}
+
+void QRemoteObjectMetaObjectManager::addFromMetaObject(const QMetaObject *metaObject)
+{
+ QString className = QLatin1String(metaObject->className());
+ if (!className.endsWith(QLatin1String("Replica")))
+ return;
+ if (className == QLatin1String("QRemoteObjectDynamicReplica") || staticTypes.contains(className))
+ return;
+ className.chop(7); //Remove 'Replica' from name
+ staticTypes.insert(className, metaObject);
+}
+
+void QRemoteObjectNodePrivate::connectReplica(QObject *object, QRemoteObjectReplica *instance)
+{
+ int nConnections = 0;
+ const QMetaObject *us = instance->metaObject();
+ const QMetaObject *them = object->metaObject();
+
+ static const int memberOffset = QRemoteObjectReplica::staticMetaObject.methodCount();
+ for (int idx = memberOffset; idx < us->methodCount(); ++idx) {
+ const QMetaMethod mm = us->method(idx);
+
+ qROPrivDebug() << idx << mm.name();
+ if (mm.methodType() != QMetaMethod::Signal)
+ continue;
+
+ // try to connect to a signal on the parent that has the same method signature
+ QByteArray sig = QMetaObject::normalizedSignature(mm.methodSignature().constData());
+ qROPrivDebug() << sig;
+ if (them->indexOfSignal(sig.constData()) == -1)
+ continue;
+
+ sig.prepend(QSIGNAL_CODE + '0');
+ const char * const csig = sig.constData();
+ const bool res = QObject::connect(object, csig, instance, csig);
+ Q_UNUSED(res)
+ ++nConnections;
+
+ qROPrivDebug() << sig << res;
+ }
+
+ qROPrivDebug() << "# connections =" << nConnections;
+}
+
+void QRemoteObjectNodePrivate::openConnectionIfNeeded(const QString &name)
+{
+ qROPrivDebug() << Q_FUNC_INFO << name << this;
+ if (!remoteObjectAddresses().contains(name)) {
+ qROPrivDebug() << name << "not available - available addresses:" << remoteObjectAddresses();
+ return;
+ }
+
+ if (!initConnection(remoteObjectAddresses().value(name).hostUrl))
+ qROPrivWarning() << "failed to open connection to" << name;
+}
+
+bool QRemoteObjectNodePrivate::initConnection(const QUrl &address)
+{
+ Q_Q(QRemoteObjectNode);
+ if (requestedUrls.contains(address)) {
+ qROPrivDebug() << "Connection already requested for " << address.toString();
+ return true;
+ }
+
+ requestedUrls.insert(address);
+
+ if (schemaHandlers.contains(address.scheme())) {
+ schemaHandlers[address.scheme()](address);
+ return true;
+ }
+
+ QtROClientIoDevice *connection = QtROClientFactory::instance()->create(address, q);
+ if (!connection) {
+ qROPrivWarning() << "Could not create QtROClientIoDevice for client. Invalid url/scheme provided?" << address;
+ return false;
+ }
+ qROPrivDebug() << "Opening connection to" << address.toString();
+ qROPrivDebug() << "Replica Connection isValid" << connection->isOpen();
+ QObject::connect(connection, &QtROClientIoDevice::shouldReconnect, q, [this, connection]() {
+ onShouldReconnect(connection);
+ });
+ QObject::connect(connection, &QtROIoDeviceBase::readyRead, q, [this, connection]() {
+ onClientRead(connection);
+ });
+ connection->connectToServer();
+
+ return true;
+}
+
+bool QRemoteObjectNodePrivate::hasInstance(const QString &name)
+{
+ if (!replicas.contains(name))
+ return false;
+
+ QSharedPointer<QReplicaImplementationInterface> rep = replicas.value(name).toStrongRef();
+ if (!rep) { //already deleted
+ replicas.remove(name);
+ return false;
+ }
+
+ return true;
+}
+
+static QDebug operator<<(QDebug debug,
+ const QHash<QString, QWeakPointer<QReplicaImplementationInterface>> &hash)
+{
+ const QDebugStateSaver saver(debug);
+ debug.nospace() << "QHash(";
+ for (auto it = hash.cbegin(); it != hash.cend(); ++it)
+ debug << '(' << it.key() << ", " << it.value().isNull() << ')';
+ debug << ')';
+ return debug;
+}
+
+void QRemoteObjectNodePrivate::onRemoteObjectSourceAdded(const QRemoteObjectSourceLocation &entry)
+{
+ qROPrivDebug() << "onRemoteObjectSourceAdded" << entry << replicas << replicas.contains(entry.first);
+ if (!entry.first.isEmpty()) {
+ QRemoteObjectSourceLocations locs = registry->sourceLocations();
+ locs[entry.first] = entry.second;
+ //TODO Is there a way to extend QRemoteObjectSourceLocations in place?
+ registry->d_impl->setProperty(0, QVariant::fromValue(locs));
+ registry->notifySourceLocationsChanged();
+ qROPrivDebug() << "onRemoteObjectSourceAdded, now locations =" << locs;
+ }
+ if (replicas.contains(entry.first)) //We have a replica waiting on this remoteObject
+ {
+ QSharedPointer<QReplicaImplementationInterface> rep = replicas.value(entry.first).toStrongRef();
+ if (!rep) { //replica has been deleted, remove from list
+ replicas.remove(entry.first);
+ return;
+ }
+
+ initConnection(entry.second.hostUrl);
+
+ qROPrivDebug() << "Called initConnection due to new RemoteObjectSource added via registry" << entry.first;
+ }
+}
+
+void QRemoteObjectNodePrivate::onRemoteObjectSourceRemoved(const QRemoteObjectSourceLocation &entry)
+{
+ if (!entry.first.isEmpty()) {
+ QRemoteObjectSourceLocations locs = registry->sourceLocations();
+ locs.remove(entry.first);
+ registry->d_impl->setProperty(0, QVariant::fromValue(locs));
+ registry->notifySourceLocationsChanged();
+ }
+}
+
+void QRemoteObjectNodePrivate::onRegistryInitialized()
+{
+ qROPrivDebug() << "Registry Initialized" << remoteObjectAddresses();
+
+ const auto remotes = remoteObjectAddresses();
+ for (auto i = remotes.cbegin(), end = remotes.cend(); i != end; ++i) {
+ if (replicas.contains(i.key())) //We have a replica waiting on this remoteObject
+ {
+ QSharedPointer<QReplicaImplementationInterface> rep = replicas.value(i.key()).toStrongRef();
+ if (rep && !requestedUrls.contains(i.value().hostUrl))
+ initConnection(i.value().hostUrl);
+ else if (!rep) //replica has been deleted, remove from list
+ replicas.remove(i.key());
+
+ continue;
+ }
+ }
+}
+
+void QRemoteObjectNodePrivate::onShouldReconnect(QtROClientIoDevice *ioDevice)
+{
+ Q_Q(QRemoteObjectNode);
+
+ const auto remoteObjects = ioDevice->remoteObjects();
+ for (const QString &remoteObject : remoteObjects) {
+ connectedSources.remove(remoteObject);
+ ioDevice->removeSource(remoteObject);
+ if (replicas.contains(remoteObject)) { //We have a replica waiting on this remoteObject
+ QSharedPointer<QConnectedReplicaImplementation> rep = qSharedPointerCast<QConnectedReplicaImplementation>(replicas.value(remoteObject).toStrongRef());
+ if (rep && !rep->connectionToSource.isNull()) {
+ rep->setDisconnected();
+ } else if (!rep) {
+ replicas.remove(remoteObject);
+ }
+ }
+ }
+ if (requestedUrls.contains(ioDevice->url())) {
+ // Only try to reconnect to URLs requested via connectToNode
+ // If we connected via registry, wait for the registry to see the node/source again
+ pendingReconnect.insert(ioDevice);
+ if (!reconnectTimer.isActive()) {
+ reconnectTimer.start(retryInterval, q);
+ qROPrivDebug() << "Starting reconnect timer";
+ }
+ } else {
+ qROPrivDebug() << "Url" << ioDevice->url().toDisplayString().toLatin1()
+ << "lost. We will reconnect Replicas if they reappear on the Registry.";
+ }
+}
+
+//This version of handleNewAcquire creates a QConnectedReplica. If this is a
+//host node, the QRemoteObjectHostBasePrivate overload is called instead.
+QReplicaImplementationInterface *QRemoteObjectNodePrivate::handleNewAcquire(const QMetaObject *meta, QRemoteObjectReplica *instance, const QString &name)
+{
+ Q_Q(QRemoteObjectNode);
+ QConnectedReplicaImplementation *rp = new QConnectedReplicaImplementation(name, meta, q);
+ rp->configurePrivate(instance);
+ if (connectedSources.contains(name)) { //Either we have a peer connections, or existing connection via registry
+ handleReplicaConnection(connectedSources[name].objectSignature, rp, connectedSources[name].device);
+ } else {
+ //No existing connection, but we know we can connect via registry
+ const auto &sourceLocations = remoteObjectAddresses();
+ const auto it = sourceLocations.constFind(name);
+ // This will try the connection, and if successful, the remoteObjects will be sent
+ // The link to the replica will be handled then
+ if (it != sourceLocations.constEnd())
+ initConnection(it.value().hostUrl);
+ }
+ return rp;
+}
+
+void QRemoteObjectNodePrivate::handleReplicaConnection(const QString &name)
+{
+ QSharedPointer<QRemoteObjectReplicaImplementation> rep = qSharedPointerCast<QRemoteObjectReplicaImplementation>(replicas.value(name).toStrongRef());
+ if (!rep) { //replica has been deleted, remove from list
+ replicas.remove(name);
+ return;
+ }
+
+ if (rep->isShortCircuit())
+ return;
+
+ QConnectedReplicaImplementation *connectedRep = static_cast<QConnectedReplicaImplementation *>(rep.data());
+ if (connectedRep->connectionToSource.isNull()) {
+ const auto sourceInfo = connectedSources.value(name);
+ handleReplicaConnection(sourceInfo.objectSignature, connectedRep, sourceInfo.device);
+ }
+}
+
+void QRemoteObjectNodePrivate::handleReplicaConnection(const QByteArray &sourceSignature, QConnectedReplicaImplementation *rep, QtROIoDeviceBase *connection)
+{
+ if (!checkSignatures(rep->m_objectSignature, sourceSignature)) {
+ qROPrivWarning() << "Signature mismatch for" << rep->m_metaObject->className() << (rep->m_objectName.isEmpty() ? QLatin1String("(unnamed)") : rep->m_objectName);
+ rep->setState(QRemoteObjectReplica::SignatureMismatch);
+ return;
+ }
+ rep->setConnection(connection);
+}
+
+//Host Nodes can use the more efficient InProcess Replica if we (this Node) hold the Source for the
+//requested Replica. If not, fall back to the Connected Replica case.
+QReplicaImplementationInterface *QRemoteObjectHostBasePrivate::handleNewAcquire(const QMetaObject *meta, QRemoteObjectReplica *instance, const QString &name)
+{
+ QMap<QString, QRemoteObjectSourceBase*>::const_iterator mapIt;
+ if (remoteObjectIo && map_contains(remoteObjectIo->m_sourceObjects, name, mapIt)) {
+ Q_Q(QRemoteObjectHostBase);
+ QInProcessReplicaImplementation *rp = new QInProcessReplicaImplementation(name, meta, q);
+ rp->configurePrivate(instance);
+ connectReplica(mapIt.value()->m_object, instance);
+ rp->connectionToSource = mapIt.value();
+ return rp;
+ }
+ return QRemoteObjectNodePrivate::handleNewAcquire(meta, instance, name);
+}
+
+void QRemoteObjectNodePrivate::onClientRead(QObject *obj)
+{
+ using namespace QRemoteObjectPackets;
+ QtROIoDeviceBase *connection = qobject_cast<QtROIoDeviceBase*>(obj);
+ QRemoteObjectPacketTypeEnum packetType;
+ Q_ASSERT(connection);
+ auto &codec = connection->d_func()->m_codec;
+
+ do {
+ if (!connection->read(packetType, rxName))
+ return;
+
+ if (packetType != Handshake && codec == nullptr) {
+ qROPrivWarning() << "Expected Handshake, got " << packetType;
+ setLastError(QRemoteObjectNode::ProtocolMismatch);
+ connection->close();
+ break;
+ }
+
+ switch (packetType) {
+ case QRemoteObjectPacketTypeEnum::Pong:
+ {
+ QSharedPointer<QRemoteObjectReplicaImplementation> rep = qSharedPointerCast<QRemoteObjectReplicaImplementation>(replicas.value(rxName).toStrongRef());
+ if (rep)
+ rep->notifyAboutReply(0, {});
+ else //replica has been deleted, remove from list
+ replicas.remove(rxName);
+ break;
+ }
+ case QRemoteObjectPacketTypeEnum::Handshake:
+ if (rxName != QtRemoteObjects::protocolVersion) {
+ qWarning() << "*** Protocol Mismatch, closing connection ***. Got" << rxName << "expected" << QtRemoteObjects::protocolVersion;
+ setLastError(QRemoteObjectNode::ProtocolMismatch);
+ connection->close();
+ } else {
+ // TODO should have some sort of manager for the codec
+ codec = new QRemoteObjectPackets::QDataStreamCodec;
+ }
+ break;
+ case QRemoteObjectPacketTypeEnum::ObjectList:
+ {
+ codec->deserializeObjectListPacket(connection->d_func()->stream(), rxObjects);
+ qROPrivDebug() << "newObjects:" << rxObjects;
+ // We need to make sure all of the source objects are in connectedSources before we add connections,
+ // otherwise nested QObjects could fail (we want to acquire children before parents, and the object
+ // list is unordered)
+ for (const auto &remoteObject : std::as_const(rxObjects)) {
+ qROPrivDebug() << " connectedSources.contains(" << remoteObject << ")" << connectedSources.contains(remoteObject.name) << replicas.contains(remoteObject.name);
+ if (!connectedSources.contains(remoteObject.name)) {
+ connectedSources[remoteObject.name] = SourceInfo{connection, remoteObject.typeName, remoteObject.signature};
+ connection->addSource(remoteObject.name);
+ // Make sure we handle Registry first if it is available
+ if (remoteObject.name == QLatin1String("Registry") && replicas.contains(remoteObject.name))
+ handleReplicaConnection(remoteObject.name);
+ }
+ }
+ for (const auto &remoteObject : std::as_const(rxObjects)) {
+ if (replicas.contains(remoteObject.name)) //We have a replica waiting on this remoteObject
+ handleReplicaConnection(remoteObject.name);
+ }
+ break;
+ }
+ case QRemoteObjectPacketTypeEnum::InitPacket:
+ {
+ qROPrivDebug() << "InitPacket-->" << rxName << this;
+ QSharedPointer<QConnectedReplicaImplementation> rep = qSharedPointerCast<QConnectedReplicaImplementation>(replicas.value(rxName).toStrongRef());
+ //Use m_rxArgs (a QVariantList to hold the properties QVariantList)
+ codec->deserializeInitPacket(connection->d_func()->stream(), rxArgs);
+ if (rep)
+ {
+ handlePointerToQObjectProperties(rep.data(), rxArgs);
+ rep->initialize(std::move(rxArgs));
+ } else { //replica has been deleted, remove from list
+ replicas.remove(rxName);
+ }
+ break;
+ }
+ case QRemoteObjectPacketTypeEnum::InitDynamicPacket:
+ {
+ qROPrivDebug() << "InitDynamicPacket-->" << rxName << this;
+ const QMetaObject *meta = dynamicTypeManager.addDynamicType(connection, connection->d_func()->stream());
+ codec->deserializeInitPacket(connection->d_func()->stream(), rxArgs);
+ QSharedPointer<QConnectedReplicaImplementation> rep = qSharedPointerCast<QConnectedReplicaImplementation>(replicas.value(rxName).toStrongRef());
+ if (rep)
+ {
+ rep->setDynamicMetaObject(meta);
+ handlePointerToQObjectProperties(rep.data(), rxArgs);
+ rep->setDynamicProperties(std::move(rxArgs));
+ } else { //replica has been deleted, remove from list
+ replicas.remove(rxName);
+ }
+ break;
+ }
+ case QRemoteObjectPacketTypeEnum::RemoveObject:
+ {
+ qROPrivDebug() << "RemoveObject-->" << rxName << this;
+ connectedSources.remove(rxName);
+ connection->removeSource(rxName);
+ if (replicas.contains(rxName)) { //We have a replica using the removed source
+ QSharedPointer<QConnectedReplicaImplementation> rep = qSharedPointerCast<QConnectedReplicaImplementation>(replicas.value(rxName).toStrongRef());
+ if (rep && !rep->connectionToSource.isNull()) {
+ rep->connectionToSource.clear();
+ rep->setState(QRemoteObjectReplica::Suspect);
+ } else if (!rep) {
+ replicas.remove(rxName);
+ }
+ }
+ break;
+ }
+ case QRemoteObjectPacketTypeEnum::PropertyChangePacket:
+ {
+ int propertyIndex;
+ codec->deserializePropertyChangePacket(connection->d_func()->stream(), propertyIndex, rxValue);
+ QSharedPointer<QRemoteObjectReplicaImplementation> rep = qSharedPointerCast<QRemoteObjectReplicaImplementation>(replicas.value(rxName).toStrongRef());
+ if (rep) {
+ QConnectedReplicaImplementation *connectedRep = nullptr;
+ if (!rep->isShortCircuit()) {
+ connectedRep = static_cast<QConnectedReplicaImplementation *>(rep.data());
+ if (!connectedRep->childIndices().contains(propertyIndex))
+ connectedRep = nullptr; //connectedRep will be a valid pointer only if propertyIndex is a child index
+ }
+ if (connectedRep)
+ rep->setProperty(propertyIndex, handlePointerToQObjectProperty(connectedRep, propertyIndex, rxValue));
+ else {
+ const QMetaProperty property = rep->m_metaObject->property(propertyIndex + rep->m_metaObject->propertyOffset());
+ if (property.userType() == QMetaType::QVariant && rxValue.canConvert<QRO_>()) {
+ // This is a type that requires registration
+ QRO_ typeInfo = rxValue.value<QRO_>();
+ QDataStream in(typeInfo.classDefinition);
+ parseGadgets(connection, in);
+ QDataStream ds(typeInfo.parameters);
+ ds >> rxValue;
+ }
+ rep->setProperty(propertyIndex, decodeVariant(std::move(rxValue), property.metaType()));
+ }
+ } else { //replica has been deleted, remove from list
+ replicas.remove(rxName);
+ }
+ break;
+ }
+ case QRemoteObjectPacketTypeEnum::InvokePacket:
+ {
+ int call, index, serialId, propertyIndex;
+ codec->deserializeInvokePacket(connection->d_func()->stream(), call, index, rxArgs, serialId, propertyIndex);
+ QSharedPointer<QRemoteObjectReplicaImplementation> rep = qSharedPointerCast<QRemoteObjectReplicaImplementation>(replicas.value(rxName).toStrongRef());
+ if (rep) {
+ static QVariant null(QMetaType::fromType<QObject *>(), nullptr);
+ QVariant paramValue;
+ // Qt usually supports 9 arguments, so ten should be usually safe
+ QVarLengthArray<void*, 10> param(rxArgs.size() + 1);
+ param[0] = null.data(); //Never a return value
+ if (rxArgs.size()) {
+ auto signal = rep->m_metaObject->method(index+rep->m_signalOffset);
+ for (int i = 0; i < rxArgs.size(); i++) {
+ if (signal.parameterType(i) == QMetaType::QVariant)
+ param[i + 1] = const_cast<void*>(reinterpret_cast<const void*>(&rxArgs.at(i)));
+ else {
+ rxArgs[i] = decodeVariant(std::move(rxArgs[i]), signal.parameterMetaType(i));
+ param[i + 1] = const_cast<void *>(rxArgs.at(i).data());
+ }
+ }
+ } else if (propertyIndex != -1) {
+ param.resize(2);
+ paramValue = rep->getProperty(propertyIndex);
+ param[1] = paramValue.data();
+ }
+ qROPrivDebug() << "Replica Invoke-->" << rxName << rep->m_metaObject->method(index+rep->m_signalOffset).name() << index << rep->m_signalOffset;
+ // We activate on rep->metaobject() so the private metacall is used, not m_metaobject (which
+ // is the class thie replica looks like)
+ QMetaObject::activate(rep.data(), rep->metaObject(), index+rep->m_signalOffset, param.data());
+ } else { //replica has been deleted, remove from list
+ replicas.remove(rxName);
+ }
+ break;
+ }
+ case QRemoteObjectPacketTypeEnum::InvokeReplyPacket:
+ {
+ int ackedSerialId;
+ codec->deserializeInvokeReplyPacket(connection->d_func()->stream(), ackedSerialId, rxValue);
+ QSharedPointer<QRemoteObjectReplicaImplementation> rep = qSharedPointerCast<QRemoteObjectReplicaImplementation>(replicas.value(rxName).toStrongRef());
+ if (rep) {
+ qROPrivDebug() << "Received InvokeReplyPacket ack'ing serial id:" << ackedSerialId;
+ rep->notifyAboutReply(ackedSerialId, rxValue);
+ } else { //replica has been deleted, remove from list
+ replicas.remove(rxName);
+ }
+ break;
+ }
+ case QRemoteObjectPacketTypeEnum::AddObject:
+ case QRemoteObjectPacketTypeEnum::Invalid:
+ case QRemoteObjectPacketTypeEnum::Ping:
+ qROPrivWarning() << "Unexpected packet received";
+ }
+ } while (connection->bytesAvailable()); // have bytes left over, so do another iteration
+}
+
+/*!
+ \class QRemoteObjectNode
+ \inmodule QtRemoteObjects
+ \brief A node on a Qt Remote Objects network.
+
+ The QRemoteObjectNode class provides an entry point to a QtRemoteObjects
+ network. A network can be as simple as two nodes, or an arbitrarily complex
+ set of processes and devices.
+
+ A QRemoteObjectNode does not have a url that other nodes can connect to,
+ and thus is able to acquire replicas only. It is not able to share source
+ objects (only QRemoteObjectHost and QRemoteObjectRegistryHost Nodes can
+ share).
+
+ Nodes may connect to each other directly using \l connectToNode, or
+ they can use the QRemoteObjectRegistry to simplify connections.
+
+ The QRemoteObjectRegistry is a special replica available to every node that
+ connects to the Registry Url. It knows how to connect to every
+ QRemoteObjectSource object on the network.
+
+ \sa QRemoteObjectHost, QRemoteObjectRegistryHost
+*/
+
+/*!
+ \class QRemoteObjectHostBase
+ \inmodule QtRemoteObjects
+ \brief The QRemoteObjectHostBase class provides base functionality common to \l
+ {QRemoteObjectHost} {Host} and \l {QRemoteObjectRegistryHost} {RegistryHost} classes.
+
+ QRemoteObjectHostBase is a base class that cannot be instantiated directly.
+ It provides the enableRemoting and disableRemoting functionality shared by
+ all host nodes (\l {QRemoteObjectHost} {Host} and \l
+ {QRemoteObjectRegistryHost} {RegistryHost}) as well as the logic required
+ to expose \l {Source} objects on the Remote Objects network.
+*/
+
+/*!
+ \class QRemoteObjectHost
+ \inmodule QtRemoteObjects
+ \brief A (Host) Node on a Qt Remote Objects network.
+
+ The QRemoteObjectHost class provides an entry point to a QtRemoteObjects
+ network. A network can be as simple as two nodes, or an arbitrarily complex
+ set of processes and devices.
+
+ QRemoteObjectHosts have the same capabilities as QRemoteObjectNodes, but
+ they can also be connected to and can share source objects on the network.
+
+ Nodes may connect to each other directly using \l connectToNode, or they
+ can use the QRemoteObjectRegistry to simplify connections.
+
+ The QRemoteObjectRegistry is a special replica available to every node that
+ connects to the registry Url. It knows how to connect to every
+ QRemoteObjectSource object on the network.
+
+ \sa QRemoteObjectNode, QRemoteObjectRegistryHost
+*/
+
+/*!
+ \class QRemoteObjectRegistryHost
+ \inmodule QtRemoteObjects
+ \brief A (Host/Registry) node on a Qt Remote Objects network.
+
+ The QRemoteObjectRegistryHost class provides an entry point to a QtRemoteObjects
+ network. A network can be as simple as two Nodes, or an arbitrarily complex
+ set of processes and devices.
+
+ A QRemoteObjectRegistryHost has the same capability that a
+ QRemoteObjectHost has (which includes everything a QRemoteObjectNode
+ supports), and in addition is the owner of the Registry. Any
+ QRemoteObjectHost node that connects to this Node will have all of their
+ Source objects made available by the Registry.
+
+ Nodes only support connection to one \l registry, calling \l
+ QRemoteObjectNode::setRegistryUrl when a Registry is already set is
+ considered an error. For something like a secure and insecure network
+ (where different Registries would be applicable), the recommendation is to
+ create separate Nodes to connect to each, in effect creating two
+ independent Qt Remote Objects networks.
+
+ Nodes may connect to each other directly using \l connectToNode, or they
+ can use the QRemoteObjectRegistry to simplify connections.
+
+ The QRemoteObjectRegistry is a special Replica available to every Node that
+ connects to the Registry Url. It knows how to connect to every
+ QRemoteObjectSource object on the network.
+
+ \sa QRemoteObjectNode, QRemoteObjectHost
+*/
+
+/*!
+ \enum QRemoteObjectNode::ErrorCode
+
+ This enum type specifies the various error codes associated with QRemoteObjectNode errors:
+
+ \value NoError No error.
+ \value RegistryNotAcquired The registry could not be acquired.
+ \value RegistryAlreadyHosted The registry is already defined and hosting Sources.
+ \value NodeIsNoServer The given QRemoteObjectNode is not a host node.
+ \value ServerAlreadyCreated The host node has already been initialized.
+ \value UnintendedRegistryHosting An attempt was made to create a host QRemoteObjectNode and connect to itself as the registry.
+ \value OperationNotValidOnClientNode The attempted operation is not valid on a client QRemoteObjectNode.
+ \value SourceNotRegistered The given QRemoteObjectSource is not registered on this node.
+ \value MissingObjectName The given QObject does not have objectName() set.
+ \value HostUrlInvalid The given url has an invalid or unrecognized scheme.
+ \value ProtocolMismatch The client and the server have different protocol versions.
+ \value ListenFailed Can't listen on the specified host port.
+*/
+
+/*!
+ \enum QRemoteObjectHostBase::AllowedSchemas
+
+ This enum is used to specify whether a Node will accept a url with an
+ unrecognized schema for the hostUrl. By default only urls with known
+ schemas are accepted, but using \c AllowExternalRegistration will enable
+ the \l Registry to pass your external (to QtRO) url to client Nodes.
+
+ \value BuiltInSchemasOnly Only allow the hostUrl to be set to a QtRO
+ supported schema. This is the default value, and causes a Node error to be
+ set if an unrecognized schema is provided.
+ \value AllowExternalRegistration The provided schema is registered as an
+ \l {External Schemas}{External Schema}
+
+ \sa QRemoteObjectHost
+*/
+
+/*!
+ \fn ObjectType *QRemoteObjectNode::acquire(const QString &name)
+
+ Returns a pointer to a Replica of type ObjectType (which is a template
+ parameter and must inherit from \l QRemoteObjectReplica). That is, the
+ template parameter must be a \l {repc} generated type. The \a name
+ parameter can be used to specify the \a name given to the object
+ during the QRemoteObjectHost::enableRemoting() call.
+*/
+
+void QRemoteObjectNodePrivate::initialize()
+{
+ qRegisterMetaType<QRemoteObjectNode *>();
+ qRegisterMetaType<QRemoteObjectNode::ErrorCode>();
+ qRegisterMetaType<QAbstractSocket::SocketError>(); //For queued qnx error()
+ qRegisterMetaType<QRemoteObjectPackets::QRO_>();
+ qRegisterMetaType<QRemoteObjectPackets::QSQ_>();
+ qRegisterMetaType<QRemoteObjectPackets::QAS_>();
+ qRegisterMetaType<QtROSequentialContainer>();
+ qRegisterMetaType<QtROAssociativeContainer>();
+ // To support dynamic MODELs, we need to make sure the types are registered
+ QAbstractItemModelSourceAdapter::registerTypes();
+}
+
+bool QRemoteObjectNodePrivate::checkSignatures(const QByteArray &a, const QByteArray &b)
+{
+ // if any of a or b is empty it means it's a dynamic ojects or an item model
+ if (a.isEmpty() || b.isEmpty())
+ return true;
+ return a == b;
+}
+
+
+void QRemoteObjectNode::persistProperties(const QString &repName, const QByteArray &repSig, const QVariantList &props)
+{
+ Q_D(QRemoteObjectNode);
+ if (d->persistedStore) {
+ d->persistedStore->saveProperties(repName, repSig, props);
+ } else {
+ qCWarning(QT_REMOTEOBJECT) << qPrintable(objectName()) << "Unable to store persisted properties for" << repName;
+ qCWarning(QT_REMOTEOBJECT) << " No persisted store set.";
+ }
+}
+
+QVariantList QRemoteObjectNode::retrieveProperties(const QString &repName, const QByteArray &repSig)
+{
+ Q_D(QRemoteObjectNode);
+ if (d->persistedStore) {
+ return d->persistedStore->restoreProperties(repName, repSig);
+ }
+ qCWarning(QT_REMOTEOBJECT) << qPrintable(objectName()) << "Unable to retrieve persisted properties for" << repName;
+ qCWarning(QT_REMOTEOBJECT) << " No persisted store set.";
+ return QVariantList();
+}
+
+/*!
+ Default constructor for QRemoteObjectNode with the given \a parent. A Node
+ constructed in this manner can not be connected to, and thus can not expose
+ Source objects on the network. It also will not include a \l
+ QRemoteObjectRegistry, unless set manually using setRegistryUrl.
+
+ \sa connectToNode, setRegistryUrl
+*/
+QRemoteObjectNode::QRemoteObjectNode(QObject *parent)
+ : QObject(*new QRemoteObjectNodePrivate, parent)
+{
+ Q_D(QRemoteObjectNode);
+ d->initialize();
+}
+
+/*!
+ QRemoteObjectNode connected to a {QRemoteObjectRegistry} {Registry}. A Node
+ constructed in this manner can not be connected to, and thus can not expose
+ Source objects on the network. Finding and connecting to other (Host) Nodes
+ is handled by the QRemoteObjectRegistry specified by \a registryAddress.
+
+ \sa connectToNode, setRegistryUrl, QRemoteObjectHost, QRemoteObjectRegistryHost
+*/
+QRemoteObjectNode::QRemoteObjectNode(const QUrl ®istryAddress, QObject *parent)
+ : QObject(*new QRemoteObjectNodePrivate, parent)
+{
+ Q_D(QRemoteObjectNode);
+ d->initialize();
+ d->setRegistryUrlNodeImpl(registryAddress);
+}
+
+QRemoteObjectNode::QRemoteObjectNode(QRemoteObjectNodePrivate &dptr, QObject *parent)
+ : QObject(dptr, parent)
+{
+ Q_D(QRemoteObjectNode);
+ d->initialize();
+}
+
+/*!
+ \qmltype Host
+ \instantiates QRemoteObjectHost
+ \inqmlmodule QtRemoteObjects
+ \brief A host node on a Qt Remote Objects network.
+
+ The Host type provides an entry point to a Qt Remote Objects network. A network
+ can be as simple as two nodes, or an arbitrarily complex set of processes and devices.
+
+ Hosts have the same capabilities as Nodes, but they can also be connected to and can
+ share source objects on the network.
+*/
+
+/*!
+ \internal This is a base class for both QRemoteObjectHost and
+ QRemoteObjectRegistryHost to provide the shared features/functions for
+ sharing \l Source objects.
+*/
+QRemoteObjectHostBase::QRemoteObjectHostBase(QRemoteObjectHostBasePrivate &d, QObject *parent)
+ : QRemoteObjectNode(d, parent)
+{ }
+
+/*!
+ Constructs a new QRemoteObjectHost Node (i.e., a Node that supports
+ exposing \l Source objects on the QtRO network) with the given \a parent.
+ This constructor is meant specific to support QML in the future as it will
+ not be available to connect to until \l {QRemoteObjectHost::}{setHostUrl} is called.
+
+ \sa setHostUrl(), setRegistryUrl()
+*/
+QRemoteObjectHost::QRemoteObjectHost(QObject *parent)
+ : QRemoteObjectHostBase(*new QRemoteObjectHostPrivate, parent)
+{ }
+
+/*!
+ Constructs a new QRemoteObjectHost Node (i.e., a Node that supports
+ exposing \l Source objects on the QtRO network) with address \a address. If
+ set, \a registryAddress will be used to connect to the \l
+ QRemoteObjectRegistry at the provided address. The \a allowedSchemas
+ parameter is only needed (and should be set to \l
+ {QRemoteObjectHostBase::AllowExternalRegistration}
+ {AllowExternalRegistration}) if the schema of the url should be used as an
+ \l {External Schemas} {External Schema} by the registry.
+
+ \sa setHostUrl(), setRegistryUrl()
+*/
+QRemoteObjectHost::QRemoteObjectHost(const QUrl &address, const QUrl ®istryAddress,
+ AllowedSchemas allowedSchemas, QObject *parent)
+ : QRemoteObjectHostBase(*new QRemoteObjectHostPrivate, parent)
+{
+ if (!address.isEmpty()) {
+ if (!d_func()->setHostUrlBaseImpl(address, allowedSchemas))
+ return;
+ }
+
+ if (!registryAddress.isEmpty())
+ d_func()->setRegistryUrlNodeImpl(registryAddress);
+}
+
+/*!
+ Constructs a new QRemoteObjectHost Node (i.e., a Node that supports
+ exposing \l Source objects on the QtRO network) with a url of \a
+ address and the given \a parent. This overload is provided as a convenience for specifying a
+ QObject parent without providing a registry address.
+
+ \sa setHostUrl(), setRegistryUrl()
+*/
+QRemoteObjectHost::QRemoteObjectHost(const QUrl &address, QObject *parent)
+ : QRemoteObjectHostBase(*new QRemoteObjectHostPrivate, parent)
+{
+ if (!address.isEmpty())
+ d_func()->setHostUrlBaseImpl(address);
+}
+
+/*!
+ \internal QRemoteObjectHost::QRemoteObjectHost
+*/
+QRemoteObjectHost::QRemoteObjectHost(QRemoteObjectHostPrivate &d, QObject *parent)
+ : QRemoteObjectHostBase(d, parent)
+{ }
+
+QRemoteObjectHost::~QRemoteObjectHost() {}
+
+/*!
+ Constructs a new QRemoteObjectRegistryHost Node with the given \a parent. RegistryHost Nodes have
+ the same functionality as \l QRemoteObjectHost Nodes, except rather than
+ being able to connect to a \l QRemoteObjectRegistry, the provided Host QUrl
+ (\a registryAddress) becomes the address of the registry for other Nodes to
+ connect to.
+*/
+QRemoteObjectRegistryHost::QRemoteObjectRegistryHost(const QUrl ®istryAddress, QObject *parent)
+ : QRemoteObjectHostBase(*new QRemoteObjectRegistryHostPrivate, parent)
+{
+ if (registryAddress.isEmpty())
+ return;
+
+ d_func()->setRegistryUrlRegistryHostImpl(registryAddress);
+}
+
+/*!
+ \internal
+*/
+QRemoteObjectRegistryHost::QRemoteObjectRegistryHost(QRemoteObjectRegistryHostPrivate &d, QObject *parent)
+ : QRemoteObjectHostBase(d, parent)
+{ }
+
+QRemoteObjectRegistryHost::~QRemoteObjectRegistryHost() {}
+
+QRemoteObjectNode::~QRemoteObjectNode()
+{ }
+
+QRemoteObjectHostBase::~QRemoteObjectHostBase()
+{ }
+
+/*!
+ Sets \a name as the internal name for this Node. This
+ is then output as part of the logging (if enabled).
+ This is primarily useful if you merge log data from multiple nodes.
+*/
+void QRemoteObjectNode::setName(const QString &name)
+{
+ setObjectName(name);
+}
+
+/*!
+ Similar to QObject::setObjectName() (which this method calls), but this
+ version also applies the \a name to internal classes as well, which are
+ used in some of the debugging output.
+*/
+void QRemoteObjectHostBase::setName(const QString &name)
+{
+ Q_D(QRemoteObjectHostBase);
+ setObjectName(name);
+ if (d->remoteObjectIo)
+ d->remoteObjectIo->setObjectName(name);
+}
+
+/*!
+ \internal The HostBase version of this method is protected so the method
+ isn't exposed on RegistryHost nodes.
+*/
+QUrl QRemoteObjectHostBase::hostUrl() const
+{
+ Q_D(const QRemoteObjectHostBase);
+ if (d->remoteObjectIo)
+ return d->remoteObjectIo->serverAddress();
+ return QUrl();
+}
+
+/*!
+ \internal The HostBase version of this method is protected so the method
+ isn't exposed on RegistryHost nodes.
+*/
+bool QRemoteObjectHostBase::setHostUrl(const QUrl &hostAddress, AllowedSchemas allowedSchemas)
+{
+ return d_func()->setHostUrlBaseImpl(hostAddress, allowedSchemas);
+}
+
+bool QRemoteObjectHostBasePrivate::setHostUrlBaseImpl(
+ const QUrl &hostAddress, QRemoteObjectHostBase::AllowedSchemas allowedSchemas)
+{
+ Q_Q(QRemoteObjectHostBase);
+ if (remoteObjectIo) {
+ setLastError(QRemoteObjectHostBase::ServerAlreadyCreated);
+ return false;
+ }
+
+ if (allowedSchemas == QRemoteObjectHostBase::AllowedSchemas::BuiltInSchemasOnly && !QtROServerFactory::instance()->isValid(hostAddress)) {
+ setLastError(QRemoteObjectHostBase::HostUrlInvalid);
+ return false;
+ }
+
+ if (allowedSchemas == QRemoteObjectHostBase::AllowedSchemas::AllowExternalRegistration && QtROServerFactory::instance()->isValid(hostAddress)) {
+ qWarning() << qPrintable(q->objectName()) << "Overriding a valid QtRO url (" << hostAddress << ") with AllowExternalRegistration is not allowed.";
+ setLastError(QRemoteObjectHostBase::HostUrlInvalid);
+ return false;
+ }
+ remoteObjectIo = new QRemoteObjectSourceIo(hostAddress, q);
+
+ if (allowedSchemas == QRemoteObjectHostBase::AllowedSchemas::BuiltInSchemasOnly && !remoteObjectIo->startListening()) {
+ setLastError(QRemoteObjectHostBase::ListenFailed);
+ delete remoteObjectIo;
+ remoteObjectIo = nullptr;
+ return false;
+ }
+
+ //If we've given a name to the node, set it on the sourceIo as well
+ if (!q->objectName().isEmpty())
+ remoteObjectIo->setObjectName(q->objectName());
+ //Since we don't know whether setHostUrl or setRegistryUrl/setRegistryHost will be called first,
+ //break it into two pieces. setHostUrl connects the RemoteObjectSourceIo->[add/remove]RemoteObjectSource to QRemoteObjectReplicaNode->[add/remove]RemoteObjectSource
+ //setRegistry* calls appropriately connect RemoteObjecSourcetIo->[add/remove]RemoteObjectSource to the registry when it is created
+ QObject::connect(remoteObjectIo, &QRemoteObjectSourceIo::remoteObjectAdded, q, &QRemoteObjectHostBase::remoteObjectAdded);
+ QObject::connect(remoteObjectIo, &QRemoteObjectSourceIo::remoteObjectRemoved, q, &QRemoteObjectHostBase::remoteObjectRemoved);
+
+ return true;
+}
+
+/*!
+ \qmlproperty url Host::hostUrl
+
+ The host address for the node.
+
+ This is the address where source objects remoted by this node will reside.
+*/
+
+/*!
+ \property QRemoteObjectHost::hostUrl
+ \brief The host address for the node.
+
+ This is the address where source objects remoted by this node will reside.
+*/
+
+/*!
+ Returns the host address for the QRemoteObjectNode as a QUrl. If the Node
+ is not a Host node, returns an empty QUrl.
+
+ \sa setHostUrl()
+*/
+QUrl QRemoteObjectHost::hostUrl() const
+{
+ return QRemoteObjectHostBase::hostUrl();
+}
+
+/*!
+ Sets the \a hostAddress for a host QRemoteObjectNode.
+
+ Returns \c true if the Host address is set, otherwise \c false.
+
+ The \a allowedSchemas parameter is only needed (and should be set to \l
+ {QRemoteObjectHostBase::AllowExternalRegistration}
+ {AllowExternalRegistration}) if the schema of the url should be used as an
+ \l {External Schemas} {External Schema} by the registry.
+*/
+bool QRemoteObjectHost::setHostUrl(const QUrl &hostAddress, AllowedSchemas allowedSchemas)
+{
+ return d_func()->setHostUrlHostImpl(hostAddress, allowedSchemas);
+}
+
+bool QRemoteObjectHostPrivate::setHostUrlHostImpl(
+ const QUrl &hostAddress, QRemoteObjectHostBase::AllowedSchemas allowedSchemas)
+{
+ bool success = setHostUrlBaseImpl(hostAddress, allowedSchemas);
+ if (success)
+ q_func()->hostUrlChanged();
+ return success;
+}
+
+/*!
+ This method can be used to set the address of this Node to \a registryUrl
+ (used for other Nodes to connect to this one), if the QUrl isn't set in the
+ constructor. Since this Node becomes the Registry, calling this setter
+ method causes this Node to use the url as the host address. All other
+ Node's use the \l {QRemoteObjectNode::setRegistryUrl} method initiate a
+ connection to the Registry.
+
+ Returns \c true if the registry address is set, otherwise \c false.
+
+ \sa QRemoteObjectRegistryHost(), QRemoteObjectNode::setRegistryUrl
+*/
+bool QRemoteObjectRegistryHost::setRegistryUrl(const QUrl ®istryUrl)
+{
+ return d_func()->setRegistryUrlRegistryHostImpl(registryUrl);
+}
+
+bool QRemoteObjectRegistryHostPrivate::setRegistryUrlRegistryHostImpl(const QUrl ®istryUrl)
+{
+ Q_Q(QRemoteObjectRegistryHost);
+ if (setHostUrlBaseImpl(registryUrl)) {
+ if (!remoteObjectIo) {
+ setLastError(QRemoteObjectNode::ServerAlreadyCreated);
+ return false;
+ } else if (registry) {
+ setLastError(QRemoteObjectNode::RegistryAlreadyHosted);
+ return false;
+ }
+
+ QRegistrySource *remoteObject = new QRegistrySource(q);
+ q->enableRemoting(remoteObject);
+ registryAddress = remoteObjectIo->serverAddress();
+ registrySource = remoteObject;
+ //Connect RemoteObjectSourceIo->remoteObject[Added/Removde] to the registry Slot
+ QObject::connect(q, &QRemoteObjectRegistryHost::remoteObjectAdded, registrySource, &QRegistrySource::addSource);
+ QObject::connect(q, &QRemoteObjectRegistryHost::remoteObjectRemoved, registrySource, &QRegistrySource::removeSource);
+ QObject::connect(remoteObjectIo, &QRemoteObjectSourceIo::serverRemoved, registrySource, &QRegistrySource::removeServer);
+ //onAdd/Remove update the known remoteObjects list in the RegistrySource, so no need to connect to the RegistrySource remoteObjectAdded/Removed signals
+ setRegistry(q->acquire<QRemoteObjectRegistry>());
+ return true;
+ }
+ return false;
+}
+
+/*!
+ Returns the last error set.
+*/
+QRemoteObjectNode::ErrorCode QRemoteObjectNode::lastError() const
+{
+ Q_D(const QRemoteObjectNode);
+ return d->lastError;
+}
+
+/*!
+ \qmlproperty url Node::registryUrl
+
+ The address of the \l {QRemoteObjectRegistry} {Registry} used by this node.
+
+ This is an empty QUrl if there is no registry in use.
+*/
+
+/*!
+ \property QRemoteObjectNode::registryUrl
+ \brief The address of the \l {QRemoteObjectRegistry} {Registry} used by this node.
+
+ This is an empty QUrl if there is no registry in use.
+*/
+QUrl QRemoteObjectNode::registryUrl() const
+{
+ Q_D(const QRemoteObjectNode);
+ return d->registryAddress;
+}
+
+bool QRemoteObjectNode::setRegistryUrl(const QUrl ®istryAddress)
+{
+ Q_D(QRemoteObjectNode);
+ return d->setRegistryUrlNodeImpl(registryAddress);
+}
+
+bool QRemoteObjectNodePrivate::setRegistryUrlNodeImpl(const QUrl ®istryAddr)
+{
+ Q_Q(QRemoteObjectNode);
+ if (registry) {
+ setLastError(QRemoteObjectNode::RegistryAlreadyHosted);
+ return false;
+ }
+
+ registryAddress = registryAddr;
+ setRegistry(q->acquire<QRemoteObjectRegistry>());
+ //Connect remoteObject[Added/Removed] to the registry Slot
+ QObject::connect(q, &QRemoteObjectNode::remoteObjectAdded, registry, &QRemoteObjectRegistry::addSource);
+ QObject::connect(q, &QRemoteObjectNode::remoteObjectRemoved, registry, &QRemoteObjectRegistry::removeSource);
+ q->connectToNode(registryAddress);
+ return true;
+}
+
+void QRemoteObjectNodePrivate::setRegistry(QRemoteObjectRegistry *reg)
+{
+ Q_Q(QRemoteObjectNode);
+ registry = reg;
+ reg->setParent(q);
+ //Make sure when we get the registry initialized, we update our replicas
+ QObject::connect(reg, &QRemoteObjectRegistry::initialized, q, [this]() {
+ onRegistryInitialized();
+ });
+ //Make sure we handle new RemoteObjectSources on Registry...
+ QObject::connect(reg, &QRemoteObjectRegistry::remoteObjectAdded,
+ q, [this](const QRemoteObjectSourceLocation &location) {
+ onRemoteObjectSourceAdded(location);
+ });
+ QObject::connect(reg, &QRemoteObjectRegistry::remoteObjectRemoved,
+ q, [this](const QRemoteObjectSourceLocation &location) {
+ onRemoteObjectSourceRemoved(location);
+ });
+}
+
+QVariant QRemoteObjectNodePrivate::handlePointerToQObjectProperty(QConnectedReplicaImplementation *rep, int index, const QVariant &property)
+{
+ Q_Q(QRemoteObjectNode);
+ using namespace QRemoteObjectPackets;
+
+ QVariant retval;
+
+ Q_ASSERT(property.canConvert<QRO_>());
+ QRO_ childInfo = property.value<QRO_>();
+ qROPrivDebug() << "QRO_:" << childInfo.name << replicas.contains(childInfo.name) << replicas.keys();
+ if (childInfo.isNull) {
+ // Either the source has changed the pointer and we need to update it, or the source pointer is a nullptr
+ if (replicas.contains(childInfo.name))
+ replicas.remove(childInfo.name);
+ if (childInfo.type == ObjectType::CLASS)
+ retval = QVariant::fromValue<QRemoteObjectDynamicReplica*>(nullptr);
+ else
+ retval = QVariant::fromValue<QAbstractItemModelReplica*>(nullptr);
+ return retval;
+ }
+
+ const bool newReplica = !replicas.contains(childInfo.name) || rep->isInitialized();
+ if (newReplica) {
+ if (rep->isInitialized()) {
+ auto childRep = qSharedPointerCast<QConnectedReplicaImplementation>(replicas.take(childInfo.name));
+ if (childRep && !childRep->isShortCircuit()) {
+ qCDebug(QT_REMOTEOBJECT) << "Checking if dynamic type should be added to dynamicTypeManager (type =" << childRep->m_metaObject->className() << ")";
+ dynamicTypeManager.addFromMetaObject(childRep->m_metaObject);
+ }
+ }
+ if (childInfo.type == ObjectType::CLASS)
+ retval = QVariant::fromValue(q->acquireDynamic(childInfo.name));
+ else
+ retval = QVariant::fromValue(q->acquireModel(childInfo.name));
+ } else //We are receiving the initial data for the QObject
+ retval = rep->getProperty(index); //Use existing value so changed signal isn't emitted
+
+ QSharedPointer<QConnectedReplicaImplementation> childRep = qSharedPointerCast<QConnectedReplicaImplementation>(replicas.value(childInfo.name).toStrongRef());
+ if (childRep->connectionToSource.isNull())
+ childRep->connectionToSource = rep->connectionToSource;
+ QVariantList parameters;
+ QDataStream ds(childInfo.parameters);
+ if (childRep->needsDynamicInitialization()) {
+ if (childInfo.classDefinition.isEmpty()) {
+ auto typeName = childInfo.typeName;
+ if (typeName == QLatin1String("QObject")) {
+ // The sender would have included the class name if needed
+ // So the acquire must have been templated, and we have the typeName
+ typeName = QString::fromLatin1(rep->getProperty(index).typeName());
+ if (typeName.endsWith(QLatin1String("Replica*")))
+ typeName.chop(8);
+ }
+ childRep->setDynamicMetaObject(dynamicTypeManager.metaObjectForType(typeName));
+ } else {
+ QDataStream in(childInfo.classDefinition);
+ childRep->setDynamicMetaObject(dynamicTypeManager.addDynamicType(rep->connectionToSource, in));
+ }
+ if (!childInfo.parameters.isEmpty())
+ ds >> parameters;
+ handlePointerToQObjectProperties(childRep.data(), parameters);
+ childRep->setDynamicProperties(std::move(parameters));
+ } else {
+ if (!childInfo.parameters.isEmpty())
+ ds >> parameters;
+ handlePointerToQObjectProperties(childRep.data(), parameters);
+ childRep->initialize(std::move(parameters));
+ }
+
+ return retval;
+}
+
+void QRemoteObjectNodePrivate::handlePointerToQObjectProperties(QConnectedReplicaImplementation *rep, QVariantList &properties)
+{
+ for (const int index : rep->childIndices())
+ properties[index] = handlePointerToQObjectProperty(rep, index, properties.at(index));
+}
+
+/*!
+ Blocks until this Node's \l Registry is initialized or \a timeout (in
+ milliseconds) expires. Returns \c true if the \l Registry is successfully
+ initialized upon return, or \c false otherwise.
+*/
+bool QRemoteObjectNode::waitForRegistry(int timeout)
+{
+ Q_D(QRemoteObjectNode);
+ if (!d->registry) {
+ qCWarning(QT_REMOTEOBJECT) << qPrintable(objectName()) << "waitForRegistry() error: No valid registry url set";
+ return false;
+ }
+
+ return d->registry->waitForSource(timeout);
+}
+
+/*!
+ Connects a client node to the host node at \a address.
+
+ Connections will remain valid until the host node is deleted or no longer
+ accessible over a network.
+
+ Once a client is connected to a host, valid Replicas can then be acquired
+ if the corresponding Source is being remoted.
+
+ Return \c true on success, \c false otherwise (usually an unrecognized url,
+ or connecting to already connected address).
+*/
+bool QRemoteObjectNode::connectToNode(const QUrl &address)
+{
+ Q_D(QRemoteObjectNode);
+ if (!d->initConnection(address)) {
+ d->setLastError(RegistryNotAcquired);
+ return false;
+ }
+ return true;
+}
+
+/*!
+ \since 5.12
+
+ In order to \l QRemoteObjectNode::acquire() \l Replica objects over \l
+ {External QIODevices}, Qt Remote Objects needs access to the communications
+ channel (a \l QIODevice) between the respective nodes. It is the
+ addClientSideConnection() call that enables this, taking the \a ioDevice as
+ input. Any acquire() call made without calling addClientSideConnection will
+ still work, but the Node will not be able to initialize the \l Replica
+ without being provided the connection to the Host node.
+
+ \sa {QRemoteObjectHostBase::addHostSideConnection}
+*/
+void QRemoteObjectNode::addClientSideConnection(QIODevice *ioDevice)
+{
+ Q_D(QRemoteObjectNode);
+ if (!ioDevice || !ioDevice->isOpen()) {
+ qWarning() << "A null or closed QIODevice was passed to addClientSideConnection(). Ignoring.";
+ return;
+ }
+ QtROExternalIoDevice *device = new QtROExternalIoDevice(ioDevice, this);
+ connect(device, &QtROIoDeviceBase::readyRead, this, [d, device]() {
+ d->onClientRead(device);
+ });
+ if (device->bytesAvailable())
+ d->onClientRead(device);
+}
+
+/*!
+ \fn void QRemoteObjectNode::remoteObjectAdded(const QRemoteObjectSourceLocation &loc)
+
+ This signal is emitted whenever a new \l {Source} object is added to
+ the Registry. The signal will not be emitted if there is no Registry set
+ (i.e., Sources over connections made via connectToNode directly). The \a
+ loc parameter contains the information about the added Source, including
+ name, type and the QUrl of the hosting Node.
+
+ \sa remoteObjectRemoved(), instances()
+*/
+
+/*!
+ \fn void QRemoteObjectNode::remoteObjectRemoved(const QRemoteObjectSourceLocation &loc)
+
+ This signal is emitted whenever a known \l {Source} object is
+ removed from the Registry. The signal will not be emitted if there is no
+ Registry set (i.e., Sources over connections made via connectToNode
+ directly). The \a loc parameter contains the information about the removed
+ Source, including name, type and the QUrl of the hosting Node.
+
+ \sa remoteObjectAdded, instances
+*/
+
+/*!
+ \fn QStringList QRemoteObjectNode::instances() const
+
+ This templated function (taking a \l repc generated type as the template parameter) will
+ return the list of names of every instance of that type on the Remote
+ Objects network. For example, if you have a Shape class defined in a .rep file,
+ and Circle and Square classes inherit from the Source definition, they can
+ be shared on the Remote Objects network using \l {QRemoteObjectHostBase::enableRemoting} {enableRemoting}.
+ \code
+ Square square;
+ Circle circle;
+ myHost.enableRemoting(&square, "Square");
+ myHost.enableRemoting(&circle, "Circle");
+ \endcode
+ Then instance can be used to find the available instances of Shape.
+ \code
+ QStringList instances = clientNode.instances<Shape>();
+ // will return a QStringList containing "Circle" and "Square"
+ auto instance1 = clientNode.acquire<Shape>("Circle");
+ auto instance2 = clientNode.acquire<Shape>("Square");
+ ...
+ \endcode
+*/
+
+/*!
+ \overload instances()
+
+ This convenience function provides the same result as the templated
+ version, but takes the name of the \l {Source} class as a parameter (\a
+ typeName) rather than deriving it from the class type.
+*/
+QStringList QRemoteObjectNode::instances(QStringView typeName) const
+{
+ Q_D(const QRemoteObjectNode);
+ QStringList names;
+ for (auto it = d->connectedSources.cbegin(), end = d->connectedSources.cend(); it != end; ++it) {
+ if (it.value().typeName == typeName) {
+ names << it.key();
+ }
+ }
+ return names;
+}
+
+/*!
+ \keyword dynamic acquire
+ Returns a QRemoteObjectDynamicReplica of the Source \a name.
+*/
+QRemoteObjectDynamicReplica *QRemoteObjectNode::acquireDynamic(const QString &name)
+{
+ return new QRemoteObjectDynamicReplica(this, name);
+}
+
+/*!
+ \qmlmethod bool Host::enableRemoting(object object, string name)
+ Enables a host node to dynamically provide remote access to the QObject \a
+ object. Client nodes connected to the node hosting this object may obtain
+ Replicas of this Source.
+
+ The optional \a name defines the lookup-name under which the QObject can be acquired
+ using \l QRemoteObjectNode::acquire() . If not explicitly set then the name
+ given in the QCLASSINFO_REMOTEOBJECT_TYPE will be used. If no such macro
+ was defined for the QObject then the \l QObject::objectName() is used.
+
+ Returns \c false if the current node is a client node, or if the QObject is already
+ registered to be remoted, and \c true if remoting is successfully enabled
+ for the dynamic QObject.
+
+ \sa disableRemoting()
+*/
+
+/*!
+ Enables a host node to dynamically provide remote access to the QObject \a
+ object. Client nodes connected to the node
+ hosting this object may obtain Replicas of this Source.
+
+ The optional \a name defines the lookup-name under which the QObject can be acquired
+ using \l QRemoteObjectNode::acquire() . If not explicitly set then the name
+ given in the QCLASSINFO_REMOTEOBJECT_TYPE will be used. If no such macro
+ was defined for the QObject then the \l QObject::objectName() is used.
+
+ Returns \c false if the current node is a client node, or if the QObject is already
+ registered to be remoted, and \c true if remoting is successfully enabled
+ for the dynamic QObject.
+
+ \sa disableRemoting()
+*/
+bool QRemoteObjectHostBase::enableRemoting(QObject *object, const QString &name)
+{
+ Q_D(QRemoteObjectHostBase);
+ if (!d->remoteObjectIo) {
+ d->setLastError(OperationNotValidOnClientNode);
+ return false;
+ }
+
+ const QMetaObject *meta = object->metaObject();
+ QString _name = name;
+ QString typeName = getTypeNameAndMetaobjectFromClassInfo(meta);
+ if (typeName.isEmpty()) { //This is a passed in QObject, use its API
+ if (_name.isEmpty()) {
+ _name = object->objectName();
+ if (_name.isEmpty()) {
+ d->setLastError(MissingObjectName);
+ qCWarning(QT_REMOTEOBJECT) << qPrintable(objectName()) << "enableRemoting() Error: Unable to Replicate an object that does not have objectName() set.";
+ return false;
+ }
+ }
+ } else if (_name.isEmpty())
+ _name = typeName;
+ return d->remoteObjectIo->enableRemoting(object, meta, _name, typeName);
+}
+
+/*!
+ This overload of enableRemoting() is specific to \l QAbstractItemModel types
+ (or any type derived from \l QAbstractItemModel). This is useful if you want
+ to have a model and the HMI for the model in different processes.
+
+ The three required parameters are the \a model itself, the \a name by which
+ to lookup the model, and the \a roles that should be exposed on the Replica
+ side. If you want to synchronize selection between \l Source and \l
+ Replica, the optional \a selectionModel parameter can be used. This is only
+ recommended when using a single Replica.
+
+ Behind the scenes, Qt Remote Objects batches data() lookups and prefetches
+ data when possible to make the model interaction as responsive as possible.
+
+ Returns \c false if the current node is a client node, or if the QObject is already
+ registered to be remoted, and \c true if remoting is successfully enabled
+ for the QAbstractItemModel.
+
+ \sa disableRemoting()
+ */
+bool QRemoteObjectHostBase::enableRemoting(QAbstractItemModel *model, const QString &name, const QList<int> roles, QItemSelectionModel *selectionModel)
+{
+ //This looks complicated, but hopefully there is a way to have an adapter be a template
+ //parameter and this makes sure that is supported.
+ QObject *adapter = QAbstractItemModelSourceAdapter::staticMetaObject.newInstance(Q_ARG(QAbstractItemModel*, model),
+ Q_ARG(QItemSelectionModel*, selectionModel),
+ Q_ARG(QList<int>, roles));
+ QAbstractItemAdapterSourceAPI<QAbstractItemModel, QAbstractItemModelSourceAdapter> *api =
+ new QAbstractItemAdapterSourceAPI<QAbstractItemModel, QAbstractItemModelSourceAdapter>(name);
+ if (!this->objectName().isEmpty())
+ adapter->setObjectName(this->objectName().append(QLatin1String("Adapter")));
+ return enableRemoting(model, api, adapter);
+}
+
+/*!
+ \fn template <template <typename> class ApiDefinition, typename ObjectType> bool QRemoteObjectHostBase::enableRemoting(ObjectType *object)
+
+ This templated function overload enables a host node to provide remote
+ access to a QObject \a object with a specified (and compile-time checked)
+ interface. Client nodes connected to the node hosting this object may
+ obtain Replicas of this Source.
+
+ This is best illustrated by example:
+ \code
+ #include "rep_TimeModel_source.h"
+ MinuteTimer timer;
+ hostNode.enableRemoting<MinuteTimerSourceAPI>(&timer);
+ \endcode
+
+ Here the MinuteTimerSourceAPI is the set of Signals/Slots/Properties
+ defined by the TimeModel.rep file. Compile time checks are made to verify
+ the input QObject can expose the requested API, it will fail to compile
+ otherwise. This allows a subset of \a object 's interface to be exposed,
+ and allows the types of conversions supported by Signal/Slot connections.
+
+ Returns \c false if the current node is a client node, or if the QObject is
+ already registered to be remoted, and \c true if remoting is successfully
+ enabled for the QObject.
+
+ \sa disableRemoting()
+*/
+
+/*!
+ \internal
+ Enables a host node to provide remote access to a QObject \a object
+ with the API defined by \a api. Client nodes connected to the node
+ hosting this object may obtain Replicas of this Source.
+
+ Returns \c false if the current node is a client node, or if the QObject is
+ already registered to be remoted, and \c true if remoting is successfully
+ enabled for the QObject.
+
+ \sa disableRemoting()
+*/
+bool QRemoteObjectHostBase::enableRemoting(QObject *object, const SourceApiMap *api, QObject *adapter)
+{
+ Q_D(QRemoteObjectHostBase);
+ return d->remoteObjectIo->enableRemoting(object, api, adapter);
+}
+
+/*!
+ \qmlmethod bool Host::disableRemoting(object remoteObject)
+ Disables remote access for the QObject \a remoteObject. Returns \c false if
+ the current node is a client node or if the \a remoteObject is not
+ registered, and returns \c true if remoting is successfully disabled for
+ the Source object.
+
+ \warning Replicas of this object will no longer be valid after calling this method.
+
+ \sa enableRemoting()
+*/
+
+/*!
+ Disables remote access for the QObject \a remoteObject. Returns \c false if
+ the current node is a client node or if the \a remoteObject is not
+ registered, and returns \c true if remoting is successfully disabled for
+ the Source object.
+
+ \warning Replicas of this object will no longer be valid after calling this method.
+
+ \sa enableRemoting()
+*/
+bool QRemoteObjectHostBase::disableRemoting(QObject *remoteObject)
+{
+ Q_D(QRemoteObjectHostBase);
+ if (!d->remoteObjectIo) {
+ d->setLastError(OperationNotValidOnClientNode);
+ return false;
+ }
+
+ if (!d->remoteObjectIo->disableRemoting(remoteObject)) {
+ d->setLastError(SourceNotRegistered);
+ return false;
+ }
+
+ return true;
+}
+
+/*!
+ \since 5.12
+
+ In order to \l QRemoteObjectHost::enableRemoting() \l Source objects over
+ \l {External QIODevices}, Qt Remote Objects needs access to the
+ communications channel (a \l QIODevice) between the respective nodes. It is
+ the addHostSideConnection() call that enables this on the \l Source side,
+ taking the \a ioDevice as input. Any enableRemoting() call will still work
+ without calling addHostSideConnection, but the Node will not be able to
+ share the \l Source objects without being provided the connection to
+ the Replica node. Before calling this function you must call
+ \l {QRemoteObjectHost::}{setHostUrl}() with a unique URL and
+ \l {QRemoteObjectHost::}{AllowExternalRegistration}.
+
+ \sa addClientSideConnection
+*/
+void QRemoteObjectHostBase::addHostSideConnection(QIODevice *ioDevice)
+{
+ Q_D(QRemoteObjectHostBase);
+ if (!ioDevice || !ioDevice->isOpen()) {
+ qWarning() << "A null or closed QIODevice was passed to addHostSideConnection(). Ignoring.";
+ return;
+ }
+ if (!d->remoteObjectIo)
+ d->remoteObjectIo = new QRemoteObjectSourceIo(this);
+ QtROExternalIoDevice *device = new QtROExternalIoDevice(ioDevice, this);
+ return d->remoteObjectIo->newConnection(device);
+}
+
+/*!
+ Returns a pointer to a \l Replica which is specifically derived from \l
+ QAbstractItemModel. The \a name provided must match the name used with the
+ matching \l {QRemoteObjectHostBase::}{enableRemoting} that put
+ the \l Model on the network. \a action specifies whether the model should
+ fetch data before the \l {QRemoteObjectReplica::}{initialized} signal is
+ emitted. If it's set to QtRemoteObjects::PrefetchData, then the data for
+ roles in the \a rolesHint will be prefetched. If \a rolesHint is empty, then
+ the data for all the roles exposed by \l Source will be prefetched.
+
+ The returned model will be empty until it is initialized with the \l Source.
+*/
+QAbstractItemModelReplica *QRemoteObjectNode::acquireModel(const QString &name, QtRemoteObjects::InitialAction action, const QList<int> &rolesHint)
+{
+ QAbstractItemModelReplicaImplementation *rep = acquire<QAbstractItemModelReplicaImplementation>(name);
+ return new QAbstractItemModelReplica(rep, action, rolesHint);
+}
+
+QRemoteObjectHostBasePrivate::QRemoteObjectHostBasePrivate()
+ : QRemoteObjectNodePrivate()
+ , remoteObjectIo(nullptr)
+{ }
+
+QRemoteObjectHostBasePrivate::~QRemoteObjectHostBasePrivate()
+{ }
+
+QRemoteObjectHostPrivate::QRemoteObjectHostPrivate()
+ : QRemoteObjectHostBasePrivate()
+{ }
+
+QRemoteObjectHostPrivate::~QRemoteObjectHostPrivate()
+{ }
+
+QRemoteObjectRegistryHostPrivate::QRemoteObjectRegistryHostPrivate()
+ : QRemoteObjectHostBasePrivate()
+ , registrySource(nullptr)
+{ }
+
+QRemoteObjectRegistryHostPrivate::~QRemoteObjectRegistryHostPrivate()
+{ }
+
+ProxyInfo::ProxyInfo(QRemoteObjectNode *node, QRemoteObjectHostBase *parent,
+ QRemoteObjectHostBase::RemoteObjectNameFilter filter)
+ : QObject(parent)
+ , proxyNode(node)
+ , parentNode(parent)
+ , proxyFilter(filter)
+{
+ const auto registry = node->registry();
+ proxyNode->setObjectName(QLatin1String("_ProxyNode"));
+
+ connect(registry, &QRemoteObjectRegistry::remoteObjectAdded, this,
+ [this](const QRemoteObjectSourceLocation &entry)
+ {
+ this->proxyObject(entry, ProxyDirection::Forward);
+ });
+ connect(registry, &QRemoteObjectRegistry::remoteObjectRemoved, this,
+ &ProxyInfo::unproxyObject);
+ connect(registry, &QRemoteObjectRegistry::initialized, this, [registry, this]() {
+ QRemoteObjectSourceLocations locations = registry->sourceLocations();
+ QRemoteObjectSourceLocations::const_iterator i = locations.constBegin();
+ while (i != locations.constEnd()) {
+ proxyObject(QRemoteObjectSourceLocation(i.key(), i.value()));
+ ++i;
+ }
+ });
+
+ connect(registry, &QRemoteObjectRegistry::stateChanged, this,
+ [this](QRemoteObjectRegistry::State state, QRemoteObjectRegistry::State /*oldState*/) {
+ if (state != QRemoteObjectRegistry::Suspect)
+ return;
+ // unproxy all objects
+ for (ProxyReplicaInfo* info : proxiedReplicas)
+ disableAndDeleteObject(info);
+ proxiedReplicas.clear();
+ });
+}
+
+ProxyInfo::~ProxyInfo() {
+ for (ProxyReplicaInfo* info : proxiedReplicas)
+ delete info;
+ delete proxyNode;
+}
+
+bool ProxyInfo::setReverseProxy(QRemoteObjectHostBase::RemoteObjectNameFilter filter)
+{
+ if (qobject_cast<QRemoteObjectRegistryHost *>(parentNode) == nullptr) {
+ qWarning() << "Setting up reverseProxy() can only be done on a Registry node.";
+ return false;
+ }
+ const auto registry = parentNode->registry();
+ this->reverseFilter = filter;
+
+ connect(registry, &QRemoteObjectRegistry::remoteObjectAdded, this,
+ [this](const QRemoteObjectSourceLocation &entry)
+ {
+ this->proxyObject(entry, ProxyDirection::Reverse);
+ });
+ connect(registry, &QRemoteObjectRegistry::remoteObjectRemoved, this,
+ &ProxyInfo::unproxyObject);
+ connect(registry, &QRemoteObjectRegistry::initialized, this, [registry, this]() {
+ QRemoteObjectSourceLocations locations = registry->sourceLocations();
+ QRemoteObjectSourceLocations::const_iterator i = locations.constBegin();
+ while (i != locations.constEnd()) {
+ proxyObject(QRemoteObjectSourceLocation(i.key(), i.value()), ProxyDirection::Reverse);
+ ++i;
+ }
+ });
+
+ return true;
+}
+
+void ProxyInfo::proxyObject(const QRemoteObjectSourceLocation &entry, ProxyDirection direction)
+{
+ const QString name = entry.first;
+ const QString typeName = entry.second.typeName;
+
+ if (direction == ProxyDirection::Forward) {
+ // If we are using the reverse proxy, this can be called when reverse proxy objects are added
+ // Don't try to proxy those back. We can detect this because the hosting node will be our proxyNode.
+ auto host = qobject_cast<QRemoteObjectHost *>(proxyNode);
+ if (host && entry.second.hostUrl == host->hostUrl())
+ return;
+ if (!proxyFilter(name, typeName))
+ return;
+ Q_ASSERT(!proxiedReplicas.contains(name));
+
+ qCDebug(QT_REMOTEOBJECT) << "Starting proxy for" << name << "from" << entry.second.hostUrl;
+
+ if (entry.second.typeName == QAIMADAPTER()) {
+ QAbstractItemModelReplica *rep = proxyNode->acquireModel(name);
+ proxiedReplicas.insert(name, new ProxyReplicaInfo{rep, direction});
+ connect(rep, &QAbstractItemModelReplica::initialized, this,
+ [rep, name, this]() { this->parentNode->enableRemoting(rep, name, QList<int>()); });
+ } else {
+ QRemoteObjectDynamicReplica *rep = proxyNode->acquireDynamic(name);
+ proxiedReplicas.insert(name, new ProxyReplicaInfo{rep, direction});
+ connect(rep, &QRemoteObjectDynamicReplica::initialized, this,
+ [rep, name, this]() { this->parentNode->enableRemoting(rep, name); });
+ }
+ } else {
+ // If we are using the reverse proxy, this can be called when proxy objects are added
+ // Don't try to proxy those back. We can detect this because the hosting node will be the parentNode.
+ // Since we know the parentNode has to be a RegistryNode for reverse proxy to work, we compare against
+ // the registryUrl().
+ if (entry.second.hostUrl == parentNode->registryUrl())
+ return;
+ if (!reverseFilter(name, typeName))
+ return;
+ Q_ASSERT(!proxiedReplicas.contains(name));
+
+ qCDebug(QT_REMOTEOBJECT) << "Starting reverse proxy for" << name << "from" << entry.second.hostUrl;
+
+ if (entry.second.typeName == QAIMADAPTER()) {
+ QAbstractItemModelReplica *rep = this->parentNode->acquireModel(name);
+ proxiedReplicas.insert(name, new ProxyReplicaInfo{rep, direction});
+ connect(rep, &QAbstractItemModelReplica::initialized, this,
+ [rep, name, this]()
+ {
+ QRemoteObjectHostBase *host = qobject_cast<QRemoteObjectHostBase *>(this->proxyNode);
+ Q_ASSERT(host);
+ host->enableRemoting(rep, name, QList<int>());
+ });
+ } else {
+ QRemoteObjectDynamicReplica *rep = this->parentNode->acquireDynamic(name);
+ proxiedReplicas.insert(name, new ProxyReplicaInfo{rep, direction});
+ connect(rep, &QRemoteObjectDynamicReplica::initialized, this,
+ [rep, name, this]()
+ {
+ QRemoteObjectHostBase *host = qobject_cast<QRemoteObjectHostBase *>(this->proxyNode);
+ Q_ASSERT(host);
+ host->enableRemoting(rep, name);
+ });
+ }
+ }
+
+}
+
+void ProxyInfo::unproxyObject(const QRemoteObjectSourceLocation &entry)
+{
+ const QString name = entry.first;
+
+ if (proxiedReplicas.contains(name)) {
+ qCDebug(QT_REMOTEOBJECT) << "Stopping proxy for" << name;
+ auto const info = proxiedReplicas.take(name);
+ disableAndDeleteObject(info);
+ }
+}
+
+void ProxyInfo::disableAndDeleteObject(ProxyReplicaInfo* info)
+{
+ if (info->direction == ProxyDirection::Forward)
+ this->parentNode->disableRemoting(info->replica);
+ else {
+ QRemoteObjectHostBase *host = qobject_cast<QRemoteObjectHostBase *>(this->proxyNode);
+ Q_ASSERT(host);
+ host->disableRemoting(info->replica);
+ }
+ delete info;
+}
+
+
+QT_END_NAMESPACE
+
+#include "moc_qremoteobjectnode.cpp"
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QREMOTEOBJECTNODE_H
+#define QREMOTEOBJECTNODE_H
+
+#include <QtCore/qsharedpointer.h>
+#include <QtCore/qmetaobject.h>
+#include <QtRemoteObjects/qtremoteobjectglobal.h>
+#include <QtRemoteObjects/qremoteobjectregistry.h>
+#include <QtRemoteObjects/qremoteobjectdynamicreplica.h>
+
+#include <functional>
+
+QT_BEGIN_NAMESPACE
+
+class QRemoteObjectReplica;
+class SourceApiMap;
+class QAbstractItemModel;
+class QAbstractItemModelReplica;
+class QItemSelectionModel;
+class QRemoteObjectAbstractPersistedStorePrivate;
+class QRemoteObjectNodePrivate;
+class QRemoteObjectHostBasePrivate;
+class QRemoteObjectHostPrivate;
+class QRemoteObjectRegistryHostPrivate;
+
+class Q_REMOTEOBJECTS_EXPORT QRemoteObjectAbstractPersistedStore : public QObject
+{
+ Q_OBJECT
+
+public:
+ QRemoteObjectAbstractPersistedStore (QObject *parent = nullptr);
+ virtual ~QRemoteObjectAbstractPersistedStore();
+
+ virtual void saveProperties(const QString &repName, const QByteArray &repSig, const QVariantList &values) = 0;
+ virtual QVariantList restoreProperties(const QString &repName, const QByteArray &repSig) = 0;
+
+protected:
+ QRemoteObjectAbstractPersistedStore(QRemoteObjectAbstractPersistedStorePrivate &, QObject *parent);
+ Q_DECLARE_PRIVATE(QRemoteObjectAbstractPersistedStore)
+};
+
+class Q_REMOTEOBJECTS_EXPORT QRemoteObjectNode : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QUrl registryUrl READ registryUrl WRITE setRegistryUrl)
+ Q_PROPERTY(QRemoteObjectAbstractPersistedStore* persistedStore READ persistedStore WRITE setPersistedStore)
+ Q_PROPERTY(int heartbeatInterval READ heartbeatInterval WRITE setHeartbeatInterval NOTIFY heartbeatIntervalChanged)
+
+public:
+ enum ErrorCode{
+ NoError,
+ RegistryNotAcquired,
+ RegistryAlreadyHosted,
+ NodeIsNoServer,
+ ServerAlreadyCreated,
+ UnintendedRegistryHosting,
+ OperationNotValidOnClientNode,
+ SourceNotRegistered,
+ MissingObjectName,
+ HostUrlInvalid,
+ ProtocolMismatch,
+ ListenFailed
+ };
+ Q_ENUM(ErrorCode)
+
+ QRemoteObjectNode(QObject *parent = nullptr);
+ QRemoteObjectNode(const QUrl ®istryAddress, QObject *parent = nullptr);
+ ~QRemoteObjectNode() override;
+
+ Q_INVOKABLE bool connectToNode(const QUrl &address);
+ void addClientSideConnection(QIODevice *ioDevice);
+ virtual void setName(const QString &name);
+ template < class ObjectType >
+ ObjectType *acquire(const QString &name = QString())
+ {
+ return new ObjectType(this, name);
+ }
+
+ template<typename T>
+ QStringList instances() const
+ {
+ const QMetaObject *mobj = &T::staticMetaObject;
+ const int index = mobj->indexOfClassInfo(QCLASSINFO_REMOTEOBJECT_TYPE);
+ if (index == -1)
+ return QStringList();
+
+ const QString typeName = QString::fromLatin1(mobj->classInfo(index).value());
+ return instances(typeName);
+ }
+ QStringList instances(QStringView typeName) const;
+
+ QRemoteObjectDynamicReplica *acquireDynamic(const QString &name);
+ QAbstractItemModelReplica *acquireModel(const QString &name, QtRemoteObjects::InitialAction action = QtRemoteObjects::FetchRootSize, const QList<int> &rolesHint = {});
+ QUrl registryUrl() const;
+ virtual bool setRegistryUrl(const QUrl ®istryAddress);
+ bool waitForRegistry(int timeout = 30000);
+ const QRemoteObjectRegistry *registry() const;
+
+ QRemoteObjectAbstractPersistedStore *persistedStore() const;
+ void setPersistedStore(QRemoteObjectAbstractPersistedStore *persistedStore);
+
+ ErrorCode lastError() const;
+
+ int heartbeatInterval() const;
+ void setHeartbeatInterval(int interval);
+
+ typedef std::function<void (QUrl)> RemoteObjectSchemaHandler;
+ void registerExternalSchema(const QString &schema, RemoteObjectSchemaHandler handler);
+
+Q_SIGNALS:
+ void remoteObjectAdded(const QRemoteObjectSourceLocation &);
+ void remoteObjectRemoved(const QRemoteObjectSourceLocation &);
+
+ void error(QRemoteObjectNode::ErrorCode errorCode);
+ void heartbeatIntervalChanged(int heartbeatInterval);
+
+protected:
+ QRemoteObjectNode(QRemoteObjectNodePrivate &, QObject *parent);
+
+ void timerEvent(QTimerEvent*) override;
+
+private:
+ void initializeReplica(QRemoteObjectReplica *instance, const QString &name = QString());
+ void persistProperties(const QString &repName, const QByteArray &repSig, const QVariantList &props);
+ QVariantList retrieveProperties(const QString &repName, const QByteArray &repSig);
+
+ Q_DECLARE_PRIVATE(QRemoteObjectNode)
+ friend class QRemoteObjectReplica;
+ friend class QConnectedReplicaImplementation;
+};
+
+class Q_REMOTEOBJECTS_EXPORT QRemoteObjectHostBase : public QRemoteObjectNode
+{
+ Q_OBJECT
+public:
+ enum AllowedSchemas { BuiltInSchemasOnly, AllowExternalRegistration };
+ Q_ENUM(AllowedSchemas)
+ ~QRemoteObjectHostBase() override;
+ void setName(const QString &name) override;
+
+ template <template <typename> class ApiDefinition, typename ObjectType>
+ bool enableRemoting(ObjectType *object)
+ {
+ ApiDefinition<ObjectType> *api = new ApiDefinition<ObjectType>(object);
+ return enableRemoting(object, api);
+ }
+ Q_INVOKABLE bool enableRemoting(QObject *object, const QString &name = QString());
+ bool enableRemoting(QAbstractItemModel *model, const QString &name, const QList<int> roles, QItemSelectionModel *selectionModel = nullptr);
+ Q_INVOKABLE bool disableRemoting(QObject *remoteObject);
+ void addHostSideConnection(QIODevice *ioDevice);
+
+ typedef std::function<bool(QStringView, QStringView)> RemoteObjectNameFilter;
+ bool proxy(const QUrl ®istryUrl, const QUrl &hostUrl={},
+ RemoteObjectNameFilter filter=[](QStringView, QStringView) {return true; });
+ // TODO: Currently the reverse aspect requires the registry, so this is supported only for
+ // QRemoteObjectRegistryHost for now. Consider enabling it also for QRemoteObjectHost.
+ bool reverseProxy(RemoteObjectNameFilter filter=[](QStringView, QStringView) {return true; });
+
+protected:
+ virtual QUrl hostUrl() const;
+ virtual bool setHostUrl(const QUrl &hostAddress, AllowedSchemas allowedSchemas=BuiltInSchemasOnly);
+ QRemoteObjectHostBase(QRemoteObjectHostBasePrivate &, QObject *);
+
+private:
+ bool enableRemoting(QObject *object, const SourceApiMap *, QObject *adapter = nullptr);
+ Q_DECLARE_PRIVATE(QRemoteObjectHostBase)
+};
+
+class Q_REMOTEOBJECTS_EXPORT QRemoteObjectHost : public QRemoteObjectHostBase
+{
+ Q_OBJECT
+ Q_PROPERTY(QUrl hostUrl READ hostUrl WRITE setHostUrl NOTIFY hostUrlChanged)
+
+public:
+ QRemoteObjectHost(QObject *parent = nullptr);
+ QRemoteObjectHost(const QUrl &address, const QUrl ®istryAddress = QUrl(),
+ AllowedSchemas allowedSchemas=BuiltInSchemasOnly, QObject *parent = nullptr);
+ QRemoteObjectHost(const QUrl &address, QObject *parent);
+ ~QRemoteObjectHost() override;
+ QUrl hostUrl() const override;
+ bool setHostUrl(const QUrl &hostAddress, AllowedSchemas allowedSchemas=BuiltInSchemasOnly) override;
+
+Q_SIGNALS:
+ void hostUrlChanged();
+
+protected:
+ QRemoteObjectHost(QRemoteObjectHostPrivate &, QObject *);
+
+private:
+ Q_DECLARE_PRIVATE(QRemoteObjectHost)
+};
+
+class Q_REMOTEOBJECTS_EXPORT QRemoteObjectRegistryHost : public QRemoteObjectHostBase
+{
+ Q_OBJECT
+public:
+ QRemoteObjectRegistryHost(const QUrl ®istryAddress = QUrl(), QObject *parent = nullptr);
+ ~QRemoteObjectRegistryHost() override;
+ bool setRegistryUrl(const QUrl ®istryUrl) override;
+
+protected:
+ QRemoteObjectRegistryHost(QRemoteObjectRegistryHostPrivate &, QObject *);
+
+private:
+ Q_DECLARE_PRIVATE(QRemoteObjectRegistryHost)
+};
+
+QT_END_NAMESPACE
+
+#endif
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QREMOTEOBJECTNODE_P_H
+#define QREMOTEOBJECTNODE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/private/qobject_p.h>
+#include "qremoteobjectsourceio_p.h"
+#include "qremoteobjectreplica.h"
+#include "qremoteobjectnode.h"
+
+#include <QtCore/qbasictimer.h>
+#include <QtCore/qmutex.h>
+
+QT_BEGIN_NAMESPACE
+
+#define qRODebug(x) qCDebug(QT_REMOTEOBJECT) << qPrintable(QtPrivate::deref_for_methodcall(x).objectName())
+#define qROWarning(x) qCWarning(QT_REMOTEOBJECT) << qPrintable(QtPrivate::deref_for_methodcall(x).objectName())
+#define qROCritical(x) qCCritical(QT_REMOTEOBJECT) << qPrintable(QtPrivate::deref_for_methodcall(x).objectName())
+#define qROFatal(x) qCFatal(QT_REMOTEOBJECT) << qPrintable(QtPrivate::deref_for_methodcall(x).objectName())
+#define qROPrivDebug() qCDebug(QT_REMOTEOBJECT) << qPrintable(q_ptr->objectName())
+#define qROPrivWarning() qCWarning(QT_REMOTEOBJECT) << qPrintable(q_ptr->objectName())
+#define qROPrivCritical() qCCritical(QT_REMOTEOBJECT) << qPrintable(q_ptr->objectName())
+#define qROPrivFatal() qCFatal(QT_REMOTEOBJECT) << qPrintable(q_ptr->objectName())
+
+class QRemoteObjectRegistry;
+class QRegistrySource;
+class QConnectedReplicaImplementation;
+
+class QRemoteObjectAbstractPersistedStorePrivate : public QObjectPrivate
+{
+public:
+ QRemoteObjectAbstractPersistedStorePrivate();
+ virtual ~QRemoteObjectAbstractPersistedStorePrivate();
+
+ Q_DECLARE_PUBLIC(QRemoteObjectAbstractPersistedStore)
+};
+
+class QRemoteObjectMetaObjectManager
+{
+public:
+ QRemoteObjectMetaObjectManager() {}
+ ~QRemoteObjectMetaObjectManager();
+
+ const QMetaObject *metaObjectForType(const QString &type);
+ QMetaObject *addDynamicType(QtROIoDeviceBase* connection, QDataStream &in);
+ void addFromMetaObject(const QMetaObject *);
+
+private:
+ QHash<QString, QMetaObject*> dynamicTypes;
+ QHash<QString, const QMetaObject*> staticTypes;
+ QHash<QtPrivate::QMetaTypeInterface *, QMetaType> enumsToBeAssignedMetaObject;
+ QHash<QMetaObject *, QList<QMetaType>> enumTypes;
+};
+
+struct ProxyReplicaInfo;
+class ProxyInfo : public QObject
+{
+ Q_OBJECT
+public:
+ ProxyInfo(QRemoteObjectNode *node, QRemoteObjectHostBase *parent, QRemoteObjectHostBase::RemoteObjectNameFilter filter);
+ ~ProxyInfo() override;
+ enum class ProxyDirection { Forward, Reverse };
+
+ bool setReverseProxy(QRemoteObjectHostBase::RemoteObjectNameFilter filter);
+ void proxyObject(const QRemoteObjectSourceLocation &entry, ProxyDirection direction = ProxyDirection::Forward);
+ void unproxyObject(const QRemoteObjectSourceLocation &entry);
+
+ QRemoteObjectNode *proxyNode;
+ QRemoteObjectHostBase *parentNode;
+ QRemoteObjectHostBase::RemoteObjectNameFilter proxyFilter;
+ QRemoteObjectHostBase::RemoteObjectNameFilter reverseFilter;
+ QHash<QString, ProxyReplicaInfo*> proxiedReplicas;
+
+private:
+ void disableAndDeleteObject(ProxyReplicaInfo* info);
+};
+
+struct ProxyReplicaInfo
+{
+ // We need QObject, so we can hold Dynamic Replicas and QAIM Adapters
+ QObject* replica;
+ ProxyInfo::ProxyDirection direction;
+ ~ProxyReplicaInfo() { delete replica; }
+};
+
+class QRemoteObjectNodePrivate : public QObjectPrivate
+{
+public:
+ QRemoteObjectNodePrivate();
+ ~QRemoteObjectNodePrivate() override;
+
+ virtual QRemoteObjectSourceLocations remoteObjectAddresses() const;
+
+ void setReplicaImplementation(const QMetaObject *, QRemoteObjectReplica *, const QString &);
+
+ void setLastError(QRemoteObjectNode::ErrorCode errorCode);
+
+ void connectReplica(QObject *object, QRemoteObjectReplica *instance);
+ void openConnectionIfNeeded(const QString &name);
+
+ bool initConnection(const QUrl &address);
+ bool hasInstance(const QString &name);
+ void setRegistry(QRemoteObjectRegistry *);
+ QVariant handlePointerToQObjectProperty(QConnectedReplicaImplementation *rep, int index, const QVariant &property);
+ void handlePointerToQObjectProperties(QConnectedReplicaImplementation *rep, QVariantList &properties);
+
+ void onClientRead(QObject *obj);
+ void onRemoteObjectSourceAdded(const QRemoteObjectSourceLocation &entry);
+ void onRemoteObjectSourceRemoved(const QRemoteObjectSourceLocation &entry);
+ void onRegistryInitialized();
+ void onShouldReconnect(QtROClientIoDevice *ioDevice);
+
+ virtual QReplicaImplementationInterface *handleNewAcquire(const QMetaObject *meta, QRemoteObjectReplica *instance, const QString &name);
+ void handleReplicaConnection(const QString &name);
+ void handleReplicaConnection(const QByteArray &sourceSignature, QConnectedReplicaImplementation *rep, QtROIoDeviceBase *connection);
+ void initialize();
+ bool setRegistryUrlNodeImpl(const QUrl ®istryAddr);
+
+private:
+ bool checkSignatures(const QByteArray &a, const QByteArray &b);
+
+public:
+ struct SourceInfo
+ {
+ QtROIoDeviceBase* device;
+ QString typeName;
+ QByteArray objectSignature;
+ };
+
+ QMutex mutex;
+ QUrl registryAddress;
+ QHash<QString, QWeakPointer<QReplicaImplementationInterface> > replicas;
+ QMap<QString, SourceInfo> connectedSources;
+ QMap<QString, QRemoteObjectNode::RemoteObjectSchemaHandler> schemaHandlers;
+ QSet<QtROClientIoDevice*> pendingReconnect;
+ QSet<QUrl> requestedUrls;
+ QRemoteObjectRegistry *registry;
+ int retryInterval;
+ QBasicTimer reconnectTimer;
+ QRemoteObjectNode::ErrorCode lastError;
+ QString rxName;
+ QRemoteObjectPackets::ObjectInfoList rxObjects;
+ QVariantList rxArgs;
+ QVariant rxValue;
+ QRemoteObjectAbstractPersistedStore *persistedStore;
+ int m_heartbeatInterval = 0;
+ QRemoteObjectMetaObjectManager dynamicTypeManager;
+ Q_DECLARE_PUBLIC(QRemoteObjectNode)
+};
+
+class QRemoteObjectHostBasePrivate : public QRemoteObjectNodePrivate
+{
+public:
+ QRemoteObjectHostBasePrivate();
+ ~QRemoteObjectHostBasePrivate() override;
+ QReplicaImplementationInterface *handleNewAcquire(const QMetaObject *meta, QRemoteObjectReplica *instance, const QString &name) override;
+
+ bool setHostUrlBaseImpl(const QUrl &hostAddress,
+ QRemoteObjectHostBase::AllowedSchemas allowedSchemas =
+ QRemoteObjectHostBase::BuiltInSchemasOnly);
+
+public:
+ QRemoteObjectSourceIo *remoteObjectIo;
+ ProxyInfo *proxyInfo = nullptr;
+ Q_DECLARE_PUBLIC(QRemoteObjectHostBase);
+};
+
+class QRemoteObjectHostPrivate : public QRemoteObjectHostBasePrivate
+{
+public:
+ QRemoteObjectHostPrivate();
+ ~QRemoteObjectHostPrivate() override;
+
+ bool setHostUrlHostImpl(const QUrl &hostAddress,
+ QRemoteObjectHostBase::AllowedSchemas allowedSchemas =
+ QRemoteObjectHostBase::BuiltInSchemasOnly);
+
+ Q_DECLARE_PUBLIC(QRemoteObjectHost);
+};
+
+class QRemoteObjectRegistryHostPrivate : public QRemoteObjectHostBasePrivate
+{
+public:
+ QRemoteObjectRegistryHostPrivate();
+ ~QRemoteObjectRegistryHostPrivate() override;
+ QRemoteObjectSourceLocations remoteObjectAddresses() const override;
+ QRegistrySource *registrySource;
+
+ bool setRegistryUrlRegistryHostImpl(const QUrl ®istryUrl);
+
+ Q_DECLARE_PUBLIC(QRemoteObjectRegistryHost);
+};
+
+QT_END_NAMESPACE
+
+#endif
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <QtCore/qabstractitemmodel.h>
+#include <QtCore/qbytearrayview.h>
+
+#include "qremoteobjectcontainers_p.h"
+#include "qremoteobjectpendingcall.h"
+#include "qremoteobjectsource.h"
+#include "qremoteobjectsource_p.h"
+#include "qremoteobjectpacket_p.h"
+#include "qconnectionfactories.h"
+#include "qconnectionfactories_p.h"
+#include <cstring>
+
+//#define QTRO_VERBOSE_PROTOCOL
+QT_BEGIN_NAMESPACE
+
+
+// Add methods so we can use QMetaEnum in a set
+// Note for both functions we are skipping string comparisons/hashes. Since the
+// metaObjects are the same, we can just use the address of the string.
+inline bool operator==(const QMetaEnum e1, const QMetaEnum e2)
+{
+ return e1.enclosingMetaObject() == e2.enclosingMetaObject()
+ && e1.name() == e2.name()
+ && e1.enumName() == e2.enumName()
+ && e1.scope() == e2.scope();
+}
+
+inline size_t qHash(const QMetaEnum &key, size_t seed=0) Q_DECL_NOTHROW
+{
+ return qHash(key.enclosingMetaObject(), seed) ^ qHash(static_cast<const void *>(key.name()), seed)
+ ^ qHash(static_cast<const void *>(key.enumName()), seed) ^ qHash(static_cast<const void *>(key.scope()), seed);
+}
+
+static bool isSequentialGadgetType(QMetaType metaType)
+{
+ if (QMetaType::canConvert(metaType, QMetaType::fromType<QSequentialIterable>())) {
+ static QHash<int, bool> lookup;
+ if (!lookup.contains(metaType.id())) {
+ auto stubVariant = QVariant(metaType, nullptr);
+ auto asIterable = stubVariant.value<QSequentialIterable>();
+ auto valueMetaType = asIterable.metaContainer().valueMetaType();
+ lookup[metaType.id()] = valueMetaType.flags().testFlag(QMetaType::IsGadget);
+ }
+ return lookup[metaType.id()];
+ }
+ return false;
+}
+
+static bool isAssociativeGadgetType(QMetaType metaType)
+{
+ if (QMetaType::canConvert(metaType, QMetaType::fromType<QAssociativeIterable>())) {
+ static QHash<int, bool> lookup;
+ if (!lookup.contains(metaType.id())) {
+ auto stubVariant = QVariant(metaType, nullptr);
+ auto asIterable = stubVariant.value<QAssociativeIterable>();
+ auto valueMetaType = asIterable.metaContainer().mappedMetaType();
+ lookup[metaType.id()] = valueMetaType.flags().testFlag(QMetaType::IsGadget);
+ }
+ return lookup[metaType.id()];
+ }
+ return false;
+}
+
+using namespace QtRemoteObjects;
+
+namespace QRemoteObjectPackets {
+
+QMetaType transferTypeForEnum(QMetaType enumType)
+{
+ const auto size = enumType.sizeOf();
+ switch (size) {
+ case 1: return QMetaType::fromType<qint8>();
+ case 2: return QMetaType::fromType<qint16>();
+ case 4: return QMetaType::fromType<qint32>();
+ // Qt currently only supports enum values of 4 or less bytes (QMetaEnum value(index) returns int)
+// case 8: args.push_back(QVariant(QMetaType::Int, argv[i + 1])); break;
+ default:
+ qCWarning(QT_REMOTEOBJECT_IO) << "Invalid enum detected (Dynamic Replica)" << enumType.name() << "with size" << size;
+ return QMetaType::fromType<qint32>();
+ }
+}
+
+// QDataStream sends QVariants of custom types by sending their typename, allowing decode
+// on the receiving side. For QtRO and enums, this won't work, as the enums have different
+// scopes. E.g., the examples have ParentClassSource::MyEnum and ParentClassReplica::MyEnum.
+// Dynamic types will be created as ParentClass::MyEnum. So instead, we change the variants
+// to integers (encodeVariant) when sending them. On the receive side, the we know the
+// types of properties and the signatures for methods, so we can use that information to
+// decode the integer variant into an enum variant (via decodeVariant).
+QVariant encodeVariant(const QVariant &value)
+{
+ const auto metaType = value.metaType();
+ if (metaType.flags().testFlag(QMetaType::IsEnumeration)) {
+ auto converted = QVariant(value);
+ auto transferType = transferTypeForEnum(metaType);
+ converted.convert(transferType);
+#ifdef QTRO_VERBOSE_PROTOCOL
+ qDebug() << "Converting from enum to integer type" << transferType.sizeOf() << converted << value;
+#endif
+ return converted;
+ }
+ if (isSequentialGadgetType(metaType)) { // Doesn't include QtROSequentialContainer
+ // TODO Way to create the QVariant without copying the QSQ_?
+ QSQ_ sequence(value);
+#ifdef QTRO_VERBOSE_PROTOCOL
+ qDebug() << "Encoding sequential container" << metaType.name() << "to QSQ_ to transmit";
+#endif
+ return QVariant::fromValue<QSQ_>(sequence);
+ }
+ if (metaType == QMetaType::fromType<QtROSequentialContainer>()) {
+ QSQ_ sequence(value);
+#ifdef QTRO_VERBOSE_PROTOCOL
+ qDebug() << "Encoding QtROSequentialContainer container to QSQ_ to transmit";
+#endif
+ return QVariant::fromValue<QSQ_>(sequence);
+ }
+ if (isAssociativeGadgetType(metaType)) { // Doesn't include QtROAssociativeContainer
+ QAS_ map(value);
+#ifdef QTRO_VERBOSE_PROTOCOL
+ qDebug() << "Encoding associative container" << metaType.name() << "to QAS_ to transmit";
+#endif
+ return QVariant::fromValue<QAS_>(map);
+ }
+ if (metaType == QMetaType::fromType<QtROAssociativeContainer>()) {
+ QAS_ map(value);
+#ifdef QTRO_VERBOSE_PROTOCOL
+ qDebug() << "Encoding QtROAssociativeContainer container to QAS_ to transmit";
+#endif
+ return QVariant::fromValue<QAS_>(map);
+ }
+ return value;
+}
+
+QVariant decodeVariant(QVariant &&value, QMetaType metaType)
+{
+ if (metaType.flags().testFlag(QMetaType::IsEnumeration)) {
+#ifdef QTRO_VERBOSE_PROTOCOL
+ QVariant encoded(value);
+#endif
+ value.convert(metaType);
+#ifdef QTRO_VERBOSE_PROTOCOL
+ qDebug() << "Converting to enum from integer type" << value << encoded;
+#endif
+ } else if (value.metaType() == QMetaType::fromType<QRemoteObjectPackets::QSQ_>()) {
+ const auto *qsq_ = static_cast<const QRemoteObjectPackets::QSQ_ *>(value.constData());
+ QDataStream in(qsq_->values);
+ auto containerType = QMetaType::fromName(qsq_->typeName.constData());
+ bool isRegistered = containerType.isRegistered();
+ if (isRegistered) {
+ QVariant seq{containerType, nullptr};
+ if (!seq.canView<QSequentialIterable>()) {
+ qWarning() << "Unsupported container" << qsq_->typeName.constData()
+ << "(not viewable)";
+ return QVariant();
+ }
+ QSequentialIterable seqIter = seq.view<QSequentialIterable>();
+ if (!seqIter.metaContainer().canAddValue()) {
+ qWarning() << "Unsupported container" << qsq_->typeName.constData()
+ << "(Unable to add values)";
+ return QVariant();
+ }
+ QByteArray valueTypeName;
+ quint32 count;
+ in >> valueTypeName;
+ in >> count;
+ QMetaType valueType = QMetaType::fromName(valueTypeName.constData());
+ QVariant tmp{valueType, nullptr};
+ for (quint32 i = 0; i < count; i++) {
+ if (!valueType.load(in, tmp.data())) {
+ if (seqIter.metaContainer().canRemoveValue() || i == 0) {
+ for (quint32 ii = 0; ii < i; ii++)
+ seqIter.removeValue();
+ qWarning("QSQ_: unable to load type '%s', returning an empty list.", valueTypeName.constData());
+ } else {
+ qWarning("QSQ_: unable to load type '%s', returning a partial list.", valueTypeName.constData());
+ }
+ break;
+ }
+ seqIter.addValue(tmp);
+ }
+ value = seq;
+#ifdef QTRO_VERBOSE_PROTOCOL
+ qDebug() << "Decoding QSQ_ to sequential container" << containerType.name()
+ << valueTypeName;
+#endif
+ } else {
+ QtROSequentialContainer container{};
+ in >> container;
+ container.m_typeName = qsq_->typeName;
+ value = QVariant(QMetaType::fromType<QtROSequentialContainer>(), &container);
+#ifdef QTRO_VERBOSE_PROTOCOL
+ qDebug() << "Decoding QSQ_ to QtROSequentialContainer of"
+ << container.m_valueTypeName;
+#endif
+ }
+ } else if (value.metaType() == QMetaType::fromType<QRemoteObjectPackets::QAS_>()) {
+ const auto *qas_ = static_cast<const QRemoteObjectPackets::QAS_ *>(value.constData());
+ QDataStream in(qas_->values);
+ auto containerType = QMetaType::fromName(qas_->typeName.constData());
+ bool isRegistered = containerType.isRegistered();
+ if (isRegistered) {
+ QVariant map{containerType, nullptr};
+ if (!map.canView<QAssociativeIterable>()) {
+ qWarning() << "Unsupported container" << qas_->typeName.constData()
+ << "(not viewable)";
+ return QVariant();
+ }
+ QAssociativeIterable mapIter = map.view<QAssociativeIterable>();
+ if (!mapIter.metaContainer().canSetMappedAtKey()) {
+ qWarning() << "Unsupported container" << qas_->typeName.constData()
+ << "(Unable to insert values)";
+ return QVariant();
+ }
+ QByteArray keyTypeName, valueTypeName;
+ quint32 count;
+ in >> keyTypeName;
+ QMetaType keyType = QMetaType::fromName(keyTypeName.constData());
+ if (!keyType.isValid()) {
+ // This happens for class enums, where the passed keyType is <ClassName>::<enum>
+ // For a compiled replica, the keyType is <ClassName>Replica::<enum>
+ // Since the full typename is registered, we can pull the keyType from there
+ keyType = mapIter.metaContainer().keyMetaType();
+ }
+ QMetaType transferType = keyType;
+ if (keyType.flags().testFlag(QMetaType::IsEnumeration))
+ transferType = transferTypeForEnum(keyType);
+ QVariant key{transferType, nullptr};
+ in >> valueTypeName;
+ QMetaType valueType = QMetaType::fromName(valueTypeName.constData());
+ QVariant val{valueType, nullptr};
+ in >> count;
+ for (quint32 i = 0; i < count; i++) {
+ if (!transferType.load(in, key.data())) {
+ map = QVariant{containerType, nullptr};
+ qWarning("QAS_: unable to load key of type '%s', returning an empty map.",
+ keyTypeName.constData());
+ break;
+ }
+ if (!valueType.load(in, val.data())) {
+ map = QVariant{containerType, nullptr};
+ qWarning("QAS_: unable to load value of type '%s', returning an empty map.",
+ valueTypeName.constData());
+ break;
+ }
+ if (transferType != keyType) {
+ QVariant enumKey(key);
+ enumKey.convert(keyType);
+ mapIter.setValue(enumKey, val);
+ } else {
+ mapIter.setValue(key, val);
+ }
+ }
+ value = map;
+#ifdef QTRO_VERBOSE_PROTOCOL
+ qDebug() << "Decoding QAS_ to associative container" << containerType.name()
+ << valueTypeName << keyTypeName << count << mapIter.size() << map;
+#endif
+ } else {
+ QtROAssociativeContainer container{};
+ in >> container;
+ container.m_typeName = qas_->typeName;
+ value = QVariant(QMetaType::fromType<QtROAssociativeContainer>(), &container);
+#ifdef QTRO_VERBOSE_PROTOCOL
+ qDebug() << "Decoding QAS_ to QtROAssociativeContainer of"
+ << container.m_valueTypeName;
+#endif
+ }
+ }
+ return std::move(value);
+}
+
+void QDataStreamCodec::serializeProperty(const QRemoteObjectSourceBase *source, int internalIndex)
+{
+ serializeProperty(m_packet, source, internalIndex);
+}
+
+void QDataStreamCodec::serializeProperty(QDataStream &ds, const QRemoteObjectSourceBase *source, int internalIndex)
+{
+ const int propertyIndex = source->m_api->sourcePropertyIndex(internalIndex);
+ Q_ASSERT (propertyIndex >= 0);
+ const auto target = source->m_api->isAdapterProperty(internalIndex) ? source->m_adapter : source->m_object;
+ const auto property = target->metaObject()->property(propertyIndex);
+ const QVariant value = property.read(target);
+ if (property.metaType().flags().testFlag(QMetaType::PointerToQObject)) {
+ auto const childSource = source->m_children.value(internalIndex);
+ auto valueAsPointerToQObject = qvariant_cast<QObject *>(value);
+ if (childSource->m_object != valueAsPointerToQObject)
+ childSource->resetObject(valueAsPointerToQObject);
+ QRO_ qro(childSource);
+ if (source->d->isDynamic && qro.type == ObjectType::CLASS && childSource->m_object && !source->d->sentTypes.contains(qro.typeName)) {
+ QDataStream classDef(&qro.classDefinition, QIODevice::WriteOnly);
+ serializeDefinition(classDef, childSource);
+ source->d->sentTypes.insert(qro.typeName);
+ }
+ ds << QVariant::fromValue<QRO_>(qro);
+ if (qro.isNull)
+ return;
+ const int propertyCount = childSource->m_api->propertyCount();
+ // Put the properties in a buffer, the receiver may not know how to
+ // interpret the types until it registers new ones.
+ QDataStream params(&qro.parameters, QIODevice::WriteOnly);
+ params << propertyCount;
+ for (int internalIndex = 0; internalIndex < propertyCount; ++internalIndex)
+ serializeProperty(params, childSource, internalIndex);
+ ds << qro.parameters;
+ return;
+ }
+ if (source->d->isDynamic && property.userType() == QMetaType::QVariant
+ && value.metaType().flags().testFlag(QMetaType::IsGadget)) {
+ const auto typeName = QString::fromLatin1(value.metaType().name());
+ if (!source->d->sentTypes.contains(typeName)) {
+ QRO_ qro(value);
+ ds << QVariant::fromValue<QRO_>(qro);
+ ds << qro.parameters;
+ source->d->sentTypes.insert(typeName);
+ return;
+ }
+ }
+ ds << encodeVariant(value);
+}
+
+void QDataStreamCodec::serializeHandshakePacket()
+{
+ m_packet.setId(Handshake);
+ m_packet << QString(protocolVersion);
+ m_packet.finishPacket();
+}
+
+void QDataStreamCodec::serializeInitPacket(const QRemoteObjectRootSource *source)
+{
+ m_packet.setId(InitPacket);
+ m_packet << source->name();
+ serializeProperties(source);
+ m_packet.finishPacket();
+}
+
+void QDataStreamCodec::serializeProperties(const QRemoteObjectSourceBase *source)
+{
+ const SourceApiMap *api = source->m_api;
+
+ //Now copy the property data
+ const int numProperties = api->propertyCount();
+ m_packet << quint32(numProperties); //Number of properties
+
+ for (int internalIndex = 0; internalIndex < numProperties; ++internalIndex)
+ serializeProperty(source, internalIndex);
+}
+
+bool deserializeQVariantList(QDataStream &s, QVariantList &l)
+{
+ // note: optimized version of: QDataStream operator>>(QDataStream& s, QList<T>& l)
+ quint32 c;
+ s >> c;
+
+ const qsizetype count = static_cast<qsizetype>(c);
+ const qsizetype listSize = l.size();
+ if (listSize < count)
+ l.reserve(count);
+ else if (listSize > count)
+ l.resize(count);
+
+ for (int i = 0; i < l.size(); ++i)
+ {
+ if (s.atEnd())
+ return false;
+ s >> l[i];
+ }
+ for (auto i = l.size(); i < count; ++i)
+ {
+ if (s.atEnd())
+ return false;
+ s >> l.emplace_back();
+ }
+ return true;
+}
+
+void QDataStreamCodec::deserializeInitPacket(QDataStream &in, QVariantList &values)
+{
+ const bool success = deserializeQVariantList(in, values);
+ Q_ASSERT(success);
+ Q_UNUSED(success)
+}
+
+void QDataStreamCodec::serializeInitDynamicPacket(const QRemoteObjectRootSource *source)
+{
+ m_packet.setId(InitDynamicPacket);
+ m_packet << source->name();
+ serializeDefinition(m_packet, source);
+ serializeProperties(source);
+ m_packet.finishPacket();
+}
+
+static ObjectType getObjectType(const QString &typeName)
+{
+ if (typeName == QLatin1String("QAbstractItemModelAdapter"))
+ return ObjectType::MODEL;
+ auto tid = QMetaType::fromName(typeName.toUtf8()).id();
+ if (tid == QMetaType::UnknownType)
+ return ObjectType::CLASS;
+ QMetaType type(tid);
+ auto mo = type.metaObject();
+ if (mo && mo->inherits(&QAbstractItemModel::staticMetaObject))
+ return ObjectType::MODEL;
+ return ObjectType::CLASS;
+}
+
+static QByteArrayView resolveEnumName(QMetaType t, bool &isFlag)
+{
+ // Takes types like `MyPOD::Position` or `QFlags<MyPOD::Position>` and returns 'Position`
+ QByteArrayView enumName(t.name());
+ isFlag = enumName.startsWith("QFlags<");
+ auto lastColon = enumName.lastIndexOf(':');
+ if (lastColon >= 0)
+ enumName = QByteArrayView(t.name() + lastColon + 1);
+ if (isFlag)
+ enumName.chop(1);
+ return enumName;
+}
+
+static QMetaEnum metaEnumFromType(QMetaType t)
+{
+ if (t.flags().testFlag(QMetaType::IsEnumeration)) {
+ if (const QMetaObject *m = t.metaObject()) {
+ bool isFlag;
+ auto enumName = resolveEnumName(t, isFlag);
+ if (isFlag) {
+ for (int i = m->enumeratorOffset(); i < m->enumeratorCount(); i++) {
+ auto testType = m->enumerator(i);
+ if (testType.isFlag() &&
+ enumName.compare(QByteArrayView(testType.enumName())) == 0)
+ return testType;
+ }
+ }
+ return m->enumerator(m->indexOfEnumerator(enumName.data()));
+ }
+ }
+ return QMetaEnum();
+}
+
+static bool checkEnum(QMetaType metaType, QSet<QMetaEnum> &enums)
+{
+ if (metaType.flags().testFlag(QMetaType::IsEnumeration)) {
+ QMetaEnum meta = metaEnumFromType(metaType);
+ enums.insert(meta);
+ return true;
+ }
+ return false;
+}
+
+static void recurseMetaobject(const QMetaObject *mo, QSet<const QMetaObject *> &gadgets, QSet<QMetaEnum> &enums)
+{
+ if (!mo || gadgets.contains(mo))
+ return;
+ gadgets.insert(mo);
+ const int numProperties = mo->propertyCount();
+ for (int i = 0; i < numProperties; ++i) {
+ const auto property = mo->property(i);
+ if (checkEnum(property.metaType(), enums))
+ continue;
+ if (property.metaType().flags().testFlag(QMetaType::IsGadget))
+ recurseMetaobject(property.metaType().metaObject(), gadgets, enums);
+ }
+}
+
+// A Source may only use a subset of the metaobjects properties/signals/slots, so we only search
+// the ones in the API. For nested pointer types, we will have another api to limit the search.
+// For nested PODs/enums, we search the entire qobject (using the recurseMetaobject call()).
+void recurseForGadgets(QSet<const QMetaObject *> &gadgets, QSet<QMetaEnum> &enums, const QRemoteObjectSourceBase *source)
+{
+ const SourceApiMap *api = source->m_api;
+
+ const int numSignals = api->signalCount();
+ const int numMethods = api->methodCount();
+ const int numProperties = api->propertyCount();
+
+ for (int si = 0; si < numSignals; ++si) {
+ const int params = api->signalParameterCount(si);
+ for (int pi = 0; pi < params; ++pi) {
+ const int type = api->signalParameterType(si, pi);
+ const auto metaType = QMetaType(type);
+ if (checkEnum(metaType, enums))
+ continue;
+ if (!metaType.flags().testFlag(QMetaType::IsGadget))
+ continue;
+ const auto mo = metaType.metaObject();
+ if (source->d->sentTypes.contains(QLatin1String(mo->className())))
+ continue;
+ recurseMetaobject(mo, gadgets, enums);
+ source->d->sentTypes.insert(QLatin1String(mo->className()));
+ }
+ }
+
+ for (int mi = 0; mi < numMethods; ++mi) {
+ const int params = api->methodParameterCount(mi);
+ for (int pi = 0; pi < params; ++pi) {
+ const int type = api->methodParameterType(mi, pi);
+ const auto metaType = QMetaType(type);
+ if (checkEnum(metaType, enums))
+ continue;
+ if (!metaType.flags().testFlag(QMetaType::IsGadget))
+ continue;
+ const auto mo = metaType.metaObject();
+ if (source->d->sentTypes.contains(QLatin1String(mo->className())))
+ continue;
+ recurseMetaobject(mo, gadgets, enums);
+ source->d->sentTypes.insert(QLatin1String(mo->className()));
+ }
+ }
+ for (int pi = 0; pi < numProperties; ++pi) {
+ const int index = api->sourcePropertyIndex(pi);
+ Q_ASSERT(index >= 0);
+ const auto target = api->isAdapterProperty(pi) ? source->m_adapter : source->m_object;
+ const auto metaProperty = target->metaObject()->property(index);
+ const auto metaType = metaProperty.metaType();
+ if (checkEnum(metaType, enums))
+ continue;
+ if (metaType.flags().testFlag(QMetaType::PointerToQObject)) {
+ auto const objectType = getObjectType(QString::fromLatin1(metaProperty.typeName()));
+ if (objectType == ObjectType::CLASS) {
+ auto const childSource = source->m_children.value(pi);
+ if (childSource->m_object)
+ recurseForGadgets(gadgets, enums, childSource);
+ }
+ }
+ if (!metaType.flags().testFlag(QMetaType::IsGadget))
+ continue;
+ const auto mo = metaType.metaObject();
+ if (source->d->sentTypes.contains(QLatin1String(mo->className())))
+ continue;
+ recurseMetaobject(mo, gadgets, enums);
+ source->d->sentTypes.insert(QLatin1String(mo->className()));
+ }
+}
+
+static bool checkForEnumsInSource(const QMetaObject *meta, const QRemoteObjectSourceBase *source)
+{
+ if (source->m_object->inherits(meta->className()))
+ return true;
+ for (const auto &child : source->m_children) {
+ if (child->m_object && checkForEnumsInSource(meta, child))
+ return true;
+ }
+ return false;
+}
+
+static void serializeEnum(QDataStream &ds, const QMetaEnum &enumerator)
+{
+ ds << QByteArray::fromRawData(enumerator.name(), qsizetype(qstrlen(enumerator.name())));
+ ds << enumerator.isFlag();
+ ds << enumerator.isScoped();
+ const auto typeName = QByteArray(enumerator.scope()).append("::").append(enumerator.name());
+ const quint32 size = quint32(QMetaType::fromName(typeName.constData()).sizeOf());
+ ds << size;
+#ifdef QTRO_VERBOSE_PROTOCOL
+ qDebug(" Enum (name = %s, size = %d, isFlag = %s, isScoped = %s):", enumerator.name(), size, enumerator.isFlag() ? "true" : "false", enumerator.isScoped() ? "true" : "false");
+#endif
+ const int keyCount = enumerator.keyCount();
+ ds << keyCount;
+ for (int k = 0; k < keyCount; ++k) {
+ ds << QByteArray::fromRawData(enumerator.key(k), qsizetype(qstrlen(enumerator.key(k))));
+ ds << enumerator.value(k);
+#ifdef QTRO_VERBOSE_PROTOCOL
+ qDebug(" Key %d (name = %s, value = %d):", k, enumerator.key(k), enumerator.value(k));
+#endif
+ }
+}
+
+static void serializeGadgets(QDataStream &ds, const QSet<const QMetaObject *> &gadgets, const QSet<QMetaEnum> &enums, const QRemoteObjectSourceBase *source=nullptr)
+{
+ // Determine how to handle the enums found
+ QSet<QMetaEnum> qtEnums;
+ QSet<const QMetaObject *> dynamicEnumMetaObjects;
+ for (const auto &metaEnum : enums) {
+ auto const metaObject = metaEnum.enclosingMetaObject();
+ if (gadgets.contains(metaObject)) // Part of a gadget will we serialize
+ continue;
+ // This checks if the enum is defined in our object heirarchy, in which case it will
+ // already have been serialized.
+ if (source && checkForEnumsInSource(metaObject, source->d->root))
+ continue;
+ // qtEnums are enumerations already known by Qt, so we only need register them.
+ // We don't need to send all of the key/value data.
+ if (metaObject == &Qt::staticMetaObject) // Are the other Qt metaclasses for enums?
+ qtEnums.insert(metaEnum);
+ else
+ dynamicEnumMetaObjects.insert(metaEnum.enclosingMetaObject());
+ }
+ ds << quint32(qtEnums.size());
+ for (const auto &metaEnum : qtEnums) {
+ QByteArray enumName(metaEnum.scope());
+ enumName.append("::", 2).append(metaEnum.name());
+ ds << enumName;
+ }
+ const auto allMetaObjects = gadgets + dynamicEnumMetaObjects;
+ ds << quint32(allMetaObjects.size());
+#ifdef QTRO_VERBOSE_PROTOCOL
+ qDebug() << " Found" << gadgets.size() << "gadget/pod and" << (allMetaObjects.size() - gadgets.size()) << "enum types";
+ int i = 0;
+#endif
+ // There isn't an easy way to update a metaobject incrementally, so we
+ // send all of the metaobject's enums, but no properties, when an external
+ // enum is requested.
+ for (auto const meta : allMetaObjects) {
+ ds << QByteArray::fromRawData(meta->className(), qsizetype(qstrlen(meta->className())));
+ int propertyCount = gadgets.contains(meta) ? meta->propertyCount() : 0;
+ ds << quint32(propertyCount);
+#ifdef QTRO_VERBOSE_PROTOCOL
+ qDebug(" Gadget %d (name = %s, # properties = %d, # enums = %d):", i++, meta->className(), propertyCount, meta->enumeratorCount() - meta->enumeratorOffset());
+#endif
+ for (int j = 0; j < propertyCount; j++) {
+ auto prop = meta->property(j);
+#ifdef QTRO_VERBOSE_PROTOCOL
+ qDebug(" Data member %d (name = %s, type = %s):", j, prop.name(), prop.typeName());
+#endif
+ ds << QByteArray::fromRawData(prop.name(), qsizetype(qstrlen(prop.name())));
+ ds << QByteArray::fromRawData(prop.typeName(), qsizetype(qstrlen(prop.typeName())));
+ }
+ int enumCount = meta->enumeratorCount() - meta->enumeratorOffset();
+ ds << quint32(enumCount);
+ for (int j = meta->enumeratorOffset(); j < meta->enumeratorCount(); j++) {
+ auto const enumMeta = meta->enumerator(j);
+ serializeEnum(ds, enumMeta);
+ }
+ }
+}
+
+void QDataStreamCodec::serializeDefinition(QDataStream &ds, const QRemoteObjectSourceBase *source)
+{
+ const SourceApiMap *api = source->m_api;
+ const QByteArray desiredClassName(api->typeName().toLatin1());
+ const QByteArray originalClassName = api->className();
+ // The dynamic class will be called typeName on the receiving side of this definition
+ // However, there are types like enums that have the QObject's class name. Replace()
+ // will convert a parameter such as "ParentClassSource::MyEnum" to "ParentClass::MyEnum"
+ // so the type can be properly resolved and registered.
+ auto replace = [&originalClassName, &desiredClassName](QByteArray &name) {
+ name.replace(originalClassName, desiredClassName);
+ };
+
+ ds << source->m_api->typeName();
+#ifdef QTRO_VERBOSE_PROTOCOL
+ qDebug() << "Serializing definition for" << source->m_api->typeName();
+#endif
+
+ //Now copy the property data
+ const int numEnums = api->enumCount();
+ const auto metaObject = source->m_object->metaObject();
+ ds << quint32(numEnums); //Number of Enums
+#ifdef QTRO_VERBOSE_PROTOCOL
+ qDebug() << " Found" << numEnums << "enumeration types";
+#endif
+ for (int i = 0; i < numEnums; ++i) {
+ auto enumerator = metaObject->enumerator(api->sourceEnumIndex(i));
+ Q_ASSERT(enumerator.isValid());
+ serializeEnum(ds, enumerator);
+ }
+
+ QSet<const QMetaObject *> gadgets;
+ QSet<QMetaEnum> enums;
+ recurseForGadgets(gadgets, enums, source);
+ serializeGadgets(ds, gadgets, enums, source);
+
+ const int numSignals = api->signalCount();
+ ds << quint32(numSignals); //Number of signals
+ for (int i = 0; i < numSignals; ++i) {
+ const int index = api->sourceSignalIndex(i);
+ Q_ASSERT(index >= 0);
+ auto signature = api->signalSignature(i);
+ replace(signature);
+ const int count = api->signalParameterCount(i);
+ for (int pi = 0; pi < count; ++pi) {
+ const auto metaType = QMetaType(api->signalParameterType(i, pi));
+ if (isSequentialGadgetType(metaType))
+ signature.replace(metaType.name(), "QtROSequentialContainer");
+ else if (isAssociativeGadgetType(metaType))
+ signature.replace(metaType.name(), "QtROAssociativeContainer");
+ }
+#ifdef QTRO_VERBOSE_PROTOCOL
+ qDebug() << " Signal" << i << "(signature =" << signature << "parameter names =" << api->signalParameterNames(i) << ")";
+#endif
+ ds << signature;
+ ds << api->signalParameterNames(i);
+ }
+
+ const int numMethods = api->methodCount();
+ ds << quint32(numMethods); //Number of methods
+ for (int i = 0; i < numMethods; ++i) {
+ const int index = api->sourceMethodIndex(i);
+ Q_ASSERT(index >= 0);
+ auto signature = api->methodSignature(i);
+ replace(signature);
+ const int count = api->methodParameterCount(i);
+ for (int pi = 0; pi < count; ++pi) {
+ const auto metaType = QMetaType(api->methodParameterType(i, pi));
+ if (isSequentialGadgetType(metaType))
+ signature.replace(metaType.name(), "QtROSequentialContainer");
+ else if (isAssociativeGadgetType(metaType))
+ signature.replace(metaType.name(), "QtROAssociativeContainer");
+ }
+ auto typeName = api->typeName(i);
+ replace(typeName);
+#ifdef QTRO_VERBOSE_PROTOCOL
+ qDebug() << " Slot" << i << "(signature =" << signature << "parameter names =" << api->methodParameterNames(i) << "return type =" << typeName << ")";
+#endif
+ ds << signature;
+ ds << typeName;
+ ds << api->methodParameterNames(i);
+ }
+
+ const int numProperties = api->propertyCount();
+ ds << quint32(numProperties); //Number of properties
+ for (int i = 0; i < numProperties; ++i) {
+ const int index = api->sourcePropertyIndex(i);
+ Q_ASSERT(index >= 0);
+
+ const auto target = api->isAdapterProperty(i) ? source->m_adapter : source->m_object;
+ const auto metaProperty = target->metaObject()->property(index);
+ ds << metaProperty.name();
+#ifdef QTRO_VERBOSE_PROTOCOL
+ qDebug() << " Property" << i << "name =" << metaProperty.name();
+#endif
+ if (metaProperty.metaType().flags().testFlag(QMetaType::PointerToQObject)) {
+ auto objectType = getObjectType(QLatin1String(metaProperty.typeName()));
+ ds << (objectType == ObjectType::CLASS ? "QObject*" : "QAbstractItemModel*");
+#ifdef QTRO_VERBOSE_PROTOCOL
+ qDebug() << " Type:" << (objectType == ObjectType::CLASS ? "QObject*" : "QAbstractItemModel*");
+#endif
+ } else {
+ if (isSequentialGadgetType(metaProperty.metaType())) {
+ ds << "QtROSequentialContainer";
+#ifdef QTRO_VERBOSE_PROTOCOL
+ qDebug() << " Type:" << "QtROSequentialContainer";
+#endif
+ } else if (isAssociativeGadgetType(metaProperty.metaType())) {
+ ds << "QtROAssociativeContainer";
+#ifdef QTRO_VERBOSE_PROTOCOL
+ qDebug() << " Type:" << "QtROAssociativeContainer";
+#endif
+ } else {
+ ds << metaProperty.typeName();
+#ifdef QTRO_VERBOSE_PROTOCOL
+ qDebug() << " Type:" << metaProperty.typeName();
+#endif
+ }
+ }
+ if (metaProperty.notifySignalIndex() == -1) {
+ ds << QByteArray();
+#ifdef QTRO_VERBOSE_PROTOCOL
+ qDebug() << " Notification signal: None";
+#endif
+ } else {
+ auto signature = metaProperty.notifySignal().methodSignature();
+ replace(signature);
+ ds << signature;
+#ifdef QTRO_VERBOSE_PROTOCOL
+ qDebug() << " Notification signal:" << signature;
+#endif
+ }
+ }
+}
+
+void QDataStreamCodec::serializeAddObjectPacket(const QString &name, bool isDynamic)
+{
+ m_packet.setId(AddObject);
+ m_packet << name;
+ m_packet << isDynamic;
+ m_packet.finishPacket();
+}
+
+void QDataStreamCodec::deserializeAddObjectPacket(QDataStream &ds, bool &isDynamic)
+{
+ ds >> isDynamic;
+}
+
+void QDataStreamCodec::serializeRemoveObjectPacket(const QString &name)
+{
+ m_packet.setId(RemoveObject);
+ m_packet << name;
+ m_packet.finishPacket();
+}
+//There is no deserializeRemoveObjectPacket - no parameters other than id and name
+
+void QDataStreamCodec::serializeInvokePacket(const QString &name, int call, int index, const QVariantList &args, int serialId, int propertyIndex)
+{
+ m_packet.setId(InvokePacket);
+ m_packet << name;
+ m_packet << call;
+ m_packet << index;
+
+ m_packet << quint32(args.size());
+ for (const auto &arg : args)
+ m_packet << encodeVariant(arg);
+
+ m_packet << serialId;
+ m_packet << propertyIndex;
+ m_packet.finishPacket();
+}
+
+void QDataStreamCodec::deserializeInvokePacket(QDataStream& in, int &call, int &index, QVariantList &args, int &serialId, int &propertyIndex)
+{
+ in >> call;
+ in >> index;
+ const bool success = deserializeQVariantList(in, args);
+ Q_ASSERT(success);
+ Q_UNUSED(success)
+ in >> serialId;
+ in >> propertyIndex;
+}
+
+void QDataStreamCodec::serializeInvokeReplyPacket(const QString &name, int ackedSerialId, const QVariant &value)
+{
+ m_packet.setId(InvokeReplyPacket);
+ m_packet << name;
+ m_packet << ackedSerialId;
+ m_packet << value;
+ m_packet.finishPacket();
+}
+
+void QDataStreamCodec::deserializeInvokeReplyPacket(QDataStream& in, int &ackedSerialId, QVariant &value){
+ in >> ackedSerialId;
+ in >> value;
+}
+
+void QDataStreamCodec::serializePropertyChangePacket(QRemoteObjectSourceBase *source, int signalIndex)
+{
+ int internalIndex = source->m_api->propertyRawIndexFromSignal(signalIndex);
+ m_packet.setId(PropertyChangePacket);
+ m_packet << source->name();
+ m_packet << internalIndex;
+ serializeProperty(source, internalIndex);
+ m_packet.finishPacket();
+}
+
+void QDataStreamCodec::deserializePropertyChangePacket(QDataStream& in, int &index, QVariant &value)
+{
+ in >> index;
+ in >> value;
+}
+
+void QDataStreamCodec::serializeObjectListPacket(const ObjectInfoList &objects)
+{
+ m_packet.setId(ObjectList);
+ m_packet << objects;
+ m_packet.finishPacket();
+}
+
+void QDataStreamCodec::deserializeObjectListPacket(QDataStream &in, ObjectInfoList &objects)
+{
+ in >> objects;
+}
+
+void QDataStreamCodec::serializePingPacket(const QString &name)
+{
+ m_packet.setId(Ping);
+ m_packet << name;
+ m_packet.finishPacket();
+}
+
+void QDataStreamCodec::serializePongPacket(const QString &name)
+{
+ m_packet.setId(Pong);
+ m_packet << name;
+ m_packet.finishPacket();
+}
+
+QRO_::QRO_(QRemoteObjectSourceBase *source)
+ : name(source->name())
+ , typeName(source->m_api->typeName())
+ , type(source->m_adapter ? ObjectType::MODEL : getObjectType(typeName))
+ , isNull(source->m_object == nullptr)
+ , classDefinition()
+ , parameters()
+{}
+
+QRO_::QRO_(const QVariant &value)
+ : type(ObjectType::GADGET)
+ , isNull(false)
+{
+ const auto metaType = value.metaType();
+ auto meta = metaType.metaObject();
+ QDataStream out(&classDefinition, QIODevice::WriteOnly);
+ const int numProperties = meta->propertyCount();
+ const auto name = metaType.name();
+ const auto typeName = QByteArray::fromRawData(name, qsizetype(qstrlen(name)));
+ out << quint32(0) << quint32(1);
+ out << typeName;
+ out << numProperties;
+#ifdef QTRO_VERBOSE_PROTOCOL
+ qDebug("Serializing POD definition to QRO_ (name = %s)", typeName.constData());
+#endif
+ for (int i = 0; i < numProperties; ++i) {
+ const auto property = meta->property(i);
+#ifdef QTRO_VERBOSE_PROTOCOL
+ qDebug(" Data member %d (name = %s, type = %s):", i, property.name(), property.typeName());
+#endif
+ out << QByteArray::fromRawData(property.name(), qsizetype(qstrlen(property.name())));
+ out << QByteArray::fromRawData(property.typeName(), qsizetype(qstrlen(property.typeName())));
+ }
+ int enumCount = meta->enumeratorCount() - meta->enumeratorOffset();
+ out << quint32(enumCount);
+ for (int j = meta->enumeratorOffset(); j < meta->enumeratorCount(); j++) {
+ auto const enumMeta = meta->enumerator(j);
+ serializeEnum(out, enumMeta);
+ }
+ QDataStream ds(¶meters, QIODevice::WriteOnly);
+ ds << value;
+#ifdef QTRO_VERBOSE_PROTOCOL
+ qDebug() << " Value:" << value;
+#endif
+}
+
+QDataStream &operator<<(QDataStream &stream, const QRO_ &info)
+{
+ stream << info.name << info.typeName << quint8(info.type) << info.classDefinition << info.isNull;
+ qCDebug(QT_REMOTEOBJECT) << "Serializing " << info;
+ // info.parameters will be filled in by serializeProperty
+ return stream;
+}
+
+QDataStream &operator>>(QDataStream &stream, QRO_ &info)
+{
+ quint8 tmpType;
+ stream >> info.name >> info.typeName >> tmpType >> info.classDefinition >> info.isNull;
+ info.type = static_cast<ObjectType>(tmpType);
+ qCDebug(QT_REMOTEOBJECT) << "Deserializing " << info;
+ if (!info.isNull)
+ stream >> info.parameters;
+ return stream;
+}
+
+QSQ_::QSQ_(const QVariant &variant)
+{
+ QSequentialIterable sequence;
+ QMetaType valueType;
+ if (variant.metaType() == QMetaType::fromType<QtROSequentialContainer>()) {
+ auto container = static_cast<const QtROSequentialContainer *>(variant.constData());
+ typeName = container->m_typeName;
+ valueType = container->m_valueType;
+ valueTypeName = container->m_valueTypeName;
+ sequence = QSequentialIterable(reinterpret_cast<const QVariantList *>(variant.constData()));
+ } else {
+ sequence = variant.value<QSequentialIterable>();
+ typeName = QByteArray(variant.metaType().name());
+ valueType = sequence.metaContainer().valueMetaType();
+ valueTypeName = QByteArray(valueType.name());
+ }
+#ifdef QTRO_VERBOSE_PROTOCOL
+ qDebug("Serializing POD sequence to QSQ_ (type = %s, valueType = %s) with size = %lld",
+ typeName.constData(), valueTypeName.constData(), sequence.size());
+#endif
+ QDataStream ds(&values, QIODevice::WriteOnly);
+ ds << valueTypeName;
+ auto pos = ds.device()->pos();
+ ds << quint32(sequence.size());
+ for (const auto &v : sequence) {
+ if (!valueType.save(ds, v.data())) {
+ ds.device()->seek(pos);
+ ds.resetStatus();
+ ds << quint32(0);
+ values.resize(ds.device()->pos());
+ qWarning("QSQ_: unable to save type '%s', sending empty list.", valueType.name());
+ break;
+ }
+ }
+}
+
+QDataStream &operator<<(QDataStream &stream, const QSQ_ &sequence)
+{
+ stream << sequence.typeName << sequence.valueTypeName << sequence.values;
+ qCDebug(QT_REMOTEOBJECT) << "Serializing " << sequence;
+ return stream;
+}
+
+QDataStream &operator>>(QDataStream &stream, QSQ_ &sequence)
+{
+ stream >> sequence.typeName >> sequence.valueTypeName >> sequence.values;
+ qCDebug(QT_REMOTEOBJECT) << "Deserializing " << sequence;
+ return stream;
+}
+
+QAS_::QAS_(const QVariant &variant)
+{
+ QAssociativeIterable map;
+ QMetaType keyType, transferType, valueType;
+ const QtROAssociativeContainer *container = nullptr;
+ if (variant.metaType() == QMetaType::fromType<QtROAssociativeContainer>()) {
+ container = static_cast<const QtROAssociativeContainer *>(variant.constData());
+ typeName = container->m_typeName;
+ keyType = container->m_keyType;
+ keyTypeName = container->m_keyTypeName;
+ valueType = container->m_valueType;
+ valueTypeName = container->m_valueTypeName;
+ map = QAssociativeIterable(reinterpret_cast<const QVariantMap *>(variant.constData()));
+ } else {
+ map = variant.value<QAssociativeIterable>();
+ typeName = QByteArray(variant.metaType().name());
+ keyType = map.metaContainer().keyMetaType();
+ keyTypeName = QByteArray(keyType.name());
+ valueType = map.metaContainer().mappedMetaType();
+ valueTypeName = QByteArray(valueType.name());
+ }
+ // Special handling for enums...
+ transferType = keyType;
+ if (keyType.flags().testFlag(QMetaType::IsEnumeration)) {
+ transferType = transferTypeForEnum(keyType);
+ auto meta = keyType.metaObject();
+ // If we are the source, make sure the typeName is converted for any downstream replicas
+ // the `meta` variable will be non-null when the enum is a class enum
+ if (meta && !container) {
+ const int ind = meta->indexOfClassInfo(QCLASSINFO_REMOTEOBJECT_TYPE);
+ if (ind >= 0) {
+ bool isFlag = keyTypeName.startsWith("QFlags<");
+ if (isFlag || keyTypeName.startsWith(meta->className())) {
+#ifdef QTRO_VERBOSE_PROTOCOL
+ QByteArray orig(keyTypeName);
+#endif
+ if (isFlag) {
+ // Q_DECLARE_FLAGS(Flags, Enum) -> `typedef QFlags<Enum> Flags;`
+ // We know we have an enum for `Flags` because we sent the enums
+ // from the source, so we just need to look up the alias.
+ keyTypeName = keyTypeName.mid(7);
+ keyTypeName.chop(1); // Remove trailing '>'
+ int index = keyTypeName.lastIndexOf(':');
+ for (int i = meta->enumeratorOffset(); i < meta->enumeratorCount(); i++) {
+ auto en = meta->enumerator(i);
+ auto name = keyTypeName.data() + index + 1;
+ if (en.isFlag() && qstrcmp(en.enumName(), name) == 0)
+ keyTypeName.replace(index + 1, qstrlen(en.enumName()), en.name());
+ }
+ }
+ keyTypeName.replace(meta->className(), meta->classInfo(ind).value());
+ QByteArray repName(meta->classInfo(ind).value());
+ repName.append("Replica");
+ typeName.replace(meta->className(), repName);
+#ifdef QTRO_VERBOSE_PROTOCOL
+ qDebug() << "Converted map key typename from" << orig << "to" << keyTypeName;
+#endif
+ }
+ }
+ }
+ }
+
+#ifdef QTRO_VERBOSE_PROTOCOL
+ qDebug("Serializing POD map to QAS_ (type = %s, keyType = %s, valueType = %s), size = %lld",
+ typeName.constData(), keyTypeName.constData(), valueTypeName.constData(),
+ map.size());
+#endif
+ QDataStream ds(&values, QIODevice::WriteOnly);
+ ds << keyTypeName;
+ ds << valueTypeName;
+ auto pos = ds.device()->pos();
+ ds << quint32(map.size());
+ QAssociativeIterable::const_iterator iter = map.begin();
+ for (int i = 0; i < map.size(); i++) {
+ QVariant key(container ? container->m_keys.at(i) : iter.key());
+ if (transferType != keyType)
+ key.convert(transferType);
+ if (!transferType.save(ds, key.data())) {
+ ds.device()->seek(pos);
+ ds.resetStatus();
+ ds << quint32(0);
+ values.resize(ds.device()->pos());
+ qWarning("QAS_: unable to save key '%s', sending empty map.", keyType.name());
+ break;
+ }
+ if (!valueType.save(ds, iter.value().data())) {
+ ds.device()->seek(pos);
+ ds.resetStatus();
+ ds << quint32(0);
+ values.resize(ds.device()->pos());
+ qWarning("QAS_: unable to save value '%s', sending empty map.", valueType.name());
+ break;
+ }
+ iter++;
+ }
+}
+
+QDataStream &operator<<(QDataStream &stream, const QAS_ &map)
+{
+ stream << map.typeName << map.keyTypeName << map.valueTypeName << map.values;
+ qCDebug(QT_REMOTEOBJECT) << "Serializing " << map;
+ return stream;
+}
+
+QDataStream &operator>>(QDataStream &stream, QAS_ &map)
+{
+ stream >> map.typeName >> map.keyTypeName >> map.valueTypeName >> map.values;
+ qCDebug(QT_REMOTEOBJECT) << "Deserializing " << map;
+ return stream;
+}
+
+DataStreamPacket::DataStreamPacket(quint16 id)
+ : QDataStream(&array, QIODevice::WriteOnly), baseAddress(0), size(0)
+{
+ this->setVersion(QtRemoteObjects::dataStreamVersion);
+ this->setByteOrder(QDataStream::LittleEndian);
+ *this << quint32(0);
+ *this << id;
+}
+
+void CodecBase::send(const QSet<QtROIoDeviceBase *> &connections)
+{
+ const auto bytearray = getPayload();
+ for (auto conn : connections)
+ conn->write(bytearray);
+ reset();
+}
+
+void CodecBase::send(const QList<QtROIoDeviceBase *> &connections)
+{
+ const auto bytearray = getPayload();
+ for (auto conn : connections)
+ conn->write(bytearray);
+ reset();
+}
+
+void CodecBase::send(QtROIoDeviceBase *connection)
+{
+ const auto bytearray = getPayload();
+ connection->write(bytearray);
+ reset();
+}
+
+} // namespace QRemoteObjectPackets
+
+QT_IMPL_METATYPE_EXTERN_TAGGED(QRemoteObjectPackets::QRO_, QRemoteObjectPackets__QRO_)
+
+QT_END_NAMESPACE
--- /dev/null
+// Copyright (C) 2021 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QTREMOTEOBJECTPACKET_P_H
+#define QTREMOTEOBJECTPACKET_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qtremoteobjectglobal.h"
+#include "qremoteobjectsource.h"
+#include "qconnectionfactories.h"
+
+#include <QtCore/qassociativeiterable.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qpair.h>
+#include <QtCore/qsequentialiterable.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qvariant.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qdatastream.h>
+#include <QtCore/private/qglobal_p.h>
+
+#include <cstdlib>
+
+QT_BEGIN_NAMESPACE
+
+class QMetaObjectBuilder;
+class QRemoteObjectSourceBase;
+class QRemoteObjectRootSource;
+
+namespace QRemoteObjectPackets {
+
+Q_NAMESPACE
+
+class DataStreamPacket;
+
+struct ObjectInfo
+{
+ QString name;
+ QString typeName;
+ QByteArray signature;
+};
+
+inline QDebug operator<<(QDebug dbg, const ObjectInfo &info)
+{
+ dbg.nospace() << "ObjectInfo(" << info.name << ", " << info.typeName << ", " << info.signature <<")";
+ return dbg.space();
+}
+
+inline QDataStream& operator<<(QDataStream &stream, const ObjectInfo &info)
+{
+ return stream << info.name << info.typeName << info.signature;
+}
+
+inline QDataStream& operator>>(QDataStream &stream, ObjectInfo &info)
+{
+ return stream >> info.name >> info.typeName >> info.signature;
+}
+
+using ObjectInfoList = QList<ObjectInfo>;
+
+enum class ObjectType : quint8 { CLASS, MODEL, GADGET };
+Q_ENUM_NS(ObjectType)
+
+// Use a short name, as QVariant::save writes the name every time a qvariant of
+// this type is serialized
+class QRO_
+{
+public:
+ QRO_() : type(ObjectType::CLASS), isNull(true) {}
+ explicit QRO_(QRemoteObjectSourceBase *source);
+ explicit QRO_(const QVariant &value);
+ QString name, typeName;
+ ObjectType type;
+ bool isNull;
+ QByteArray classDefinition;
+ QByteArray parameters;
+};
+
+inline QDebug operator<<(QDebug dbg, const QRO_ &info)
+{
+ dbg.nospace() << "QRO_(name: " << info.name << ", typeName: " << info.typeName
+ << ", type: " << info.type << ", valid: " << (info.isNull ? "true" : "false")
+ << ", parameters: {" << info.parameters << ")"
+ << (info.classDefinition.isEmpty() ? " no definitions)" : " with definitions)");
+ return dbg.space();
+}
+
+QDataStream& operator<<(QDataStream &stream, const QRO_ &info);
+
+QDataStream& operator>>(QDataStream &stream, QRO_ &info);
+
+// Class for transmitting sequence data. Needed because containers for custom
+// types (and even primitive types in Qt5) are not registered with the metaObject
+// system. This wrapper allows us to create the desired container if it is
+// registered, or a QtROSequentialContainer if it is not. QtROSequentialContainer
+// is derived from QVariantList, so it can be used from QML similar to the API
+// type.
+class QSQ_
+{
+public:
+ QSQ_() {}
+ explicit QSQ_(const QVariant &lst);
+ QByteArray typeName, valueTypeName;
+ QByteArray values;
+};
+
+inline QDebug operator<<(QDebug dbg, const QSQ_ &seq)
+{
+ dbg.nospace() << "QSQ_(typeName: " << seq.typeName << ", valueType: " << seq.valueTypeName
+ << ", values: {" << seq.values <<")";
+ return dbg.space();
+}
+
+QDataStream& operator<<(QDataStream &stream, const QSQ_ &info);
+
+QDataStream& operator>>(QDataStream &stream, QSQ_ &info);
+
+// Class for transmitting associative containers. Needed because containers for
+// custom types (and even primitive types in Qt5) are not registered with the
+// metaObject system. This wrapper allows us to create the desired container if
+// it is registered, or a QtROAssociativeContainer if it is not.
+// QtROAssociativeContainer is derived from QVariantMap, so it can be used from
+// QML similar to the API type.
+class QAS_
+{
+public:
+ QAS_() {}
+ explicit QAS_(const QVariant &lst);
+ QByteArray typeName, keyTypeName, valueTypeName;
+ QByteArray values;
+};
+
+inline QDebug operator<<(QDebug dbg, const QAS_ &seq)
+{
+ dbg.nospace() << "QAS_(typeName: " << seq.typeName << ", keyType: " << seq.keyTypeName
+ << ", valueType: " << seq.valueTypeName << ", values: {" << seq.values <<")";
+ return dbg.space();
+}
+
+QDataStream& operator<<(QDataStream &stream, const QAS_ &info);
+
+QDataStream& operator>>(QDataStream &stream, QAS_ &info);
+
+//Helper class for creating a QByteArray from a QRemoteObjectPacket
+class DataStreamPacket : public QDataStream
+{
+public:
+ DataStreamPacket(quint16 id = QtRemoteObjects::InvokePacket);
+
+ void setId(quint16 id)
+ {
+ device()->seek(baseAddress);
+ *this << quint32(0);
+ *this << id;
+ }
+
+ void finishPacket()
+ {
+ size = device()->pos();
+ device()->seek(baseAddress);
+ *this << quint32(size - baseAddress - sizeof(quint32));
+ baseAddress = size; // Allow appending until reset() is called
+ }
+
+ const QByteArray &payload()
+ {
+ array.resize(size);
+ return array;
+ }
+
+ void reset()
+ {
+ baseAddress = 0;
+ size = 0;
+ array.clear();
+ }
+
+private:
+ QByteArray array;
+ int baseAddress;
+ int size;
+
+ Q_DISABLE_COPY(DataStreamPacket)
+};
+
+class CodecBase
+{
+public:
+ CodecBase() = default;
+ CodecBase(const CodecBase &) = default;
+ CodecBase(CodecBase &&) = default;
+ CodecBase &operator=(const CodecBase &) = default;
+ CodecBase &operator=(CodecBase &&) = default;
+ virtual ~CodecBase() = default;
+
+ virtual void serializeObjectListPacket(const ObjectInfoList &) = 0;
+ virtual void deserializeObjectListPacket(QDataStream &in, ObjectInfoList &) = 0;
+ virtual void serializeInitPacket(const QRemoteObjectRootSource *) = 0;
+ virtual void serializeInitDynamicPacket(const QRemoteObjectRootSource *) = 0;
+ virtual void serializePropertyChangePacket(QRemoteObjectSourceBase *source,
+ int signalIndex) = 0;
+ virtual void deserializePropertyChangePacket(QDataStream &in, int &index, QVariant &value) = 0;
+ virtual void serializeProperty(const QRemoteObjectSourceBase *source, int internalIndex) = 0;
+ // Heartbeat packets
+ virtual void serializePingPacket(const QString &name) = 0;
+ virtual void serializePongPacket(const QString &name) = 0;
+ virtual void serializeInvokePacket(const QString &name, int call, int index,
+ const QVariantList &args, int serialId = -1,
+ int propertyIndex = -1) = 0;
+ virtual void deserializeInvokePacket(QDataStream &in, int &call, int &index, QVariantList &args,
+ int &serialId, int &propertyIndex) = 0;
+ virtual void serializeInvokeReplyPacket(const QString &name, int ackedSerialId,
+ const QVariant &value) = 0;
+ virtual void serializeHandshakePacket() = 0;
+ virtual void serializeRemoveObjectPacket(const QString &name) = 0;
+ //There is no deserializeRemoveObjectPacket - no parameters other than id and name
+ virtual void serializeAddObjectPacket(const QString &name, bool isDynamic) = 0;
+ virtual void deserializeAddObjectPacket(QDataStream &, bool &isDynamic) = 0;
+ virtual void deserializeInitPacket(QDataStream &, QVariantList &) = 0;
+ virtual void deserializeInvokeReplyPacket(QDataStream &in, int &ackedSerialId,
+ QVariant &value) = 0;
+ void send(const QSet<QtROIoDeviceBase *> &connections);
+ void send(const QVector<QtROIoDeviceBase *> &connections);
+ void send(QtROIoDeviceBase *connection);
+
+protected:
+ // A payload can consist of one or more packets
+ virtual const QByteArray &getPayload() = 0;
+ virtual void reset() {}
+};
+
+class QDataStreamCodec : public CodecBase
+{
+public:
+ void serializeObjectListPacket(const ObjectInfoList &) override;
+ void deserializeObjectListPacket(QDataStream &in, ObjectInfoList &) override;
+ void serializeInitPacket(const QRemoteObjectRootSource *) override;
+ void serializeInitDynamicPacket(const QRemoteObjectRootSource*) override;
+ void serializePropertyChangePacket(QRemoteObjectSourceBase *source, int signalIndex) override;
+ void deserializePropertyChangePacket(QDataStream &in, int &index, QVariant &value) override;
+ void serializeProperty(const QRemoteObjectSourceBase *source, int internalIndex) override;
+ void serializePingPacket(const QString &name) override;
+ void serializePongPacket(const QString &name) override;
+ void serializeInvokePacket(const QString &name, int call, int index, const QVariantList &args,
+ int serialId = -1, int propertyIndex = -1) override;
+ void deserializeInvokePacket(QDataStream &in, int &call, int &index, QVariantList &args,
+ int &serialId, int &propertyIndex) override;
+ void serializeInvokeReplyPacket(const QString &name, int ackedSerialId,
+ const QVariant &value) override;
+ void serializeHandshakePacket() override;
+ void serializeRemoveObjectPacket(const QString &name) override;
+ void serializeAddObjectPacket(const QString &name, bool isDynamic) override;
+ void deserializeAddObjectPacket(QDataStream &, bool &isDynamic) override;
+ void deserializeInitPacket(QDataStream &, QVariantList &) override;
+ void deserializeInvokeReplyPacket(QDataStream &in, int &ackedSerialId,
+ QVariant &value) override;
+
+protected:
+ const QByteArray &getPayload() override {
+ return m_packet.payload();
+ }
+ void reset() override {
+ m_packet.reset();
+ }
+private:
+ void serializeDefinition(QDataStream &, const QRemoteObjectSourceBase *);
+ void serializeProperty(QDataStream &ds, const QRemoteObjectSourceBase *source, int internalIndex);
+ void serializeProperties(const QRemoteObjectSourceBase *source);
+ DataStreamPacket m_packet;
+};
+
+QMetaType transferTypeForEnum(QMetaType enumType);
+QVariant encodeVariant(const QVariant &value);
+QVariant decodeVariant(QVariant &&value, QMetaType metaType);
+
+} // namespace QRemoteObjectPackets
+
+QT_END_NAMESPACE
+
+QT_DECL_METATYPE_EXTERN_TAGGED(QRemoteObjectPackets::QRO_, QRemoteObjectPackets__QRO_,
+ /* not exported */)
+
+#endif
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qremoteobjectpendingcall.h"
+#include "qremoteobjectpendingcall_p.h"
+
+#include "qremoteobjectreplica_p.h"
+
+#include <QtCore/qcoreapplication.h>
+
+#include <private/qobject_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QT_IMPL_METATYPE_EXTERN(QRemoteObjectPendingCall)
+
+QRemoteObjectPendingCallData::QRemoteObjectPendingCallData(int serialId, QRemoteObjectReplicaImplementation *replica)
+ : replica(replica)
+ , serialId(serialId)
+ , error(QRemoteObjectPendingCall::InvalidMessage)
+ , watcherHelper(nullptr)
+{
+}
+
+QRemoteObjectPendingCallData::~QRemoteObjectPendingCallData()
+{
+}
+
+void QRemoteObjectPendingCallWatcherHelper::add(QRemoteObjectPendingCallWatcher *watcher)
+{
+ connect(this, &QRemoteObjectPendingCallWatcherHelper::finished, watcher, [watcher]() {
+ emit watcher->finished(watcher);
+ }, Qt::QueuedConnection);
+}
+
+void QRemoteObjectPendingCallWatcherHelper::emitSignals()
+{
+ emit finished();
+}
+
+/*!
+ \class QRemoteObjectPendingCall
+ \inmodule QtRemoteObjects
+ \brief Encapsulates the result of an asynchronous method call.
+*/
+
+QRemoteObjectPendingCall::QRemoteObjectPendingCall()
+ : d(new QRemoteObjectPendingCallData)
+{
+}
+
+QRemoteObjectPendingCall::~QRemoteObjectPendingCall()
+{
+}
+
+QRemoteObjectPendingCall::QRemoteObjectPendingCall(const QRemoteObjectPendingCall& other)
+ : d(other.d)
+{
+}
+
+QRemoteObjectPendingCall::QRemoteObjectPendingCall(QRemoteObjectPendingCallData *dd)
+ : d(dd)
+{
+}
+
+QRemoteObjectPendingCall &QRemoteObjectPendingCall::operator=(const QRemoteObjectPendingCall &other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ Returns the return value of the remote call.
+
+ returnValue will only be valid when the remote call has finished and there
+ are no \l {error}s.
+*/
+QVariant QRemoteObjectPendingCall::returnValue() const
+{
+ if (!d)
+ return QVariant();
+
+ QMutexLocker locker(&d->mutex);
+ return d->returnValue;
+}
+
+/*!
+ \enum QRemoteObjectPendingCall::Error
+
+ This enum type specifies the possible error values for a remote call:
+
+ \value NoError
+ No error occurred.
+ \value InvalidMessage
+ The default error state prior to the remote call finishing.
+*/
+
+/*!
+ Returns the error, if any, from the remote call.
+*/
+QRemoteObjectPendingCall::Error QRemoteObjectPendingCall::error() const
+{
+ if (!d)
+ return QRemoteObjectPendingCall::InvalidMessage;
+
+ QMutexLocker locker(&d->mutex);
+ return d->error;
+}
+
+/*!
+ Returns true if the remote call has finished, false otherwise.
+
+ A finished call will include a returnValue or \l error.
+*/
+bool QRemoteObjectPendingCall::isFinished() const
+{
+ if (!d)
+ return true; // considered finished
+
+ QMutexLocker locker(&d->mutex);
+ return d->error != InvalidMessage;
+}
+
+/*!
+ Blocks for up to \a timeout milliseconds, until the remote call has finished.
+
+ Returns \c true on success, \c false otherwise.
+*/
+bool QRemoteObjectPendingCall::waitForFinished(int timeout)
+{
+ if (!d)
+ return false;
+
+ if (d->error != QRemoteObjectPendingCall::InvalidMessage)
+ return true; // already finished
+
+ QMutexLocker locker(&d->mutex);
+ if (!d->replica)
+ return false;
+
+ return d->replica->waitForFinished(*this, timeout);
+}
+
+QRemoteObjectPendingCall QRemoteObjectPendingCall::fromCompletedCall(const QVariant &returnValue)
+{
+ QRemoteObjectPendingCallData *data = new QRemoteObjectPendingCallData;
+ data->returnValue = returnValue;
+ data->error = NoError;
+ return QRemoteObjectPendingCall(data);
+}
+
+class QRemoteObjectPendingCallWatcherPrivate: public QObjectPrivate
+{
+public:
+ Q_DECLARE_PUBLIC(QRemoteObjectPendingCallWatcher)
+};
+
+/*!
+ \class QRemoteObjectPendingCallWatcher
+ \inmodule QtRemoteObjects
+ \brief Provides a QObject-based API for watching a QRemoteObjectPendingCall.
+
+ QRemoteObjectPendingCallWatcher provides a signal indicating when a QRemoteObjectPendingCall
+ has finished, allowing for convenient, non-blocking handling of the call.
+*/
+
+QRemoteObjectPendingCallWatcher::QRemoteObjectPendingCallWatcher(const QRemoteObjectPendingCall &call, QObject *parent)
+ : QObject(*new QRemoteObjectPendingCallWatcherPrivate, parent)
+ , QRemoteObjectPendingCall(call)
+{
+ if (d) {
+ QMutexLocker locker(&d->mutex);
+ if (!d->watcherHelper) {
+ d->watcherHelper.reset(new QRemoteObjectPendingCallWatcherHelper);
+ if (d->error != QRemoteObjectPendingCall::InvalidMessage) {
+ // cause a signal emission anyways
+ QMetaObject::invokeMethod(d->watcherHelper.data(), "finished", Qt::QueuedConnection);
+ }
+ }
+ d->watcherHelper->add(this);
+ }
+}
+
+QRemoteObjectPendingCallWatcher::~QRemoteObjectPendingCallWatcher()
+{
+}
+
+/*!
+ Returns true if the remote call has finished, false otherwise.
+
+ A finished call will include a returnValue or error.
+*/
+bool QRemoteObjectPendingCallWatcher::isFinished() const
+{
+ if (!d)
+ return true; // considered finished
+
+ QMutexLocker locker(&d->mutex);
+ return d->error != QRemoteObjectPendingCall::InvalidMessage;
+}
+
+/*!
+ Blocks until the remote call has finished.
+*/
+void QRemoteObjectPendingCallWatcher::waitForFinished()
+{
+ if (d) {
+ QRemoteObjectPendingCall::waitForFinished();
+
+ // our signals were queued, so deliver them
+ QCoreApplication::sendPostedEvents(d->watcherHelper.data(), QEvent::MetaCall);
+ QCoreApplication::sendPostedEvents(this, QEvent::MetaCall);
+ }
+}
+
+/*!
+ \fn QRemoteObjectPendingCallWatcher::finished(QRemoteObjectPendingCallWatcher *self)
+
+ This signal is emitted when the remote call has finished. \a self is the pointer to
+ the watcher object that emitted the signal. A finished call will include a
+ returnValue or error.
+*/
+
+/*!
+ \class QRemoteObjectPendingReply
+ \inmodule QtRemoteObjects
+ \brief A templated version of QRemoteObjectPendingCall.
+*/
+
+/*! \fn template <typename T> T QRemoteObjectPendingReply<T>::returnValue() const
+
+ Returns a strongly typed version of the return value of the remote call.
+*/
+
+QT_END_NAMESPACE
+
+#include "moc_qremoteobjectpendingcall.cpp"
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QREMOTEOBJECTPENDINGCALL_H
+#define QREMOTEOBJECTPENDINGCALL_H
+
+#include <QtRemoteObjects/qtremoteobjectglobal.h>
+
+#include <QtCore/qvariant.h>
+
+QT_BEGIN_NAMESPACE
+
+class QRemoteObjectPendingCallWatcherPrivate;
+class QRemoteObjectPendingCallData;
+
+class Q_REMOTEOBJECTS_EXPORT QRemoteObjectPendingCall
+{
+public:
+ enum Error {
+ NoError,
+ InvalidMessage
+ };
+
+ QRemoteObjectPendingCall();
+ QRemoteObjectPendingCall(const QRemoteObjectPendingCall &other);
+ ~QRemoteObjectPendingCall();
+
+ QRemoteObjectPendingCall &operator=(const QRemoteObjectPendingCall &other);
+
+ QVariant returnValue() const;
+ QRemoteObjectPendingCall::Error error() const;
+
+ bool isFinished() const;
+
+ bool waitForFinished(int timeout = 30000);
+
+ static QRemoteObjectPendingCall fromCompletedCall(const QVariant &returnValue);
+
+protected:
+ QRemoteObjectPendingCall(QRemoteObjectPendingCallData *dd);
+
+ /// Shared data, note: might be null
+ QExplicitlySharedDataPointer<QRemoteObjectPendingCallData> d;
+
+private:
+ friend class QConnectedReplicaImplementation;
+};
+
+QT_END_NAMESPACE
+QT_DECL_METATYPE_EXTERN(QRemoteObjectPendingCall, Q_REMOTEOBJECTS_EXPORT)
+QT_BEGIN_NAMESPACE
+
+class Q_REMOTEOBJECTS_EXPORT QRemoteObjectPendingCallWatcher: public QObject, public QRemoteObjectPendingCall
+{
+ Q_OBJECT
+
+public:
+ QRemoteObjectPendingCallWatcher(const QRemoteObjectPendingCall &call, QObject *parent = nullptr);
+ ~QRemoteObjectPendingCallWatcher() override;
+
+ bool isFinished() const;
+
+ void waitForFinished();
+
+Q_SIGNALS:
+ void finished(QRemoteObjectPendingCallWatcher *self);
+
+private:
+ Q_DECLARE_PRIVATE(QRemoteObjectPendingCallWatcher)
+};
+
+template<typename T>
+class QRemoteObjectPendingReply : public QRemoteObjectPendingCall
+{
+public:
+ typedef T Type;
+
+ QRemoteObjectPendingReply() = default;
+ explicit QRemoteObjectPendingReply(const QRemoteObjectPendingCall &call)
+ : QRemoteObjectPendingCall(call)
+ {
+ }
+
+ QRemoteObjectPendingReply &operator=(const QRemoteObjectPendingCall &other)
+ {
+ QRemoteObjectPendingCall::operator=(other);
+ return *this;
+ }
+
+ Type returnValue() const
+ {
+ return qvariant_cast<Type>(QRemoteObjectPendingCall::returnValue());
+ }
+
+};
+
+QT_END_NAMESPACE
+
+#endif
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QREMOTEOBJECTPENDINGCALL_P_H
+#define QREMOTEOBJECTPENDINGCALL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qremoteobjectpendingcall.h"
+
+#include <QtCore/qmutex.h>
+#include <QtCore/private/qglobal_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QRemoteObjectPendingCallWatcherHelper;
+class QRemoteObjectReplicaImplementation;
+
+class QRemoteObjectPendingCallData : public QSharedData
+{
+public:
+ typedef QExplicitlySharedDataPointer<QRemoteObjectPendingCallData> Ptr;
+
+ explicit QRemoteObjectPendingCallData(int serialId = -1, QRemoteObjectReplicaImplementation *replica = nullptr);
+ ~QRemoteObjectPendingCallData();
+
+ QRemoteObjectReplicaImplementation *replica;
+ int serialId;
+
+ QVariant returnValue;
+ QRemoteObjectPendingCall::Error error;
+
+ mutable QMutex mutex;
+
+ mutable QScopedPointer<QRemoteObjectPendingCallWatcherHelper> watcherHelper;
+};
+
+class QRemoteObjectPendingCallWatcherHelper: public QObject
+{
+ Q_OBJECT
+public:
+ void add(QRemoteObjectPendingCallWatcher *watcher);
+
+ void emitSignals();
+
+Q_SIGNALS:
+ void finished();
+};
+
+QT_END_NAMESPACE
+
+#endif
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qremoteobjectregistry.h"
+#include "qremoteobjectreplica_p.h"
+
+#include <private/qobject_p.h>
+#include <QtCore/qset.h>
+#include <QtCore/qdatastream.h>
+
+QT_BEGIN_NAMESPACE
+
+class QRemoteObjectRegistryPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QRemoteObjectRegistry)
+
+ QRemoteObjectSourceLocations sourceLocationsActualCalculation() const
+ {
+ return q_func()->propAsVariant(0).value<QRemoteObjectSourceLocations>();
+ }
+ Q_OBJECT_COMPUTED_PROPERTY(QRemoteObjectRegistryPrivate, QRemoteObjectSourceLocations,
+ sourceLocations,
+ &QRemoteObjectRegistryPrivate::sourceLocationsActualCalculation)
+ QRemoteObjectSourceLocations hostedSources;
+};
+
+/*!
+ \class QRemoteObjectRegistry
+ \inmodule QtRemoteObjects
+ \brief A class holding information about \l {Source} objects available on the Qt Remote Objects network.
+
+ The Registry is a special Source/Replica pair held by a \l
+ {QRemoteObjectNode} {node} itself. It knows about all other \l {Source}s
+ available on the network, and simplifies the process of connecting to other
+ \l {QRemoteObjectNode} {node}s.
+*/
+QRemoteObjectRegistry::QRemoteObjectRegistry(QObject *parent)
+ : QRemoteObjectReplica(*new QRemoteObjectRegistryPrivate, parent)
+{
+ connect(this, &QRemoteObjectRegistry::stateChanged, this, &QRemoteObjectRegistry::pushToRegistryIfNeeded);
+}
+
+QRemoteObjectRegistry::QRemoteObjectRegistry(QRemoteObjectNode *node, const QString &name, QObject *parent)
+ : QRemoteObjectReplica(*new QRemoteObjectRegistryPrivate, parent)
+{
+ connect(this, &QRemoteObjectRegistry::stateChanged, this, &QRemoteObjectRegistry::pushToRegistryIfNeeded);
+ initializeNode(node, name);
+}
+
+/*!
+ \fn void QRemoteObjectRegistry::remoteObjectAdded(const QRemoteObjectSourceLocation &entry)
+
+ This signal is emitted whenever a new source location is added to the registry.
+
+ \a entry is a QRemoteObjectSourceLocation, a typedef for QPair<QString, QUrl>.
+
+ \sa remoteObjectRemoved()
+*/
+
+/*!
+ \fn void QRemoteObjectRegistry::remoteObjectRemoved(const QRemoteObjectSourceLocation &entry)
+
+ This signal is emitted whenever a Source location is removed from the Registry.
+
+ \a entry is a QRemoteObjectSourceLocation, a typedef for QPair<QString, QUrl>.
+
+ \sa remoteObjectAdded()
+*/
+
+/*!
+ \property QRemoteObjectRegistry::sourceLocations
+ \brief The set of sources known to the registry.
+
+ This property is a QRemoteObjectSourceLocations, which is a typedef for
+ QHash<QString, QUrl>. Each known \l Source is the QString key, while the
+ url for the host node is the corresponding value for that key in the hash.
+*/
+
+/*!
+ Destructor for QRemoteObjectRegistry.
+*/
+QRemoteObjectRegistry::~QRemoteObjectRegistry()
+{}
+
+void QRemoteObjectRegistry::registerMetatypes()
+{
+ static bool initialized = false;
+ if (initialized)
+ return;
+ initialized = true;
+ qRegisterMetaType<QRemoteObjectSourceLocation>();
+ qRegisterMetaType<QRemoteObjectSourceLocations>();
+}
+
+void QRemoteObjectRegistry::initialize()
+{
+ QRemoteObjectRegistry::registerMetatypes();
+ QVariantList properties;
+ properties.reserve(3);
+ properties << QVariant::fromValue(QRemoteObjectSourceLocations());
+ properties << QVariant::fromValue(QRemoteObjectSourceLocation());
+ properties << QVariant::fromValue(QRemoteObjectSourceLocation());
+ setProperties(std::move(properties));
+}
+
+void QRemoteObjectRegistry::notifySourceLocationsChanged()
+{
+ d_func()->sourceLocations.notify();
+}
+
+/*!
+ Returns a QRemoteObjectSourceLocations object, which includes the name
+ and additional information of all sources known to the registry.
+*/
+QRemoteObjectSourceLocations QRemoteObjectRegistry::sourceLocations() const
+{
+ return d_func()->sourceLocations.value();
+}
+
+QBindable<QRemoteObjectSourceLocations> QRemoteObjectRegistry::bindableSourceLocations() const
+{
+ return &d_func()->sourceLocations;
+}
+
+/*!
+ \internal
+*/
+void QRemoteObjectRegistry::addSource(const QRemoteObjectSourceLocation &entry)
+{
+ Q_D(QRemoteObjectRegistry);
+ if (d->hostedSources.contains(entry.first)) {
+ qCWarning(QT_REMOTEOBJECT) << "Node warning: ignoring source" << entry.first
+ << "as this node already has a source by that name.";
+ return;
+ }
+ d->hostedSources.insert(entry.first, entry.second);
+ if (state() != QRemoteObjectReplica::State::Valid)
+ return;
+
+ if (sourceLocations().contains(entry.first)) {
+ qCWarning(QT_REMOTEOBJECT) << "Node warning: ignoring source" << entry.first
+ << "as another source (" << sourceLocations().value(entry.first)
+ << ") has already registered that name.";
+ return;
+ }
+ qCDebug(QT_REMOTEOBJECT) << "An entry was added to the registry - Sending to source" << entry.first << entry.second;
+ // This does not set any data to avoid a coherency problem between client and server
+ static int index = QRemoteObjectRegistry::staticMetaObject.indexOfMethod("addSource(QRemoteObjectSourceLocation)");
+ QVariantList args;
+ args << QVariant::fromValue(entry);
+ send(QMetaObject::InvokeMetaMethod, index, args);
+}
+
+/*!
+ \internal
+*/
+void QRemoteObjectRegistry::removeSource(const QRemoteObjectSourceLocation &entry)
+{
+ Q_D(QRemoteObjectRegistry);
+ if (!d->hostedSources.contains(entry.first))
+ return;
+
+ d->hostedSources.remove(entry.first);
+ if (state() != QRemoteObjectReplica::State::Valid)
+ return;
+
+ qCDebug(QT_REMOTEOBJECT) << "An entry was removed from the registry - Sending to source" << entry.first << entry.second;
+ // This does not set any data to avoid a coherency problem between client and server
+ static int index = QRemoteObjectRegistry::staticMetaObject.indexOfMethod("removeSource(QRemoteObjectSourceLocation)");
+ QVariantList args;
+ args << QVariant::fromValue(entry);
+ send(QMetaObject::InvokeMetaMethod, index, args);
+}
+
+/*!
+ \internal
+ This internal function supports the edge case where the \l Registry
+ is connected after \l Source objects are added to this \l Node, or
+ the connection to the \l Registry is lost. When connected/reconnected, this
+ function synchronizes local \l Source objects with the \l Registry.
+*/
+void QRemoteObjectRegistry::pushToRegistryIfNeeded()
+{
+ Q_D(QRemoteObjectRegistry);
+ if (state() != QRemoteObjectReplica::State::Valid)
+ return;
+
+ if (d->hostedSources.isEmpty())
+ return;
+
+ const auto &sourceLocs = sourceLocations();
+ for (auto it = d->hostedSources.begin(); it != d->hostedSources.end(); ) {
+ const QString &loc = it.key();
+ const auto sourceLocsIt = sourceLocs.constFind(loc);
+ if (sourceLocsIt != sourceLocs.cend()) {
+ qCWarning(QT_REMOTEOBJECT) << "Node warning: Ignoring Source" << loc << "as another source ("
+ << sourceLocsIt.value() << ") has already registered that name.";
+ it = d->hostedSources.erase(it);
+ } else {
+ static const int index = QRemoteObjectRegistry::staticMetaObject.indexOfMethod("addSource(QRemoteObjectSourceLocation)");
+ QVariantList args{QVariant::fromValue(QRemoteObjectSourceLocation(loc, it.value()))};
+ send(QMetaObject::InvokeMetaMethod, index, args);
+ ++it;
+ }
+ }
+}
+
+QT_END_NAMESPACE
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QREMOTEOBJECTREGISTRY_P_H
+#define QREMOTEOBJECTREGISTRY_P_H
+
+#include <QtRemoteObjects/qremoteobjectreplica.h>
+
+QT_BEGIN_NAMESPACE
+
+class QRemoteObjectRegistryPrivate;
+class QRemoteObjectNodePrivate;
+
+class Q_REMOTEOBJECTS_EXPORT QRemoteObjectRegistry : public QRemoteObjectReplica
+{
+ Q_OBJECT
+ Q_CLASSINFO(QCLASSINFO_REMOTEOBJECT_TYPE, "Registry")
+
+ Q_PROPERTY(QRemoteObjectSourceLocations sourceLocations READ sourceLocations STORED false
+ BINDABLE bindableSourceLocations)
+
+public:
+ ~QRemoteObjectRegistry() override;
+ static void registerMetatypes();
+
+ QRemoteObjectSourceLocations sourceLocations() const;
+ QBindable<QRemoteObjectSourceLocations> bindableSourceLocations() const;
+
+Q_SIGNALS:
+ void remoteObjectAdded(const QRemoteObjectSourceLocation &entry);
+ void remoteObjectRemoved(const QRemoteObjectSourceLocation &entry);
+
+protected Q_SLOTS:
+ void addSource(const QRemoteObjectSourceLocation &entry);
+ void removeSource(const QRemoteObjectSourceLocation &entry);
+ void pushToRegistryIfNeeded();
+
+private:
+ void initialize() override;
+ void notifySourceLocationsChanged();
+
+ explicit QRemoteObjectRegistry(QObject *parent = nullptr);
+ explicit QRemoteObjectRegistry(QRemoteObjectNode *node, const QString &name, QObject *parent = nullptr);
+
+ Q_DECLARE_PRIVATE(QRemoteObjectRegistry)
+ friend class QT_PREPEND_NAMESPACE(QRemoteObjectNode);
+ friend class QT_PREPEND_NAMESPACE(QRemoteObjectNodePrivate);
+};
+
+QT_END_NAMESPACE
+
+#endif
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qremoteobjectregistrysource_p.h"
+#include <QtCore/qdatastream.h>
+
+QT_BEGIN_NAMESPACE
+
+QRegistrySource::QRegistrySource(QObject *parent)
+ : QObject(parent)
+{
+}
+
+QRegistrySource::~QRegistrySource()
+{
+}
+
+QRemoteObjectSourceLocations QRegistrySource::sourceLocations() const
+{
+ qCDebug(QT_REMOTEOBJECT) << "sourceLocations property requested on RegistrySource" << m_sourceLocations;
+ return m_sourceLocations;
+}
+
+void QRegistrySource::removeServer(const QUrl &url)
+{
+ for (auto it = m_sourceLocations.begin(), end = m_sourceLocations.end(); it != end; /* erasing */) {
+ if (it.value().hostUrl == url)
+ it = m_sourceLocations.erase(it);
+ else
+ ++it;
+ }
+}
+
+void QRegistrySource::addSource(const QRemoteObjectSourceLocation &entry)
+{
+ qCDebug(QT_REMOTEOBJECT) << "An entry was added to the RegistrySource" << entry;
+ if (m_sourceLocations.contains(entry.first)) {
+ if (m_sourceLocations[entry.first].hostUrl == entry.second.hostUrl)
+ qCWarning(QT_REMOTEOBJECT) << "Node warning: Ignoring Source" << entry.first
+ << "as this Node already has a Source by that name.";
+ else
+ qCWarning(QT_REMOTEOBJECT) << "Node warning: Ignoring Source" << entry.first
+ << "as another source (" << m_sourceLocations[entry.first]
+ << ") has already registered that name.";
+ return;
+ }
+ m_sourceLocations[entry.first] = entry.second;
+ emit remoteObjectAdded(entry);
+}
+
+void QRegistrySource::removeSource(const QRemoteObjectSourceLocation &entry)
+{
+ if (m_sourceLocations.contains(entry.first) && m_sourceLocations[entry.first].hostUrl == entry.second.hostUrl) {
+ m_sourceLocations.remove(entry.first);
+ emit remoteObjectRemoved(entry);
+ }
+}
+
+QT_END_NAMESPACE
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QREGISTRYSOURCE_P_H
+#define QREGISTRYSOURCE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qtremoteobjectglobal.h"
+#include "private/qglobal_p.h"
+
+#include <QtCore/qobject.h>
+
+QT_BEGIN_NAMESPACE
+
+class QRegistrySource : public QObject
+{
+ Q_OBJECT
+ Q_CLASSINFO(QCLASSINFO_REMOTEOBJECT_TYPE, "Registry")
+
+ Q_PROPERTY(QRemoteObjectSourceLocations sourceLocations READ sourceLocations)
+
+public:
+ explicit QRegistrySource(QObject *parent = nullptr);
+ ~QRegistrySource() override;
+
+ QRemoteObjectSourceLocations sourceLocations() const;
+
+Q_SIGNALS:
+ void remoteObjectAdded(const QRemoteObjectSourceLocation &entry);
+ void remoteObjectRemoved(const QRemoteObjectSourceLocation &entry);
+
+public Q_SLOTS:
+ void addSource(const QRemoteObjectSourceLocation &entry);
+ void removeSource(const QRemoteObjectSourceLocation &entry);
+ void removeServer(const QUrl &url);
+
+private:
+ QRemoteObjectSourceLocations m_sourceLocations;
+};
+
+QT_END_NAMESPACE
+
+#endif
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qremoteobjectreplica.h"
+#include "qremoteobjectreplica_p.h"
+
+#include "qremoteobjectnode.h"
+#include "qremoteobjectnode_p.h"
+#include "qremoteobjectdynamicreplica.h"
+#include "qremoteobjectpacket_p.h"
+#include "qremoteobjectpendingcall_p.h"
+#include "qconnectionfactories_p.h"
+#include "qremoteobjectsource_p.h"
+
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qdatastream.h>
+#include <QtCore/qelapsedtimer.h>
+#include <QtCore/qvariant.h>
+#include <QtCore/qthread.h>
+
+#include <limits>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QRemoteObjectPackets;
+
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_GCC("-Wtautological-compare")
+
+#if !defined(Q_OS_WIN) && !defined(Q_OS_INTEGRITY)
+Q_STATIC_ASSERT_X(&QRemoteObjectReplica::staticMetaObject == &QRemoteObjectDynamicReplica::staticMetaObject,
+ "m_signalOffset initializer expects QRemoteObjectDynamicReplica to not have a unique staticMetaObject");
+#endif
+
+QT_WARNING_POP
+
+// If QRemoteObjectDynamicReplica ever gets its own staticMetaObject, some commented out code will need to be
+// used. It was changed to avoid a Coverity complaint. We use the above static assert to detect if this changes
+// in the future. See FIX #1, #2, #3 in this file.
+
+QRemoteObjectReplicaImplementation::QRemoteObjectReplicaImplementation(const QString &name, const QMetaObject *meta, QRemoteObjectNode *_node)
+ : QObject(nullptr), m_objectName(name), m_metaObject(meta), m_numSignals(0), m_methodOffset(0)
+ // Uncomment the following two lines if QRemoteObjectDynamicReplica gets a unique staticMetaObject (FIX #1, #2)
+ //, m_signalOffset(meta ? QRemoteObjectReplica::staticMetaObject.methodCount() : QRemoteObjectDynamicReplica::staticMetaObject.methodCount())
+ //, m_propertyOffset(meta ? QRemoteObjectReplica::staticMetaObject.propertyCount() : QRemoteObjectDynamicReplica::staticMetaObject.propertyCount())
+ , m_signalOffset(QRemoteObjectReplica::staticMetaObject.methodCount())
+ , m_propertyOffset(QRemoteObjectReplica::staticMetaObject.propertyCount())
+ , m_node(_node)
+ , m_objectSignature(QtPrivate::qtro_classinfo_signature(m_metaObject))
+ , m_state(meta ? QRemoteObjectReplica::Default : QRemoteObjectReplica::Uninitialized)
+{
+}
+
+QRemoteObjectReplicaImplementation::~QRemoteObjectReplicaImplementation()
+{
+}
+
+QConnectedReplicaImplementation::QConnectedReplicaImplementation(const QString &name, const QMetaObject *meta, QRemoteObjectNode *node)
+ : QRemoteObjectReplicaImplementation(name, meta, node), connectionToSource(nullptr)
+{
+ m_heartbeatTimer.setTimerType(Qt::CoarseTimer);
+ m_heartbeatTimer.setSingleShot(true);
+ m_heartbeatTimer.setInterval(node->heartbeatInterval());
+
+ connect(node, &QRemoteObjectNode::heartbeatIntervalChanged, this, [this](int interval) {
+ m_heartbeatTimer.stop();
+ m_heartbeatTimer.setInterval(interval);
+ if (interval)
+ m_heartbeatTimer.start();
+ });
+ connect(&m_heartbeatTimer, &QTimer::timeout, this, [this] {
+ // TODO: Revisit if a baseclass method can be used to avoid specialized cast
+ // conditional logic.
+
+ if (m_pendingCalls.contains(0)) {
+ m_pendingCalls.take(0);
+ // The source didn't respond in time, disconnect the connection
+ if (connectionToSource) {
+ auto clientIo = qobject_cast<QtROClientIoDevice *>(connectionToSource);
+ if (clientIo)
+ clientIo->disconnectFromServer();
+ else
+ connectionToSource->close();
+ }
+ } else {
+ if (connectionToSource.isNull()) {
+ qCDebug(QT_REMOTEOBJECT) << "Ignoring heartbeat as there is no source connected.";
+ return;
+ }
+ connectionToSource->d_func()->m_codec->serializePingPacket(m_objectName);
+ if (sendCommandWithReply(0).d->serialId == -1) {
+ m_heartbeatTimer.stop();
+ auto clientIo = qobject_cast<QtROClientIoDevice *>(connectionToSource);
+ if (clientIo)
+ clientIo->disconnectFromServer();
+ else
+ connectionToSource->close();
+ }
+ }
+ });
+
+ if (!meta)
+ return;
+
+ auto offsetMeta = m_metaObject;
+ QtRemoteObjects::getTypeNameAndMetaobjectFromClassInfo(offsetMeta);
+ for (int index = offsetMeta->propertyOffset(); index < offsetMeta->propertyCount(); ++index) {
+ const QMetaProperty property = offsetMeta->property(index);
+ if (property.metaType().flags().testFlag(QMetaType::PointerToQObject))
+ m_childIndices << index - offsetMeta->propertyOffset();
+ }
+}
+
+QConnectedReplicaImplementation::~QConnectedReplicaImplementation()
+{
+ if (!connectionToSource.isNull()) {
+ qCDebug(QT_REMOTEOBJECT) << "Replica deleted: sending RemoveObject to RemoteObjectSource" << m_objectName;
+ connectionToSource->d_func()->m_codec->serializeRemoveObjectPacket(m_objectName);
+ sendCommand();
+ }
+ for (auto prop : m_propertyStorage) {
+ if (prop.canConvert<QObject*>())
+ prop.value<QObject *>()->deleteLater();
+ }
+}
+
+bool QRemoteObjectReplicaImplementation::needsDynamicInitialization() const
+{
+ return m_metaObject == nullptr;
+}
+
+void QRemoteObjectReplicaImplementation::setState(QRemoteObjectReplica::State state)
+{
+ if (m_state.loadAcquire() != QRemoteObjectReplica::Suspect && m_state.loadAcquire() >= state)
+ return;
+
+ int oldState = m_state.loadAcquire();
+ m_state.storeRelease(state);
+
+ // We should emit initialized before emitting any changed signals in case connections are made in a
+ // Slot responding to initialized/validChanged.
+ if (m_state.loadAcquire() == QRemoteObjectReplica::Valid) {
+ // we're initialized now, emit signal
+ emitInitialized();
+ }
+
+ const static int stateChangedIndex = QRemoteObjectReplica::staticMetaObject.indexOfMethod("stateChanged(State,State)");
+ Q_ASSERT(stateChangedIndex != -1);
+ void *args[] = {nullptr, &state, &oldState};
+ QMetaObject::activate(this, metaObject(), stateChangedIndex, args);
+}
+
+void QRemoteObjectReplicaImplementation::emitNotified()
+{
+ const static int notifiedIndex = QRemoteObjectReplica::staticMetaObject.indexOfMethod("notified()");
+ Q_ASSERT(notifiedIndex != -1);
+ void *args[] = {nullptr};
+ QMetaObject::activate(this, metaObject(), notifiedIndex, args);
+}
+
+bool QConnectedReplicaImplementation::sendCommand()
+{
+ Q_ASSERT(connectionToSource);
+ if (!connectionToSource->isOpen())
+ return false;
+
+ connectionToSource->d_func()->m_codec->send(connectionToSource);
+ if (m_heartbeatTimer.interval())
+ m_heartbeatTimer.start();
+ return true;
+}
+
+QList<int> QConnectedReplicaImplementation::childIndices() const
+{
+ return m_childIndices;
+}
+
+void QConnectedReplicaImplementation::initialize(QVariantList &&values)
+{
+ qCDebug(QT_REMOTEOBJECT) << "initialize()" << m_propertyStorage.size();
+ const int nParam = int(values.size());
+ QVarLengthArray<int> changedProperties(nParam);
+ const int offset = m_propertyOffset;
+ for (int i = 0; i < nParam; ++i) {
+ qCDebug(QT_REMOTEOBJECT) << " in loop" << i << m_propertyStorage.size();
+ changedProperties[i] = -1;
+ if (m_propertyStorage[i] != values.at(i)) {
+ const QMetaProperty property = m_metaObject->property(i+offset);
+ m_propertyStorage[i] = QRemoteObjectPackets::decodeVariant(std::move(values[i]), property.metaType());
+ changedProperties[i] = i;
+ }
+ qCDebug(QT_REMOTEOBJECT) << "SETPROPERTY" << i << m_metaObject->property(i+offset).name()
+ << m_propertyStorage[i].typeName()
+ << m_propertyStorage[i].toString();
+ }
+
+ Q_ASSERT(m_state.loadAcquire() < QRemoteObjectReplica::Valid || m_state.loadAcquire() == QRemoteObjectReplica::Suspect);
+ setState(QRemoteObjectReplica::Valid);
+
+ void *args[] = {nullptr, nullptr};
+ for (int i = 0; i < nParam; ++i) {
+ if (changedProperties[i] < 0)
+ continue;
+ const int notifyIndex = m_metaObject->property(changedProperties[i]+offset).notifySignalIndex();
+ if (notifyIndex < 0)
+ continue;
+ qCDebug(QT_REMOTEOBJECT) << " Before activate" << notifyIndex << m_metaObject->property(notifyIndex).name();
+ args[1] = m_propertyStorage[i].data();
+ QMetaObject::activate(this, metaObject(), notifyIndex, args);
+ }
+ emitNotified();
+
+ qCDebug(QT_REMOTEOBJECT) << "isSet = true for" << m_objectName;
+ if (node()->heartbeatInterval())
+ m_heartbeatTimer.start();
+}
+
+void QRemoteObjectReplicaImplementation::emitInitialized()
+{
+ const static int initializedIndex = QRemoteObjectReplica::staticMetaObject.indexOfMethod("initialized()");
+ Q_ASSERT(initializedIndex != -1);
+ void *noArgs[] = {nullptr};
+ QMetaObject::activate(this, metaObject(), initializedIndex, noArgs);
+}
+
+/*!
+ \internal
+*/
+void QRemoteObjectReplica::persistProperties(const QString &repName, const QByteArray &repSig, const QVariantList &props) const
+{
+ if (!node()) {
+ qWarning("Tried calling persistProperties on a replica (%s) that hasn't been initialized with a node", qPrintable(repName));
+ return;
+ }
+ node()->persistProperties(repName, repSig, props);
+}
+
+/*!
+ \internal
+*/
+QVariantList QRemoteObjectReplica::retrieveProperties(const QString &repName, const QByteArray &repSig) const
+{
+ if (!node()) {
+ qWarning("Tried calling retrieveProperties on a replica (%s) that hasn't been initialized with a node", qPrintable(repName));
+ return QVariantList();
+ }
+ return node()->retrieveProperties(repName, repSig);
+}
+
+void QRemoteObjectReplicaImplementation::setDynamicMetaObject(const QMetaObject *meta)
+{
+ Q_ASSERT(!m_metaObject);
+
+ m_metaObject = meta;
+}
+
+void QConnectedReplicaImplementation::setDynamicMetaObject(const QMetaObject *meta)
+{
+ QRemoteObjectReplicaImplementation::setDynamicMetaObject(meta);
+
+ for (int index = m_metaObject->propertyOffset(); index < m_metaObject->propertyCount(); ++index) {
+ const QMetaProperty property = m_metaObject->property(index);
+ if (property.metaType().flags().testFlag(QMetaType::PointerToQObject))
+ m_childIndices << index - m_metaObject->propertyOffset();
+ }
+}
+
+void QRemoteObjectReplicaImplementation::setDynamicProperties(QVariantList &&values)
+{
+ const int offset = m_propertyOffset;
+ int propertyIndex = -1;
+ for (auto &prop : values) {
+ propertyIndex++;
+ const QMetaProperty property = m_metaObject->property(propertyIndex+offset);
+ prop = QRemoteObjectPackets::decodeVariant(std::move(prop), property.metaType());
+ }
+ //rely on order of properties;
+ setProperties(std::move(values));
+}
+
+void QConnectedReplicaImplementation::setDynamicProperties(QVariantList &&values)
+{
+ QRemoteObjectReplicaImplementation::setDynamicProperties(std::move(values));
+ for (QRemoteObjectReplica *obj : qExchange(m_parentsNeedingConnect, {}))
+ configurePrivate(obj);
+
+ Q_ASSERT(m_state.loadAcquire() < QRemoteObjectReplica::Valid);
+ setState(QRemoteObjectReplica::Valid);
+
+ void *args[] = {nullptr, nullptr};
+ for (int index = m_metaObject->propertyOffset(); index < m_metaObject->propertyCount(); ++index) {
+ const QMetaProperty mp = m_metaObject->property(index);
+ if (mp.hasNotifySignal()) {
+ qCDebug(QT_REMOTEOBJECT) << " Before activate" << index << m_metaObject->property(index).name();
+ args[1] = this->m_propertyStorage[index-m_propertyOffset].data();
+ QMetaObject::activate(this, metaObject(), mp.notifySignalIndex(), args);
+ }
+ }
+ emitNotified();
+
+ qCDebug(QT_REMOTEOBJECT) << "isSet = true for" << m_objectName;
+}
+
+bool QConnectedReplicaImplementation::isInitialized() const
+{
+ return m_state.loadAcquire() > QRemoteObjectReplica::Default && m_state.loadAcquire() != QRemoteObjectReplica::SignatureMismatch;
+}
+
+bool QConnectedReplicaImplementation::waitForSource(int timeout)
+{
+ switch (state()) {
+ case QRemoteObjectReplica::State::Valid:
+ return true;
+ case QRemoteObjectReplica::State::SignatureMismatch:
+ return false;
+ default:
+ break;
+ }
+
+ const static int stateChangedIndex = QRemoteObjectReplica::staticMetaObject.indexOfMethod("stateChanged(State,State)");
+ Q_ASSERT(stateChangedIndex != -1);
+
+ QEventLoop loop;
+ QMetaObject::connect(this, stateChangedIndex,
+ &loop, QEventLoop::staticMetaObject.indexOfMethod("quit()"),
+ Qt::DirectConnection, nullptr);
+
+ QTimer t; // NB: Related to QTBUG-94570 - don't use QTimer::singleShot here.
+ if (timeout >= 0) {
+ t.setSingleShot(true);
+ connect(&t, &QTimer::timeout, &loop, &QEventLoop::quit);
+ t.start(timeout);
+ }
+
+ // enter the event loop and wait for a reply
+ loop.exec(QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents);
+
+ return state() == QRemoteObjectReplica::State::Valid;
+}
+
+void QConnectedReplicaImplementation::_q_send(QMetaObject::Call call, int index, const QVariantList &args)
+{
+ static const bool debugArgs = qEnvironmentVariableIsSet("QT_REMOTEOBJECT_DEBUG_ARGUMENTS");
+
+ Q_ASSERT(call == QMetaObject::InvokeMetaMethod || call == QMetaObject::WriteProperty);
+ if (connectionToSource.isNull()) {
+ qCWarning(QT_REMOTEOBJECT) << "connectionToSource is null";
+ return;
+ }
+
+ if (call == QMetaObject::InvokeMetaMethod) {
+ if (debugArgs) {
+ qCDebug(QT_REMOTEOBJECT) << "Send" << call << this->m_metaObject->method(index).name() << index << args << connectionToSource;
+ } else {
+ qCDebug(QT_REMOTEOBJECT) << "Send" << call << this->m_metaObject->method(index).name() << index << connectionToSource;
+ }
+ if (index < m_methodOffset) //index - m_methodOffset < 0 is invalid, and can't be resolved on the Source side
+ qCWarning(QT_REMOTEOBJECT) << "Skipping invalid method invocation. Index not found:" << index << "( offset =" << m_methodOffset << ") object:" << m_objectName << this->m_metaObject->method(index).name();
+ else {
+ connectionToSource->d_func()->m_codec->serializeInvokePacket(m_objectName, call, index - m_methodOffset, args);
+ sendCommand();
+ }
+ } else {
+ qCDebug(QT_REMOTEOBJECT) << "Send" << call << this->m_metaObject->property(index).name() << index << args << connectionToSource;
+ if (index < m_propertyOffset) //index - m_propertyOffset < 0 is invalid, and can't be resolved on the Source side
+ qCWarning(QT_REMOTEOBJECT) << "Skipping invalid property invocation. Index not found:" << index << "( offset =" << m_propertyOffset << ") object:" << m_objectName << this->m_metaObject->property(index).name();
+ else {
+ connectionToSource->d_func()->m_codec->serializeInvokePacket(m_objectName, call, index - m_propertyOffset, args);
+ sendCommand();
+ }
+ }
+}
+
+QRemoteObjectPendingCall QConnectedReplicaImplementation::_q_sendWithReply(QMetaObject::Call call, int index, const QVariantList &args)
+{
+ Q_ASSERT(call == QMetaObject::InvokeMetaMethod);
+ if (connectionToSource.isNull()) {
+ qCWarning(QT_REMOTEOBJECT) << "connectionToSource is null";
+ return QRemoteObjectPendingCall();
+ }
+
+ qCDebug(QT_REMOTEOBJECT) << "Send" << call << this->m_metaObject->method(index).name() << index << args << connectionToSource;
+ int serialId = (m_curSerialId == std::numeric_limits<int>::max() ? 1 : m_curSerialId++);
+ connectionToSource->d_func()->m_codec->serializeInvokePacket(m_objectName, call, index - m_methodOffset, args, serialId);
+ return sendCommandWithReply(serialId);
+}
+
+QRemoteObjectPendingCall QConnectedReplicaImplementation::sendCommandWithReply(int serialId)
+{
+ bool success = sendCommand();
+ if (!success) {
+ return QRemoteObjectPendingCall(); // invalid
+ }
+
+ qCDebug(QT_REMOTEOBJECT) << "Sent InvokePacket with serial id:" << serialId;
+ QRemoteObjectPendingCall pendingCall(new QRemoteObjectPendingCallData(serialId, this));
+ Q_ASSERT(!m_pendingCalls.contains(serialId));
+ m_pendingCalls[serialId] = pendingCall;
+ return pendingCall;
+}
+
+void QConnectedReplicaImplementation::notifyAboutReply(int ackedSerialId, const QVariant &value)
+{
+ QRemoteObjectPendingCall call = m_pendingCalls.take(ackedSerialId);
+ if (ackedSerialId == 0) {
+ m_heartbeatTimer.stop();
+ if (m_heartbeatTimer.interval())
+ m_heartbeatTimer.start();
+ return;
+ }
+
+ QMutexLocker mutex(&call.d->mutex);
+
+ // clear error flag
+ call.d->error = QRemoteObjectPendingCall::NoError;
+ call.d->returnValue = value;
+
+ // notify watchers if needed
+ if (call.d->watcherHelper)
+ call.d->watcherHelper->emitSignals();
+}
+
+bool QConnectedReplicaImplementation::waitForFinished(const QRemoteObjectPendingCall& call, int timeout)
+{
+ if (!call.d->watcherHelper)
+ call.d->watcherHelper.reset(new QRemoteObjectPendingCallWatcherHelper);
+
+ call.d->mutex.unlock();
+
+ QEventLoop loop;
+ loop.connect(call.d->watcherHelper.data(), &QRemoteObjectPendingCallWatcherHelper::finished,
+ &loop, &QEventLoop::quit);
+
+ QTimer t; // NB: Related to QTBUG-94570 - don't use QTimer::singleShot here.
+ if (timeout >= 0) {
+ t.setSingleShot(true);
+ connect(&t, &QTimer::timeout, &loop, &QEventLoop::quit);
+ t.start(timeout);
+ }
+
+ // enter the event loop and wait for a reply
+ loop.exec(QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents);
+
+ call.d->mutex.lock();
+
+ return call.d->error != QRemoteObjectPendingCall::InvalidMessage;
+}
+
+const QVariant QConnectedReplicaImplementation::getProperty(int i) const
+{
+ Q_ASSERT_X(i >= 0 && i < m_propertyStorage.size(), __FUNCTION__, qPrintable(QString(QLatin1String("0 <= %1 < %2")).arg(i).arg(m_propertyStorage.size())));
+ return m_propertyStorage[i];
+}
+
+void QConnectedReplicaImplementation::setProperties(QVariantList &&properties)
+{
+ Q_ASSERT(m_propertyStorage.isEmpty());
+ m_propertyStorage.reserve(properties.size());
+ m_propertyStorage = std::move(properties);
+}
+
+void QConnectedReplicaImplementation::setProperty(int i, const QVariant &prop)
+{
+ m_propertyStorage[i] = prop;
+}
+
+void QConnectedReplicaImplementation::setConnection(QtROIoDeviceBase *conn)
+{
+ if (connectionToSource.isNull()) {
+ connectionToSource = conn;
+ qCDebug(QT_REMOTEOBJECT) << "setConnection started" << conn << m_objectName;
+ }
+ requestRemoteObjectSource();
+}
+
+void QConnectedReplicaImplementation::setDisconnected()
+{
+ Q_ASSERT(connectionToSource);
+ connectionToSource.clear();
+ setState(QRemoteObjectReplica::State::Suspect);
+ for (const int index : childIndices()) {
+ auto pointerToQObject = qvariant_cast<QObject *>(getProperty(index));
+ auto child = qobject_cast<QRemoteObjectReplica *>(pointerToQObject);
+ if (child)
+ static_cast<QConnectedReplicaImplementation *>(child->d_impl.data())->setDisconnected();
+ }
+}
+
+void QConnectedReplicaImplementation::requestRemoteObjectSource()
+{
+ Q_ASSERT(connectionToSource);
+ connectionToSource->d_func()->m_codec->serializeAddObjectPacket(m_objectName, needsDynamicInitialization());
+ sendCommand();
+}
+
+void QRemoteObjectReplicaImplementation::configurePrivate(QRemoteObjectReplica *rep)
+{
+ qCDebug(QT_REMOTEOBJECT) << "configurePrivate starting for" << this->m_objectName;
+ //We need to connect the Replicant only signals too
+ // Uncomment the following two lines if QRemoteObjectDynamicReplica gets a unique staticMetaObject (FIX #3)
+ //const QMetaObject *m = rep->inherits("QRemoteObjectDynamicReplica") ?
+ // &QRemoteObjectDynamicReplica::staticMetaObject : &QRemoteObjectReplica::staticMetaObject;
+ const QMetaObject *m = &QRemoteObjectReplica::staticMetaObject;
+ for (int i = m->methodOffset(); i < m->methodCount(); ++i)
+ {
+ const QMetaMethod mm = m->method(i);
+ if (mm.methodType() == QMetaMethod::Signal) {
+ const bool res = QMetaObject::connect(this, i, rep, i, Qt::DirectConnection, nullptr);
+ qCDebug(QT_REMOTEOBJECT) << " Rep connect"<<i<<res<<mm.name();
+ Q_UNUSED(res)
+ }
+ }
+ if (m_methodOffset == 0) //We haven't initialized the offsets yet
+ {
+ const int index = m_metaObject->indexOfClassInfo(QCLASSINFO_REMOTEOBJECT_TYPE);
+ const QMetaObject *metaObject = m_metaObject;
+ if (index != -1) { //We have an object created from repc or at least with QCLASSINFO defined
+ while (true) {
+ Q_ASSERT(metaObject->superClass()); //This recurses to QObject, which doesn't have QCLASSINFO_REMOTEOBJECT_TYPE
+ if (index != metaObject->superClass()->indexOfClassInfo(QCLASSINFO_REMOTEOBJECT_TYPE)) //At the point we don't find the same QCLASSINFO_REMOTEOBJECT_TYPE,
+ //we have the metaobject we should work from
+ break;
+ metaObject = metaObject->superClass();
+ }
+ }
+
+ for (int i = m_signalOffset; i < metaObject->methodCount(); ++i) {
+ const QMetaMethod mm = metaObject->method(i);
+ if (mm.methodType() == QMetaMethod::Signal) {
+ ++m_numSignals;
+ const bool res = QMetaObject::connect(this, i, rep, i, Qt::DirectConnection, nullptr);
+ qCDebug(QT_REMOTEOBJECT) << " Connect"<<i<<res<<mm.name();
+ Q_UNUSED(res)
+ }
+ }
+ m_methodOffset = m_signalOffset + m_numSignals;
+ qCDebug(QT_REMOTEOBJECT) << QStringLiteral("configurePrivate finished, signalOffset = %1, methodOffset = %2, #Signals = %3").arg(m_signalOffset).arg(m_methodOffset).arg(m_numSignals);
+ } else { //We have initialized offsets, this is an additional Replica attaching
+ for (int i = m_signalOffset; i < m_methodOffset; ++i) {
+ const bool res = QMetaObject::connect(this, i, rep, i, Qt::DirectConnection, nullptr);
+ qCDebug(QT_REMOTEOBJECT) << " Connect"<<i<<res<<m_metaObject->method(i).name();
+ Q_UNUSED(res)
+ }
+ if (isInitialized()) {
+ qCDebug(QT_REMOTEOBJECT) << QStringLiteral("ReplicaImplementation initialized, emitting signal on replica");
+ emit rep->initialized(); //Emit from new replica only
+ }
+ if (state() != QRemoteObjectReplica::Valid) {
+ qCDebug(QT_REMOTEOBJECT) << QStringLiteral("ReplicaImplementation not currently valid, emitting signal on replica");
+ emit rep->stateChanged(state(), m_metaObject ? QRemoteObjectReplica::Default : QRemoteObjectReplica::Uninitialized);
+ }
+
+ qCDebug(QT_REMOTEOBJECT) << QStringLiteral("configurePrivate finished, added replica to existing ReplicaImplementation");
+ }
+}
+
+void QConnectedReplicaImplementation::configurePrivate(QRemoteObjectReplica *rep)
+{
+ if (m_metaObject) {
+ // see QRemoteObjectReplicaImplementation::configurePrivate
+ const bool firstReplicaInstance = (m_methodOffset == 0);
+
+ QRemoteObjectReplicaImplementation::configurePrivate(rep);
+
+ // ensure that notify signals are emitted for the new replica, when
+ // we are initializing an nth replica of the same type
+ if (!firstReplicaInstance) {
+ const int offset = m_propertyOffset;
+ const int nParam = int(m_propertyStorage.size());
+ void *args[] = {nullptr, nullptr};
+ for (int i = 0; i < nParam; ++i) {
+ const int notifyIndex = m_metaObject->property(i+offset).notifySignalIndex();
+ if (notifyIndex < 0)
+ continue;
+ qCDebug(QT_REMOTEOBJECT) << " Before activate" << notifyIndex << m_metaObject->property(i+offset).name();
+ args[1] = m_propertyStorage[i].data();
+ // NOTE: this over-emits (assumes all values have changed)
+ QMetaObject::activate(rep, rep->metaObject(), notifyIndex - m_signalOffset, args);
+ }
+ }
+ } else
+ m_parentsNeedingConnect.append(rep);
+}
+
+/*!
+ \class QRemoteObjectReplica
+ \inmodule QtRemoteObjects
+ \brief A class interacting with (but not implementing) a Qt API on the Remote Object network.
+
+ A Remote Object Replica is a QObject proxy for another QObject (called the
+ \l {Source} object). Once initialized, a replica can be considered a
+ "latent copy" of the \l {Source} object. That is, every change to a
+ Q_PROPERTY on the \l {Source}, or signal emitted by the \l {Source} will be
+ updated/emitted by all \l {Replica} objects. Latency
+ is introduced by process scheduling by any OSes involved and network
+ communication latency. As long as the replica has been initialized and the
+ communication is not disrupted, receipt and order of changes is guaranteed.
+
+ The \l {isInitialized} and \l {state} properties (and corresponding
+ \l {initialized()}/\l {stateChanged()} signals) allow the state of a
+ \l {Replica} to be determined.
+
+ While Qt Remote Objects (QtRO) handles the initialization and
+ synchronization of \l {Replica} objects, there are numerous steps happening
+ behind the scenes which can fail and that aren't encountered in single
+ process Qt applications. See \l {Troubleshooting} for advice on how to
+ handle such issues when using a remote objects network.
+*/
+
+/*!
+ \enum QRemoteObjectReplica::State
+
+ This enum type specifies the various state codes associated with QRemoteObjectReplica states:
+
+ \value Uninitialized Initial value of DynamicReplica, where nothing is
+ known about the replica before connection to source.
+
+ \value Default Initial value of static replica, where any defaults set in
+ the .rep file are available so it can be used if necessary.
+
+ \value Valid Indicates the replica is connected, has good property values
+ and can be interacted with.
+
+ \value Suspect Error state that occurs if the connection to the source is
+ lost after it is initialized.
+
+ \value SignatureMismatch Error state that occurs if a connection to the
+ source is made, but the source and replica are not derived from the same
+ .rep (only possible for static Replicas).
+*/
+
+/*!
+ \fn void QRemoteObjectReplica::stateChanged(State state, State oldState)
+
+ This signal is emitted whenever a replica's state toggles between
+ \l QRemoteObjectReplica::State.
+
+ The change in state is represented with \a state and \a oldState.
+
+ \sa state(), initialized()
+*/
+
+/*!
+ \fn void QRemoteObjectReplica::initialized()
+
+ This signal is emitted once the replica is initialized. An intialized replica
+ has all property values set, but has not yet emitted any property change
+ notifications.
+
+ \sa isInitialized(), stateChanged()
+*/
+
+/*!
+ \fn void QRemoteObjectReplica::notified()
+
+ This signal is emitted once the replica is initialized and all property change
+ notifications have been emitted.
+
+ It is sometimes useful to respond to property changes as events.
+ For example, you might want to display a user notification when a certain
+ property change occurs. However, this user notification would then also be
+ triggered when a replica first became \c QRemoteObjectReplica::Valid, as
+ all property change signals are emitted at that time. This isn't always desirable,
+ and \c notified allows the developer to distinguish between these two cases.
+*/
+
+/*!
+ \internal
+ \enum QRemoteObjectReplica::ConstructorType
+*/
+
+/*!
+ \property QRemoteObjectReplica::state
+ \brief Returns the replica state.
+
+ This property holds the replica \l QRemoteObjectReplica::State.
+*/
+
+/*!
+ \property QRemoteObjectReplica::node
+ \brief A pointer to the node this object was acquired from.
+*/
+
+/*!
+ \internal This (protected) constructor for QRemoteObjectReplica can be used to create
+ replica objects from QML.
+*/
+QRemoteObjectReplica::QRemoteObjectReplica(ConstructorType t)
+ : QObject(nullptr)
+ , d_impl(t == DefaultConstructor ? new QStubReplicaImplementation : nullptr)
+{
+ qRegisterMetaType<State>("State");
+}
+
+QRemoteObjectReplica::QRemoteObjectReplica(QObjectPrivate &dptr, QObject *parent)
+ : QObject(dptr, parent)
+ , d_impl(new QStubReplicaImplementation)
+{
+}
+
+/*!
+ \internal
+*/
+QRemoteObjectReplica::~QRemoteObjectReplica()
+{
+}
+
+/*!
+ \internal
+*/
+void QRemoteObjectReplica::send(QMetaObject::Call call, int index, const QVariantList &args)
+{
+ Q_ASSERT(index != -1);
+
+ d_impl->_q_send(call, index, args);
+}
+
+/*!
+ \internal
+*/
+QRemoteObjectPendingCall QRemoteObjectReplica::sendWithReply(QMetaObject::Call call, int index, const QVariantList &args)
+{
+ return d_impl->_q_sendWithReply(call, index, args);
+}
+
+/*!
+ \internal
+*/
+const QVariant QRemoteObjectReplica::propAsVariant(int i) const
+{
+ return d_impl->getProperty(i);
+}
+
+/*!
+ \internal
+*/
+void QRemoteObjectReplica::initializeNode(QRemoteObjectNode *node, const QString &name)
+{
+ node->initializeReplica(this, name);
+}
+
+/*!
+ \internal
+*/
+void QRemoteObjectReplica::setProperties(QVariantList &&properties)
+{
+ d_impl->setProperties(std::move(properties));
+}
+
+/*!
+ \internal
+*/
+void QRemoteObjectReplica::setChild(int i, const QVariant &value)
+{
+ d_impl->setProperty(i, value);
+}
+
+/*!
+ Returns \c true if this replica has been initialized with data from the
+ \l {Source} object. Returns \c false otherwise.
+
+ \sa state()
+*/
+bool QRemoteObjectReplica::isInitialized() const
+{
+ return d_impl->isInitialized();
+}
+
+/*!
+ Returns \c true if this replica has been initialized with data from the
+ \l {Source} object. Returns \c false otherwise.
+
+ \sa isInitialized()
+*/
+QRemoteObjectReplica::State QRemoteObjectReplica::state() const
+{
+ return d_impl->state();
+}
+
+QRemoteObjectNode *QRemoteObjectReplica::node() const
+{
+ return d_impl->node();
+}
+
+void QRemoteObjectReplica::setNode(QRemoteObjectNode *_node)
+{
+ const QRemoteObjectNode *curNode = node();
+ if (curNode) {
+ qCWarning(QT_REMOTEOBJECT) << "Ignoring call to setNode as the node has already been set";
+ return;
+ }
+ d_impl.clear();
+ _node->initializeReplica(this);
+}
+
+/*!
+ \internal
+*/
+void QRemoteObjectReplica::initialize()
+{
+}
+
+/*!
+ Returns \c true if this replica has been initialized and has a valid
+ connection with the \l {QRemoteObjectNode} {node} hosting the \l {Source}.
+ Returns \c false otherwise.
+
+ \sa isInitialized()
+*/
+bool QRemoteObjectReplica::isReplicaValid() const
+{
+ return state() == Valid;
+}
+
+/*!
+ Blocking call that waits for the replica to become initialized or until the
+ \a timeout (in ms) expires. Returns \c true if the replica is initialized
+ when the call completes, \c false otherwise.
+
+ If \a timeout is -1, this function will not time out.
+
+ \sa isInitialized(), initialized()
+*/
+bool QRemoteObjectReplica::waitForSource(int timeout)
+{
+ return d_impl->waitForSource(timeout);
+}
+
+QInProcessReplicaImplementation::QInProcessReplicaImplementation(const QString &name, const QMetaObject *meta, QRemoteObjectNode * node)
+ : QRemoteObjectReplicaImplementation(name, meta, node)
+{
+}
+
+QInProcessReplicaImplementation::~QInProcessReplicaImplementation()
+{
+}
+
+const QVariant QInProcessReplicaImplementation::getProperty(int i) const
+{
+ Q_ASSERT(connectionToSource);
+ Q_ASSERT(connectionToSource->m_object);
+ const int index = i + QRemoteObjectSource::qobjectPropertyOffset;
+ Q_ASSERT(index >= 0 && index < connectionToSource->m_object->metaObject()->propertyCount());
+ return connectionToSource->m_object->metaObject()->property(index).read(connectionToSource->m_object);
+}
+
+void QInProcessReplicaImplementation::setProperties(QVariantList &&)
+{
+ //TODO some verification here maybe?
+}
+
+void QInProcessReplicaImplementation::setProperty(int i, const QVariant &property)
+{
+ Q_ASSERT(connectionToSource);
+ Q_ASSERT(connectionToSource->m_object);
+ const int index = i + QRemoteObjectSource::qobjectPropertyOffset;
+ Q_ASSERT(index >= 0 && index < connectionToSource->m_object->metaObject()->propertyCount());
+ connectionToSource->m_object->metaObject()->property(index).write(connectionToSource->m_object, property);
+}
+
+void QInProcessReplicaImplementation::_q_send(QMetaObject::Call call, int index, const QVariantList &args)
+{
+ Q_ASSERT(call == QMetaObject::InvokeMetaMethod || call == QMetaObject::WriteProperty);
+
+ const SourceApiMap *api = connectionToSource->m_api;
+ if (call == QMetaObject::InvokeMetaMethod) {
+ const int resolvedIndex = api->sourceMethodIndex(index - m_methodOffset);
+ if (resolvedIndex < 0)
+ qCWarning(QT_REMOTEOBJECT) << "Skipping invalid invocation. Index not found:" << index - m_methodOffset;
+ else
+ connectionToSource->invoke(call, index - m_methodOffset, args);
+ } else {
+ const int resolvedIndex = connectionToSource->m_api->sourcePropertyIndex(index - m_propertyOffset);
+ if (resolvedIndex < 0)
+ qCWarning(QT_REMOTEOBJECT) << "Skipping invalid property setter. Index not found:" << index - m_propertyOffset;
+ else
+ connectionToSource->invoke(call, index - m_propertyOffset, args);
+ }
+}
+
+QRemoteObjectPendingCall QInProcessReplicaImplementation::_q_sendWithReply(QMetaObject::Call call, int index, const QVariantList &args)
+{
+ Q_ASSERT(call == QMetaObject::InvokeMetaMethod);
+
+ const int ReplicaIndex = index - m_methodOffset;
+ auto metaType = QMetaType::fromName(connectionToSource->m_api->typeName(ReplicaIndex).constData());
+ if (!metaType.sizeOf())
+ metaType = QMetaType(QMetaType::UnknownType);
+ QVariant returnValue(metaType, nullptr);
+
+ const int resolvedIndex = connectionToSource->m_api->sourceMethodIndex(ReplicaIndex);
+ if (resolvedIndex < 0) {
+ qCWarning(QT_REMOTEOBJECT) << "Skipping invalid invocation. Index not found:" << ReplicaIndex;
+ return QRemoteObjectPendingCall();
+ }
+
+ connectionToSource->invoke(call, ReplicaIndex, args, &returnValue);
+ return QRemoteObjectPendingCall::fromCompletedCall(returnValue);
+}
+
+QStubReplicaImplementation::QStubReplicaImplementation() {}
+
+QStubReplicaImplementation::~QStubReplicaImplementation() {}
+
+const QVariant QStubReplicaImplementation::getProperty(int i) const
+{
+ Q_ASSERT_X(i >= 0 && i < m_propertyStorage.size(), __FUNCTION__, qPrintable(QString(QLatin1String("0 <= %1 < %2")).arg(i).arg(m_propertyStorage.size())));
+ return m_propertyStorage[i];
+}
+
+void QStubReplicaImplementation::setProperties(QVariantList &&properties)
+{
+ Q_ASSERT(m_propertyStorage.isEmpty());
+ m_propertyStorage.reserve(properties.size());
+ m_propertyStorage = std::move(properties);
+}
+
+void QStubReplicaImplementation::setProperty(int i, const QVariant &prop)
+{
+ m_propertyStorage[i] = prop;
+}
+
+void QStubReplicaImplementation::_q_send(QMetaObject::Call call, int index, const QVariantList &args)
+{
+ Q_UNUSED(call)
+ Q_UNUSED(index)
+ Q_UNUSED(args)
+ qWarning("Tried calling a slot or setting a property on a replica that hasn't been initialized with a node");
+}
+
+QRemoteObjectPendingCall QStubReplicaImplementation::_q_sendWithReply(QMetaObject::Call call, int index, const QVariantList &args)
+{
+ Q_UNUSED(call)
+ Q_UNUSED(index)
+ Q_UNUSED(args)
+ qWarning("Tried calling a slot or setting a property on a replica that hasn't been initialized with a node");
+ return QRemoteObjectPendingCall(); //Invalid
+}
+
+QT_END_NAMESPACE
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQREMOTEOBJECTREPLICA_H
+#define QQREMOTEOBJECTREPLICA_H
+
+#include <QtRemoteObjects/qtremoteobjectglobal.h>
+
+#include <QtCore/qobject.h>
+#include <QtCore/qsharedpointer.h>
+
+Q_MOC_INCLUDE(<QtRemoteObjects/qremoteobjectnode.h>)
+
+QT_BEGIN_NAMESPACE
+
+class QObjectPrivate;
+class QRemoteObjectPendingCall;
+class QRemoteObjectReplicaImplementation;
+class QReplicaImplementationInterface;
+class QRemoteObjectNode;
+
+class Q_REMOTEOBJECTS_EXPORT QRemoteObjectReplica : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QRemoteObjectNode *node READ node WRITE setNode)
+ Q_PROPERTY(State state READ state NOTIFY stateChanged)
+public:
+ enum State {
+ Uninitialized,
+ Default,
+ Valid,
+ Suspect,
+ SignatureMismatch
+ };
+ Q_ENUM(State)
+
+public:
+ ~QRemoteObjectReplica() override;
+
+ bool isReplicaValid() const;
+ bool waitForSource(int timeout = 30000);
+ bool isInitialized() const;
+ State state() const;
+ QRemoteObjectNode *node() const;
+ virtual void setNode(QRemoteObjectNode *node);
+
+Q_SIGNALS:
+ void initialized();
+ void notified();
+ void stateChanged(State state, State oldState);
+
+protected:
+ enum ConstructorType {DefaultConstructor, ConstructWithNode};
+ explicit QRemoteObjectReplica(ConstructorType t = DefaultConstructor);
+ QRemoteObjectReplica(QObjectPrivate &dptr, QObject *parent);
+
+ virtual void initialize();
+ void send(QMetaObject::Call call, int index, const QVariantList &args);
+ QRemoteObjectPendingCall sendWithReply(QMetaObject::Call call, int index, const QVariantList &args);
+
+protected:
+ void setProperties(QVariantList &&);
+ void setChild(int i, const QVariant &);
+ const QVariant propAsVariant(int i) const;
+ void persistProperties(const QString &repName, const QByteArray &repSig, const QVariantList &props) const;
+ QVariantList retrieveProperties(const QString &repName, const QByteArray &repSig) const;
+ void initializeNode(QRemoteObjectNode *node, const QString &name = QString());
+ QSharedPointer<QReplicaImplementationInterface> d_impl;
+private:
+ friend class QRemoteObjectNodePrivate;
+ friend class QConnectedReplicaImplementation;
+};
+
+QT_END_NAMESPACE
+
+#endif
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QREMOTEOBJECTREPLICA_P_H
+#define QREMOTEOBJECTREPLICA_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qremoteobjectreplica.h"
+
+#include "qremoteobjectpendingcall.h"
+
+#include "qremoteobjectpacket_p.h"
+
+#include <QtCore/qcompilerdetection.h>
+#include <QtCore/qdatastream.h>
+#include <QtCore/qpointer.h>
+#include <QtCore/qtimer.h>
+
+QT_BEGIN_NAMESPACE
+
+class QRemoteObjectReplica;
+class QRemoteObjectSource;
+class QtROIoDeviceBase;
+
+class QReplicaImplementationInterface
+{
+public:
+ virtual ~QReplicaImplementationInterface() {}
+ virtual const QVariant getProperty(int i) const = 0;
+ virtual void setProperties(QVariantList &&) = 0;
+ virtual void setProperty(int i, const QVariant &) = 0;
+ virtual bool isInitialized() const = 0;
+ virtual QRemoteObjectReplica::State state() const = 0;
+ virtual bool waitForSource(int) = 0;
+ virtual QRemoteObjectNode *node() const = 0;
+
+ virtual void _q_send(QMetaObject::Call call, int index, const QVariantList &args) = 0;
+ virtual QRemoteObjectPendingCall _q_sendWithReply(QMetaObject::Call call, int index, const QVariantList &args) = 0;
+};
+
+class QStubReplicaImplementation final : public QReplicaImplementationInterface
+{
+public:
+ explicit QStubReplicaImplementation();
+ ~QStubReplicaImplementation() override;
+
+ const QVariant getProperty(int i) const override;
+ void setProperties(QVariantList &&) override;
+ void setProperty(int i, const QVariant &) override;
+ bool isInitialized() const override { return false; }
+ QRemoteObjectReplica::State state() const override { return QRemoteObjectReplica::State::Uninitialized;}
+ bool waitForSource(int) override { return false; }
+ QRemoteObjectNode *node() const override { return nullptr; }
+
+ void _q_send(QMetaObject::Call call, int index, const QVariantList &args) override;
+ QRemoteObjectPendingCall _q_sendWithReply(QMetaObject::Call call, int index, const QVariantList &args) override;
+ QVariantList m_propertyStorage;
+};
+
+class QRemoteObjectReplicaImplementation : public QObject, public QReplicaImplementationInterface
+{
+public:
+ explicit QRemoteObjectReplicaImplementation(const QString &name, const QMetaObject *, QRemoteObjectNode *);
+ ~QRemoteObjectReplicaImplementation() override;
+
+ bool needsDynamicInitialization() const;
+
+ const QVariant getProperty(int i) const override = 0;
+ void setProperties(QVariantList &&) override = 0;
+ void setProperty(int i, const QVariant &) override = 0;
+ virtual bool isShortCircuit() const = 0;
+ bool isInitialized() const override { return true; }
+ QRemoteObjectReplica::State state() const override { return QRemoteObjectReplica::State(m_state.loadRelaxed()); }
+ void setState(QRemoteObjectReplica::State state);
+ bool waitForSource(int) override { return true; }
+ virtual bool waitForFinished(const QRemoteObjectPendingCall &, int) { return true; }
+ virtual void notifyAboutReply(int, const QVariant &) {}
+ virtual void configurePrivate(QRemoteObjectReplica *);
+ void emitInitialized();
+ void emitNotified();
+ QRemoteObjectNode *node() const override { return m_node; }
+
+ void _q_send(QMetaObject::Call call, int index, const QVariantList &args) override = 0;
+ QRemoteObjectPendingCall _q_sendWithReply(QMetaObject::Call call, int index, const QVariantList &args) override = 0;
+
+ //Dynamic replica functions
+ virtual void setDynamicMetaObject(const QMetaObject *meta);
+ virtual void setDynamicProperties(QVariantList &&values);
+
+ QString m_objectName;
+ const QMetaObject *m_metaObject;
+
+ //Dynamic Replica data
+ int m_numSignals;//TODO maybe here too
+ int m_methodOffset;
+ int m_signalOffset;
+ int m_propertyOffset;
+ QRemoteObjectNode *m_node;
+ QByteArray m_objectSignature;
+ QAtomicInt m_state;
+};
+
+class QConnectedReplicaImplementation final : public QRemoteObjectReplicaImplementation
+{
+public:
+ explicit QConnectedReplicaImplementation(const QString &name, const QMetaObject *, QRemoteObjectNode *);
+ ~QConnectedReplicaImplementation() override;
+ const QVariant getProperty(int i) const override;
+ void setProperties(QVariantList &&) override;
+ void setProperty(int i, const QVariant &) override;
+ bool isShortCircuit() const final { return false; }
+ bool isInitialized() const override;
+ bool waitForSource(int timeout) override;
+ QList<int> childIndices() const;
+ void initialize(QVariantList &&values);
+ void configurePrivate(QRemoteObjectReplica *) override;
+ void requestRemoteObjectSource();
+ bool sendCommand();
+ QRemoteObjectPendingCall sendCommandWithReply(int serialId);
+ bool waitForFinished(const QRemoteObjectPendingCall &call, int timeout) override;
+ void notifyAboutReply(int ackedSerialId, const QVariant &value) override;
+ void setConnection(QtROIoDeviceBase *conn);
+ void setDisconnected();
+
+ void _q_send(QMetaObject::Call call, int index, const QVariantList &args) override;
+ QRemoteObjectPendingCall _q_sendWithReply(QMetaObject::Call call, int index, const QVariantList& args) override;
+
+ void setDynamicMetaObject(const QMetaObject *meta) override;
+ void setDynamicProperties(QVariantList &&) override;
+ QList<QRemoteObjectReplica *> m_parentsNeedingConnect;
+ QVariantList m_propertyStorage;
+ QList<int> m_childIndices;
+ QPointer<QtROIoDeviceBase> connectionToSource;
+
+ // pending call data
+ int m_curSerialId = 1; // 0 is reserved for heartbeat signals
+ QHash<int, QRemoteObjectPendingCall> m_pendingCalls;
+ QTimer m_heartbeatTimer;
+};
+
+class QInProcessReplicaImplementation final : public QRemoteObjectReplicaImplementation
+{
+public:
+ explicit QInProcessReplicaImplementation(const QString &name, const QMetaObject *, QRemoteObjectNode *);
+ ~QInProcessReplicaImplementation() override;
+
+ const QVariant getProperty(int i) const override;
+ void setProperties(QVariantList &&) override;
+ void setProperty(int i, const QVariant &) override;
+ bool isShortCircuit() const final { return true; }
+
+ void _q_send(QMetaObject::Call call, int index, const QVariantList &args) override;
+ QRemoteObjectPendingCall _q_sendWithReply(QMetaObject::Call call, int index, const QVariantList& args) override;
+
+ QPointer<QRemoteObjectSourceBase> connectionToSource;
+};
+
+QT_END_NAMESPACE
+
+#endif
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qremoteobjectsettingsstore.h"
+
+#include "qremoteobjectnode_p.h"
+
+#include <QtCore/private/qobject_p.h>
+#include <QtCore/qsettings.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype SettingsStore
+ \inqmlmodule QtRemoteObjects
+ \brief A basic store for persisted properties.
+
+ This type provides simple QSettings-based storage for properties marked as PERSISTED. It is used in
+ conjunction with Node::persistedStore:
+
+ \code
+ Node {
+ persistedStore: SettingsStore {}
+ }
+ \endcode
+*/
+
+class QRemoteObjectSettingsStorePrivate : public QRemoteObjectAbstractPersistedStorePrivate
+{
+public:
+ QRemoteObjectSettingsStorePrivate();
+ virtual ~QRemoteObjectSettingsStorePrivate();
+
+ QSettings settings;
+ Q_DECLARE_PUBLIC(QRemoteObjectSettingsStore)
+};
+
+QRemoteObjectSettingsStorePrivate::QRemoteObjectSettingsStorePrivate()
+{
+}
+
+QRemoteObjectSettingsStorePrivate::~QRemoteObjectSettingsStorePrivate()
+{
+}
+
+QRemoteObjectSettingsStore::QRemoteObjectSettingsStore(QObject *parent)
+ : QRemoteObjectAbstractPersistedStore(*new QRemoteObjectSettingsStorePrivate, parent)
+{
+}
+
+QRemoteObjectSettingsStore::~QRemoteObjectSettingsStore()
+{
+}
+
+QVariantList QRemoteObjectSettingsStore::restoreProperties(const QString &repName, const QByteArray &repSig)
+{
+ Q_D(QRemoteObjectSettingsStore);
+ d->settings.beginGroup(repName + QLatin1Char('/') + QString::fromLatin1(repSig));
+ QVariantList values = d->settings.value(QStringLiteral("values")).toList();
+ d->settings.endGroup();
+ return values;
+}
+
+void QRemoteObjectSettingsStore::saveProperties(const QString &repName, const QByteArray &repSig, const QVariantList &values)
+{
+ Q_D(QRemoteObjectSettingsStore);
+ d->settings.beginGroup(repName + QLatin1Char('/') + QString::fromLatin1(repSig));
+ d->settings.setValue(QStringLiteral("values"), values);
+ d->settings.endGroup();
+ d->settings.sync();
+}
+
+QT_END_NAMESPACE
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QREMOTEOBJECTSETTINGSSTORE_H
+#define QREMOTEOBJECTSETTINGSSTORE_H
+
+#include <QtRemoteObjects/qremoteobjectnode.h>
+
+QT_BEGIN_NAMESPACE
+
+class QRemoteObjectSettingsStorePrivate;
+
+class Q_REMOTEOBJECTS_EXPORT QRemoteObjectSettingsStore : public QRemoteObjectAbstractPersistedStore
+{
+ Q_OBJECT
+
+public:
+ QRemoteObjectSettingsStore(QObject *parent = nullptr);
+ ~QRemoteObjectSettingsStore() override;
+
+ void saveProperties(const QString &repName, const QByteArray &repSig, const QVariantList &values) override;
+ QVariantList restoreProperties(const QString &repName, const QByteArray &repSig) override;
+
+private:
+ Q_DECLARE_PRIVATE(QRemoteObjectSettingsStore)
+};
+
+QT_END_NAMESPACE
+
+#endif // QREMOTEOBJECTSETTINGSSTORE_H
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qremoteobjectsource.h"
+#include "qremoteobjectsource_p.h"
+#include "qremoteobjectnode.h"
+#include "qremoteobjectdynamicreplica.h"
+
+#include "qconnectionfactories_p.h"
+#include "qremoteobjectsourceio_p.h"
+#include "qremoteobjectabstractitemmodeladapter_p.h"
+
+#include <QtCore/qmetaobject.h>
+#include <QtCore/qvarlengtharray.h>
+#include <QtCore/qabstractitemmodel.h>
+
+#include <algorithm>
+#include <iterator>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QRemoteObjectPackets;
+using namespace QRemoteObjectStringLiterals;
+
+const int QRemoteObjectSourceBase::qobjectPropertyOffset = QObject::staticMetaObject.propertyCount();
+const int QRemoteObjectSourceBase::qobjectMethodOffset = QObject::staticMetaObject.methodCount();
+static const QByteArray s_classinfoRemoteobjectSignature(QCLASSINFO_REMOTEOBJECT_SIGNATURE);
+
+namespace QtPrivate {
+
+// The stringData, methodMatch and QMetaObjectPrivate methods are modified versions of the code
+// from qmetaobject_p.h/qmetaobject.cpp. The modifications are based on our custom need to match
+// a method name that comes from the .rep file.
+// The QMetaObjectPrivate struct should only have members appended to maintain binary compatibility,
+// so we should be fine with only the listed version with the fields we use.
+inline const QByteArray apiStringData(const QMetaObject *mo, int index)
+{
+ uint offset = mo->d.stringdata[2*index];
+ uint length = mo->d.stringdata[2*index + 1];
+ const char *string = reinterpret_cast<const char *>(mo->d.stringdata) + offset;
+ return QByteArray::fromRawData(string, length);
+}
+
+
+// From QMetaMethod in qmetaobject.h
+struct Data {
+ enum { Size = 6 };
+
+ uint name() const { return d[0]; }
+ uint argc() const { return d[1]; }
+ uint parameters() const { return d[2]; }
+ uint tag() const { return d[3]; }
+ uint flags() const { return d[4]; }
+ uint metaTypeOffset() const { return d[5]; }
+ bool operator==(const Data &other) const { return d == other.d; }
+
+ const uint *d;
+};
+
+inline bool apiMethodMatch(const QMetaObject *m, const Data &data, const QByteArray &name, int argc,
+ const int *types)
+{
+ if (data.argc() != uint(argc))
+ return false;
+ if (apiStringData(m, data.name()) != name)
+ return false;
+ for (int i = 0; i < argc; ++i) {
+ auto mt = QMetaType(m->d.metaTypes[data.metaTypeOffset() + i + 1]);
+ if (mt != QMetaType(types[i]))
+ return false;
+ }
+ return true;
+}
+
+struct QMetaObjectPrivate
+{
+ // revision 7 is Qt 5.0 everything lower is not supported
+ // revision 8 is Qt 5.12: It adds the enum name to QMetaEnum
+ enum { OutputRevision = 8 }; // Used by moc, qmetaobjectbuilder and qdbus
+
+ int revision;
+ int className;
+ int classInfoCount, classInfoData;
+ int methodCount, methodData;
+ int propertyCount, propertyData;
+ int enumeratorCount, enumeratorData;
+ int constructorCount, constructorData;
+ int flags;
+ int signalCount;
+};
+
+inline Data fromRelativeMethodIndex(const QMetaObject *mobj, int index)
+{
+ const auto priv = reinterpret_cast<const QMetaObjectPrivate*>(mobj->d.data);
+ return { mobj->d.data + priv->methodData + index * Data::Size };
+}
+
+int qtro_method_index_impl(const QMetaObject * staticMetaObj, const char *className,
+ const char *methodName, int *count, int const **types)
+{
+ int result = staticMetaObj->indexOfMethod(methodName);
+ if (result >= 0)
+ return result;
+ // We can have issues, specifically with enums, since the compiler can infer the class. Since
+ // indexOfMethod() is doing string comparisons for registered types, "MyEnum" and "MyClass::MyEnum"
+ // won't match.
+ // Below is similar to QMetaObject->indexOfMethod, but template magic has already matched parameter
+ // types, so we need to find a match for the API method name + parameters. Neither approach works
+ // 100%, as the below code doesn't match a parameter of type "size_t" (which the template match
+ // identifies as "ulong"). These subtleties can cause the below string comparison fails.
+ // There is no known case that would fail both methods.
+ // TODO: is there a way to make this a constexpr so a failure is detected at compile time?
+ int nameLength = strchr(methodName, '(') - methodName;
+ const auto name = QByteArray::fromRawData(methodName, nameLength);
+ for (const QMetaObject *m = staticMetaObj; m; m = m->d.superdata) {
+ const auto priv = reinterpret_cast<const QMetaObjectPrivate*>(m->d.data);
+ int i = (priv->methodCount - 1);
+ const int end = priv->signalCount;
+ for (; i >= end; --i) {
+ const Data data = fromRelativeMethodIndex(m, i);
+ if (apiMethodMatch(m, data, name, *count, *types))
+ return i + m->methodOffset();
+ }
+ }
+ qWarning() << "No matching method for" << methodName << "in the provided metaclass" << className;
+ return -1;
+}
+
+} // namespace QtPrivate
+
+QByteArray QtPrivate::qtro_classinfo_signature(const QMetaObject *metaObject)
+{
+ if (!metaObject)
+ return QByteArray{};
+
+ for (int i = metaObject->classInfoOffset(); i < metaObject->classInfoCount(); ++i) {
+ auto ci = metaObject->classInfo(i);
+ if (s_classinfoRemoteobjectSignature == ci.name())
+ return ci.value();
+ }
+ return QByteArray{};
+}
+
+inline bool qtro_is_cloned_method(const QMetaObject *mobj, int index)
+{
+ int local_method_index = index - mobj->methodOffset();
+ if (local_method_index < 0 && mobj->superClass())
+ return qtro_is_cloned_method(mobj->superClass(), index);
+ const QtPrivate::Data data = QtPrivate::fromRelativeMethodIndex(mobj, local_method_index);
+ if (data.flags() & 0x20 /*MethodFlags::MethodCloned*/)
+ return true;
+ return false;
+}
+
+SourceApiMap::~SourceApiMap()
+ = default;
+
+QRemoteObjectSourceBase::QRemoteObjectSourceBase(QObject *obj, Private *d, const SourceApiMap *api,
+ QObject *adapter)
+ : QObject(obj),
+ m_object(obj),
+ m_adapter(adapter),
+ m_api(api),
+ d(d)
+{
+ if (!obj) {
+ qCWarning(QT_REMOTEOBJECT) << "QRemoteObjectSourceBase: Cannot replicate a NULL object" << m_api->name();
+ return;
+ }
+
+ setConnections();
+
+ const auto nChildren = api->m_models.size() + api->m_subclasses.size();
+ if (nChildren > 0) {
+ QList<int> roles;
+ const int numProperties = api->propertyCount();
+ int modelIndex = 0, subclassIndex = 0;
+ for (int i = 0; i < numProperties; ++i) {
+ if (api->isAdapterProperty(i))
+ continue;
+ const int index = api->sourcePropertyIndex(i);
+ const auto property = m_object->metaObject()->property(index);
+ const auto metaType = property.metaType();
+ if (metaType.flags().testFlag(QMetaType::PointerToQObject)) {
+ auto propertyMeta = metaType.metaObject();
+ QObject *child = property.read(m_object).value<QObject *>();
+ const QMetaObject *meta = child ? child->metaObject() : propertyMeta;
+ if (!meta)
+ continue;
+ if (meta->inherits(&QAbstractItemModel::staticMetaObject)) {
+ const auto modelInfo = api->m_models.at(modelIndex++);
+ QAbstractItemModel *model = qobject_cast<QAbstractItemModel *>(child);
+ QAbstractItemAdapterSourceAPI<QAbstractItemModel, QAbstractItemModelSourceAdapter> *modelApi =
+ new QAbstractItemAdapterSourceAPI<QAbstractItemModel, QAbstractItemModelSourceAdapter>(modelInfo.name);
+ if (!model)
+ m_children.insert(i, new QRemoteObjectSource(nullptr, d, modelApi, nullptr, api->name()));
+ else {
+ roles.clear();
+ const auto knownRoles = model->roleNames();
+ for (auto role : modelInfo.roles.split('|')) {
+ if (role.isEmpty())
+ continue;
+ const int roleIndex = knownRoles.key(role, -1);
+ if (roleIndex == -1) {
+ qCWarning(QT_REMOTEOBJECT) << "Invalid role" << role << "for model" << model->metaObject()->className();
+ qCWarning(QT_REMOTEOBJECT) << " known roles:" << knownRoles;
+ } else
+ roles << roleIndex;
+ }
+ auto adapter = new QAbstractItemModelSourceAdapter(model, nullptr,
+ roles.isEmpty() ? knownRoles.keys().toVector() : roles);
+ m_children.insert(i, new QRemoteObjectSource(model, d, modelApi, adapter, api->name()));
+ }
+ } else {
+ const auto classApi = api->m_subclasses.at(subclassIndex++);
+ m_children.insert(i, new QRemoteObjectSource(child, d, classApi, nullptr, api->name()));
+ }
+ }
+ }
+ }
+}
+
+QRemoteObjectSource::QRemoteObjectSource(QObject *obj, Private *dd, const SourceApiMap *api, QObject *adapter, const QString &parentName)
+ : QRemoteObjectSourceBase(obj, dd, api, adapter)
+ , m_name(api->typeName() == QLatin1String("QAbstractItemModelAdapter") ? MODEL().arg(parentName + QLatin1String("::") + api->name()) :
+ CLASS().arg(parentName + QLatin1String("::") + api->name()))
+{
+ if (obj)
+ d->m_sourceIo->registerSource(this);
+}
+
+QRemoteObjectRootSource::QRemoteObjectRootSource(QObject *obj, const SourceApiMap *api,
+ QObject *adapter, QRemoteObjectSourceIo *sourceIo)
+ : QRemoteObjectSourceBase(obj, new Private(sourceIo, this), api, adapter)
+ , m_name(api->name())
+{
+ d->m_sourceIo->registerSource(this);
+}
+
+QRemoteObjectSourceBase::~QRemoteObjectSourceBase()
+{
+ delete m_api;
+}
+
+void QRemoteObjectSourceBase::setConnections()
+{
+ const QMetaObject *meta = m_object->metaObject();
+
+ const int index = meta->indexOfClassInfo(QCLASSINFO_REMOTEOBJECT_TYPE);
+ if (index != -1) { //We have an object created from repc or at least with QCLASSINFO defined
+ while (true) {
+ Q_ASSERT(meta->superClass()); //This recurses to QObject, which doesn't have QCLASSINFO_REMOTEOBJECT_TYPE
+ if (index != meta->superClass()->indexOfClassInfo(QCLASSINFO_REMOTEOBJECT_TYPE)) //At the point we don't find the same QCLASSINFO_REMOTEOBJECT_TYPE,
+ //we have the metaobject we should work from
+ break;
+ meta = meta->superClass();
+ }
+ }
+
+ for (int idx = 0; idx < m_api->signalCount(); ++idx) {
+ const int sourceIndex = m_api->sourceSignalIndex(idx);
+ const bool isAdapter = m_api->isAdapterSignal(idx);
+ const auto targetMeta = isAdapter ? m_adapter->metaObject() : meta;
+
+ // don't connect cloned signals, or we end up with multiple emissions
+ if (qtro_is_cloned_method(targetMeta, sourceIndex))
+ continue;
+ // This basically connects the parent Signals (note, all dynamic properties have onChange
+ //notifications, thus signals) to us. Normally each Signal is mapped to a unique index,
+ //but since we are forwarding them all, we keep the offset constant.
+ //
+ //We know no one will inherit from this class, so no need to worry about indices from
+ //derived classes.
+ const auto target = isAdapter ? m_adapter : m_object;
+ if (!QMetaObject::connect(target, sourceIndex, this, QRemoteObjectSource::qobjectMethodOffset+idx, Qt::DirectConnection, nullptr)) {
+ qCWarning(QT_REMOTEOBJECT) << "QRemoteObjectSourceBase: QMetaObject::connect returned false. Unable to connect.";
+ return;
+ }
+
+ qCDebug(QT_REMOTEOBJECT) << "Connection made" << idx << sourceIndex
+ << targetMeta->method(sourceIndex).name();
+ }
+}
+
+void QRemoteObjectSourceBase::resetObject(QObject *newObject)
+{
+ if (m_object)
+ m_object->disconnect(this);
+ if (m_adapter) {
+ m_adapter->disconnect(this);
+ delete m_adapter;
+ m_adapter = nullptr;
+ }
+ // We need some dynamic replica specific code here, in case an object had null sub-classes that
+ // have been replaced with real objects. In this case, the ApiMap could be wrong and need updating.
+ if (newObject && qobject_cast<QRemoteObjectDynamicReplica *>(newObject) && m_api->isDynamic()) {
+ auto api = static_cast<const DynamicApiMap*>(m_api);
+ if (api->m_properties[0] == 0) { // 0 is an index into QObject itself, so this isn't a valid QtRO index
+ const auto rep = qobject_cast<QRemoteObjectDynamicReplica *>(newObject);
+ auto tmp = m_api;
+ m_api = new DynamicApiMap(newObject, rep->metaObject(), api->m_name, QLatin1String(rep->metaObject()->className()));
+ qCDebug(QT_REMOTEOBJECT) << " Reset m_api for" << api->m_name << "using new metaObject:" << rep->metaObject()->className();
+ delete tmp;
+ }
+ }
+
+ m_object = newObject;
+ auto model = qobject_cast<QAbstractItemModel *>(newObject);
+ if (model) {
+ d->m_sourceIo->registerSource(this);
+ m_adapter = new QAbstractItemModelSourceAdapter(model, nullptr, model->roleNames().keys().toVector());
+ }
+
+ setParent(newObject);
+ if (newObject)
+ setConnections();
+
+ const auto nChildren = m_api->m_models.size() + m_api->m_subclasses.size();
+ if (nChildren == 0)
+ return;
+
+ if (!newObject) {
+ for (auto child : m_children)
+ child->resetObject(nullptr);
+ return;
+ }
+
+ for (int i : m_children.keys()) {
+ const int index = m_api->sourcePropertyIndex(i);
+ const auto property = m_object->metaObject()->property(index);
+ QObject *child = property.read(m_object).value<QObject *>();
+ m_children[i]->resetObject(child);
+ }
+}
+
+QRemoteObjectSource::~QRemoteObjectSource()
+{
+ for (auto it : m_children) {
+ // We used QPointers for m_children because we don't control the lifetime of child QObjects
+ // Since the this/source QObject's parent is the referenced QObject, it could have already
+ // been deleted
+ delete it;
+ }
+}
+
+QRemoteObjectRootSource::~QRemoteObjectRootSource()
+{
+ for (auto it : m_children) {
+ // We used QPointers for m_children because we don't control the lifetime of child QObjects
+ // Since the this/source QObject's parent is the referenced QObject, it could have already
+ // been deleted
+ delete it;
+ }
+ d->m_sourceIo->unregisterSource(this);
+ // removeListener tries to modify d->m_listeners, this is O(N²),
+ // so clear d->m_listeners prior to calling unregister (consume loop).
+ // We can do this, because we don't care about the return value of removeListener() here.
+ for (QtROIoDeviceBase *io : qExchange(d->m_listeners, {})) {
+ removeListener(io, true);
+ }
+ delete d;
+}
+
+QVariantList* QRemoteObjectSourceBase::marshalArgs(int index, void **a)
+{
+ QVariantList &list = m_marshalledArgs;
+ int N = m_api->signalParameterCount(index);
+ if (N == 1 && QMetaType(m_api->signalParameterType(index, 0)).flags().testFlag(QMetaType::PointerToQObject))
+ N = 0; // Don't try to send pointers, the will be handle by QRO_
+ if (list.size() < N)
+ list.reserve(N);
+ const int minFill = std::min(int(list.size()), N);
+ for (int i = 0; i < minFill; ++i) {
+ const int type = m_api->signalParameterType(index, i);
+ if (type == QMetaType::QVariant)
+ list[i] = *reinterpret_cast<QVariant *>(a[i + 1]);
+ else
+ list[i] = QVariant(QMetaType(type), a[i + 1]);
+ }
+ for (int i = int(list.size()); i < N; ++i) {
+ const int type = m_api->signalParameterType(index, i);
+ if (type == QMetaType::QVariant)
+ list << *reinterpret_cast<QVariant *>(a[i + 1]);
+ else
+ list << QVariant(QMetaType(type), a[i + 1]);
+ }
+ for (int i = N; i < list.size(); ++i)
+ list.removeLast();
+ return &m_marshalledArgs;
+}
+
+bool QRemoteObjectSourceBase::invoke(QMetaObject::Call c, int index, const QVariantList &args, QVariant* returnValue)
+{
+ int status = -1;
+ int flags = 0;
+ bool forAdapter = (c == QMetaObject::InvokeMetaMethod ? m_api->isAdapterMethod(index) : m_api->isAdapterProperty(index));
+ int resolvedIndex = (c == QMetaObject::InvokeMetaMethod ? m_api->sourceMethodIndex(index) : m_api->sourcePropertyIndex(index));
+ if (resolvedIndex < 0)
+ return false;
+ QVarLengthArray<void*, 10> param(args.size() + 1);
+
+ if (c == QMetaObject::InvokeMetaMethod) {
+ QMetaMethod method;
+ if (!forAdapter)
+ method = parent()->metaObject()->method(resolvedIndex);
+
+ if (returnValue) {
+ if (!forAdapter && method.isValid() && method.returnType() == QMetaType::QVariant)
+ param[0] = const_cast<void*>(reinterpret_cast<const void*>(returnValue));
+ else
+ param[0] = returnValue->data();
+ } else {
+ param[0] = nullptr;
+ }
+
+ auto argument = [&](int i) -> void * {
+ if ((forAdapter && m_api->methodParameterType(index, i) == QMetaType::QVariant) ||
+ (method.isValid() && method.parameterType(i) == QMetaType::QVariant)) {
+ return const_cast<void*>(reinterpret_cast<const void*>(&args.at(i)));
+ }
+ return const_cast<void*>(args.at(i).data());
+ };
+
+ for (int i = 0; i < args.size(); ++i) {
+ param[i + 1] = argument(i);
+ }
+ } else if (c == QMetaObject::WriteProperty || c == QMetaObject::ReadProperty) {
+ bool isQVariant = !forAdapter && parent()->metaObject()->property(resolvedIndex).userType() == QMetaType::QVariant;
+ for (int i = 0; i < args.size(); ++i) {
+ if (isQVariant)
+ param[i] = const_cast<void*>(reinterpret_cast<const void*>(&args.at(i)));
+ else
+ param[i] = const_cast<void*>(args.at(i).data());
+ }
+ if (c == QMetaObject::WriteProperty) {
+ Q_ASSERT(param.size() == 2); // for return-value and setter value
+ // check QMetaProperty::write for an explanation of these
+ param.append(&status);
+ param.append(&flags);
+ }
+ } else {
+ // Better safe than sorry
+ return false;
+ }
+ int r = -1;
+ if (forAdapter)
+ r = m_adapter->qt_metacall(c, resolvedIndex, param.data());
+ else
+ r = parent()->qt_metacall(c, resolvedIndex, param.data());
+ return r == -1 && status == -1;
+}
+
+void QRemoteObjectSourceBase::handleMetaCall(int index, QMetaObject::Call call, void **a)
+{
+ if (d->m_listeners.empty())
+ return;
+
+ int propertyIndex = m_api->propertyIndexFromSignal(index);
+ if (propertyIndex >= 0) {
+ const int internalIndex = m_api->propertyRawIndexFromSignal(index);
+ const auto target = m_api->isAdapterProperty(internalIndex) ? m_adapter : m_object;
+ const QMetaProperty mp = target->metaObject()->property(propertyIndex);
+ qCDebug(QT_REMOTEOBJECT) << "Sending Invoke Property" << (m_api->isAdapterSignal(internalIndex) ? "via adapter" : "") << internalIndex << propertyIndex << mp.name() << mp.read(target);
+
+ d->codec->serializePropertyChangePacket(this, index);
+ propertyIndex = internalIndex;
+ }
+
+ qCDebug(QT_REMOTEOBJECT) << "# Listeners" << d->m_listeners.size();
+ qCDebug(QT_REMOTEOBJECT) << "Invoke args:" << m_object
+ << (call == 0 ? QLatin1String("InvokeMetaMethod") : QStringLiteral("Non-invoked call: %d").arg(call))
+ << m_api->signalSignature(index) << *marshalArgs(index, a);
+
+ d->codec->serializeInvokePacket(name(), call, index, *marshalArgs(index, a), -1, propertyIndex);
+
+ d->codec->send(d->m_listeners);
+}
+
+void QRemoteObjectRootSource::addListener(QtROIoDeviceBase *io, bool dynamic)
+{
+ d->m_listeners.append(io);
+ d->isDynamic = d->isDynamic || dynamic;
+
+ if (dynamic) {
+ d->sentTypes.clear();
+ d->codec->serializeInitDynamicPacket(this);
+ d->codec->send(io);
+ } else {
+ d->codec->serializeInitPacket(this);
+ d->codec->send(io);
+ }
+}
+
+int QRemoteObjectRootSource::removeListener(QtROIoDeviceBase *io, bool shouldSendRemove)
+{
+ d->m_listeners.removeAll(io);
+ if (shouldSendRemove)
+ {
+ d->codec->serializeRemoveObjectPacket(m_api->name());
+ d->codec->send(io);
+ }
+ return int(d->m_listeners.size());
+}
+
+int QRemoteObjectSourceBase::qt_metacall(QMetaObject::Call call, int methodId, void **a)
+{
+ methodId = QObject::qt_metacall(call, methodId, a);
+ if (methodId < 0)
+ return methodId;
+
+ if (call == QMetaObject::InvokeMetaMethod)
+ handleMetaCall(methodId, call, a);
+
+ return -1;
+}
+
+DynamicApiMap::DynamicApiMap(QObject *object, const QMetaObject *metaObject, const QString &name, const QString &typeName)
+ : m_name(name),
+ m_typeName(typeName),
+ m_metaObject(metaObject),
+ m_cachedMetamethodIndex(-1)
+{
+ m_enumOffset = metaObject->enumeratorOffset();
+ m_enumCount = metaObject->enumeratorCount() - m_enumOffset;
+
+ const int propCount = metaObject->propertyCount();
+ const int propOffset = metaObject->propertyOffset();
+ m_properties.reserve(propCount-propOffset);
+ QSet<int> invalidSignals;
+ for (int i = propOffset; i < propCount; ++i) {
+ const QMetaProperty property = metaObject->property(i);
+ const auto metaType = property.metaType();
+ if (metaType.flags().testFlag(QMetaType::PointerToQObject)) {
+ auto propertyMeta = metaType.metaObject();
+ QObject *child = property.read(object).value<QObject *>();
+ const QMetaObject *meta = child ? child->metaObject() : propertyMeta;
+ if (!meta) {
+ const int notifyIndex = metaObject->property(i).notifySignalIndex();
+ if (notifyIndex != -1)
+ invalidSignals << notifyIndex;
+ continue;
+ }
+ if (meta->inherits(&QAbstractItemModel::staticMetaObject)) {
+ const QByteArray name = QByteArray::fromRawData(property.name(),
+ qsizetype(qstrlen(property.name())));
+ const QByteArray infoName = name.toUpper() + QByteArrayLiteral("_ROLES");
+ const int infoIndex = metaObject->indexOfClassInfo(infoName.constData());
+ QByteArray roleInfo;
+ if (infoIndex >= 0) {
+ auto ci = metaObject->classInfo(infoIndex);
+ roleInfo = QByteArray::fromRawData(ci.value(), qsizetype(qstrlen(ci.value())));
+ }
+ m_models << ModelInfo({qobject_cast<QAbstractItemModel *>(child),
+ QString::fromLatin1(property.name()),
+ roleInfo});
+ } else {
+ QString typeName = QtRemoteObjects::getTypeNameAndMetaobjectFromClassInfo(meta);
+ if (typeName.isNull()) {
+ typeName = QString::fromLatin1(meta->className());
+ if (typeName.contains(QLatin1String("QQuick")))
+ typeName.remove(QLatin1String("QQuick"));
+ else if (int index = typeName.indexOf(QLatin1String("_QMLTYPE_")))
+ typeName.truncate(index);
+ // TODO better way to ensure we have consistent typenames between source/replicas?
+ else if (typeName.endsWith(QLatin1String("Source")))
+ typeName.chop(6);
+ }
+
+ m_subclasses << new DynamicApiMap(child, meta, QString::fromLatin1(property.name()), typeName);
+ }
+ }
+ m_properties << i;
+ const int notifyIndex = metaObject->property(i).notifySignalIndex();
+ if (notifyIndex != -1) {
+ m_signals << notifyIndex;
+ m_propertyAssociatedWithSignal.append(i-propOffset);
+ //The starting values of _signals will be the notify signals
+ //So if we are processing _signal with index i, api->sourcePropertyIndex(_propertyAssociatedWithSignal.at(i))
+ //will be the property that changed. This is only valid if i < _propertyAssociatedWithSignal.size().
+ }
+ }
+ const int methodCount = metaObject->methodCount();
+ const int methodOffset = metaObject->methodOffset();
+ for (int i = methodOffset; i < methodCount; ++i) {
+ const QMetaMethod mm = metaObject->method(i);
+ const QMetaMethod::MethodType m = mm.methodType();
+ if (m == QMetaMethod::Signal) {
+ if (m_signals.indexOf(i) >= 0) // Already added as a property notifier
+ continue;
+ if (invalidSignals.contains(i)) // QObject with no metatype
+ continue;
+ m_signals << i;
+ } else if (m == QMetaMethod::Slot || m == QMetaMethod::Method)
+ m_methods << i;
+ }
+
+ m_objectSignature = QtPrivate::qtro_classinfo_signature(metaObject);
+}
+
+QByteArrayList DynamicApiMap::signalParameterNames(int index) const
+{
+ const int objectIndex = m_signals.at(index);
+ checkCache(objectIndex);
+ return m_cachedMetamethod.parameterNames();
+}
+
+int DynamicApiMap::parameterCount(int objectIndex) const
+{
+ checkCache(objectIndex);
+ return m_cachedMetamethod.parameterCount();
+}
+
+int DynamicApiMap::parameterType(int objectIndex, int paramIndex) const
+{
+ checkCache(objectIndex);
+ return m_cachedMetamethod.parameterType(paramIndex);
+}
+
+const QByteArray DynamicApiMap::signature(int objectIndex) const
+{
+ checkCache(objectIndex);
+ return m_cachedMetamethod.methodSignature();
+}
+
+QMetaMethod::MethodType DynamicApiMap::methodType(int index) const
+{
+ const int objectIndex = m_methods.at(index);
+ checkCache(objectIndex);
+ return m_cachedMetamethod.methodType();
+}
+
+const QByteArray DynamicApiMap::typeName(int index) const
+{
+ const int objectIndex = m_methods.at(index);
+ checkCache(objectIndex);
+ return m_cachedMetamethod.typeName();
+}
+
+QByteArrayList DynamicApiMap::methodParameterNames(int index) const
+{
+ const int objectIndex = m_methods.at(index);
+ checkCache(objectIndex);
+ return m_cachedMetamethod.parameterNames();
+}
+
+QRemoteObjectSourceBase::Private::Private(QRemoteObjectSourceIo *io, QRemoteObjectRootSource *root)
+ : m_sourceIo(io), codec(io->m_codec.data()), isDynamic(false), root(root)
+{
+}
+
+QT_END_NAMESPACE
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QREMOTEOBJECTSOURCE_H
+#define QREMOTEOBJECTSOURCE_H
+
+#include <QtCore/qscopedpointer.h>
+#include <QtRemoteObjects/qtremoteobjectglobal.h>
+#include <QtCore/qmetaobject.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QtPrivate {
+
+//Based on compile time checks for static connect() from qobjectdefs_impl.h
+template <class ObjectType, typename Func1, typename Func2>
+static inline int qtro_property_index(Func1, Func2, const char *propName)
+{
+ typedef QtPrivate::FunctionPointer<Func1> Type1;
+ typedef QtPrivate::FunctionPointer<Func2> Type2;
+
+ //compilation error if the arguments do not match.
+ Q_STATIC_ASSERT_X(int(Type1::ArgumentCount) >= int(Type2::ArgumentCount),
+ "Argument counts are not compatible.");
+ Q_STATIC_ASSERT_X((QtPrivate::CheckCompatibleArguments<typename Type1::Arguments, typename Type2::Arguments>::value),
+ "Arguments are not compatible.");
+ Q_STATIC_ASSERT_X((QtPrivate::AreArgumentsCompatible<typename Type1::ReturnType, typename Type2::ReturnType>::value),
+ "Return types are not compatible.");
+ return ObjectType::staticMetaObject.indexOfProperty(propName);
+}
+
+template <class ObjectType, typename Func1, typename Func2>
+static inline int qtro_signal_index(Func1 func, Func2, int *count, int const **types)
+{
+ typedef QtPrivate::FunctionPointer<Func1> Type1;
+ typedef QtPrivate::FunctionPointer<Func2> Type2;
+
+ //compilation error if the arguments do not match.
+ Q_STATIC_ASSERT_X(int(Type1::ArgumentCount) >= int(Type2::ArgumentCount),
+ "Argument counts are not compatible.");
+ Q_STATIC_ASSERT_X((QtPrivate::CheckCompatibleArguments<typename Type1::Arguments, typename Type2::Arguments>::value),
+ "Arguments are not compatible.");
+ Q_STATIC_ASSERT_X((QtPrivate::AreArgumentsCompatible<typename Type1::ReturnType, typename Type2::ReturnType>::value),
+ "Return types are not compatible.");
+ const QMetaMethod sig = QMetaMethod::fromSignal(func);
+ *count = Type2::ArgumentCount;
+ *types = QtPrivate::ConnectionTypes<typename Type2::Arguments>::types();
+ return sig.methodIndex();
+}
+
+template <class ObjectType, typename Func1, typename Func2>
+static inline void qtro_method_test(Func1, Func2)
+{
+ typedef QtPrivate::FunctionPointer<Func1> Type1;
+ typedef QtPrivate::FunctionPointer<Func2> Type2;
+
+ //compilation error if the arguments do not match.
+ Q_STATIC_ASSERT_X(int(Type1::ArgumentCount) >= int(Type2::ArgumentCount),
+ "Argument counts are not compatible.");
+ Q_STATIC_ASSERT_X((QtPrivate::CheckCompatibleArguments<typename Type1::Arguments, typename Type2::Arguments>::value),
+ "Arguments are not compatible.");
+ Q_STATIC_ASSERT_X((QtPrivate::AreArgumentsCompatible<typename Type1::ReturnType, typename Type2::ReturnType>::value),
+ "Return types are not compatible.");
+}
+
+Q_REMOTEOBJECTS_EXPORT
+int qtro_method_index_impl(const QMetaObject *staticMetaObj, const char *className,
+ const char *methodName, int *count, const int **types);
+
+template <class ObjectType, typename Func1, typename Func2>
+static inline int qtro_method_index(Func1, Func2, const char *methodName, int *count, int const **types)
+{
+ typedef QtPrivate::FunctionPointer<Func1> Type1;
+ typedef QtPrivate::FunctionPointer<Func2> Type2;
+
+ //compilation error if the arguments do not match.
+ Q_STATIC_ASSERT_X(int(Type1::ArgumentCount) >= int(Type2::ArgumentCount),
+ "Argument counts are not compatible.");
+ Q_STATIC_ASSERT_X((QtPrivate::CheckCompatibleArguments<typename Type1::Arguments, typename Type2::Arguments>::value),
+ "Arguments are not compatible.");
+ Q_STATIC_ASSERT_X((QtPrivate::AreArgumentsCompatible<typename Type1::ReturnType, typename Type2::ReturnType>::value),
+ "Return types are not compatible.");
+ *count = Type2::ArgumentCount;
+ *types = QtPrivate::ConnectionTypes<typename Type2::Arguments>::types();
+
+ return qtro_method_index_impl(&ObjectType::staticMetaObject,
+ ObjectType::staticMetaObject.className(), methodName, count,
+ types);
+}
+
+template <class ObjectType>
+static inline QByteArray qtro_enum_signature(const char *enumName)
+{
+ const auto qme = ObjectType::staticMetaObject.enumerator(ObjectType::staticMetaObject.indexOfEnumerator(enumName));
+ return QByteArrayLiteral("1::2").replace("1", qme.scope()).replace("2", qme.name());
+}
+
+QByteArray qtro_classinfo_signature(const QMetaObject *metaObject);
+
+}
+
+// TODO ModelInfo just needs roles, and no need for SubclassInfo
+class QAbstractItemModel;
+
+struct ModelInfo
+{
+ QAbstractItemModel *ptr;
+ QString name;
+ QByteArray roles;
+};
+
+class Q_REMOTEOBJECTS_EXPORT SourceApiMap
+{
+protected:
+ SourceApiMap() {}
+public:
+ virtual ~SourceApiMap();
+ virtual QString name() const = 0;
+ virtual QString typeName() const = 0;
+ virtual QByteArray className() const { return typeName().toLatin1().append("Source"); }
+ virtual int enumCount() const = 0;
+ virtual int propertyCount() const = 0;
+ virtual int signalCount() const = 0;
+ virtual int methodCount() const = 0;
+ virtual int sourceEnumIndex(int index) const = 0;
+ virtual int sourcePropertyIndex(int index) const = 0;
+ virtual int sourceSignalIndex(int index) const = 0;
+ virtual int sourceMethodIndex(int index) const = 0;
+ virtual int signalParameterCount(int index) const = 0;
+ virtual int signalParameterType(int sigIndex, int paramIndex) const = 0;
+ virtual const QByteArray signalSignature(int index) const = 0;
+ virtual QByteArrayList signalParameterNames(int index) const = 0;
+ virtual int methodParameterCount(int index) const = 0;
+ virtual int methodParameterType(int methodIndex, int paramIndex) const = 0;
+ virtual const QByteArray methodSignature(int index) const = 0;
+ virtual QMetaMethod::MethodType methodType(int index) const = 0;
+ virtual const QByteArray typeName(int index) const = 0;
+ virtual QByteArrayList methodParameterNames(int index) const = 0;
+ virtual int propertyIndexFromSignal(int index) const = 0;
+ virtual int propertyRawIndexFromSignal(int index) const = 0;
+ virtual QByteArray objectSignature() const = 0;
+ virtual bool isDynamic() const { return false; }
+ virtual bool isAdapterSignal(int) const { return false; }
+ virtual bool isAdapterMethod(int) const { return false; }
+ virtual bool isAdapterProperty(int) const { return false; }
+ QList<ModelInfo> m_models;
+ QList<SourceApiMap *> m_subclasses;
+};
+
+QT_END_NAMESPACE
+
+#endif
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QREMOTEOBJECTSOURCE_P_H
+#define QREMOTEOBJECTSOURCE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qlist.h>
+#include <QtCore/qmetaobject.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qpointer.h>
+#include "qremoteobjectsource.h"
+#include "qremoteobjectpacket_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QRemoteObjectSourceIo;
+class QtROIoDeviceBase;
+
+class QRemoteObjectSourceBase : public QObject
+{
+public:
+ ~QRemoteObjectSourceBase() override;
+
+ void setConnections();
+ void resetObject(QObject *newObject);
+ int qt_metacall(QMetaObject::Call call, int methodId, void **a) final;
+ QObject *m_object, *m_adapter;
+ const SourceApiMap *m_api;
+ QVariantList m_marshalledArgs;
+ bool hasAdapter() const { return m_adapter; }
+ virtual QString name() const = 0;
+ virtual bool isRoot() const = 0;
+
+ QVariantList* marshalArgs(int index, void **a);
+ void handleMetaCall(int index, QMetaObject::Call call, void **a);
+ bool invoke(QMetaObject::Call c, int index, const QVariantList& args, QVariant* returnValue = nullptr);
+ QByteArray m_objectChecksum;
+ QMap<int, QPointer<QRemoteObjectSourceBase>> m_children;
+ struct Private {
+ Private(QRemoteObjectSourceIo *io, QRemoteObjectRootSource *root);
+ QRemoteObjectSourceIo *m_sourceIo;
+ QList<QtROIoDeviceBase*> m_listeners;
+ // Pointer to codec, not owned by Private. We can assume it is valid.
+ QRemoteObjectPackets::CodecBase *codec;
+
+ // Types needed during recursively sending a root to a new listener
+ QSet<QString> sentTypes;
+ bool isDynamic;
+ QRemoteObjectRootSource *root;
+ };
+ Private *d;
+ static const int qobjectPropertyOffset;
+ static const int qobjectMethodOffset;
+protected:
+ explicit QRemoteObjectSourceBase(QObject *object, Private *d, const SourceApiMap *, QObject *adapter);
+};
+
+class QRemoteObjectSource : public QRemoteObjectSourceBase
+{
+public:
+ explicit QRemoteObjectSource(QObject *object, Private *d, const SourceApiMap *, QObject *adapter, const QString &parentName);
+ ~QRemoteObjectSource() override;
+
+ bool isRoot() const override { return false; }
+ QString name() const override { return m_name; }
+
+ QString m_name;
+};
+
+class QRemoteObjectRootSource : public QRemoteObjectSourceBase
+{
+public:
+ explicit QRemoteObjectRootSource(QObject *object, const SourceApiMap *,
+ QObject *adapter, QRemoteObjectSourceIo *sourceIo);
+ ~QRemoteObjectRootSource() override;
+
+ bool isRoot() const override { return true; }
+ QString name() const override { return m_name; }
+ void addListener(QtROIoDeviceBase *io, bool dynamic = false);
+ int removeListener(QtROIoDeviceBase *io, bool shouldSendRemove = false);
+
+ QString m_name;
+};
+
+class DynamicApiMap final : public SourceApiMap
+{
+public:
+ DynamicApiMap(QObject *object, const QMetaObject *metaObject, const QString &name, const QString &typeName);
+ ~DynamicApiMap() override {}
+ QString name() const override { return m_name; }
+ QString typeName() const override { return m_typeName; }
+ QByteArray className() const override { return QByteArray(m_metaObject->className()); }
+ int enumCount() const override { return m_enumCount; }
+ int propertyCount() const override { return m_properties.size(); }
+ int signalCount() const override { return m_signals.size(); }
+ int methodCount() const override { return m_methods.size(); }
+ int sourceEnumIndex(int index) const override
+ {
+ if (index < 0 || index >= enumCount())
+ return -1;
+ return m_enumOffset + index;
+ }
+ int sourcePropertyIndex(int index) const override
+ {
+ if (index < 0 || index >= propertyCount())
+ return -1;
+ return m_properties.at(index);
+ }
+ int sourceSignalIndex(int index) const override
+ {
+ if (index < 0 || index >= signalCount())
+ return -1;
+ return m_signals.at(index);
+ }
+ int sourceMethodIndex(int index) const override
+ {
+ if (index < 0 || index >= methodCount())
+ return -1;
+ return m_methods.at(index);
+ }
+ int signalParameterCount(int index) const override { return parameterCount(m_signals.at(index)); }
+ int signalParameterType(int sigIndex, int paramIndex) const override { return parameterType(m_signals.at(sigIndex), paramIndex); }
+ const QByteArray signalSignature(int index) const override { return signature(m_signals.at(index)); }
+ QByteArrayList signalParameterNames(int index) const override;
+
+ int methodParameterCount(int index) const override { return parameterCount(m_methods.at(index)); }
+ int methodParameterType(int methodIndex, int paramIndex) const override { return parameterType(m_methods.at(methodIndex), paramIndex); }
+ const QByteArray methodSignature(int index) const override { return signature(m_methods.at(index)); }
+ QMetaMethod::MethodType methodType(int index) const override;
+ const QByteArray typeName(int index) const override;
+ QByteArrayList methodParameterNames(int index) const override;
+
+ int propertyIndexFromSignal(int index) const override
+ {
+ if (index >= 0 && index < m_propertyAssociatedWithSignal.size())
+ return m_properties.at(m_propertyAssociatedWithSignal.at(index));
+ return -1;
+ }
+ int propertyRawIndexFromSignal(int index) const override
+ {
+ if (index >= 0 && index < m_propertyAssociatedWithSignal.size())
+ return m_propertyAssociatedWithSignal.at(index);
+ return -1;
+ }
+ QByteArray objectSignature() const override { return m_objectSignature; }
+
+ bool isDynamic() const override { return true; }
+
+ int parameterCount(int objectIndex) const;
+ int parameterType(int objectIndex, int paramIndex) const;
+ const QByteArray signature(int objectIndex) const;
+ inline void checkCache(int objectIndex) const
+ {
+ if (objectIndex != m_cachedMetamethodIndex) {
+ m_cachedMetamethodIndex = objectIndex;
+ m_cachedMetamethod = m_metaObject->method(objectIndex);
+ }
+ }
+
+ QString m_name;
+ QString m_typeName;
+ int m_enumCount;
+ int m_enumOffset;
+ QList<int> m_properties;
+ QList<int> m_signals;
+ QList<int> m_methods;
+ QList<int> m_propertyAssociatedWithSignal;
+ const QMetaObject *m_metaObject;
+ mutable QMetaMethod m_cachedMetamethod;
+ mutable int m_cachedMetamethodIndex;
+ QByteArray m_objectSignature;
+};
+
+QT_END_NAMESPACE
+
+#endif
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qremoteobjectsourceio_p.h"
+
+#include "qremoteobjectpacket_p.h"
+#include "qremoteobjectsource_p.h"
+#include "qremoteobjectnode_p.h"
+#include "qremoteobjectpendingcall.h"
+#include "qtremoteobjectglobal.h"
+
+#include <QtCore/qstringlist.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QtRemoteObjects;
+
+QRemoteObjectSourceIo::QRemoteObjectSourceIo(const QUrl &address, QObject *parent)
+ : QObject(parent)
+ , m_server(QtROServerFactory::instance()->isValid(address) ?
+ QtROServerFactory::instance()->create(address, this) : nullptr)
+ , m_address(address)
+{
+ if (m_server == nullptr)
+ qRODebug(this) << "Using" << m_address << "as external url.";
+}
+
+QRemoteObjectSourceIo::QRemoteObjectSourceIo(QObject *parent)
+ : QObject(parent)
+ , m_server(nullptr)
+{
+}
+
+QRemoteObjectSourceIo::~QRemoteObjectSourceIo()
+{
+ qDeleteAll(m_sourceRoots.values());
+}
+
+bool QRemoteObjectSourceIo::startListening()
+{
+ if (!m_server->listen(m_address)) {
+ qROCritical(this) << "Listen failed for URL:" << m_address;
+ qROCritical(this) << m_server->serverError();
+ return false;
+ }
+
+ qRODebug(this) << "QRemoteObjectSourceIo is Listening" << m_address;
+ connect(m_server.data(), &QConnectionAbstractServer::newConnection, this,
+ &QRemoteObjectSourceIo::handleConnection);
+ return true;
+}
+
+bool QRemoteObjectSourceIo::enableRemoting(QObject *object, const QMetaObject *meta, const QString &name, const QString &typeName)
+{
+ if (m_sourceRoots.contains(name)) {
+ qROWarning(this) << "Tried to register QRemoteObjectRootSource twice" << name;
+ return false;
+ }
+
+ return enableRemoting(object, new DynamicApiMap(object, meta, name, typeName));
+}
+
+bool QRemoteObjectSourceIo::enableRemoting(QObject *object, const SourceApiMap *api, QObject *adapter)
+{
+ const QString name = api->name();
+ if (!api->isDynamic() && m_sourceRoots.contains(name)) {
+ qROWarning(this) << "Tried to register QRemoteObjectRootSource twice" << name;
+ return false;
+ }
+
+ new QRemoteObjectRootSource(object, api, adapter, this);
+ m_codec->serializeObjectListPacket({QRemoteObjectPackets::ObjectInfo{api->name(), api->typeName(), api->objectSignature()}});
+ m_codec->send(m_connections);
+ if (const int count = m_connections.size())
+ qRODebug(this) << "Wrote new QObjectListPacket for" << api->name() << "to" << count << "connections";
+ return true;
+}
+
+bool QRemoteObjectSourceIo::disableRemoting(QObject *object)
+{
+ QRemoteObjectRootSource *source = m_objectToSourceMap.take(object);
+ if (!source)
+ return false;
+
+ delete source;
+ return true;
+}
+
+void QRemoteObjectSourceIo::registerSource(QRemoteObjectSourceBase *source)
+{
+ Q_ASSERT(source);
+ const QString &name = source->name();
+ m_sourceObjects[name] = source;
+ if (source->isRoot()) {
+ QRemoteObjectRootSource *root = static_cast<QRemoteObjectRootSource *>(source);
+ qRODebug(this) << "Registering" << name;
+ m_sourceRoots[name] = root;
+ m_objectToSourceMap[source->m_object] = root;
+ if (serverAddress().isValid()) {
+ const auto &type = source->m_api->typeName();
+ emit remoteObjectAdded(qMakePair(name, QRemoteObjectSourceLocationInfo(type, serverAddress())));
+ }
+ }
+}
+
+void QRemoteObjectSourceIo::unregisterSource(QRemoteObjectSourceBase *source)
+{
+ Q_ASSERT(source);
+ const QString &name = source->name();
+ m_sourceObjects.remove(name);
+ if (source->isRoot()) {
+ const auto type = source->m_api->typeName();
+ m_objectToSourceMap.remove(source->m_object);
+ m_sourceRoots.remove(name);
+ if (serverAddress().isValid())
+ emit remoteObjectRemoved(qMakePair(name, QRemoteObjectSourceLocationInfo(type, serverAddress())));
+ }
+}
+
+void QRemoteObjectSourceIo::onServerDisconnect(QObject *conn)
+{
+ QtROIoDeviceBase *connection = qobject_cast<QtROIoDeviceBase*>(conn);
+ m_connections.remove(connection);
+
+ qRODebug(this) << "OnServerDisconnect";
+
+ for (QRemoteObjectRootSource *root : std::as_const(m_sourceRoots))
+ root->removeListener(connection);
+
+ const QUrl location = m_registryMapping.value(connection);
+ emit serverRemoved(location);
+ m_registryMapping.remove(connection);
+ connection->close();
+ connection->deleteLater();
+}
+
+void QRemoteObjectSourceIo::onServerRead(QObject *conn)
+{
+ // Assert the invariant here conn is of type QIODevice
+ QtROIoDeviceBase *connection = qobject_cast<QtROIoDeviceBase*>(conn);
+ QRemoteObjectPacketTypeEnum packetType;
+
+ do {
+
+ if (!connection->read(packetType, m_rxName))
+ return;
+
+ using namespace QRemoteObjectPackets;
+
+ switch (packetType) {
+ case Ping:
+ m_codec->serializePongPacket(m_rxName);
+ m_codec->send(connection);
+ break;
+ case AddObject:
+ {
+ bool isDynamic;
+ m_codec->deserializeAddObjectPacket(connection->d_func()->stream(), isDynamic);
+ qRODebug(this) << "AddObject" << m_rxName << isDynamic;
+ if (m_sourceRoots.contains(m_rxName)) {
+ QRemoteObjectRootSource *root = m_sourceRoots[m_rxName];
+ root->addListener(connection, isDynamic);
+ } else {
+ qROWarning(this) << "Request to attach to non-existent RemoteObjectSource:" << m_rxName;
+ }
+ break;
+ }
+ case RemoveObject:
+ {
+ qRODebug(this) << "RemoveObject" << m_rxName;
+ if (m_sourceRoots.contains(m_rxName)) {
+ QRemoteObjectRootSource *root = m_sourceRoots[m_rxName];
+ const int count = root->removeListener(connection);
+ Q_UNUSED(count);
+ //TODO - possible to have a timer that closes connections if not reopened within a timeout?
+ } else {
+ qROWarning(this) << "Request to detach from non-existent RemoteObjectSource:" << m_rxName;
+ }
+ qRODebug(this) << "RemoveObject finished" << m_rxName;
+ break;
+ }
+ case InvokePacket:
+ {
+ int call, index, serialId, propertyId;
+ m_codec->deserializeInvokePacket(connection->d_func()->stream(), call, index, m_rxArgs, serialId, propertyId);
+ if (m_rxName == QLatin1String("Registry") && !m_registryMapping.contains(connection)) {
+ const QRemoteObjectSourceLocation loc = m_rxArgs.first().value<QRemoteObjectSourceLocation>();
+ m_registryMapping[connection] = loc.second.hostUrl;
+ }
+ if (m_sourceObjects.contains(m_rxName)) {
+ QRemoteObjectSourceBase *source = m_sourceObjects[m_rxName];
+ if (call == QMetaObject::InvokeMetaMethod) {
+ const int resolvedIndex = source->m_api->sourceMethodIndex(index);
+ if (resolvedIndex < 0) { //Invalid index
+ qROWarning(this) << "Invalid method invoke packet received. Index =" << index <<"which is out of bounds for type"<<m_rxName;
+ //TODO - consider moving this to packet validation?
+ break;
+ }
+ if (source->m_api->isAdapterMethod(index))
+ qRODebug(this) << "Adapter (method) Invoke-->" << m_rxName << source->m_adapter->metaObject()->method(resolvedIndex).name();
+ else {
+ qRODebug(this) << "Source (method) Invoke-->" << m_rxName << source->m_object->metaObject()->method(resolvedIndex).methodSignature();
+ auto method = source->m_object->metaObject()->method(resolvedIndex);
+ const int parameterCount = method.parameterCount();
+ for (int i = 0; i < parameterCount; i++)
+ m_rxArgs[i] = decodeVariant(std::move(m_rxArgs[i]), method.parameterMetaType(i));
+ }
+ auto metaType = QMetaType::fromName(source->m_api->typeName(index).constData());
+ if (!metaType.sizeOf())
+ metaType = QMetaType(QMetaType::UnknownType);
+ QVariant returnValue(metaType, nullptr);
+ // If a Replica is used as a Source (which node->proxy() does) we can have a PendingCall return value.
+ // In this case, we need to wait for the pending call and send that.
+ if (source->m_api->typeName(index) == QByteArrayLiteral("QRemoteObjectPendingCall"))
+ returnValue = QVariant::fromValue<QRemoteObjectPendingCall>(QRemoteObjectPendingCall());
+ source->invoke(QMetaObject::InvokeMetaMethod, index, m_rxArgs, &returnValue);
+ // send reply if wanted
+ if (serialId >= 0) {
+ if (returnValue.canConvert<QRemoteObjectPendingCall>()) {
+ QRemoteObjectPendingCall call = returnValue.value<QRemoteObjectPendingCall>();
+ // Watcher will be destroyed when connection is, or when the finished lambda is called
+ QRemoteObjectPendingCallWatcher *watcher = new QRemoteObjectPendingCallWatcher(call, connection);
+ QObject::connect(watcher, &QRemoteObjectPendingCallWatcher::finished, connection, [this, serialId, connection, watcher]() {
+ if (watcher->error() == QRemoteObjectPendingCall::NoError) {
+ m_codec->serializeInvokeReplyPacket(this->m_rxName, serialId, encodeVariant(watcher->returnValue()));
+ m_codec->send(connection);
+ }
+ watcher->deleteLater();
+ });
+ } else {
+ m_codec->serializeInvokeReplyPacket(m_rxName, serialId, encodeVariant(returnValue));
+ m_codec->send(connection);
+ }
+ }
+ } else {
+ const int resolvedIndex = source->m_api->sourcePropertyIndex(index);
+ if (resolvedIndex < 0) {
+ qROWarning(this) << "Invalid property invoke packet received. Index =" << index <<"which is out of bounds for type"<<m_rxName;
+ //TODO - consider moving this to packet validation?
+ break;
+ }
+ if (source->m_api->isAdapterProperty(index))
+ qRODebug(this) << "Adapter (write property) Invoke-->" << m_rxName << source->m_adapter->metaObject()->property(resolvedIndex).name();
+ else
+ qRODebug(this) << "Source (write property) Invoke-->" << m_rxName << source->m_object->metaObject()->property(resolvedIndex).name();
+ source->invoke(QMetaObject::WriteProperty, index, m_rxArgs);
+ }
+ }
+ break;
+ }
+ default:
+ qRODebug(this) << "OnReadReady invalid type" << packetType;
+ }
+ } while (connection->bytesAvailable()); // have bytes left over, so do another iteration
+}
+
+void QRemoteObjectSourceIo::handleConnection()
+{
+ qRODebug(this) << "handleConnection" << m_connections;
+
+ QtROServerIoDevice *conn = m_server->nextPendingConnection();
+ newConnection(conn);
+}
+
+void QRemoteObjectSourceIo::newConnection(QtROIoDeviceBase *conn)
+{
+ m_connections.insert(conn);
+ connect(conn, &QtROIoDeviceBase::readyRead, this, [this, conn]() {
+ onServerRead(conn);
+ });
+ connect(conn, &QtROIoDeviceBase::disconnected, this, [this, conn]() {
+ onServerDisconnect(conn);
+ });
+
+ m_codec->serializeHandshakePacket();
+ m_codec->send(conn);
+
+ QRemoteObjectPackets::ObjectInfoList infos;
+ infos.reserve(m_sourceRoots.size());
+ for (auto remoteObject : std::as_const(m_sourceRoots)) {
+ infos << QRemoteObjectPackets::ObjectInfo{remoteObject->m_api->name(), remoteObject->m_api->typeName(), remoteObject->m_api->objectSignature()};
+ }
+ m_codec->serializeObjectListPacket(infos);
+ m_codec->send(conn);
+ qRODebug(this) << "Wrote ObjectList packet from Server" << QStringList(m_sourceRoots.keys());
+}
+
+QUrl QRemoteObjectSourceIo::serverAddress() const
+{
+ if (m_server)
+ return m_server->address();
+ return m_address;
+}
+
+QT_END_NAMESPACE
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QREMOTEOBJECTSOURCEIO_P_H
+#define QREMOTEOBJECTSOURCEIO_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qconnectionfactories_p.h"
+#include "qtremoteobjectglobal.h"
+#include "qremoteobjectpacket_p.h"
+
+#include <QtCore/qiodevice.h>
+#include <QtCore/qscopedpointer.h>
+
+QT_BEGIN_NAMESPACE
+
+class QRemoteObjectSourceBase;
+class QRemoteObjectRootSource;
+class SourceApiMap;
+class QRemoteObjectHostBase;
+
+class QRemoteObjectSourceIo : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QRemoteObjectSourceIo(const QUrl &address, QObject *parent = nullptr);
+ explicit QRemoteObjectSourceIo(QObject *parent = nullptr);
+ ~QRemoteObjectSourceIo() override;
+
+ bool startListening();
+ bool enableRemoting(QObject *object, const QMetaObject *meta, const QString &name,
+ const QString &typeName);
+ bool enableRemoting(QObject *object, const SourceApiMap *api, QObject *adapter = nullptr);
+ bool disableRemoting(QObject *object);
+ void newConnection(QtROIoDeviceBase *conn);
+
+ QUrl serverAddress() const;
+
+public Q_SLOTS:
+ void handleConnection();
+ void onServerDisconnect(QObject *obj = nullptr);
+ void onServerRead(QObject *obj);
+
+Q_SIGNALS:
+ void remoteObjectAdded(const QRemoteObjectSourceLocation &);
+ void remoteObjectRemoved(const QRemoteObjectSourceLocation &);
+ void serverRemoved(const QUrl& url);
+
+public:
+ void registerSource(QRemoteObjectSourceBase *source);
+ void unregisterSource(QRemoteObjectSourceBase *source);
+
+ QHash<QIODevice*, quint32> m_readSize;
+ QSet<QtROIoDeviceBase*> m_connections;
+ QHash<QObject *, QRemoteObjectRootSource*> m_objectToSourceMap;
+ QMap<QString, QRemoteObjectSourceBase*> m_sourceObjects;
+ QMap<QString, QRemoteObjectRootSource*> m_sourceRoots;
+ QHash<QtROIoDeviceBase*, QUrl> m_registryMapping;
+ QScopedPointer<QConnectionAbstractServer> m_server;
+ // TODO should have some sort of manager for the codec
+ QScopedPointer<QRemoteObjectPackets::CodecBase> m_codec{new QRemoteObjectPackets::QDataStreamCodec};
+ QString m_rxName;
+ QVariantList m_rxArgs;
+ QUrl m_address;
+};
+
+QT_END_NAMESPACE
+
+#endif
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qtremoteobjectglobal.h"
+#include "qremoteobjectpacket_p.h"
+
+#include <QtCore/qdatastream.h>
+#include <QtCore/qmetaobject.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(QT_REMOTEOBJECT, "qt.remoteobjects", QtWarningMsg)
+Q_LOGGING_CATEGORY(QT_REMOTEOBJECT_MODELS, "qt.remoteobjects.models", QtWarningMsg)
+Q_LOGGING_CATEGORY(QT_REMOTEOBJECT_IO, "qt.remoteobjects.io", QtWarningMsg)
+
+QT_IMPL_METATYPE_EXTERN(QRemoteObjectSourceLocation)
+QT_IMPL_METATYPE_EXTERN(QRemoteObjectSourceLocations)
+QT_IMPL_METATYPE_EXTERN(QIntHash)
+
+/*!
+ \namespace QtRemoteObjects
+ \inmodule QtRemoteObjects
+
+ \brief The QtRemoteObjects namespace contains identifiers used in the
+ Remote Objects module, as well as some functions used from code generated
+ by the \l{Qt Remote Objects Compiler}{Replica Compiler (repc)}.
+*/
+
+/*!
+ \enum QtRemoteObjects::InitialAction
+
+ This enum type specifies the initial action when acquiring a \l Replica derived
+ from QAbstractItemModel.
+
+ \value FetchRootSize Only the size of the model is requested before the
+ \l {QRemoteObjectReplica::}{initialized} signal is emitted,
+ no data will be prefetched before that.
+ \value PrefetchData Some data can be prefetched before the
+ \l {QRemoteObjectReplica::}{initialized} signal is emitted.
+
+ \sa QRemoteObjectNode::acquireModel(), QRemoteObjectReplica::initialized()
+*/
+
+namespace QtRemoteObjects {
+
+void copyStoredProperties(const QMetaObject *mo, const void *src, void *dst)
+{
+ if (!src) {
+ qCWarning(QT_REMOTEOBJECT) << Q_FUNC_INFO << ": trying to copy from a null source";
+ return;
+ }
+ if (!dst) {
+ qCWarning(QT_REMOTEOBJECT) << Q_FUNC_INFO << ": trying to copy to a null destination";
+ return;
+ }
+
+ for (int i = 0, end = mo->propertyCount(); i != end; ++i) {
+ const QMetaProperty mp = mo->property(i);
+ mp.writeOnGadget(dst, mp.readOnGadget(src));
+ }
+}
+
+void copyStoredProperties(const QMetaObject *mo, const void *src, QDataStream &dst)
+{
+ if (!src) {
+ qCWarning(QT_REMOTEOBJECT) << Q_FUNC_INFO << ": trying to copy from a null source";
+ return;
+ }
+
+ for (int i = 0, end = mo->propertyCount(); i != end; ++i) {
+ const QMetaProperty mp = mo->property(i);
+ dst << QRemoteObjectPackets::encodeVariant(mp.readOnGadget(src));
+ }
+}
+
+void copyStoredProperties(const QMetaObject *mo, QDataStream &src, void *dst)
+{
+ if (!dst) {
+ qCWarning(QT_REMOTEOBJECT) << Q_FUNC_INFO << ": trying to copy to a null destination";
+ return;
+ }
+
+ for (int i = 0, end = mo->propertyCount(); i != end; ++i) {
+ const QMetaProperty mp = mo->property(i);
+ QVariant v;
+ src >> v;
+ mp.writeOnGadget(dst, QRemoteObjectPackets::decodeVariant(std::move(v), mp.metaType()));
+ }
+}
+
+} // namespace QtRemoteObjects
+
+QT_END_NAMESPACE
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QTREMOTEOBJECTGLOBAL_H
+#define QTREMOTEOBJECTGLOBAL_H
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtRemoteObjects/qtremoteobjectsexports.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QRemoteObjectSourceLocationInfo
+{
+ QRemoteObjectSourceLocationInfo() = default;
+ QRemoteObjectSourceLocationInfo(const QString &typeName_, const QUrl &hostUrl_)
+ : typeName(typeName_), hostUrl(hostUrl_) {}
+
+ inline bool operator==(const QRemoteObjectSourceLocationInfo &other) const Q_DECL_NOTHROW
+ {
+ return other.typeName == typeName && other.hostUrl == hostUrl;
+ }
+ inline bool operator!=(const QRemoteObjectSourceLocationInfo &other) const Q_DECL_NOTHROW
+ {
+ return !(*this == other);
+ }
+
+ QString typeName;
+ QUrl hostUrl;
+};
+
+inline QDebug operator<<(QDebug dbg, const QRemoteObjectSourceLocationInfo &info)
+{
+ dbg.nospace() << "SourceLocationInfo(" << info.typeName << ", " << info.hostUrl << ")";
+ return dbg.space();
+}
+
+inline QDataStream& operator<<(QDataStream &stream, const QRemoteObjectSourceLocationInfo &info)
+{
+ return stream << info.typeName << info.hostUrl;
+}
+
+inline QDataStream& operator>>(QDataStream &stream, QRemoteObjectSourceLocationInfo &info)
+{
+ return stream >> info.typeName >> info.hostUrl;
+}
+
+typedef QPair<QString, QRemoteObjectSourceLocationInfo> QRemoteObjectSourceLocation;
+typedef QHash<QString, QRemoteObjectSourceLocationInfo> QRemoteObjectSourceLocations;
+typedef QHash<int, QByteArray> QIntHash;
+
+QT_END_NAMESPACE
+QT_DECL_METATYPE_EXTERN(QRemoteObjectSourceLocation, Q_REMOTEOBJECTS_EXPORT)
+QT_DECL_METATYPE_EXTERN(QRemoteObjectSourceLocations, Q_REMOTEOBJECTS_EXPORT)
+QT_DECL_METATYPE_EXTERN(QIntHash, /* not exported */)
+QT_BEGIN_NAMESPACE
+
+#define QCLASSINFO_REMOTEOBJECT_TYPE "RemoteObject Type"
+#define QCLASSINFO_REMOTEOBJECT_SIGNATURE "RemoteObject Signature"
+
+class QDataStream;
+
+namespace QRemoteObjectStringLiterals {
+
+// when QStringLiteral is used with the same string in different functions,
+// it creates duplicate static data. Wrapping it in inline functions prevents it.
+
+inline QString local() { return QStringLiteral("local"); }
+inline QString localabstract() { return QStringLiteral("localabstract"); }
+inline QString tcp() { return QStringLiteral("tcp"); }
+inline QString CLASS() { return QStringLiteral("Class::%1"); }
+inline QString MODEL() { return QStringLiteral("Model::%1"); }
+inline QString QAIMADAPTER() { return QStringLiteral("QAbstractItemModelAdapter"); }
+
+}
+
+Q_DECLARE_LOGGING_CATEGORY(QT_REMOTEOBJECT)
+Q_DECLARE_LOGGING_CATEGORY(QT_REMOTEOBJECT_MODELS)
+Q_DECLARE_LOGGING_CATEGORY(QT_REMOTEOBJECT_IO)
+
+namespace QtRemoteObjects {
+
+Q_NAMESPACE
+
+Q_REMOTEOBJECTS_EXPORT void copyStoredProperties(const QMetaObject *mo, const void *src, void *dst);
+Q_REMOTEOBJECTS_EXPORT void copyStoredProperties(const QMetaObject *mo, const void *src, QDataStream &dst);
+Q_REMOTEOBJECTS_EXPORT void copyStoredProperties(const QMetaObject *mo, QDataStream &src, void *dst);
+
+QString getTypeNameAndMetaobjectFromClassInfo(const QMetaObject *& meta);
+
+template <typename T>
+void copyStoredProperties(const T *src, T *dst)
+{
+ copyStoredProperties(&T::staticMetaObject, src, dst);
+}
+
+template <typename T>
+void copyStoredProperties(const T *src, QDataStream &dst)
+{
+ copyStoredProperties(&T::staticMetaObject, src, dst);
+}
+
+template <typename T>
+void copyStoredProperties(QDataStream &src, T *dst)
+{
+ copyStoredProperties(&T::staticMetaObject, src, dst);
+}
+
+template <typename E>
+constexpr typename std::underlying_type<E>::type to_underlying(E e) noexcept {
+ return static_cast<typename std::underlying_type<E>::type>(e);
+}
+
+enum QRemoteObjectPacketTypeEnum
+{
+ Invalid = 0,
+ Handshake,
+ InitPacket,
+ InitDynamicPacket,
+ AddObject,
+ RemoveObject,
+ InvokePacket,
+ InvokeReplyPacket,
+ PropertyChangePacket,
+ ObjectList,
+ Ping,
+ Pong
+};
+Q_ENUM_NS(QRemoteObjectPacketTypeEnum)
+
+enum InitialAction {
+ FetchRootSize,
+ PrefetchData
+};
+Q_ENUM_NS(InitialAction)
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QTREMOTEOBJECTSGLOBAL_H
--- /dev/null
+
+#####################################################################
+## qtremoteobjects Plugin:
+#####################################################################
+
+qt_internal_add_qml_module(RemoteObjectsQml
+ URI "QtRemoteObjects"
+ VERSION "${PROJECT_VERSION}"
+ PLUGIN_TARGET declarative_remoteobjects
+ CLASS_NAME QtRemoteObjectsPlugin
+ SOURCES
+ qremoteobjectsqml_p.h
+ PUBLIC_LIBRARIES
+ Qt::Core
+ Qt::Gui
+ Qt::Qml
+ Qt::RemoteObjects
+)
--- /dev/null
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QREMOTEOBJECTSQML_P_H
+#define QREMOTEOBJECTSQML_P_H
+
+#include <QtRemoteObjects/qremoteobjectnode.h>
+#include <QtRemoteObjects/qremoteobjectpendingcall.h>
+#include <QtRemoteObjects/qremoteobjectsettingsstore.h>
+
+#include <QtCore/qtimer.h>
+#include <QtQml/QJSValue>
+#include <QtQml/qqml.h>
+#include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlinfo.h>
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+struct QtQmlRemoteObjectsResponse
+{
+ QJSValue promise;
+ QTimer *timer;
+};
+
+// documentation updates for this class can be made in remoteobjects-qml.qdoc
+class QtQmlRemoteObjects : public QObject
+{
+ Q_OBJECT
+ QML_NAMED_ELEMENT(QtRemoteObjects)
+ QML_SINGLETON
+ QML_ADDED_IN_VERSION(5, 14)
+
+public:
+ ~QtQmlRemoteObjects()
+ {
+ auto i = m_callbacks.begin();
+ while (i != m_callbacks.end()) {
+ delete i.key();
+ delete i.value().timer;
+ i = m_callbacks.erase(i);
+ }
+ }
+
+ Q_INVOKABLE QJSValue watch(const QRemoteObjectPendingCall &reply, int timeout = 30000)
+ {
+ if (m_accessiblePromise.isUndefined())
+ m_accessiblePromise = qmlEngine(this)->evaluate(QLatin1String(
+ "(function() { var obj = {}; obj.promise = new Promise(function(resolve, "
+ "reject) { obj.resolve = resolve; obj.reject = reject; }); return obj; })"));
+
+ QRemoteObjectPendingCallWatcher *watcher = new QRemoteObjectPendingCallWatcher(reply);
+
+ QJSValue promise = m_accessiblePromise.call();
+ QtQmlRemoteObjectsResponse response;
+ response.promise = promise;
+ response.timer = new QTimer();
+ response.timer->setSingleShot(true);
+ m_callbacks.insert(watcher, response);
+
+ // handle timeout
+ connect(response.timer, &QTimer::timeout, [this, watcher]() {
+ auto i = m_callbacks.find(watcher);
+ if (i == m_callbacks.end()) {
+ qmlWarning(this) << "could not find callback for watcher.";
+ return;
+ }
+
+ QJSValue v(QLatin1String("timeout"));
+ i.value().promise.property(QLatin1String("reject")).call(QJSValueList() << v);
+
+ delete i.key();
+ delete i.value().timer;
+ m_callbacks.erase(i);
+ });
+
+ // handle success
+ connect(watcher, &QRemoteObjectPendingCallWatcher::finished,
+ [this](QRemoteObjectPendingCallWatcher *self) {
+ auto i = m_callbacks.find(self);
+ if (i == m_callbacks.end()) {
+ qmlWarning(this) << "could not find callback for watcher.";
+ return;
+ }
+ QJSValue v = qmlEngine(this)->toScriptValue(self->returnValue());
+ i.value().promise.property(QLatin1String("resolve")).call(QJSValueList() << v);
+
+ delete i.key();
+ delete i.value().timer;
+ m_callbacks.erase(i);
+ });
+
+ response.timer->start(timeout);
+ return promise.property(QLatin1String("promise"));
+ }
+
+private:
+ QHash<QRemoteObjectPendingCallWatcher *, QtQmlRemoteObjectsResponse> m_callbacks;
+ QJSValue m_accessiblePromise;
+};
+
+struct QRemoteObjectNodeForeign
+{
+ Q_GADGET
+ QML_FOREIGN(QRemoteObjectNode)
+ QML_NAMED_ELEMENT(Node)
+ QML_ADDED_IN_VERSION(5, 12)
+};
+
+struct QRemoteObjectSettingsStoreForeign
+{
+ Q_GADGET
+ QML_FOREIGN(QRemoteObjectSettingsStore)
+ QML_NAMED_ELEMENT(SettingsStore)
+ QML_ADDED_IN_VERSION(5, 12)
+};
+
+struct QRemoteObjectHostForeign
+{
+ Q_GADGET
+ QML_FOREIGN(QRemoteObjectHost)
+ QML_NAMED_ELEMENT(Host)
+ QML_ADDED_IN_VERSION(5, 15)
+};
+
+struct QRemoteObjectAbstractPersistedStoreForeign
+{
+ Q_GADGET
+ QML_FOREIGN(QRemoteObjectAbstractPersistedStore)
+ QML_NAMED_ELEMENT(PersistedStore)
+ QML_UNCREATABLE("QRemoteObjectAbstractPersistedStore is Abstract")
+ QML_ADDED_IN_VERSION(5, 12)
+};
+
+QT_END_NAMESPACE
+
+#endif // QREMOTEOBJECTSQML_P_H
--- /dev/null
+
+#####################################################################
+## RepParser Module:
+#####################################################################
+
+qt_internal_add_module(RepParser
+ HEADER_MODULE
+ PUBLIC_LIBRARIES
+ Qt::Core
+)
+
+qt_internal_module_info(module RepParser)
+qt_path_join(parser_install_dir "${QT_INSTALL_DIR}" "${INSTALL_INCLUDEDIR}" "${module}")
+
+qt_copy_or_install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/parser.g" DESTINATION "${parser_install_dir}")
--- /dev/null
+-- Copyright (C) 2014-2020 Ford Motor Company.
+-- SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+%parser rep_grammar
+%decl repparser.h
+%impl repparser.cpp
+
+%token_prefix Token_
+%token semicolon "[semicolon];"
+%token class "[class]class[ \\t]+(?<name>[A-Za-z_][A-Za-z0-9_]+)[ \\t]*"
+%token pod "[pod]POD[ \\t]*(?<name>[A-Za-z_][A-Za-z0-9_]+)[ \\t]*\\((?<types>[^\\)]*)\\);?[ \\t]*"
+%token pod2 "[pod2]POD[ \\t]*(?<name>[A-Za-z_][A-Za-z0-9_]+)[ \\t]*"
+%token flag "[flag][ \\t]*FLAG[ \t]*\\([ \t]*(?<name>[A-Za-z_][A-Za-z0-9_]*)[ \t]+(?<enum>[A-Za-z_][A-Za-z0-9_]*)[ \t]*\\)[ \t]*"
+%token enum "[enum][ \\t]*ENUM[ \t]+(?:(?<class>class[ \t]+))?(?<name>[A-Za-z_][A-Za-z0-9_]*)[ \t]*(?::[ \t]*(?<type>[a-zA-Z0-9 _:]*[a-zA-Z0-9_])[ \t]*)?"
+%token prop "[prop][ \\t]*PROP[ \\t]*\\((?<args>[^\\)]+)\\);?[ \\t]*"
+%token use_enum "[use_enum]USE_ENUM[ \\t]*\\((?<name>[^\\)]*)\\);?[ \\t]*"
+%token signal "[signal][ \\t]*SIGNAL[ \\t]*\\([ \\t]*(?<name>\\S+)[ \\t]*\\((?<args>[^\\)]*)\\)[ \\t]*\\);?[ \\t]*"
+%token slot "[slot][ \\t]*SLOT[ \\t]*\\((?<type>[^\\(]*)\\((?<args>[^\\)]*)\\)[ \\t]*\\);?[ \\t]*"
+%token model "[model][ \\t]*MODEL[ \\t]+(?<name>[A-Za-z_][A-Za-z0-9_]+)\\((?<args>[^\\)]+)\\)[ \\t]*;?[ \\t]*"
+%token childrep "[childrep][ \\t]*CLASS[ \\t]+(?<name>[A-Za-z_][A-Za-z0-9_]+)\\((?<type>[^\\)]+)\\)[ \\t]*;?[ \\t]*"
+%token qualifier "[qualifier][ \\t]*(?<value>const|unsigned|signed)[ \\t]*"
+%token passbyqual "[passbyqual][ \\t]*(?<value>&)[ \\t]*"
+%token symbol "[symbol][ \\t]*(?<symbol>[A-Za-z_][A-Za-z0-9_]*)[ \\t]*"
+%token value "[value][ \\t]*(?<value>-\\d+|0[xX][0-9A-Fa-f]+|\\d+)[ \\t]*"
+%token start "[start][ \\t]*\\{[ \\t]*"
+%token stop "[stop][ \\t]*\\};?[ \\t]*"
+%token comma "[comma],"
+%token equals "[equals]="
+%token comment "[comment](?<comment>[ \\t]*//[^\\n]*\\n)"
+%token mcomment "[mcomment,M](?<comment>/\\*(.*?)\\*/)"
+%token preprocessor_directive "[preprocessor_directive](?<preprocessor_directive>#[ \\t]*[^\\n]*\\n)"
+%token newline "[newline](\\r)?\\n"
+%token tstart "[tstart]<"
+%token tstop "[tstop]>[ \\t]*"
+
+%start TopLevel
+
+/:
+#ifndef REPPARSER_H
+#define REPPARSER_H
+
+#include <rep_grammar_p.h>
+#include <qregexparser.h>
+#include <QStringList>
+#include <QList>
+#include <QHash>
+#include <QRegularExpression>
+#include <QSet>
+
+QT_BEGIN_NAMESPACE
+class QIODevice;
+class QCryptographicHash;
+
+struct AST;
+
+struct SignedType
+{
+ SignedType(const QString &name = QString());
+ virtual ~SignedType() {}
+ void generateSignature(AST &ast);
+ virtual QString typeName() const;
+ virtual void signature_impl(const AST &ast, QCryptographicHash &checksum) = 0;
+ QString name;
+};
+
+/// A property of a Class declaration
+struct ASTProperty
+{
+ enum Modifier
+ {
+ Constant,
+ ReadOnly,
+ ReadPush,
+ ReadWrite,
+ SourceOnlySetter
+ };
+
+ ASTProperty();
+ ASTProperty(const QString &type, const QString &name, const QString &defaultValue, Modifier modifier, bool persisted,
+ bool isPointer=false);
+
+ QString type;
+ QString name;
+ QString defaultValue;
+ Modifier modifier;
+ bool persisted;
+ bool isPointer;
+};
+Q_DECLARE_TYPEINFO(ASTProperty, Q_RELOCATABLE_TYPE);
+
+struct ASTDeclaration
+{
+ enum VariableType {
+ None = 0,
+ Constant = 1,
+ Reference = 2,
+ };
+ Q_DECLARE_FLAGS(VariableTypes, VariableType)
+
+ ASTDeclaration(const QString &declarationType = QString(), const QString &declarationName = QString(), VariableTypes declarationVariableType = None)
+ : type(declarationType),
+ name(declarationName),
+ variableType(declarationVariableType)
+ {
+ }
+
+ QString asString(bool withName) const;
+
+ QString type;
+ QString name;
+ VariableTypes variableType;
+};
+Q_DECLARE_TYPEINFO(ASTDeclaration, Q_RELOCATABLE_TYPE);
+
+struct ASTFunction
+{
+ enum ParamsAsStringFormat {
+ Default,
+ Normalized
+ };
+
+ explicit ASTFunction(const QString &name = QString(), const QString &returnType = QLatin1String("void"));
+
+ QString paramsAsString(ParamsAsStringFormat format = Default) const;
+ QStringList paramNames() const;
+
+ QString returnType;
+ QString name;
+ QList<ASTDeclaration> params;
+};
+Q_DECLARE_TYPEINFO(ASTFunction, Q_RELOCATABLE_TYPE);
+
+struct ASTEnumParam
+{
+ ASTEnumParam(const QString ¶mName = QString(), int paramValue = 0)
+ : name(paramName),
+ value(paramValue)
+ {
+ }
+
+ QString asString() const;
+
+ QString name;
+ int value;
+};
+Q_DECLARE_TYPEINFO(ASTEnumParam, Q_RELOCATABLE_TYPE);
+
+struct ASTEnum : public SignedType
+{
+ explicit ASTEnum(const QString &name = QString());
+ void signature_impl(const AST &ast, QCryptographicHash &checksum) override;
+ QString typeName() const override;
+
+ QString type;
+ QString scope;
+ QList<ASTEnumParam> params;
+ bool isSigned;
+ bool isScoped;
+ int max;
+ int flagIndex = -1;
+};
+Q_DECLARE_TYPEINFO(ASTEnum, Q_RELOCATABLE_TYPE);
+
+struct ASTFlag : public SignedType
+{
+ explicit ASTFlag(const QString &name = {}, const QString &_enum = {});
+ void signature_impl(const AST &ast, QCryptographicHash &checksum) override;
+ QString typeName() const override;
+
+ bool isValid() const;
+ QString _enum;
+ QString scope;
+};
+Q_DECLARE_TYPEINFO(ASTFlag, Q_RELOCATABLE_TYPE);
+
+struct ASTModelRole
+{
+ ASTModelRole(const QString &roleName = QString())
+ : name(roleName)
+ {
+ }
+
+ QString name;
+};
+Q_DECLARE_TYPEINFO(ASTModelRole, Q_RELOCATABLE_TYPE);
+
+struct ASTModel : public SignedType
+{
+ ASTModel(const QString &name, const QString &scope, int index = -1)
+ : SignedType(name), scope(scope), propertyIndex(index) {}
+ void signature_impl(const AST &ast, QCryptographicHash &checksum) override;
+ QString typeName() const override;
+
+ QList<ASTModelRole> roles;
+ QString scope;
+ int propertyIndex;
+};
+Q_DECLARE_TYPEINFO(ASTModel, Q_RELOCATABLE_TYPE);
+
+/// A Class declaration
+struct ASTClass : public SignedType
+{
+ explicit ASTClass(const QString& name = QString());
+ void signature_impl(const AST &ast, QCryptographicHash &checksum) override;
+
+ bool isValid() const;
+ bool hasPointerObjects() const;
+
+ QList<ASTProperty> properties;
+ QList<ASTFunction> signalsList;
+ QList<ASTFunction> slotsList;
+ QList<ASTEnum> enums;
+ QList<ASTFlag> flags;
+ bool hasPersisted;
+ QList<ASTModel> modelMetadata;
+ QList<int> subClassPropertyIndices;
+};
+Q_DECLARE_TYPEINFO(ASTClass, Q_RELOCATABLE_TYPE);
+
+// The attribute of a POD
+struct PODAttribute
+{
+ explicit PODAttribute(const QString &type_ = QString(), const QString &name_ = QString())
+ : type(type_),
+ name(name_)
+ {}
+ QString type;
+ QString name;
+};
+Q_DECLARE_TYPEINFO(PODAttribute, Q_RELOCATABLE_TYPE);
+
+// A POD declaration
+struct POD : public SignedType
+{
+ void signature_impl(const AST &ast, QCryptographicHash &checksum) override;
+
+ QList<PODAttribute> attributes;
+ QList<ASTEnum> enums;
+ QList<ASTFlag> flags;
+};
+Q_DECLARE_TYPEINFO(POD, Q_RELOCATABLE_TYPE);
+
+// The AST representation of a .rep file
+struct AST
+{
+ QList<ASTClass> classes;
+ QList<POD> pods;
+ QList<ASTEnum> enums;
+ QList<ASTFlag> flags;
+ QList<QString> enumUses;
+ QStringList preprocessorDirectives;
+ QHash<QString, QByteArray> typeSignatures;
+ QByteArray typeData(const QString &type, const QString &className) const;
+ QByteArray functionsData(const QList<ASTFunction> &functions, const QString &className) const;
+};
+Q_DECLARE_TYPEINFO(AST, Q_RELOCATABLE_TYPE);
+
+class RepParser: public QRegexParser<RepParser, $table>
+{
+public:
+ explicit RepParser(QIODevice &outputDevice);
+ virtual ~RepParser() {}
+
+ bool parse() override { return QRegexParser<RepParser, $table>::parse(); }
+
+ void reset() override;
+ int nextToken();
+ bool consumeRule(int ruleno);
+
+ AST ast() const;
+
+private:
+ struct TypeParser
+ {
+ void parseArguments(const QString &arguments);
+ void appendParams(ASTFunction &slot);
+ void appendPods(POD &pods);
+ void generateFunctionParameter(QString variableName, const QString &propertyType, int &variableNameIndex, ASTDeclaration::VariableTypes variableType);
+ //Type, Variable
+ QList<ASTDeclaration> arguments;
+ };
+
+ bool parseProperty(ASTClass &astClass, const QString &propertyDeclaration);
+ /// A helper function to parse modifier flag of property declaration
+ bool parseModifierFlag(const QString &flag, ASTProperty::Modifier &modifier, bool &persisted);
+
+ bool parseRoles(ASTModel &astModel, const QString &modelRoles);
+
+ AST m_ast;
+
+ ASTClass m_astClass;
+ POD m_astPod;
+ QString m_symbol;
+ QString m_argString;
+ ASTEnum m_astEnum;
+ int m_astEnumValue;
+};
+QT_END_NAMESPACE
+#endif
+:/
+
+
+/.
+#include "repparser.h"
+
+#include <QCryptographicHash>
+#include <QDebug>
+#include <QTextStream>
+
+// for normalizeTypeInternal
+#include <private/qmetaobject_p.h>
+#include <private/qmetaobject_moc_p.h>
+
+// Code copied from moc.cpp
+// We cannot depend on QMetaObject::normalizedSignature,
+// since repc is linked against Qt5Bootstrap (which doesn't offer QMetaObject) when cross-compiling
+// Thus, just use internal API which is exported in private headers, as moc does
+static QByteArray normalizeType(const QByteArray &ba)
+{
+ const char *s = ba.constData();
+ int len = ba.size();
+ char stackbuf[64];
+ char *buf = (len >= 64 ? new char[len + 1] : stackbuf);
+ char *d = buf;
+ char last = 0;
+ while (*s && is_space(*s))
+ s++;
+ while (*s) {
+ while (*s && !is_space(*s))
+ last = *d++ = *s++;
+ while (*s && is_space(*s))
+ s++;
+ if (*s && ((is_ident_char(*s) && is_ident_char(last))
+ || ((*s == ':') && (last == '<')))) {
+ last = *d++ = ' ';
+ }
+ }
+ *d = '\0';
+ QByteArray result = normalizeTypeInternal(buf, d);
+ if (buf != stackbuf)
+ delete [] buf;
+ return result;
+}
+
+SignedType::SignedType(const QString &name) : name(name)
+{
+}
+
+void SignedType::generateSignature(AST &ast)
+{
+ QCryptographicHash checksum(QCryptographicHash::Sha1);
+ signature_impl(ast, checksum);
+ ast.typeSignatures[typeName()] = checksum.result().toHex();
+}
+
+QString SignedType::typeName() const
+{
+ return name;
+}
+
+ASTProperty::ASTProperty()
+ : modifier(ReadPush), persisted(false), isPointer(false)
+{
+}
+
+ASTProperty::ASTProperty(const QString &type, const QString &name, const QString &defaultValue, Modifier modifier, bool persisted, bool isPointer)
+ : type(type), name(name), defaultValue(defaultValue), modifier(modifier), persisted(persisted), isPointer(isPointer)
+{
+}
+
+QString ASTDeclaration::asString(bool withName) const
+{
+ QString str;
+ if (variableType & ASTDeclaration::Constant)
+ str += QLatin1String("const ");
+ str += type;
+ if (variableType & ASTDeclaration::Reference)
+ str += QLatin1String(" &");
+ if (withName)
+ str += QString::fromLatin1(" %1").arg(name);
+ return str;
+}
+
+ASTFunction::ASTFunction(const QString &name, const QString &returnType)
+ : returnType(returnType), name(name)
+{
+}
+
+QString ASTFunction::paramsAsString(ParamsAsStringFormat format) const
+{
+ QString str;
+ for (const ASTDeclaration ¶m : params) {
+ QString paramStr = param.asString(format != Normalized);
+ if (format == Normalized) {
+ paramStr = QString::fromLatin1(::normalizeType(paramStr.toLatin1().constData()));
+ str += paramStr + QLatin1Char(',');
+ } else {
+ str += paramStr + QLatin1String(", ");
+ }
+ }
+
+ str.chop((format == Normalized ? 1 : 2)); // chop trailing ',' or ', '
+
+ return str;
+}
+
+QStringList ASTFunction::paramNames() const
+{
+ QStringList names;
+ names.reserve(params.size());
+ for (const ASTDeclaration ¶m : params)
+ names << param.name;
+ return names;
+}
+
+ASTEnum::ASTEnum(const QString &name)
+ : SignedType(name), isSigned(false), isScoped(false), max(0)
+{
+}
+
+void ASTEnum::signature_impl(const AST &ast, QCryptographicHash &checksum)
+{
+ Q_UNUSED(ast)
+ checksum.addData(name.toLatin1());
+ if (isScoped)
+ checksum.addData("class");
+ if (!type.isEmpty())
+ checksum.addData(type.toLatin1());
+ for (const ASTEnumParam ¶m : params) {
+ checksum.addData(param.name.toLatin1());
+ checksum.addData(QByteArray::number(param.value));
+ }
+}
+
+QString ASTEnum::typeName() const
+{
+ if (scope.isEmpty())
+ return name;
+
+ return QLatin1String("%1::%2").arg(scope, name);
+}
+
+ASTFlag::ASTFlag(const QString &name, const QString &_enum)
+ : SignedType(name), _enum(_enum)
+{
+}
+
+void ASTFlag::signature_impl(const AST &ast, QCryptographicHash &checksum)
+{
+ checksum.addData(name.toLatin1());
+ checksum.addData(ast.typeData(_enum, scope));
+}
+
+QString ASTFlag::typeName() const
+{
+ if (scope.isEmpty())
+ return name;
+
+ return QLatin1String("%1::%2").arg(scope, name);
+}
+
+bool ASTFlag::isValid() const
+{
+ return !name.isEmpty();
+}
+
+void ASTModel::signature_impl(const AST &ast, QCryptographicHash &checksum)
+{
+ Q_UNUSED(ast)
+ QByteArrayList _roles;
+ for (const auto &role : roles)
+ _roles << role.name.toLatin1();
+ std::sort(_roles.begin(), _roles.end());
+ checksum.addData(_roles.join('_'));
+}
+
+QString ASTModel::typeName() const
+{
+ return QLatin1String("%1::%2").arg(scope, name);
+}
+
+ASTClass::ASTClass(const QString &name)
+ : SignedType(name), hasPersisted(false)
+{
+}
+
+void ASTClass::signature_impl(const AST &ast, QCryptographicHash &checksum)
+{
+ checksum.addData(name.toLatin1());
+
+ // Checksum properties
+ QSet<int> classIndices{ subClassPropertyIndices.begin(),
+ subClassPropertyIndices.end() };
+ int propertyIndex = -1;
+ int modelIndex = 0;
+ for (const ASTProperty &p : properties) {
+ propertyIndex++;
+ checksum.addData(p.name.toLatin1());
+ if (p.type == QLatin1String("QAbstractItemModel"))
+ checksum.addData(ast.typeSignatures[modelMetadata[modelIndex++].typeName()]);
+ else if (classIndices.contains(propertyIndex))
+ checksum.addData(ast.typeSignatures[p.type]);
+ else
+ checksum.addData(ast.typeData(p.type, name));
+ ASTProperty::Modifier m = p.modifier;
+ // Treat ReadOnly and SourceOnlySetter the same (interface-wise they are)
+ if (m == ASTProperty::SourceOnlySetter)
+ m = ASTProperty::ReadOnly;
+ checksum.addData({reinterpret_cast<const char *>(&m), sizeof(m)});
+ }
+
+ // Checksum signals
+ checksum.addData(ast.functionsData(signalsList, name));
+
+ // Checksum slots
+ checksum.addData(ast.functionsData(slotsList, name));
+}
+
+void POD::signature_impl(const AST &ast, QCryptographicHash &checksum)
+{
+ checksum.addData(name.toLatin1());
+ for (const PODAttribute &attr : attributes) {
+ checksum.addData(attr.name.toLatin1());
+ checksum.addData(ast.typeData(attr.type, name));
+ }
+}
+
+bool ASTClass::isValid() const
+{
+ return !name.isEmpty();
+}
+
+bool ASTClass::hasPointerObjects() const
+{
+ int count = modelMetadata.size() + subClassPropertyIndices.size();
+ return count > 0;
+}
+
+QByteArray AST::typeData(const QString &type, const QString &className) const
+{
+ static const QRegularExpression re = QRegularExpression(QLatin1String("([^<>,\\s]+)"));
+ if (type.contains(QLatin1Char('<'))) { // templated type
+ QByteArray result;
+ for (const QRegularExpressionMatch &match : re.globalMatch(type))
+ result += typeData(match.captured(1), className);
+ return result;
+ }
+ // Try enum/flags within the class first
+ if (!className.isEmpty()) {
+ auto classType = QLatin1String("%1::%2").arg(className, type);
+ auto it = typeSignatures.find(classType);
+ if (it != typeSignatures.end())
+ return it.value();
+ }
+ auto it = typeSignatures.find(type);
+ if (it != typeSignatures.end())
+ return it.value();
+ const auto pos = type.lastIndexOf(QLatin1String("::"));
+ if (pos > 0)
+ return typeData(type.mid(pos + 2), className);
+ return type.toLatin1();
+}
+
+QByteArray AST::functionsData(const QList<ASTFunction> &functions, const QString &className) const
+{
+ QByteArray ret;
+ for (const ASTFunction &func : functions) {
+ ret += func.name.toLatin1();
+ for (const ASTDeclaration ¶m : func.params) {
+ ret += param.name.toLatin1();
+ ret += typeData(param.type, className);
+ ret += QByteArray(reinterpret_cast<const char *>(¶m.variableType),
+ sizeof(param.variableType));
+ }
+ ret += typeData(func.returnType, className);
+ }
+ return ret;
+}
+
+RepParser::RepParser(QIODevice &outputDevice)
+ : QRegexParser(), m_astEnumValue(-1)
+{
+ setBufferFromDevice(&outputDevice);
+}
+
+void RepParser::reset()
+{
+ m_ast = AST();
+ m_astClass = ASTClass();
+ m_astPod = POD();
+ m_argString.clear();
+ m_astEnum = ASTEnum();
+ //setDebug();
+}
+
+bool RepParser::parseModifierFlag(const QString &flag, ASTProperty::Modifier &modifier, bool &persisted)
+{
+ QRegularExpression regex(QStringLiteral("\\s*,\\s*"));
+ QStringList flags = flag.split(regex);
+ persisted = flags.removeAll(QStringLiteral("PERSISTED")) > 0;
+ if (flags.length() == 0)
+ return true;
+ if (flags.length() > 1) {
+ // Only valid combination is "READONLY" and "CONSTANT"
+ if (flags.length() == 2 && flags.contains(QStringLiteral("READONLY")) &&
+ flags.contains(QStringLiteral("CONSTANT"))) {
+ // If we have READONLY and CONSTANT that means CONSTANT
+ modifier = ASTProperty::Constant;
+ return true;
+ } else {
+ setErrorString(QLatin1String("Invalid property declaration: combination not allowed (%1)").arg(flag));
+ return false;
+ }
+ }
+ const QString &f = flags.at(0);
+ if (f == QLatin1String("READONLY"))
+ modifier = ASTProperty::ReadOnly;
+ else if (f == QLatin1String("CONSTANT"))
+ modifier = ASTProperty::Constant;
+ else if (f == QLatin1String("READPUSH"))
+ modifier = ASTProperty::ReadPush;
+ else if (f == QLatin1String("READWRITE"))
+ modifier = ASTProperty::ReadWrite;
+ else if (f == QLatin1String("SOURCEONLYSETTER"))
+ modifier = ASTProperty::SourceOnlySetter;
+ else {
+ setErrorString(QLatin1String("Invalid property declaration: flag %1 is unknown").arg(flag));
+ return false;
+ }
+
+ return true;
+}
+
+QString stripArgs(const QString &arguments)
+{
+ // This repc parser searches for the longest possible matches, which can be multiline.
+ // This method "cleans" the string input, removing comments and converting to a single
+ // line for subsequent parsing.
+ QStringList lines = arguments.split(QRegularExpression(QStringLiteral("\r?\n")));
+ for (auto & line : lines)
+ line.replace(QRegularExpression(QStringLiteral("//.*")),QString());
+ return lines.join(QString());
+}
+
+bool RepParser::parseProperty(ASTClass &astClass, const QString &propertyDeclaration)
+{
+ QString input = stripArgs(propertyDeclaration).trimmed();
+ const QRegularExpression whitespace(QStringLiteral("\\s"));
+
+ QString propertyType;
+ QString propertyName;
+ QString propertyDefaultValue;
+ ASTProperty::Modifier propertyModifier = ASTProperty::ReadPush;
+ bool persisted = false;
+
+ // parse type declaration which could be a nested template as well
+ bool inTemplate = false;
+ int templateDepth = 0;
+ int nameIndex = -1;
+
+ for (int i = 0; i < input.size(); ++i) {
+ const QChar inputChar(input.at(i));
+ if (inputChar == QLatin1Char('<')) {
+ propertyType += inputChar;
+ inTemplate = true;
+ ++templateDepth;
+ } else if (inputChar == QLatin1Char('>')) {
+ propertyType += inputChar;
+ --templateDepth;
+ if (templateDepth == 0)
+ inTemplate = false;
+ } else if (inputChar.isSpace()) {
+ if (!inTemplate) {
+ nameIndex = i;
+ break;
+ } else {
+ propertyType += inputChar;
+ }
+ } else {
+ propertyType += inputChar;
+ }
+ }
+
+ if (nameIndex == -1) {
+ setErrorString(QLatin1String("PROP: Invalid property declaration: %1").arg(propertyDeclaration));
+ return false;
+ }
+
+ // parse the name of the property
+ input = input.mid(nameIndex).trimmed();
+
+ const int equalSignIndex = input.indexOf(QLatin1Char('='));
+ if (equalSignIndex != -1) { // we have a default value
+ propertyName = input.left(equalSignIndex).trimmed();
+
+ input = input.mid(equalSignIndex + 1).trimmed();
+ const int lastQuoteIndex = input.lastIndexOf(QLatin1Char('"'));
+ if (lastQuoteIndex != -1) {
+ propertyDefaultValue = input.left(lastQuoteIndex + 1);
+ input = input.mid(lastQuoteIndex + 1);
+ }
+ const int whitespaceIndex = input.indexOf(whitespace);
+ if (whitespaceIndex == -1) { // no flag given
+ if (propertyDefaultValue.isEmpty())
+ propertyDefaultValue = input;
+ propertyModifier = ASTProperty::ReadPush;
+ } else { // flag given
+ if (propertyDefaultValue.isEmpty())
+ propertyDefaultValue = input.left(whitespaceIndex).trimmed();
+
+ const QString flag = input.mid(whitespaceIndex + 1).trimmed();
+ if (!parseModifierFlag(flag, propertyModifier, persisted))
+ return false;
+ }
+ } else { // there is no default value
+ const int whitespaceIndex = input.indexOf(whitespace);
+ if (whitespaceIndex == -1) { // no flag given
+ propertyName = input;
+ propertyModifier = ASTProperty::ReadPush;
+ } else { // flag given
+ propertyName = input.left(whitespaceIndex).trimmed();
+
+ const QString flag = input.mid(whitespaceIndex + 1).trimmed();
+ if (!parseModifierFlag(flag, propertyModifier, persisted))
+ return false;
+ }
+ }
+
+ astClass.properties << ASTProperty(propertyType, propertyName, propertyDefaultValue, propertyModifier, persisted);
+ if (persisted)
+ astClass.hasPersisted = true;
+ return true;
+}
+
+bool RepParser::parseRoles(ASTModel &astModel, const QString &modelRoles)
+{
+ const QString input = modelRoles.trimmed();
+
+ if (input.isEmpty())
+ return true;
+
+ const QStringList roleStrings = input.split(QChar(QLatin1Char(',')));
+ for (auto role : roleStrings)
+ astModel.roles << ASTModelRole(role.trimmed());
+ return true;
+}
+
+AST RepParser::ast() const
+{
+ return m_ast;
+}
+
+void RepParser::TypeParser::parseArguments(const QString &arguments)
+{
+ const QString strippedArgs = stripArgs(arguments);
+ int templateDepth = 0;
+ bool inTemplate = false;
+ bool inVariable = false;
+ QString propertyType;
+ QString variableName;
+ ASTDeclaration::VariableTypes variableType = ASTDeclaration::None;
+ int variableNameIndex = 0;
+ for (int i = 0; i < strippedArgs.size(); ++i) {
+ const QChar inputChar(strippedArgs.at(i));
+ if (inputChar == QLatin1Char('<')) {
+ propertyType += inputChar;
+ inTemplate = true;
+ ++templateDepth;
+ } else if (inputChar == QLatin1Char('>')) {
+ propertyType += inputChar;
+ --templateDepth;
+ if (templateDepth == 0)
+ inTemplate = false;
+ } else if (inputChar.isSpace()) {
+ if (inTemplate)
+ propertyType += inputChar;
+ else if (!propertyType.isEmpty()) {
+ if (propertyType == QLatin1String("const")) {
+ propertyType.clear();
+ variableType |= ASTDeclaration::Constant;
+ } else {
+ inVariable = true;
+ }
+ }
+ } else if (inputChar == QLatin1Char('&')) {
+ variableType |= ASTDeclaration::Reference;
+ } else if (inputChar == QLatin1Char(',')) {
+ if (!inTemplate) {
+ RepParser::TypeParser::generateFunctionParameter(variableName, propertyType, variableNameIndex, variableType);
+ propertyType.clear();
+ variableName.clear();
+ variableType = ASTDeclaration::None;
+ inVariable = false;
+ } else {
+ propertyType += inputChar;
+ }
+ } else {
+ if (inVariable)
+ variableName += inputChar;
+ else
+ propertyType += inputChar;
+ }
+ }
+ if (!propertyType.isEmpty()) {
+ RepParser::TypeParser::generateFunctionParameter(variableName, propertyType, variableNameIndex, variableType);
+ }
+}
+
+void RepParser::TypeParser::generateFunctionParameter(QString variableName, const QString &propertyType, int &variableNameIndex, ASTDeclaration::VariableTypes variableType)
+{
+ if (!variableName.isEmpty())
+ variableName = variableName.trimmed();
+ else
+ variableName = QString::fromLatin1("__repc_variable_%1").arg(++variableNameIndex);
+ arguments.append(ASTDeclaration(propertyType, variableName, variableType));
+}
+
+void RepParser::TypeParser::appendParams(ASTFunction &slot)
+{
+ for (const ASTDeclaration &arg : std::as_const(arguments))
+ slot.params << arg;
+}
+
+void RepParser::TypeParser::appendPods(POD &pods)
+{
+ for (const ASTDeclaration &arg : std::as_const(arguments)) {
+ PODAttribute attr;
+ attr.type = arg.type;
+ attr.name = arg.name;
+ pods.attributes.append(qMove(attr));
+ }
+}
+
+bool RepParser::consumeRule(int ruleno)
+{
+ if (isDebug()) {
+ qDebug() << "consumeRule:" << ruleno << spell[rule_info[rule_index[ruleno]]];
+ }
+ switch (ruleno) {
+./
+
+TopLevel: Types | Newlines Types | FileComments Types | Newlines FileComments Types;
+
+FileComments: Comments;
+
+Types: Type | Type Types;
+
+Newlines: newline | newline Newlines;
+Comments: Comment | Comment Comments;
+Comment: comment | comment Newlines | mcomment | mcomment Newlines;
+Type: PreprocessorDirective | PreprocessorDirective Newlines;
+Type: Pod | Pod Newlines;
+Type: Pod2;
+Type: Class;
+Type: UseEnum | UseEnum Newlines;
+Type: Comments | Comments Newlines;
+Type: Enum;
+/.
+ case $rule_number:
+ {
+ m_astEnum.generateSignature(m_ast);
+ m_ast.enums.append(m_astEnum);
+ }
+ break;
+./
+Type: Flag | Flag Newlines;
+
+Comma: comma | comma Newlines;
+
+Equals: equals;
+
+PreprocessorDirective: preprocessor_directive;
+/.
+ case $rule_number:
+ {
+ m_ast.preprocessorDirectives.append(captured().value(QStringLiteral("preprocessor_directive")));
+ }
+ break;
+./
+
+Pod: pod;
+/.
+ case $rule_number:
+ {
+ POD pod;
+ pod.name = captured().value(QStringLiteral("name")).trimmed();
+
+ const QString argString = captured().value(QLatin1String("types")).trimmed();
+ if (argString.isEmpty()) {
+ qWarning() << "[repc] - Ignoring POD with no data members. POD name: " << qPrintable(pod.name);
+ return true;
+ }
+ if (argString.contains(QLatin1String("ENUM"))) {
+ setErrorString(QLatin1String("ENUMs are only available in PODs using bracket syntax ('{'), not parentheses"));
+ return false;
+ }
+
+ RepParser::TypeParser parseType;
+ parseType.parseArguments(argString);
+ parseType.appendPods(pod);
+ pod.generateSignature(m_ast);
+ m_ast.pods.append(pod);
+ }
+ break;
+./
+
+Class: ClassStart Start ClassTypes Stop;
+/.
+ case $rule_number:
+./
+Class: ClassStart Start Comments Stop;
+/.
+ case $rule_number:
+./
+Class: ClassStart Start Stop;
+/.
+ case $rule_number:
+ {
+ m_astClass.generateSignature(m_ast);
+ m_ast.classes.append(m_astClass);
+ }
+ break;
+./
+
+ClassTypes: ClassType | ClassType ClassTypes;
+ClassType: DecoratedProp | DecoratedSignal | DecoratedSlot | DecoratedModel | DecoratedClass | DecoratedClassFlag | Comments;
+ClassType: Enum;
+/.
+ case $rule_number:
+ {
+ m_astEnum.scope = m_astClass.name;
+ m_astEnum.generateSignature(m_ast);
+ m_astClass.enums.append(m_astEnum);
+ }
+ break;
+./
+
+Pod2: PodStart Start PodTypes Stop;
+/.
+ case $rule_number:
+./
+Pod2: PodStart Start Comments Stop;
+/.
+ case $rule_number:
+./
+Pod2: PodStart Start Stop;
+/.
+ case $rule_number:
+ {
+ RepParser::TypeParser parseType;
+ parseType.parseArguments(m_argString);
+ parseType.appendPods(m_astPod);
+ m_astPod.generateSignature(m_ast);
+ m_ast.pods.append(m_astPod);
+ }
+ break;
+./
+
+PodTypes: PodType | PodType Newlines | PodType CaptureComma PodTypes | PodType PodTypes | PodType Newlines PodTypes;
+PodType: DecoratedPODFlag | Comments;
+PodType: Enum;
+/.
+ case $rule_number:
+ {
+ m_astEnum.generateSignature(m_ast);
+ m_astPod.enums.append(m_astEnum);
+ }
+ break;
+./
+
+PodType: Parameter;
+Parameter: DecoratedParameterType ParameterName;
+
+DecoratedParameterType: ParameterType | Qualified ParameterType | ParameterType PassByReference | Qualified ParameterType PassByReference;
+
+ParameterType: SimpleType | TemplateType;
+
+TemplateType: TemplateTypename TStart ParameterTypes TStop;
+
+TemplateTypename: Symbol;
+/.
+ case $rule_number:
+ {
+ m_argString += m_symbol;
+ }
+ break;
+./
+
+TStart: tstart;
+/.
+ case $rule_number:
+ {
+ m_argString += QLatin1Char('<');
+ }
+ break;
+./
+
+TStop: tstop;
+/.
+ case $rule_number:
+ {
+ m_argString += QLatin1Char('>');
+ }
+ break;
+./
+
+Qualified: qualifier;
+/.
+ case $rule_number:
+ {
+ m_argString += captured().value(QLatin1String("value")).trimmed() + QLatin1Char(' ');
+ }
+ break;
+./
+
+PassByReference: passbyqual;
+/.
+ case $rule_number:
+ {
+ m_argString += QLatin1Char(' ') + captured().value(QLatin1String("value")).trimmed();
+ }
+ break;
+./
+
+ParameterTypes: DecoratedParameterType | DecoratedParameterType CaptureComma ParameterTypes;
+
+CaptureComma: comma;
+/.
+ case $rule_number:
+./
+CaptureComma: comma Newlines;
+/.
+ case $rule_number:
+ {
+ m_argString += QLatin1Char(',');
+ }
+ break;
+./
+
+SimpleType: Symbol;
+/.
+ case $rule_number:
+ {
+ m_argString += m_symbol;
+ }
+ break;
+./
+
+ParameterName: Symbol;
+/.
+ case $rule_number:
+ {
+ m_argString += QLatin1Char(' ') + m_symbol;
+ }
+ break;
+./
+
+DecoratedSlot: Slot | Comments Slot | Slot Newlines | Comments Slot Newlines;
+DecoratedSignal: Signal | Comments Signal | Signal Newlines | Comments Signal Newlines;
+DecoratedProp: Prop | Comments Prop | Prop Newlines | Comments Prop Newlines;
+DecoratedModel: Model | Comments Model | Model Newlines | Comments Model Newlines;
+DecoratedClass: ChildRep | Comments ChildRep | ChildRep Newlines | Comments ChildRep Newlines;
+DecoratedEnumParam: EnumParam | Comments EnumParam | EnumParam Newlines | Comments EnumParam Newlines;
+DecoratedClassFlag: ClassFlag | Comments ClassFlag | ClassFlag Newlines | Comments ClassFlag Newlines;
+
+DecoratedPODFlag: PODFlag | Comments PODFlag | PODFlag Newlines | Comments PODFlag Newlines;
+
+Start: start | Comments start | start Newlines | Comments start Newlines;
+Stop: stop | stop Newlines;
+
+Enum: EnumStart Start EnumParams Comments Stop;
+Enum: EnumStart Start EnumParams Stop;
+
+EnumStart: enum;
+/.
+ case $rule_number:
+ {
+ const QString name = captured().value(QLatin1String("name"));
+ const QString type = captured().value(QLatin1String("type"));
+ const QString _class = captured().value(QLatin1String("class"));
+
+ // new Class declaration
+ m_astEnum = ASTEnum(name);
+ if (!_class.isEmpty())
+ m_astEnum.isScoped = true;
+ if (!type.isEmpty())
+ m_astEnum.type = type;
+ m_astEnumValue = -1;
+ }
+ break;
+./
+
+EnumParams: DecoratedEnumParam | DecoratedEnumParam Comma EnumParams;
+
+EnumParam: Symbol;
+/.
+ case $rule_number:
+ {
+ ASTEnumParam param;
+ param.name = m_symbol;
+ param.value = ++m_astEnumValue;
+ if (m_astEnum.max < param.value)
+ m_astEnum.max = param.value;
+ m_astEnum.params << param;
+ }
+ break;
+./
+
+EnumParam: Symbol Equals Value;
+/.
+ case $rule_number:
+ {
+ ASTEnumParam param;
+ param.name = m_symbol;
+ param.value = m_astEnumValue;
+ if (param.value < 0) {
+ m_astEnum.isSigned = true;
+ if (m_astEnum.max < -param.value)
+ m_astEnum.max = -param.value;
+ } else if (m_astEnum.max < param.value)
+ m_astEnum.max = param.value;
+ m_astEnum.params << param;
+ }
+ break;
+./
+
+Symbol: symbol;
+/.
+ case $rule_number:
+ {
+ m_symbol = captured().value(QStringLiteral("symbol")).trimmed();
+ }
+ break;
+./
+
+Value: value;
+/.
+ case $rule_number:
+ {
+ QString value = captured().value(QStringLiteral("value")).trimmed();
+ if (value.startsWith(QLatin1String("0x"), Qt::CaseInsensitive))
+ m_astEnumValue = value.toInt(0,16);
+ else
+ m_astEnumValue = value.toInt();
+ }
+ break;
+./
+
+ClassFlag: flag;
+/.
+ case $rule_number:
+ {
+ const QString name = captured().value(QLatin1String("name"));
+ const QString _enum = captured().value(QLatin1String("enum"));
+ int enumIndex = 0;
+ for (auto &en : m_astClass.enums) {
+ if (en.name == _enum) {
+ en.flagIndex = m_astClass.flags.count();
+ break;
+ }
+ enumIndex++;
+ }
+ if (enumIndex == m_astClass.enums.count()) {
+ setErrorString(QLatin1String("FLAG: Unknown (class) enum: %1").arg(_enum));
+ return false;
+ }
+ auto flag = ASTFlag(name, _enum);
+ flag.scope = m_astClass.name;
+ flag.generateSignature(m_ast);
+ m_astClass.flags.append(flag);
+ }
+ break;
+./
+
+PODFlag: flag;
+/.
+ case $rule_number:
+ {
+ const QString name = captured().value(QLatin1String("name"));
+ const QString _enum = captured().value(QLatin1String("enum"));
+ int enumIndex = 0;
+ for (auto &en : m_astPod.enums) {
+ if (en.name == _enum) {
+ en.flagIndex = m_astPod.flags.count();
+ break;
+ }
+ enumIndex++;
+ }
+ if (enumIndex == m_astPod.enums.count()) {
+ setErrorString(QLatin1String("FLAG: Unknown (pod) enum: %1").arg(_enum));
+ return false;
+ }
+ auto flag = ASTFlag(name, _enum);
+ flag.scope = m_astPod.name;
+ flag.generateSignature(m_ast);
+ m_astPod.flags.append(flag);
+ }
+ break;
+./
+
+Prop: prop;
+/.
+ case $rule_number:
+ {
+ const QString args = captured().value(QLatin1String("args"));
+ if (!parseProperty(m_astClass, args))
+ return false;
+ }
+ break;
+./
+
+Signal: signal;
+/.
+ case $rule_number:
+ {
+ ASTFunction signal;
+ signal.name = captured().value(QLatin1String("name")).trimmed();
+
+ const QString argString = captured().value(QLatin1String("args")).trimmed();
+ RepParser::TypeParser parseType;
+ parseType.parseArguments(argString);
+ parseType.appendParams(signal);
+ m_astClass.signalsList << signal;
+ }
+ break;
+./
+
+Slot: slot;
+/.
+ case $rule_number:
+ {
+ QString returnTypeAndName = captured().value(QLatin1String("type")).trimmed();
+ const QString argString = captured().value(QLatin1String("args")).trimmed();
+
+ // compat code with old SLOT declaration: "SLOT(func(...))"
+ const bool hasWhitespace = returnTypeAndName.indexOf(u' ') != -1;
+ if (!hasWhitespace) {
+ qWarning() << "[repc] - Adding 'void' for unspecified return type on" << qPrintable(returnTypeAndName);
+ returnTypeAndName.prepend(QLatin1String("void "));
+ }
+
+ const int startOfFunctionName = returnTypeAndName.lastIndexOf(u' ') + 1;
+
+ ASTFunction slot;
+ slot.returnType = returnTypeAndName.mid(0, startOfFunctionName-1);
+ slot.name = returnTypeAndName.mid(startOfFunctionName);
+
+ RepParser::TypeParser parseType;
+ parseType.parseArguments(argString);
+ parseType.appendParams(slot);
+ m_astClass.slotsList << slot;
+ }
+ break;
+./
+
+Model: model;
+/.
+ case $rule_number:
+ {
+ const QString name = captured().value(QLatin1String("name")).trimmed();
+ const QString argString = captured().value(QLatin1String("args")).trimmed();
+
+ ASTModel model(name, m_astClass.name, m_astClass.properties.size());
+ if (!parseRoles(model, argString))
+ return false;
+
+ model.generateSignature(m_ast);
+ m_astClass.modelMetadata << model;
+ m_astClass.properties << ASTProperty(QStringLiteral("QAbstractItemModel"), name, QStringLiteral("nullptr"), ASTProperty::SourceOnlySetter, false, true);
+ }
+ break;
+./
+
+ChildRep: childrep;
+/.
+case $rule_number:
+{
+ const QString name = captured().value(QLatin1String("name")).trimmed();
+ const QString type = captured().value(QLatin1String("type")).trimmed();
+
+ m_astClass.subClassPropertyIndices << m_astClass.properties.size();
+ m_astClass.properties << ASTProperty(type, name, QStringLiteral("nullptr"), ASTProperty::SourceOnlySetter, false, true);
+}
+break;
+./
+
+ClassStart: class Newlines;
+/.
+ case $rule_number:
+./
+ClassStart: class;
+/.
+ case $rule_number:
+ {
+ const QString name = captured().value(QLatin1String("name"));
+
+ // new Class declaration
+ m_astClass = ASTClass(name);
+ }
+ break;
+./
+
+PodStart: pod2;
+/.
+ case $rule_number:
+./
+PodStart: pod2 Newlines;
+/.
+ case $rule_number:
+ {
+ // new POD declaration
+ m_astPod = POD();
+ m_astPod.name = captured().value(QLatin1String("name")).trimmed();
+ m_argString.clear();
+ }
+ break;
+./
+
+UseEnum: use_enum;
+/.
+ case $rule_number:
+ {
+ const QString name = captured().value(QLatin1String("name"));
+
+ m_ast.enumUses.append(name);
+ }
+ break;
+./
+
+Flag: flag;
+/.
+ case $rule_number:
+ {
+ const QString name = captured().value(QLatin1String("name"));
+ const QString _enum = captured().value(QLatin1String("enum"));
+ int enumIndex = 0;
+ for (auto &en : m_ast.enums) {
+ if (en.name == _enum) {
+ en.flagIndex = m_ast.flags.count();
+ break;
+ }
+ if (en.name == _enum)
+ break;
+ enumIndex++;
+ }
+ if (enumIndex == m_ast.enums.count()) {
+ setErrorString(QLatin1String("FLAG: Unknown (global) enum: %1").arg(_enum));
+ return false;
+ }
+ auto flag = ASTFlag(name, _enum);
+ flag.generateSignature(m_ast);
+ m_ast.flags.append(flag);
+ }
+ break;
+./
+
+--Error conditions/messages
+ClassType: ClassStart;
+/.
+ case $rule_number:
+ {
+ setErrorString(QStringLiteral("class: Cannot be nested"));
+ return false;
+ }
+ break;
+./
+ClassType: Pod;
+/.
+ case $rule_number:
+ {
+ setErrorString(QStringLiteral("POD: Can only be used in global scope"));
+ return false;
+ }
+ break;
+./
+ClassType: UseEnum;
+/.
+ case $rule_number:
+ {
+ setErrorString(QStringLiteral("USE_ENUM: Can only be used in global scope"));
+ return false;
+ }
+ break;
+./
+Type: Signal;
+/.
+ case $rule_number:
+ {
+ setErrorString(QStringLiteral("SIGNAL: Can only be used in class scope"));
+ return false;
+ }
+ break;
+./
+Type: Slot;
+/.
+ case $rule_number:
+ {
+ setErrorString(QStringLiteral("SLOT: Can only be used in class scope"));
+ return false;
+ }
+ break;
+./
+Type: Prop;
+/.
+ case $rule_number:
+ {
+ setErrorString(QStringLiteral("PROP: Can only be used in class scope"));
+ return false;
+ }
+ break;
+./
+Type: Model;
+/.
+ case $rule_number:
+ {
+ setErrorString(QStringLiteral("MODEL: Can only be used in class scope"));
+ return false;
+ }
+ break;
+./
+
+Type: ChildRep;
+/.
+ case $rule_number:
+ {
+ setErrorString(QStringLiteral("CLASS: Can only be used in class scope"));
+ return false;
+ }
+ break;
+./
+
+/.
+ } // switch
+ return true;
+}
+./
--- /dev/null
+// Copyright (C) 2017-2020 Ford Motor Company.
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QREGEXPARSER_H
+#define QREGEXPARSER_H
+
+#include <QtCore/qshareddata.h>
+#include <QtCore/qvarlengtharray.h>
+#include <QtCore/qvariant.h>
+#include <QtCore/qregularexpression.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qtextstream.h>
+#include <QtCore/qdebug.h>
+
+struct MatchCandidate {
+ MatchCandidate(const QString &n, const QString &t, int i) : name(n), matchText(t), index(i) {}
+ QString name;
+ QString matchText;
+ int index;
+};
+
+QT_BEGIN_NAMESPACE
+
+template <typename _Parser, typename _Table>
+class QRegexParser: protected _Table
+{
+public:
+ QRegexParser(int maxMatchLen=4096);
+ virtual ~QRegexParser();
+
+ virtual bool parse();
+
+ virtual void reset() {}
+
+ inline QVariant &sym(int index);
+
+ void setBuffer(const QString &buffer);
+
+ void setBufferFromDevice(QIODevice *device);
+
+ void setDebug();
+
+ QString errorString() const
+ {
+ return m_errorString;
+ }
+
+ void setErrorString(const QString &error)
+ {
+ m_errorString = error;
+ qWarning() << m_errorString;
+ }
+
+ inline const QMap<QString, QString>& captured() const
+ {
+ return m_captured;
+ }
+
+ inline bool isDebug() const
+ {
+ return m_debug;
+ }
+
+ inline int lineNumber() const
+ {
+ return m_lineno;
+ }
+
+private:
+ int nextToken();
+
+ inline bool consumeRule(int rule)
+ {
+ return static_cast<_Parser*> (this)->consumeRule(rule);
+ }
+
+ enum { DefaultStackSize = 128 };
+
+ struct Data: public QSharedData
+ {
+ Data(): stackSize (DefaultStackSize), tos (0) {}
+
+ QVarLengthArray<int, DefaultStackSize> stateStack;
+ QVarLengthArray<QVariant, DefaultStackSize> parseStack;
+ int stackSize;
+ int tos;
+
+ void reallocateStack() {
+ stackSize <<= 1;
+ stateStack.resize(stackSize);
+ parseStack.resize(stackSize);
+ }
+ };
+
+ inline QString escapeString(QString s)
+ {
+ return s.replace(QLatin1Char('\n'), QLatin1String("\\n")).replace(QLatin1Char('\t'), QLatin1String("\\t"));
+ }
+
+ QSharedDataPointer<Data> d;
+
+ QList<QRegularExpression> m_regexes;
+ QMap<QChar, QList<int> > regexCandidates;
+ QList<int> m_tokens;
+ QString m_buffer, m_lastMatchText;
+ int m_loc, m_lastNewlinePosition;
+ int m_lineno;
+ int m_debug;
+ QStringList m_tokenNames;
+ QMap<QString, QString> m_captured;
+ int m_maxMatchLen;
+ QString m_errorString;
+ QList<QMap<int, QString>> m_names; //storage for match names
+};
+
+template <typename _Parser, typename _Table>
+inline QVariant &QRegexParser<_Parser, _Table>::sym(int n)
+{
+ return d->parseStack [d->tos + n - 1];
+}
+
+template <typename _Parser, typename _Table>
+QRegexParser<_Parser, _Table>::~QRegexParser()
+{
+}
+
+template <typename _Parser, typename _Table>
+bool QRegexParser<_Parser, _Table>::parse()
+{
+ m_errorString.clear();
+ reset();
+ const int INITIAL_STATE = 0;
+
+ d->tos = 0;
+ d->reallocateStack();
+
+ int act = d->stateStack[++d->tos] = INITIAL_STATE;
+ int token = -1;
+
+ Q_FOREVER {
+ if (token == -1 && - _Table::TERMINAL_COUNT != _Table::action_index[act])
+ token = nextToken();
+
+ act = _Table::t_action(act, token);
+
+ if (d->stateStack[d->tos] == _Table::ACCEPT_STATE)
+ return true;
+
+ else if (act > 0) {
+ if (++d->tos == d->stackSize)
+ d->reallocateStack();
+
+ d->parseStack[d->tos] = d->parseStack[d->tos - 1];
+ d->stateStack[d->tos] = act;
+ token = -1;
+ }
+
+ else if (act < 0) {
+ int r = - act - 1;
+ d->tos -= _Table::rhs[r];
+ act = d->stateStack[d->tos++];
+ if (!consumeRule(r))
+ return false;
+ act = d->stateStack[d->tos] = _Table::nt_action(act, _Table::lhs[r] - _Table::TERMINAL_COUNT);
+ }
+
+ else break;
+ }
+
+ setErrorString(QStringLiteral("Unknown token encountered"));
+ return false;
+}
+
+template <typename _Parser, typename _Table>
+QRegexParser<_Parser, _Table>::QRegexParser(int maxMatchLen) : d(new Data()), m_loc(0), m_lastNewlinePosition(0), m_lineno(1), m_debug(0), m_maxMatchLen(maxMatchLen)
+{
+ QRegularExpression re(QStringLiteral("\\[([_a-zA-Z][_0-9a-zA-Z]*)(,\\s*M)?\\](.+)$"));
+ re.optimize();
+ QMap<QString, int> token_lookup;
+ QMap<int, QString> names;
+ for (int i = 1; i < _Table::lhs[0]; i++) {
+ const QString text = QLatin1String(_Table::spell[i]);
+ names.clear();
+ QRegularExpressionMatch match = re.match(text, 0, QRegularExpression::NormalMatch, QRegularExpression::DontCheckSubjectStringMatchOption);
+ if (match.hasMatch()) {
+ const QString token = match.captured(1);
+ const bool multiline = match.captured(2).size() > 0;
+ const QString pattern = match.captured(3);
+ m_tokenNames.append(token);
+ int index = i;
+ if (token_lookup.contains(token))
+ index = token_lookup[token];
+ else
+ token_lookup[token] = i;
+ QRegularExpression pat(pattern);
+ if (multiline)
+ pat.setPatternOptions(QRegularExpression::DotMatchesEverythingOption);
+
+ if (!pat.isValid())
+ qCritical() << "Pattern error for token #" << i << "for" << text << "pattern =" << pat << ":" << pat.errorString();
+ else {
+ pat.optimize();
+ int counter = 0;
+ const auto namedCaptureGroups = pat.namedCaptureGroups();
+ for (const QString &name : namedCaptureGroups) {
+ if (!name.isEmpty())
+ names.insert(counter, name);
+ ++counter;
+ }
+ m_names.append(names);
+ m_regexes.append(pat);
+ if (token.startsWith(QLatin1String("ignore")))
+ m_tokens.append(-1);
+ else
+ m_tokens.append(index);
+ }
+ } else {
+ qCritical() << "Error parsing regex at token #" << i << "for" << text << "Invalid syntax";
+ }
+ }
+}
+
+template <typename _Parser, typename _Table>
+void QRegexParser<_Parser, _Table>::setBuffer(const QString &buffer)
+{
+ m_buffer = buffer;
+}
+
+template <typename _Parser, typename _Table>
+void QRegexParser<_Parser, _Table>::setBufferFromDevice(QIODevice *device)
+{
+ QTextStream in(device);
+ m_buffer = in.readAll();
+}
+
+template <typename _Parser, typename _Table>
+void QRegexParser<_Parser, _Table>::setDebug()
+{
+ m_debug = true;
+ for (int r = 0; r < _Table::RULE_COUNT; ++r)
+ {
+ int ridx = _Table::rule_index[r];
+ int _rhs = _Table::rhs[r];
+ qDebug("%3d) %s ::=", r + 1, _Table::spell[_Table::rule_info[ridx]]);
+ ++ridx;
+ for (int i = ridx; i < ridx + _rhs; ++i)
+ {
+ int symbol = _Table::rule_info[i];
+ if (symbol > 0 && symbol < _Table::lhs[0])
+ qDebug(" token_%s (pattern = %s)",qPrintable(m_tokenNames[symbol-1]),qPrintable(m_regexes[symbol-1].pattern()));
+ else if (const char *name = _Table::spell[symbol])
+ qDebug(" %s", name);
+ else
+ qDebug(" #%d", symbol);
+ }
+ qDebug();
+ }
+}
+
+template <typename _Parser, typename _Table>
+int QRegexParser<_Parser, _Table>::nextToken()
+{
+ const QStringView buffer { m_buffer };
+ static const QRegularExpression newline(QLatin1String("(\\n)"));
+ int token = -1;
+ while (token < 0)
+ {
+ if (m_loc == buffer.size())
+ return _Table::EOF_SYMBOL;
+
+ //Check m_lastMatchText for newlines and update m_lineno
+ //This isn't necessary, but being able to provide the line # and character #
+ //where the match is failing sure makes building/debugging grammars easier.
+ QRegularExpressionMatchIterator matches = newline.globalMatch(m_lastMatchText);
+ while (matches.hasNext()) {
+ m_lineno++;
+ QRegularExpressionMatch match = matches.next();
+ if (!matches.hasNext())
+ m_lastNewlinePosition += match.capturedEnd();
+ }
+
+ if (m_debug) {
+ qDebug();
+ qDebug() << "nextToken loop, line =" << m_lineno
+ << "line position =" << m_loc - m_lastNewlinePosition
+ << "next 5 characters =" << escapeString(buffer.mid(m_loc, 5).toString());
+ }
+ int best = -1, maxLen = -1;
+ QRegularExpressionMatch bestRegex;
+
+ //Find the longest match.
+ //If more than one are the same (longest) length, return the first one in
+ //the order defined.
+ QList<MatchCandidate> candidates;
+ //We used PCRE's PartialMatch to eliminate most of the regexes by the first
+ //character, so we keep a regexCandidates map with the list of possible regexes
+ //based on initial characters found so far.
+ const QChar nextChar = buffer.at(m_loc);
+ //Populate the list if we haven't seeen this character before
+ if (!regexCandidates.contains(nextChar)) {
+ const QStringView tmp = buffer.mid(m_loc,1);
+ int i = 0;
+ regexCandidates[nextChar] = QList<int>();
+ for (const QRegularExpression &re : std::as_const(m_regexes))
+ {
+ QRegularExpressionMatch match = re.match(tmp, 0, QRegularExpression::PartialPreferFirstMatch, QRegularExpression::DontCheckSubjectStringMatchOption);
+ //qDebug() << nextChar << tmp << match.hasMatch() << match.hasPartialMatch() << re.pattern();
+ if (match.hasMatch() || match.hasPartialMatch())
+ regexCandidates[nextChar] << i;
+ i++;
+ }
+ }
+ const auto indices = regexCandidates.value(nextChar);
+ for (int i : indices)
+ {
+ //Seems like I should be able to run the regex on the entire string, but performance is horrible
+ //unless I use a substring.
+ //QRegularExpressionMatch match = m_regexes[i].match(m_buffer, m_loc, QRegularExpression::NormalMatch, QRegularExpression::AnchorAtOffsetMatchOption);
+ QRegularExpressionMatch match = m_regexes.at(i).match(buffer.mid(m_loc, m_maxMatchLen), 0, QRegularExpression::NormalMatch, QRegularExpression::AnchorAtOffsetMatchOption | QRegularExpression::DontCheckSubjectStringMatchOption);
+ if (match.hasMatch()) {
+ if (m_debug)
+ candidates << MatchCandidate(m_tokenNames[i], match.captured(), i);
+ if (match.capturedLength() > maxLen) {
+ best = i;
+ maxLen = match.capturedLength();
+ bestRegex = match;
+ }
+ }
+ }
+ if (best < 0) {
+ setErrorString(QLatin1String("Error generating tokens from file, next characters >%1<").arg(buffer.mid(m_loc, 15)));
+ return -1;
+ } else {
+ const QMap<int, QString> &map = m_names.at(best);
+ if (!map.isEmpty())
+ m_captured.clear();
+ for (auto iter = map.cbegin(), end = map.cend(); iter != end; ++iter)
+ m_captured.insert(iter.value(), bestRegex.captured(iter.key()));
+ if (m_debug) {
+ qDebug() << "Match candidates:";
+ for (const MatchCandidate &m : std::as_const(candidates)) {
+ QLatin1String result = m.index == best ? QLatin1String(" * ") : QLatin1String(" ");
+ qDebug() << qPrintable(result) << qPrintable(m.name) << qPrintable(escapeString(m.matchText));
+ }
+ }
+ m_loc += maxLen;
+ if (m_tokens.at(best) >= 0)
+ token = m_tokens.at(best);
+ m_lastMatchText = bestRegex.captured(0);
+ }
+ }
+ return token;
+}
+
+QT_END_NAMESPACE
+
+#endif // QREGEXPARSER_H
--- /dev/null
+%modules = ( # path to module name map
+ "QtRemoteObjects" => "$basedir/src/remoteobjects",
+ "QtRemoteObjectsQml" => "$basedir/src/remoteobjectsqml",
+ "QtRepParser" => "$basedir/src/repparser",
+);
--- /dev/null
+
+find_package(Qt6 ${PROJECT_VERSION} CONFIG OPTIONAL_COMPONENTS QuickTestUtilsPrivate)
+
+if(QT_BUILD_STANDALONE_TESTS)
+ # Add qt_find_package calls for extra dependencies that need to be found when building
+ # the standalone tests here.
+endif()
+qt_build_tests()
--- /dev/null
+
+add_subdirectory(benchmarks)
+add_subdirectory(cmake)
+add_subdirectory(modelreplica)
+add_subdirectory(modelview)
+add_subdirectory(pods)
+add_subdirectory(proxy)
+if(NOT CMAKE_CROSSCOMPILING)
+ add_subdirectory(rep_from_header)
+endif()
+add_subdirectory(repc)
+add_subdirectory(repcodegenerator)
+add_subdirectory(repparser)
+add_subdirectory(subclassreplica)
+if(QT_FEATURE_ssl)
+ add_subdirectory(external_IODevice)
+endif()
+if(TARGET Qt::Qml)
+ add_subdirectory(qml)
+endif()
+if(QT_FEATURE_process)
+ add_subdirectory(integration_multiprocess)
+ add_subdirectory(proxy_multiprocess)
+ add_subdirectory(integration_external)
+ add_subdirectory(restart)
+ add_subdirectory(reconnect)
+endif()
+add_subdirectory(localsockettestserver)
+add_subdirectory(integration)
--- /dev/null
+
+#####################################################################
+## tst_benchmarkstest Test:
+#####################################################################
+
+qt_internal_add_test(tst_benchmarkstest
+ SOURCES
+ tst_benchmarkstest.cpp
+ DEFINES
+ SRCDIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/\\\"
+ PUBLIC_LIBRARIES
+ Qt::Network
+ Qt::RemoteObjects
+)
+qt6_add_repc_sources(tst_benchmarkstest
+ ../repfiles/localdatacenter.rep
+ ../repfiles/tcpdatacenter.rep
+)
+qt6_add_repc_replicas(tst_benchmarkstest
+ ../repfiles/localdatacenter.rep
+ ../repfiles/tcpdatacenter.rep
+)
+
+#### Keys ignored in scope 1:.:.:benchmarks.pro:<TRUE>:
+# TEMPLATE = "app"
--- /dev/null
+// Copyright (C) 2017-2015 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <QString>
+#include <QDataStream>
+#include <QLocalSocket>
+#include <QLocalServer>
+#include <QtTest>
+#include <QtRemoteObjects/QAbstractItemModelReplica>
+#include <QtRemoteObjects/QRemoteObjectNode>
+#include "rep_localdatacenter_replica.h"
+#include "rep_localdatacenter_source.h"
+
+#include "../../shared/testutils.h"
+
+class BenchmarksModel : public QAbstractListModel
+{
+ // QAbstractItemModel interface
+public:
+ int rowCount(const QModelIndex &parent) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+ QHash<int, QByteArray> roleNames() const override;
+};
+
+int BenchmarksModel::rowCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent)
+ return 100000;
+}
+
+QVariant BenchmarksModel::data(const QModelIndex &index, int role) const
+{
+ switch (role) {
+ case Qt::DisplayRole:
+ return QStringLiteral("Benchmark data %1").arg(index.row());
+ case Qt::BackgroundRole:
+ return index.row() % 2 ? QStringLiteral("red") : QStringLiteral("green");
+ }
+ return QVariant();
+}
+
+QHash<int, QByteArray> BenchmarksModel::roleNames() const
+{
+ static QHash<int,QByteArray> roleNames = {
+ {Qt::DisplayRole, "_text"},
+ {Qt::BackgroundRole, "_color"}
+ };
+ return roleNames;
+}
+
+class BenchmarksTest : public QObject
+{
+ Q_OBJECT
+
+public:
+ BenchmarksTest();
+private:
+ QRemoteObjectHost m_basicServer;
+ QRemoteObjectNode m_basicClient;
+ QScopedPointer<LocalDataCenterSimpleSource> dataCenterLocal;
+ BenchmarksModel m_sourceModel;
+
+private Q_SLOTS:
+ void initTestCase();
+ void benchPropertyChangesInt();
+ void benchQDataStreamInt();
+ void benchQLocalSocketInt();
+ void benchQLocalSocketQDataStreamInt();
+ void benchModelLinearAccess();
+ void benchModelRandomAccess();
+};
+
+BenchmarksTest::BenchmarksTest()
+{
+}
+
+void BenchmarksTest::initTestCase() {
+ m_basicServer.setHostUrl(QUrl(QStringLiteral(LOCAL_SOCKET ":benchmark_replica")));
+ dataCenterLocal.reset(new LocalDataCenterSimpleSource);
+ dataCenterLocal->setData1(5);
+ const bool remoted = m_basicServer.enableRemoting(dataCenterLocal.data());
+ Q_ASSERT(remoted);
+ Q_UNUSED(remoted)
+
+ m_basicClient.connectToNode(QUrl(QStringLiteral(LOCAL_SOCKET ":benchmark_replica")));
+ Q_ASSERT(m_basicClient.lastError() == QRemoteObjectNode::NoError);
+
+ m_basicServer.enableRemoting(&m_sourceModel, QStringLiteral("BenchmarkRemoteModel"),
+ m_sourceModel.roleNames().keys().toVector());
+}
+
+void BenchmarksTest::benchPropertyChangesInt()
+{
+ QScopedPointer<LocalDataCenterReplica> center;
+ center.reset(m_basicClient.acquire<LocalDataCenterReplica>());
+ if (!center->isInitialized()) {
+ QEventLoop loop;
+ connect(center.data(), &LocalDataCenterReplica::initialized, &loop, &QEventLoop::quit);
+ loop.exec();
+ }
+ QEventLoop loop;
+ int lastValue = 0;
+ connect(center.data(), &LocalDataCenterReplica::data1Changed, [&lastValue, ¢er, &loop]() {
+ const bool res = (lastValue++ == center->data1());
+ Q_ASSERT(res);
+ Q_UNUSED(res)
+ if (lastValue == 50000)
+ loop.quit();
+ });
+ QBENCHMARK {
+ for (int i = 0; i < 50000; ++i) {
+ dataCenterLocal->setData1(i);
+ }
+ loop.exec();
+ }
+}
+// This ONLY tests the optimal case of a non resizing QByteArray
+void BenchmarksTest::benchQDataStreamInt()
+{
+ QByteArray buffer;
+ QDataStream stream(&buffer, QIODevice::WriteOnly);
+ QDataStream rStream(&buffer, QIODevice::ReadOnly);
+ int readout = 0;
+ QBENCHMARK {
+ for (int i = 0; i < 50000; ++i) {
+ stream << i;
+ }
+ for (int i = 0; i < 50000; ++i) {
+ rStream >> readout;
+ Q_ASSERT(i == readout);
+ }
+ }
+}
+
+void BenchmarksTest::benchQLocalSocketInt()
+{
+ const QString socketName = QStringLiteral("benchLocalSocket");
+ QLocalServer server;
+ QLocalServer::removeServer(socketName);
+ server.listen(socketName);
+ QLocalSocket client;
+ client.connectToServer(socketName);
+ QEventLoop loop;
+ QScopedPointer<QLocalSocket> serverSock;
+ if (!server.hasPendingConnections()) {
+ connect(&server, &QLocalServer::newConnection, &loop, &QEventLoop::quit);
+ loop.exec();
+ }
+ Q_ASSERT(server.hasPendingConnections());
+ serverSock.reset(server.nextPendingConnection());
+ int lastValue = 0;
+ connect(&client, &QLocalSocket::readyRead, [&loop, &lastValue, &client]() {
+ int readout = 0;
+ while (client.bytesAvailable() && lastValue < 50000) {
+ client.read(reinterpret_cast<char*>(&readout), sizeof(int));
+ const bool res = (lastValue++ == readout);
+ Q_ASSERT(res);
+ Q_UNUSED(res)
+ }
+ if (lastValue >= 50000)
+ loop.quit();
+ });
+ QBENCHMARK {
+ for (int i = 0; i < 50000; ++i) {
+ const int res = int(serverSock->write(reinterpret_cast<char*>(&i), sizeof(int)));
+ Q_ASSERT(res == sizeof(int));
+ Q_UNUSED(res)
+ }
+ loop.exec();
+ }
+
+#ifdef Q_OS_WIN
+ // Work-around QTBUG-38185: immediately close socket
+ client.abort();
+#endif
+}
+
+void BenchmarksTest::benchQLocalSocketQDataStreamInt()
+{
+ const QString socketName = QStringLiteral("benchLocalSocket");
+ QLocalServer server;
+ QLocalServer::removeServer(socketName);
+ server.listen(socketName);
+ QLocalSocket client;
+ client.connectToServer(socketName);
+ QDataStream readStream(&client);
+ QEventLoop loop;
+ QScopedPointer<QLocalSocket> serverSock;
+ if (!server.hasPendingConnections()) {
+ connect(&server, &QLocalServer::newConnection, &loop, &QEventLoop::quit);
+ loop.exec();
+ }
+ Q_ASSERT(server.hasPendingConnections());
+ serverSock.reset(server.nextPendingConnection());
+ QDataStream writeStream(serverSock.data());
+ int lastValue = 0;
+ connect(&client, &QIODevice::readyRead, [&loop, &lastValue, &readStream]() {
+ int readout = 0;
+ while (readStream.device()->bytesAvailable() && lastValue < 50000) {
+ readStream >> readout;
+ const bool res = (lastValue++ == readout);
+ Q_ASSERT(res);
+ Q_UNUSED(res)
+ }
+ if (lastValue >= 50000)
+ loop.quit();
+ });
+ QBENCHMARK {
+ for (int i = 0; i < 50000; ++i) {
+ writeStream << i;
+ }
+ loop.exec();
+ }
+
+#ifdef Q_OS_WIN
+ // Work-around QTBUG-38185: immediately close socket
+ client.abort();
+#endif
+}
+
+void BenchmarksTest::benchModelLinearAccess()
+{
+ // Simulate an user browse through item them stops.
+ // We're measuring the time needed needed to deliver the visible chunk of data
+ // which are the last 50 items
+ QBENCHMARK {
+ QRemoteObjectNode localClient;
+ localClient.connectToNode(QUrl(QStringLiteral(LOCAL_SOCKET ":benchmark_replica")));
+ QScopedPointer<QAbstractItemModelReplica> model(localClient.acquireModel(QStringLiteral("BenchmarkRemoteModel")));
+ QEventLoop loop;
+ QHash<int, QPair<QString, QString>> dataToWait;
+ connect(model.data(), &QAbstractItemModelReplica::dataChanged, [&model, &loop, &dataToWait](const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList<int> &roles) {
+ for (int row = topLeft.row(); row <= bottomRight.row(); ++row) {
+ // we're assuming that the view will try use the sent data,
+ // therefore we're not optimizing the code
+ auto it = dataToWait.find(row);
+ if (it == dataToWait.end()) {
+ // simulate some work with the received data
+ QThread::usleep(10);
+ continue;
+ }
+ for (int role : roles) {
+ QVariant data = model->data(model->index(row, 0), role);
+ switch (role) {
+ case Qt::DisplayRole:
+ it->first = data.toString();
+ break;
+ case Qt::BackgroundRole:
+ it->second = data.toString();
+ break;
+ }
+ }
+
+ if (it->first == QStringLiteral("Benchmark data %1").arg(row) &&
+ it->second == (row % 2 ? QStringLiteral("red") : QStringLiteral("green"))) {
+ dataToWait.erase(it);
+ if (dataToWait.isEmpty())
+ break;
+ }
+ }
+ if (dataToWait.isEmpty())
+ loop.quit();
+ });
+
+ auto beginBenchmark = [&model, &dataToWait] {
+ for (int row = 0; row < 1000; ++row) {
+ if (row >= 950)
+ dataToWait.insert(row, QPair<QString, QString>());
+ model->data(model->index(row, 0), Qt::DisplayRole);
+ model->data(model->index(row, 0), Qt::BackgroundRole);
+
+ // Views (e.g. QTreeView) are accessing other roles
+ model->data(model->index(row, 0), Qt::FontRole);
+ model->data(model->index(row, 0), Qt::DecorationRole);
+ model->data(model->index(row, 0), Qt::SizeHintRole);
+ }
+
+ };
+ connect(model.data(), &QAbstractItemModelReplica::initialized, [&model, &loop, &beginBenchmark] {
+ if (model->isInitialized()) {
+ beginBenchmark();
+ } else {
+ Q_ASSERT(false);
+ loop.quit();
+ }
+ });
+ if (model->isInitialized())
+ beginBenchmark();
+
+ QTimer::singleShot(5000, &loop, &QEventLoop::quit);
+ loop.exec();
+ QVERIFY(dataToWait.isEmpty());
+ }
+}
+
+void BenchmarksTest::benchModelRandomAccess()
+{
+ QBENCHMARK {
+ QRemoteObjectNode localClient;
+ localClient.connectToNode(QUrl(QStringLiteral(LOCAL_SOCKET ":benchmark_replica")));
+ QScopedPointer<QAbstractItemModelReplica> model(localClient.acquireModel(QStringLiteral("BenchmarkRemoteModel")));
+ model->setRootCacheSize(5000); // we need to make room for all 5000 rows that we'll use
+ QEventLoop loop;
+ QHash<int, QPair<QString, QString>> dataToWait;
+ connect(model.data(), &QAbstractItemModelReplica::dataChanged, [&model, &loop, &dataToWait](const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList<int> &roles) {
+ for (int row = topLeft.row(); row <= bottomRight.row(); ++row) {
+ // we're assuming that the view will try use the sent data,
+ // therefore we're not optimizing the code
+ auto it = dataToWait.find(row);
+ if (it == dataToWait.end()) {
+ QThread::yieldCurrentThread(); // instead to wait
+ continue;
+ }
+ for (int role : roles) {
+ QVariant data = model->data(model->index(row, 0), role);
+ switch (role) {
+ case Qt::DisplayRole:
+ it->first = data.toString();
+ break;
+ case Qt::BackgroundRole:
+ it->second = data.toString();
+ break;
+ }
+ }
+
+ if (it->first == QStringLiteral("Benchmark data %1").arg(row) &&
+ it->second == (row % 2 ? QStringLiteral("red") : QStringLiteral("green"))) {
+ dataToWait.erase(it);
+ if (dataToWait.isEmpty())
+ break;
+ }
+ }
+ if (dataToWait.isEmpty())
+ loop.quit();
+ });
+
+ auto beginBenchmark = [&model, &dataToWait] {
+ for (int chunck = 0; chunck < 100; ++chunck) {
+ int row = chunck * 950;
+ for (int r = 0; r < 50; ++r) {
+ dataToWait.insert(r + row, QPair<QString, QString>());
+ model->data(model->index(r + row, 0), Qt::DisplayRole);
+ model->data(model->index(r + row, 0), Qt::BackgroundRole);
+
+ // Views (e.g. QTreeView) are accessing other roles
+ model->data(model->index(r + row, 0), Qt::FontRole);
+ model->data(model->index(r + row, 0), Qt::DecorationRole);
+ model->data(model->index(r + row, 0), Qt::SizeHintRole);
+ }
+ }
+
+ };
+ connect(model.data(), &QAbstractItemModelReplica::initialized, [&model, &loop, &beginBenchmark] {
+ if (model->isInitialized()) {
+ beginBenchmark();
+ } else {
+ Q_ASSERT(false);
+ loop.quit();
+ }
+ });
+ if (model->isInitialized())
+ beginBenchmark();
+
+ QTimer::singleShot(5000, &loop, &QEventLoop::quit);
+ loop.exec();
+ QVERIFY(dataToWait.isEmpty());
+ }
+}
+
+QTEST_MAIN(BenchmarksTest)
+
+#include "tst_benchmarkstest.moc"
--- /dev/null
+Class std::__failure_type
+ size=1 align=1
+ base size=0 base align=1
+std::__failure_type (0x0x7f234e2095a0) 0 empty
+
+Class std::__do_is_destructible_impl
+ size=1 align=1
+ base size=0 base align=1
+std::__do_is_destructible_impl (0x0x7f234e267d20) 0 empty
+
+Class std::__do_is_nt_destructible_impl
+ size=1 align=1
+ base size=0 base align=1
+std::__do_is_nt_destructible_impl (0x0x7f234e267f60) 0 empty
+
+Class std::__do_is_default_constructible_impl
+ size=1 align=1
+ base size=0 base align=1
+std::__do_is_default_constructible_impl (0x0x7f234e2911e0) 0 empty
+
+Class std::__do_is_static_castable_impl
+ size=1 align=1
+ base size=0 base align=1
+std::__do_is_static_castable_impl (0x0x7f234e291420) 0 empty
+
+Class std::__do_is_direct_constructible_impl
+ size=1 align=1
+ base size=0 base align=1
+std::__do_is_direct_constructible_impl (0x0x7f234e2915a0) 0 empty
+
+Class std::__do_is_nary_constructible_impl
+ size=1 align=1
+ base size=0 base align=1
+std::__do_is_nary_constructible_impl (0x0x7f234e291960) 0 empty
+
+Class std::__do_is_implicitly_default_constructible_impl
+ size=1 align=1
+ base size=0 base align=1
+std::__do_is_implicitly_default_constructible_impl (0x0x7f234e2cfa80) 0 empty
+
+Class std::__do_common_type_impl
+ size=1 align=1
+ base size=0 base align=1
+std::__do_common_type_impl (0x0x7f234df4f180) 0 empty
+
+Class std::__do_member_type_wrapper
+ size=1 align=1
+ base size=0 base align=1
+std::__do_member_type_wrapper (0x0x7f234df4f240) 0 empty
+
+Class std::__invoke_memfun_ref
+ size=1 align=1
+ base size=0 base align=1
+std::__invoke_memfun_ref (0x0x7f234df4f600) 0 empty
+
+Class std::__invoke_memfun_deref
+ size=1 align=1
+ base size=0 base align=1
+std::__invoke_memfun_deref (0x0x7f234df4f660) 0 empty
+
+Class std::__invoke_memobj_ref
+ size=1 align=1
+ base size=0 base align=1
+std::__invoke_memobj_ref (0x0x7f234df4f6c0) 0 empty
+
+Class std::__invoke_memobj_deref
+ size=1 align=1
+ base size=0 base align=1
+std::__invoke_memobj_deref (0x0x7f234df4f720) 0 empty
+
+Class std::__invoke_other
+ size=1 align=1
+ base size=0 base align=1
+std::__invoke_other (0x0x7f234df4f780) 0 empty
+
+Class std::__result_of_memfun_ref_impl
+ size=1 align=1
+ base size=0 base align=1
+std::__result_of_memfun_ref_impl (0x0x7f234df4f840) 0 empty
+
+Class std::__result_of_memfun_deref_impl
+ size=1 align=1
+ base size=0 base align=1
+std::__result_of_memfun_deref_impl (0x0x7f234df4f900) 0 empty
+
+Class std::__result_of_memobj_ref_impl
+ size=1 align=1
+ base size=0 base align=1
+std::__result_of_memobj_ref_impl (0x0x7f234df4f9c0) 0 empty
+
+Class std::__result_of_memobj_deref_impl
+ size=1 align=1
+ base size=0 base align=1
+std::__result_of_memobj_deref_impl (0x0x7f234df4fa80) 0 empty
+
+Class std::__result_of_other_impl
+ size=1 align=1
+ base size=0 base align=1
+std::__result_of_other_impl (0x0x7f234df4fde0) 0 empty
+
+Class std::__swappable_details::__do_is_swappable_impl
+ size=1 align=1
+ base size=0 base align=1
+std::__swappable_details::__do_is_swappable_impl (0x0x7f234df8f180) 0 empty
+
+Class std::__swappable_details::__do_is_nothrow_swappable_impl
+ size=1 align=1
+ base size=0 base align=1
+std::__swappable_details::__do_is_nothrow_swappable_impl (0x0x7f234df8f1e0) 0 empty
+
+Class std::__nonesuch
+ size=1 align=1
+ base size=0 base align=1
+std::__nonesuch (0x0x7f234df8f780) 0 empty
+
+Class std::piecewise_construct_t
+ size=1 align=1
+ base size=0 base align=1
+std::piecewise_construct_t (0x0x7f234df8fde0) 0 empty
+
+Class std::__nonesuch_no_braces
+ size=1 align=1
+ base size=1 base align=1
+std::__nonesuch_no_braces (0x0x7f234dfa71a0) 0 empty
+ std::__nonesuch (0x0x7f234dfd2300) 0 empty
+
+Class std::__true_type
+ size=1 align=1
+ base size=0 base align=1
+std::__true_type (0x0x7f234e01fc60) 0 empty
+
+Class std::__false_type
+ size=1 align=1
+ base size=0 base align=1
+std::__false_type (0x0x7f234e01fcc0) 0 empty
+
+Class std::input_iterator_tag
+ size=1 align=1
+ base size=0 base align=1
+std::input_iterator_tag (0x0x7f234e07a9c0) 0 empty
+
+Class std::output_iterator_tag
+ size=1 align=1
+ base size=0 base align=1
+std::output_iterator_tag (0x0x7f234e07aa20) 0 empty
+
+Class std::forward_iterator_tag
+ size=1 align=1
+ base size=1 base align=1
+std::forward_iterator_tag (0x0x7f234dfa7680) 0 empty
+ std::input_iterator_tag (0x0x7f234e07aa80) 0 empty
+
+Class std::bidirectional_iterator_tag
+ size=1 align=1
+ base size=1 base align=1
+std::bidirectional_iterator_tag (0x0x7f234dfa76e8) 0 empty
+ std::forward_iterator_tag (0x0x7f234dfa7750) 0 empty
+ std::input_iterator_tag (0x0x7f234e07aae0) 0 empty
+
+Class std::random_access_iterator_tag
+ size=1 align=1
+ base size=1 base align=1
+std::random_access_iterator_tag (0x0x7f234dfa77b8) 0 empty
+ std::bidirectional_iterator_tag (0x0x7f234dfa7820) 0 empty
+ std::forward_iterator_tag (0x0x7f234dfa7888) 0 empty
+ std::input_iterator_tag (0x0x7f234e07ab40) 0 empty
+
+Class __gnu_cxx::__ops::_Iter_less_iter
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__ops::_Iter_less_iter (0x0x7f234e12d660) 0 empty
+
+Class __gnu_cxx::__ops::_Iter_less_val
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__ops::_Iter_less_val (0x0x7f234e12d780) 0 empty
+
+Class __gnu_cxx::__ops::_Val_less_iter
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__ops::_Val_less_iter (0x0x7f234e12da80) 0 empty
+
+Class __gnu_cxx::__ops::_Iter_equal_to_iter
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__ops::_Iter_equal_to_iter (0x0x7f234e12dd80) 0 empty
+
+Class __gnu_cxx::__ops::_Iter_equal_to_val
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__ops::_Iter_equal_to_val (0x0x7f234e12dea0) 0 empty
+
+Class __locale_struct
+ size=232 align=8
+ base size=232 base align=8
+__locale_struct (0x0x7f234ddeb1e0) 0
+
+Class timeval
+ size=16 align=8
+ base size=16 base align=8
+timeval (0x0x7f234ddeb4e0) 0
+
+Class timespec
+ size=16 align=8
+ base size=16 base align=8
+timespec (0x0x7f234ddeb540) 0
+
+Class __pthread_rwlock_arch_t
+ size=56 align=8
+ base size=56 base align=8
+__pthread_rwlock_arch_t (0x0x7f234ddeb600) 0
+
+Class __pthread_internal_list
+ size=16 align=8
+ base size=16 base align=8
+__pthread_internal_list (0x0x7f234ddeb660) 0
+
+Class __pthread_mutex_s
+ size=40 align=8
+ base size=40 base align=8
+__pthread_mutex_s (0x0x7f234ddeb6c0) 0
+
+Class __pthread_cond_s
+ size=48 align=8
+ base size=48 base align=8
+__pthread_cond_s (0x0x7f234ddeb720) 0
+
+Class pthread_attr_t
+ size=56 align=8
+ base size=56 base align=8
+pthread_attr_t (0x0x7f234ddeb9c0) 0
+
+Class random_data
+ size=48 align=8
+ base size=48 base align=8
+random_data (0x0x7f234ddebc60) 0
+
+Class drand48_data
+ size=24 align=8
+ base size=24 base align=8
+drand48_data (0x0x7f234ddebcc0) 0
+
+Vtable for std::exception
+std::exception::_ZTVSt9exception: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt9exception)
+16 (int (*)(...))std::exception::~exception
+24 (int (*)(...))std::exception::~exception
+32 (int (*)(...))std::exception::what
+
+Class std::exception
+ size=8 align=8
+ base size=8 base align=8
+std::exception (0x0x7f234deb4a80) 0 nearly-empty
+ vptr=((& std::exception::_ZTVSt9exception) + 16)
+
+Vtable for std::bad_exception
+std::bad_exception::_ZTVSt13bad_exception: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt13bad_exception)
+16 (int (*)(...))std::bad_exception::~bad_exception
+24 (int (*)(...))std::bad_exception::~bad_exception
+32 (int (*)(...))std::bad_exception::what
+
+Class std::bad_exception
+ size=8 align=8
+ base size=8 base align=8
+std::bad_exception (0x0x7f234dfa7bc8) 0 nearly-empty
+ vptr=((& std::bad_exception::_ZTVSt13bad_exception) + 16)
+ std::exception (0x0x7f234deb4c60) 0 nearly-empty
+ primary-for std::bad_exception (0x0x7f234dfa7bc8)
+
+Vtable for std::type_info
+std::type_info::_ZTVSt9type_info: 8 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt9type_info)
+16 (int (*)(...))std::type_info::~type_info
+24 (int (*)(...))std::type_info::~type_info
+32 (int (*)(...))std::type_info::__is_pointer_p
+40 (int (*)(...))std::type_info::__is_function_p
+48 (int (*)(...))std::type_info::__do_catch
+56 (int (*)(...))std::type_info::__do_upcast
+
+Class std::type_info
+ size=16 align=8
+ base size=16 base align=8
+std::type_info (0x0x7f234deb4e40) 0
+ vptr=((& std::type_info::_ZTVSt9type_info) + 16)
+
+Vtable for std::bad_cast
+std::bad_cast::_ZTVSt8bad_cast: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt8bad_cast)
+16 (int (*)(...))std::bad_cast::~bad_cast
+24 (int (*)(...))std::bad_cast::~bad_cast
+32 (int (*)(...))std::bad_cast::what
+
+Class std::bad_cast
+ size=8 align=8
+ base size=8 base align=8
+std::bad_cast (0x0x7f234dfa7c30) 0 nearly-empty
+ vptr=((& std::bad_cast::_ZTVSt8bad_cast) + 16)
+ std::exception (0x0x7f234deeb240) 0 nearly-empty
+ primary-for std::bad_cast (0x0x7f234dfa7c30)
+
+Vtable for std::bad_typeid
+std::bad_typeid::_ZTVSt10bad_typeid: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt10bad_typeid)
+16 (int (*)(...))std::bad_typeid::~bad_typeid
+24 (int (*)(...))std::bad_typeid::~bad_typeid
+32 (int (*)(...))std::bad_typeid::what
+
+Class std::bad_typeid
+ size=8 align=8
+ base size=8 base align=8
+std::bad_typeid (0x0x7f234dfa7c98) 0 nearly-empty
+ vptr=((& std::bad_typeid::_ZTVSt10bad_typeid) + 16)
+ std::exception (0x0x7f234deeb420) 0 nearly-empty
+ primary-for std::bad_typeid (0x0x7f234dfa7c98)
+
+Class std::__exception_ptr::exception_ptr
+ size=8 align=8
+ base size=8 base align=8
+std::__exception_ptr::exception_ptr (0x0x7f234deeb600) 0
+
+Vtable for std::nested_exception
+std::nested_exception::_ZTVSt16nested_exception: 4 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt16nested_exception)
+16 (int (*)(...))std::nested_exception::~nested_exception
+24 (int (*)(...))std::nested_exception::~nested_exception
+
+Class std::nested_exception
+ size=16 align=8
+ base size=16 base align=8
+std::nested_exception (0x0x7f234deebba0) 0
+ vptr=((& std::nested_exception::_ZTVSt16nested_exception) + 16)
+
+Vtable for std::bad_alloc
+std::bad_alloc::_ZTVSt9bad_alloc: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt9bad_alloc)
+16 (int (*)(...))std::bad_alloc::~bad_alloc
+24 (int (*)(...))std::bad_alloc::~bad_alloc
+32 (int (*)(...))std::bad_alloc::what
+
+Class std::bad_alloc
+ size=8 align=8
+ base size=8 base align=8
+std::bad_alloc (0x0x7f234dfa7d00) 0 nearly-empty
+ vptr=((& std::bad_alloc::_ZTVSt9bad_alloc) + 16)
+ std::exception (0x0x7f234df1a2a0) 0 nearly-empty
+ primary-for std::bad_alloc (0x0x7f234dfa7d00)
+
+Vtable for std::bad_array_new_length
+std::bad_array_new_length::_ZTVSt20bad_array_new_length: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt20bad_array_new_length)
+16 (int (*)(...))std::bad_array_new_length::~bad_array_new_length
+24 (int (*)(...))std::bad_array_new_length::~bad_array_new_length
+32 (int (*)(...))std::bad_array_new_length::what
+
+Class std::bad_array_new_length
+ size=8 align=8
+ base size=8 base align=8
+std::bad_array_new_length (0x0x7f234dfa7d68) 0 nearly-empty
+ vptr=((& std::bad_array_new_length::_ZTVSt20bad_array_new_length) + 16)
+ std::bad_alloc (0x0x7f234dfa7dd0) 0 nearly-empty
+ primary-for std::bad_array_new_length (0x0x7f234dfa7d68)
+ std::exception (0x0x7f234df1a480) 0 nearly-empty
+ primary-for std::bad_alloc (0x0x7f234dfa7dd0)
+
+Class std::nothrow_t
+ size=1 align=1
+ base size=0 base align=1
+std::nothrow_t (0x0x7f234df1a660) 0 empty
+
+Class std::__allocator_traits_base
+ size=1 align=1
+ base size=0 base align=1
+std::__allocator_traits_base (0x0x7f234df1a840) 0 empty
+
+Class std::__numeric_limits_base
+ size=1 align=1
+ base size=0 base align=1
+std::__numeric_limits_base (0x0x7f234db97d20) 0 empty
+
+Class qIsNull(double)::U
+ size=8 align=8
+ base size=8 base align=8
+qIsNull(double)::U (0x0x7f234d9737e0) 0
+
+Class qIsNull(float)::U
+ size=4 align=4
+ base size=4 base align=4
+qIsNull(float)::U (0x0x7f234d9738a0) 0
+
+Class QSysInfo
+ size=1 align=1
+ base size=0 base align=1
+QSysInfo (0x0x7f234d834240) 0 empty
+
+Class QMessageLogContext
+ size=32 align=8
+ base size=32 base align=8
+QMessageLogContext (0x0x7f234d834360) 0
+
+Class QMessageLogger
+ size=32 align=8
+ base size=32 base align=8
+QMessageLogger (0x0x7f234d8346c0) 0
+
+Class QFlag
+ size=4 align=4
+ base size=4 base align=4
+QFlag (0x0x7f234d834c00) 0
+
+Class QIncompatibleFlag
+ size=4 align=4
+ base size=4 base align=4
+QIncompatibleFlag (0x0x7f234d8ad3c0) 0
+
+Class std::__atomic_flag_base
+ size=1 align=1
+ base size=1 base align=1
+std::__atomic_flag_base (0x0x7f234d5437e0) 0
+
+Class std::atomic_flag
+ size=1 align=1
+ base size=1 base align=1
+std::atomic_flag (0x0x7f234d8e5c30) 0
+ std::__atomic_flag_base (0x0x7f234d543840) 0
+
+Class QAtomicInt
+ size=4 align=4
+ base size=4 base align=4
+QAtomicInt (0x0x7f234d7273a8) 0
+ QAtomicInteger<int> (0x0x7f234d727410) 0
+ QBasicAtomicInteger<int> (0x0x7f234d4787e0) 0
+
+Class QInternal
+ size=1 align=1
+ base size=0 base align=1
+QInternal (0x0x7f234d090ae0) 0 empty
+
+Class QtPrivate::QSlotObjectBase
+ size=16 align=8
+ base size=16 base align=8
+QtPrivate::QSlotObjectBase (0x0x7f234d1010c0) 0
+
+Class QGenericArgument
+ size=16 align=8
+ base size=16 base align=8
+QGenericArgument (0x0x7f234d1017e0) 0
+
+Class QGenericReturnArgument
+ size=16 align=8
+ base size=16 base align=8
+QGenericReturnArgument (0x0x7f234d240f70) 0
+ QGenericArgument (0x0x7f234d101a80) 0
+
+Class QMetaObject
+ size=48 align=8
+ base size=48 base align=8
+QMetaObject (0x0x7f234d101ea0) 0
+
+Class QMetaObject::Connection
+ size=8 align=8
+ base size=8 base align=8
+QMetaObject::Connection (0x0x7f234cd60300) 0
+
+Class QLatin1Char
+ size=1 align=1
+ base size=1 base align=1
+QLatin1Char (0x0x7f234cdc6de0) 0
+
+Class QChar
+ size=2 align=2
+ base size=2 base align=2
+QChar (0x0x7f234cde60c0) 0
+
+Class QtPrivate::RefCount
+ size=4 align=4
+ base size=4 base align=4
+QtPrivate::RefCount (0x0x7f234ce96ea0) 0
+
+Class QArrayData
+ size=24 align=8
+ base size=24 base align=8
+QArrayData (0x0x7f234ceb8240) 0
+
+Class QtPrivate::QContainerImplHelper
+ size=1 align=1
+ base size=0 base align=1
+QtPrivate::QContainerImplHelper (0x0x7f234cf1b540) 0 empty
+
+Class lconv
+ size=96 align=8
+ base size=96 base align=8
+lconv (0x0x7f234cbccd80) 0
+
+Vtable for __cxxabiv1::__forced_unwind
+__cxxabiv1::__forced_unwind::_ZTVN10__cxxabiv115__forced_unwindE: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTIN10__cxxabiv115__forced_unwindE)
+16 0
+24 0
+32 (int (*)(...))__cxa_pure_virtual
+
+Class __cxxabiv1::__forced_unwind
+ size=8 align=8
+ base size=8 base align=8
+__cxxabiv1::__forced_unwind (0x0x7f234cbcce40) 0 nearly-empty
+ vptr=((& __cxxabiv1::__forced_unwind::_ZTVN10__cxxabiv115__forced_unwindE) + 16)
+
+Class sched_param
+ size=4 align=4
+ base size=4 base align=4
+sched_param (0x0x7f234cc8ff60) 0
+
+Class timex
+ size=208 align=8
+ base size=208 base align=8
+timex (0x0x7f234ccc0060) 0
+
+Class tm
+ size=56 align=8
+ base size=56 base align=8
+tm (0x0x7f234ccc00c0) 0
+
+Class itimerspec
+ size=32 align=8
+ base size=32 base align=8
+itimerspec (0x0x7f234ccc0120) 0
+
+Class _pthread_cleanup_buffer
+ size=32 align=8
+ base size=32 base align=8
+_pthread_cleanup_buffer (0x0x7f234ccc0180) 0
+
+Class __pthread_cleanup_frame
+ size=24 align=8
+ base size=24 base align=8
+__pthread_cleanup_frame (0x0x7f234ccc02a0) 0
+
+Class __pthread_cleanup_class
+ size=24 align=8
+ base size=24 base align=8
+__pthread_cleanup_class (0x0x7f234ccc0300) 0
+
+Class _IO_marker
+ size=24 align=8
+ base size=24 base align=8
+_IO_marker (0x0x7f234ca022a0) 0
+
+Class _IO_FILE
+ size=216 align=8
+ base size=216 base align=8
+_IO_FILE (0x0x7f234ca02300) 0
+
+Class std::_Hash_impl
+ size=1 align=1
+ base size=0 base align=1
+std::_Hash_impl (0x0x7f234c7b7360) 0 empty
+
+Class std::_Fnv_hash_impl
+ size=1 align=1
+ base size=0 base align=1
+std::_Fnv_hash_impl (0x0x7f234c7b74e0) 0 empty
+
+Class std::locale
+ size=8 align=8
+ base size=8 base align=8
+std::locale (0x0x7f234c933660) 0
+
+Vtable for std::locale::facet
+std::locale::facet::_ZTVNSt6locale5facetE: 4 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTINSt6locale5facetE)
+16 (int (*)(...))std::locale::facet::~facet
+24 (int (*)(...))std::locale::facet::~facet
+
+Class std::locale::facet
+ size=16 align=8
+ base size=12 base align=8
+std::locale::facet (0x0x7f234c933a20) 0
+ vptr=((& std::locale::facet::_ZTVNSt6locale5facetE) + 16)
+
+Class std::locale::id
+ size=8 align=8
+ base size=8 base align=8
+std::locale::id (0x0x7f234c933cc0) 0
+
+Class std::locale::_Impl
+ size=40 align=8
+ base size=40 base align=8
+std::locale::_Impl (0x0x7f234c933ea0) 0
+
+Class std::__cow_string
+ size=8 align=8
+ base size=8 base align=8
+std::__cow_string (0x0x7f234c588ea0) 0
+
+Vtable for std::logic_error
+std::logic_error::_ZTVSt11logic_error: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt11logic_error)
+16 (int (*)(...))std::logic_error::~logic_error
+24 (int (*)(...))std::logic_error::~logic_error
+32 (int (*)(...))std::logic_error::what
+
+Class std::logic_error
+ size=16 align=8
+ base size=16 base align=8
+std::logic_error (0x0x7f234c7b8958) 0
+ vptr=((& std::logic_error::_ZTVSt11logic_error) + 16)
+ std::exception (0x0x7f234c588f60) 0 nearly-empty
+ primary-for std::logic_error (0x0x7f234c7b8958)
+
+Vtable for std::domain_error
+std::domain_error::_ZTVSt12domain_error: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt12domain_error)
+16 (int (*)(...))std::domain_error::~domain_error
+24 (int (*)(...))std::domain_error::~domain_error
+32 (int (*)(...))std::logic_error::what
+
+Class std::domain_error
+ size=16 align=8
+ base size=16 base align=8
+std::domain_error (0x0x7f234c7b89c0) 0
+ vptr=((& std::domain_error::_ZTVSt12domain_error) + 16)
+ std::logic_error (0x0x7f234c5d9000) 0
+ primary-for std::domain_error (0x0x7f234c7b89c0)
+ std::exception (0x0x7f234c5da000) 0 nearly-empty
+ primary-for std::logic_error (0x0x7f234c5d9000)
+
+Vtable for std::invalid_argument
+std::invalid_argument::_ZTVSt16invalid_argument: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt16invalid_argument)
+16 (int (*)(...))std::invalid_argument::~invalid_argument
+24 (int (*)(...))std::invalid_argument::~invalid_argument
+32 (int (*)(...))std::logic_error::what
+
+Class std::invalid_argument
+ size=16 align=8
+ base size=16 base align=8
+std::invalid_argument (0x0x7f234c5d9068) 0
+ vptr=((& std::invalid_argument::_ZTVSt16invalid_argument) + 16)
+ std::logic_error (0x0x7f234c5d90d0) 0
+ primary-for std::invalid_argument (0x0x7f234c5d9068)
+ std::exception (0x0x7f234c5da060) 0 nearly-empty
+ primary-for std::logic_error (0x0x7f234c5d90d0)
+
+Vtable for std::length_error
+std::length_error::_ZTVSt12length_error: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt12length_error)
+16 (int (*)(...))std::length_error::~length_error
+24 (int (*)(...))std::length_error::~length_error
+32 (int (*)(...))std::logic_error::what
+
+Class std::length_error
+ size=16 align=8
+ base size=16 base align=8
+std::length_error (0x0x7f234c5d9138) 0
+ vptr=((& std::length_error::_ZTVSt12length_error) + 16)
+ std::logic_error (0x0x7f234c5d91a0) 0
+ primary-for std::length_error (0x0x7f234c5d9138)
+ std::exception (0x0x7f234c5da0c0) 0 nearly-empty
+ primary-for std::logic_error (0x0x7f234c5d91a0)
+
+Vtable for std::out_of_range
+std::out_of_range::_ZTVSt12out_of_range: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt12out_of_range)
+16 (int (*)(...))std::out_of_range::~out_of_range
+24 (int (*)(...))std::out_of_range::~out_of_range
+32 (int (*)(...))std::logic_error::what
+
+Class std::out_of_range
+ size=16 align=8
+ base size=16 base align=8
+std::out_of_range (0x0x7f234c5d9208) 0
+ vptr=((& std::out_of_range::_ZTVSt12out_of_range) + 16)
+ std::logic_error (0x0x7f234c5d9270) 0
+ primary-for std::out_of_range (0x0x7f234c5d9208)
+ std::exception (0x0x7f234c5da120) 0 nearly-empty
+ primary-for std::logic_error (0x0x7f234c5d9270)
+
+Vtable for std::runtime_error
+std::runtime_error::_ZTVSt13runtime_error: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt13runtime_error)
+16 (int (*)(...))std::runtime_error::~runtime_error
+24 (int (*)(...))std::runtime_error::~runtime_error
+32 (int (*)(...))std::runtime_error::what
+
+Class std::runtime_error
+ size=16 align=8
+ base size=16 base align=8
+std::runtime_error (0x0x7f234c5d92d8) 0
+ vptr=((& std::runtime_error::_ZTVSt13runtime_error) + 16)
+ std::exception (0x0x7f234c5da180) 0 nearly-empty
+ primary-for std::runtime_error (0x0x7f234c5d92d8)
+
+Vtable for std::range_error
+std::range_error::_ZTVSt11range_error: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt11range_error)
+16 (int (*)(...))std::range_error::~range_error
+24 (int (*)(...))std::range_error::~range_error
+32 (int (*)(...))std::runtime_error::what
+
+Class std::range_error
+ size=16 align=8
+ base size=16 base align=8
+std::range_error (0x0x7f234c5d9340) 0
+ vptr=((& std::range_error::_ZTVSt11range_error) + 16)
+ std::runtime_error (0x0x7f234c5d93a8) 0
+ primary-for std::range_error (0x0x7f234c5d9340)
+ std::exception (0x0x7f234c5da1e0) 0 nearly-empty
+ primary-for std::runtime_error (0x0x7f234c5d93a8)
+
+Vtable for std::overflow_error
+std::overflow_error::_ZTVSt14overflow_error: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt14overflow_error)
+16 (int (*)(...))std::overflow_error::~overflow_error
+24 (int (*)(...))std::overflow_error::~overflow_error
+32 (int (*)(...))std::runtime_error::what
+
+Class std::overflow_error
+ size=16 align=8
+ base size=16 base align=8
+std::overflow_error (0x0x7f234c5d9410) 0
+ vptr=((& std::overflow_error::_ZTVSt14overflow_error) + 16)
+ std::runtime_error (0x0x7f234c5d9478) 0
+ primary-for std::overflow_error (0x0x7f234c5d9410)
+ std::exception (0x0x7f234c5da240) 0 nearly-empty
+ primary-for std::runtime_error (0x0x7f234c5d9478)
+
+Vtable for std::underflow_error
+std::underflow_error::_ZTVSt15underflow_error: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt15underflow_error)
+16 (int (*)(...))std::underflow_error::~underflow_error
+24 (int (*)(...))std::underflow_error::~underflow_error
+32 (int (*)(...))std::runtime_error::what
+
+Class std::underflow_error
+ size=16 align=8
+ base size=16 base align=8
+std::underflow_error (0x0x7f234c5d94e0) 0
+ vptr=((& std::underflow_error::_ZTVSt15underflow_error) + 16)
+ std::runtime_error (0x0x7f234c5d9548) 0
+ primary-for std::underflow_error (0x0x7f234c5d94e0)
+ std::exception (0x0x7f234c5da2a0) 0 nearly-empty
+ primary-for std::runtime_error (0x0x7f234c5d9548)
+
+Vtable for std::_V2::error_category
+std::_V2::error_category::_ZTVNSt3_V214error_categoryE: 10 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTINSt3_V214error_categoryE)
+16 0
+24 0
+32 (int (*)(...))__cxa_pure_virtual
+40 (int (*)(...))std::_V2::error_category::_M_message
+48 (int (*)(...))__cxa_pure_virtual
+56 (int (*)(...))std::_V2::error_category::default_error_condition
+64 (int (*)(...))std::_V2::error_category::equivalent
+72 (int (*)(...))std::_V2::error_category::equivalent
+
+Class std::_V2::error_category
+ size=8 align=8
+ base size=8 base align=8
+std::_V2::error_category (0x0x7f234c5da420) 0 nearly-empty
+ vptr=((& std::_V2::error_category::_ZTVNSt3_V214error_categoryE) + 16)
+
+Class std::error_code
+ size=16 align=8
+ base size=16 base align=8
+std::error_code (0x0x7f234c5da780) 0
+
+Class std::error_condition
+ size=16 align=8
+ base size=16 base align=8
+std::error_condition (0x0x7f234c623000) 0
+
+Vtable for std::system_error
+std::system_error::_ZTVSt12system_error: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt12system_error)
+16 (int (*)(...))std::system_error::~system_error
+24 (int (*)(...))std::system_error::~system_error
+32 (int (*)(...))std::runtime_error::what
+
+Class std::system_error
+ size=32 align=8
+ base size=32 base align=8
+std::system_error (0x0x7f234c5d9958) 0
+ vptr=((& std::system_error::_ZTVSt12system_error) + 16)
+ std::runtime_error (0x0x7f234c5d99c0) 0
+ primary-for std::system_error (0x0x7f234c5d9958)
+ std::exception (0x0x7f234c623ba0) 0 nearly-empty
+ primary-for std::runtime_error (0x0x7f234c5d99c0)
+
+Vtable for std::ios_base::failure
+std::ios_base::failure::_ZTVNSt8ios_base7failureB5cxx11E: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTINSt8ios_base7failureB5cxx11E)
+16 (int (*)(...))std::ios_base::failure::~failure
+24 (int (*)(...))std::ios_base::failure::~failure
+32 (int (*)(...))std::ios_base::failure::what
+
+Class std::ios_base::failure
+ size=32 align=8
+ base size=32 base align=8
+std::ios_base::failure (0x0x7f234c5d9c30) 0
+ vptr=((& std::ios_base::failure::_ZTVNSt8ios_base7failureB5cxx11E) + 16)
+ std::system_error (0x0x7f234c5d9c98) 0
+ primary-for std::ios_base::failure (0x0x7f234c5d9c30)
+ std::runtime_error (0x0x7f234c5d9d00) 0
+ primary-for std::system_error (0x0x7f234c5d9c98)
+ std::exception (0x0x7f234c680180) 0 nearly-empty
+ primary-for std::runtime_error (0x0x7f234c5d9d00)
+
+Class std::ios_base::_Callback_list
+ size=24 align=8
+ base size=24 base align=8
+std::ios_base::_Callback_list (0x0x7f234c6801e0) 0
+
+Class std::ios_base::_Words
+ size=16 align=8
+ base size=16 base align=8
+std::ios_base::_Words (0x0x7f234c680240) 0
+
+Class std::ios_base::Init
+ size=1 align=1
+ base size=0 base align=1
+std::ios_base::Init (0x0x7f234c6802a0) 0 empty
+
+Vtable for std::ios_base
+std::ios_base::_ZTVSt8ios_base: 4 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt8ios_base)
+16 (int (*)(...))std::ios_base::~ios_base
+24 (int (*)(...))std::ios_base::~ios_base
+
+Class std::ios_base
+ size=216 align=8
+ base size=216 base align=8
+std::ios_base (0x0x7f234c680120) 0
+ vptr=((& std::ios_base::_ZTVSt8ios_base) + 16)
+
+Class std::ctype_base
+ size=1 align=1
+ base size=0 base align=1
+std::ctype_base (0x0x7f234c34cba0) 0 empty
+
+Class std::__num_base
+ size=1 align=1
+ base size=0 base align=1
+std::__num_base (0x0x7f234c3f4d80) 0 empty
+
+VTT for std::basic_ostream<char>
+std::basic_ostream<char>::_ZTTSo: 2 entries
+0 ((& std::basic_ostream<char>::_ZTVSo) + 24)
+8 ((& std::basic_ostream<char>::_ZTVSo) + 64)
+
+VTT for std::basic_ostream<wchar_t>
+std::basic_ostream<wchar_t>::_ZTTSt13basic_ostreamIwSt11char_traitsIwEE: 2 entries
+0 ((& std::basic_ostream<wchar_t>::_ZTVSt13basic_ostreamIwSt11char_traitsIwEE) + 24)
+8 ((& std::basic_ostream<wchar_t>::_ZTVSt13basic_ostreamIwSt11char_traitsIwEE) + 64)
+
+VTT for std::basic_istream<char>
+std::basic_istream<char>::_ZTTSi: 2 entries
+0 ((& std::basic_istream<char>::_ZTVSi) + 24)
+8 ((& std::basic_istream<char>::_ZTVSi) + 64)
+
+VTT for std::basic_istream<wchar_t>
+std::basic_istream<wchar_t>::_ZTTSt13basic_istreamIwSt11char_traitsIwEE: 2 entries
+0 ((& std::basic_istream<wchar_t>::_ZTVSt13basic_istreamIwSt11char_traitsIwEE) + 24)
+8 ((& std::basic_istream<wchar_t>::_ZTVSt13basic_istreamIwSt11char_traitsIwEE) + 64)
+
+Construction vtable for std::basic_istream<char> (0x0x7f234bfc9410 instance) in std::basic_iostream<char>
+std::basic_iostream<char>::_ZTCSd0_Si: 10 entries
+0 24
+8 (int (*)(...))0
+16 (int (*)(...))(& _ZTISi)
+24 0
+32 0
+40 18446744073709551592
+48 (int (*)(...))-24
+56 (int (*)(...))(& _ZTISi)
+64 0
+72 0
+
+Construction vtable for std::basic_ostream<char> (0x0x7f234bfc94e0 instance) in std::basic_iostream<char>
+std::basic_iostream<char>::_ZTCSd16_So: 10 entries
+0 8
+8 (int (*)(...))0
+16 (int (*)(...))(& _ZTISo)
+24 0
+32 0
+40 18446744073709551608
+48 (int (*)(...))-8
+56 (int (*)(...))(& _ZTISo)
+64 0
+72 0
+
+VTT for std::basic_iostream<char>
+std::basic_iostream<char>::_ZTTSd: 7 entries
+0 ((& std::basic_iostream<char>::_ZTVSd) + 24)
+8 ((& std::basic_iostream<char>::_ZTCSd0_Si) + 24)
+16 ((& std::basic_iostream<char>::_ZTCSd0_Si) + 64)
+24 ((& std::basic_iostream<char>::_ZTCSd16_So) + 24)
+32 ((& std::basic_iostream<char>::_ZTCSd16_So) + 64)
+40 ((& std::basic_iostream<char>::_ZTVSd) + 104)
+48 ((& std::basic_iostream<char>::_ZTVSd) + 64)
+
+Construction vtable for std::basic_istream<wchar_t> (0x0x7f234c00b1a0 instance) in std::basic_iostream<wchar_t>
+std::basic_iostream<wchar_t>::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE0_St13basic_istreamIwS1_E: 10 entries
+0 24
+8 (int (*)(...))0
+16 (int (*)(...))(& _ZTISt13basic_istreamIwSt11char_traitsIwEE)
+24 0
+32 0
+40 18446744073709551592
+48 (int (*)(...))-24
+56 (int (*)(...))(& _ZTISt13basic_istreamIwSt11char_traitsIwEE)
+64 0
+72 0
+
+Construction vtable for std::basic_ostream<wchar_t> (0x0x7f234c00b270 instance) in std::basic_iostream<wchar_t>
+std::basic_iostream<wchar_t>::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE16_St13basic_ostreamIwS1_E: 10 entries
+0 8
+8 (int (*)(...))0
+16 (int (*)(...))(& _ZTISt13basic_ostreamIwSt11char_traitsIwEE)
+24 0
+32 0
+40 18446744073709551608
+48 (int (*)(...))-8
+56 (int (*)(...))(& _ZTISt13basic_ostreamIwSt11char_traitsIwEE)
+64 0
+72 0
+
+VTT for std::basic_iostream<wchar_t>
+std::basic_iostream<wchar_t>::_ZTTSt14basic_iostreamIwSt11char_traitsIwEE: 7 entries
+0 ((& std::basic_iostream<wchar_t>::_ZTVSt14basic_iostreamIwSt11char_traitsIwEE) + 24)
+8 ((& std::basic_iostream<wchar_t>::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE0_St13basic_istreamIwS1_E) + 24)
+16 ((& std::basic_iostream<wchar_t>::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE0_St13basic_istreamIwS1_E) + 64)
+24 ((& std::basic_iostream<wchar_t>::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE16_St13basic_ostreamIwS1_E) + 24)
+32 ((& std::basic_iostream<wchar_t>::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE16_St13basic_ostreamIwS1_E) + 64)
+40 ((& std::basic_iostream<wchar_t>::_ZTVSt14basic_iostreamIwSt11char_traitsIwEE) + 104)
+48 ((& std::basic_iostream<wchar_t>::_ZTVSt14basic_iostreamIwSt11char_traitsIwEE) + 64)
+
+Class QByteArrayDataPtr
+ size=8 align=8
+ base size=8 base align=8
+QByteArrayDataPtr (0x0x7f234c02b720) 0
+
+Class QByteArray
+ size=8 align=8
+ base size=8 base align=8
+QByteArray (0x0x7f234c02b780) 0
+
+Class QByteRef
+ size=16 align=8
+ base size=12 base align=8
+QByteRef (0x0x7f234bd47b40) 0
+
+Class QStringDataPtr
+ size=8 align=8
+ base size=8 base align=8
+QStringDataPtr (0x0x7f234bdec9c0) 0
+
+Class QStringView
+ size=16 align=8
+ base size=16 base align=8
+QStringView (0x0x7f234bdece40) 0
+
+Class QLatin1String
+ size=16 align=8
+ base size=16 base align=8
+QLatin1String (0x0x7f234bebdc00) 0
+
+Class QString::Null
+ size=1 align=1
+ base size=0 base align=1
+QString::Null (0x0x7f234bb4d660) 0 empty
+
+Class QString
+ size=8 align=8
+ base size=8 base align=8
+QString (0x0x7f234bb4d600) 0
+
+Class QCharRef
+ size=16 align=8
+ base size=12 base align=8
+QCharRef (0x0x7f234b9317e0) 0
+
+Class QStringRef
+ size=16 align=8
+ base size=16 base align=8
+QStringRef (0x0x7f234bad2060) 0
+
+Class QtPrivate::QHashCombine
+ size=1 align=1
+ base size=0 base align=1
+QtPrivate::QHashCombine (0x0x7f234b8eb360) 0 empty
+
+Class QtPrivate::QHashCombineCommutative
+ size=1 align=1
+ base size=0 base align=1
+QtPrivate::QHashCombineCommutative (0x0x7f234b8eb420) 0 empty
+
+Class std::_Bit_reference
+ size=16 align=8
+ base size=16 base align=8
+std::_Bit_reference (0x0x7f234b59a900) 0
+
+Class std::_Bit_iterator_base
+ size=16 align=8
+ base size=12 base align=8
+std::_Bit_iterator_base (0x0x7f234b8bf5b0) 0
+ std::iterator<std::random_access_iterator_tag, bool> (0x0x7f234b5bd060) 0 empty
+
+Class std::_Bit_iterator
+ size=16 align=8
+ base size=12 base align=8
+std::_Bit_iterator (0x0x7f234b8bf6e8) 0
+ std::_Bit_iterator_base (0x0x7f234b8bf750) 0
+ std::iterator<std::random_access_iterator_tag, bool> (0x0x7f234b5bd6c0) 0 empty
+
+Class std::_Bit_const_iterator
+ size=16 align=8
+ base size=12 base align=8
+std::_Bit_const_iterator (0x0x7f234b8bf7b8) 0
+ std::_Bit_iterator_base (0x0x7f234b8bf820) 0
+ std::iterator<std::random_access_iterator_tag, bool> (0x0x7f234b5bdea0) 0 empty
+
+Class std::__detail::_List_node_base
+ size=16 align=8
+ base size=16 base align=8
+std::__detail::_List_node_base (0x0x7f234b3d5540) 0
+
+Class QListData::NotArrayCompatibleLayout
+ size=1 align=1
+ base size=0 base align=1
+QListData::NotArrayCompatibleLayout (0x0x7f234b4a1300) 0 empty
+
+Class QListData::NotIndirectLayout
+ size=1 align=1
+ base size=0 base align=1
+QListData::NotIndirectLayout (0x0x7f234b4a1360) 0 empty
+
+Class QListData::ArrayCompatibleLayout
+ size=1 align=1
+ base size=1 base align=1
+QListData::ArrayCompatibleLayout (0x0x7f234b3aa270) 0 empty
+ QListData::NotIndirectLayout (0x0x7f234b4a13c0) 0 empty
+
+Class QListData::InlineWithPaddingLayout
+ size=1 align=1
+ base size=1 base align=1
+QListData::InlineWithPaddingLayout (0x0x7f234b32ee70) 0 empty
+ QListData::NotArrayCompatibleLayout (0x0x7f234b4a1420) 0 empty
+ QListData::NotIndirectLayout (0x0x7f234b4a1480) 0 empty
+
+Class QListData::IndirectLayout
+ size=1 align=1
+ base size=1 base align=1
+QListData::IndirectLayout (0x0x7f234b3aa2d8) 0 empty
+ QListData::NotArrayCompatibleLayout (0x0x7f234b4a14e0) 0 empty
+
+Class QListData::Data
+ size=24 align=8
+ base size=24 base align=8
+QListData::Data (0x0x7f234b4a1540) 0
+
+Class QListData
+ size=8 align=8
+ base size=8 base align=8
+QListData (0x0x7f234b4a12a0) 0
+
+Class QRegExp
+ size=8 align=8
+ base size=8 base align=8
+QRegExp (0x0x7f234b18e720) 0
+
+Class QStringMatcher::Data
+ size=272 align=8
+ base size=272 base align=8
+QStringMatcher::Data (0x0x7f234b26cd80) 0
+
+Class QStringMatcher
+ size=1048 align=8
+ base size=1048 base align=8
+QStringMatcher (0x0x7f234b26cd20) 0
+
+Class QStringList
+ size=8 align=8
+ base size=8 base align=8
+QStringList (0x0x7f234b2bb000) 0
+ QList<QString> (0x0x7f234b2bb068) 0
+ QListSpecialMethods<QString> (0x0x7f234b2bc000) 0 empty
+
+Class QScopedPointerPodDeleter
+ size=1 align=1
+ base size=0 base align=1
+QScopedPointerPodDeleter (0x0x7f234af29b40) 0 empty
+
+Class std::_Rb_tree_node_base
+ size=32 align=8
+ base size=32 base align=8
+std::_Rb_tree_node_base (0x0x7f234afbec60) 0
+
+Class std::_Rb_tree_header
+ size=40 align=8
+ base size=40 base align=8
+std::_Rb_tree_header (0x0x7f234afe1000) 0
+
+Class std::__erased_type
+ size=1 align=1
+ base size=0 base align=1
+std::__erased_type (0x0x7f234adb85a0) 0 empty
+
+Class std::allocator_arg_t
+ size=1 align=1
+ base size=0 base align=1
+std::allocator_arg_t (0x0x7f234adb8600) 0 empty
+
+Class std::__uses_alloc_base
+ size=1 align=1
+ base size=0 base align=1
+std::__uses_alloc_base (0x0x7f234adb8780) 0 empty
+
+Class std::__uses_alloc0::_Sink
+ size=1 align=1
+ base size=0 base align=1
+std::__uses_alloc0::_Sink (0x0x7f234adb8840) 0 empty
+
+Class std::__uses_alloc0
+ size=1 align=1
+ base size=1 base align=1
+std::__uses_alloc0 (0x0x7f234ad4b3a8) 0
+ std::__uses_alloc_base (0x0x7f234adb87e0) 0 empty
+
+Class std::_Swallow_assign
+ size=1 align=1
+ base size=0 base align=1
+std::_Swallow_assign (0x0x7f234aa9dba0) 0 empty
+
+Class QtPrivate::AbstractDebugStreamFunction
+ size=16 align=8
+ base size=16 base align=8
+QtPrivate::AbstractDebugStreamFunction (0x0x7f234ab63060) 0
+
+Class QtPrivate::AbstractComparatorFunction
+ size=24 align=8
+ base size=24 base align=8
+QtPrivate::AbstractComparatorFunction (0x0x7f234ab633c0) 0
+
+Class QtPrivate::AbstractConverterFunction
+ size=8 align=8
+ base size=8 base align=8
+QtPrivate::AbstractConverterFunction (0x0x7f234ab63900) 0
+
+Class QMetaType
+ size=80 align=8
+ base size=80 base align=8
+QMetaType (0x0x7f234ab63e40) 0
+
+Class QtMetaTypePrivate::VariantData
+ size=24 align=8
+ base size=20 base align=8
+QtMetaTypePrivate::VariantData (0x0x7f234abee060) 0
+
+Class QtMetaTypePrivate::VectorBoolElements
+ size=1 align=1
+ base size=0 base align=1
+QtMetaTypePrivate::VectorBoolElements (0x0x7f234abee720) 0 empty
+
+Class QtMetaTypePrivate::QSequentialIterableImpl
+ size=104 align=8
+ base size=104 base align=8
+QtMetaTypePrivate::QSequentialIterableImpl (0x0x7f234ac855a0) 0
+
+Class QtMetaTypePrivate::QAssociativeIterableImpl
+ size=112 align=8
+ base size=112 base align=8
+QtMetaTypePrivate::QAssociativeIterableImpl (0x0x7f234a8dcc60) 0
+
+Class QtMetaTypePrivate::QPairVariantInterfaceImpl
+ size=40 align=8
+ base size=40 base align=8
+QtMetaTypePrivate::QPairVariantInterfaceImpl (0x0x7f234a9551e0) 0
+
+Class std::chrono::_V2::system_clock
+ size=1 align=1
+ base size=0 base align=1
+std::chrono::_V2::system_clock (0x0x7f234a7a4000) 0 empty
+
+Class std::chrono::_V2::steady_clock
+ size=1 align=1
+ base size=0 base align=1
+std::chrono::_V2::steady_clock (0x0x7f234a49ea80) 0 empty
+
+Vtable for QObjectData
+QObjectData::_ZTV11QObjectData: 4 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI11QObjectData)
+16 (int (*)(...))__cxa_pure_virtual
+24 (int (*)(...))__cxa_pure_virtual
+
+Class QObjectData
+ size=48 align=8
+ base size=48 base align=8
+QObjectData (0x0x7f234a49eae0) 0
+ vptr=((& QObjectData::_ZTV11QObjectData) + 16)
+
+Class QObject::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QObject::QPrivateSignal (0x0x7f234a49ecc0) 0 empty
+
+Vtable for QObject
+QObject::_ZTV7QObject: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI7QObject)
+16 (int (*)(...))QObject::metaObject
+24 (int (*)(...))QObject::qt_metacast
+32 (int (*)(...))QObject::qt_metacall
+40 (int (*)(...))QObject::~QObject
+48 (int (*)(...))QObject::~QObject
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QObject
+ size=16 align=8
+ base size=16 base align=8
+QObject (0x0x7f234a49ec60) 0
+ vptr=((& QObject::_ZTV7QObject) + 16)
+
+Vtable for QObjectUserData
+QObjectUserData::_ZTV15QObjectUserData: 4 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI15QObjectUserData)
+16 (int (*)(...))QObjectUserData::~QObjectUserData
+24 (int (*)(...))QObjectUserData::~QObjectUserData
+
+Class QObjectUserData
+ size=8 align=8
+ base size=8 base align=8
+QObjectUserData (0x0x7f234a578ae0) 0 nearly-empty
+ vptr=((& QObjectUserData::_ZTV15QObjectUserData) + 16)
+
+Class QSignalBlocker
+ size=16 align=8
+ base size=10 base align=8
+QSignalBlocker (0x0x7f234a578c60) 0
+
+Class QAbstractAnimation::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QAbstractAnimation::QPrivateSignal (0x0x7f234a599540) 0 empty
+
+Vtable for QAbstractAnimation
+QAbstractAnimation::_ZTV18QAbstractAnimation: 18 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI18QAbstractAnimation)
+16 (int (*)(...))QAbstractAnimation::metaObject
+24 (int (*)(...))QAbstractAnimation::qt_metacast
+32 (int (*)(...))QAbstractAnimation::qt_metacall
+40 0
+48 0
+56 (int (*)(...))QAbstractAnimation::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))__cxa_pure_virtual
+120 (int (*)(...))__cxa_pure_virtual
+128 (int (*)(...))QAbstractAnimation::updateState
+136 (int (*)(...))QAbstractAnimation::updateDirection
+
+Class QAbstractAnimation
+ size=16 align=8
+ base size=16 base align=8
+QAbstractAnimation (0x0x7f234a57d5b0) 0
+ vptr=((& QAbstractAnimation::_ZTV18QAbstractAnimation) + 16)
+ QObject (0x0x7f234a5994e0) 0
+ primary-for QAbstractAnimation (0x0x7f234a57d5b0)
+
+Class QAnimationDriver::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QAnimationDriver::QPrivateSignal (0x0x7f234a599900) 0 empty
+
+Vtable for QAnimationDriver
+QAnimationDriver::_ZTV16QAnimationDriver: 18 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI16QAnimationDriver)
+16 (int (*)(...))QAnimationDriver::metaObject
+24 (int (*)(...))QAnimationDriver::qt_metacast
+32 (int (*)(...))QAnimationDriver::qt_metacall
+40 (int (*)(...))QAnimationDriver::~QAnimationDriver
+48 (int (*)(...))QAnimationDriver::~QAnimationDriver
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QAnimationDriver::advance
+120 (int (*)(...))QAnimationDriver::elapsed
+128 (int (*)(...))QAnimationDriver::start
+136 (int (*)(...))QAnimationDriver::stop
+
+Class QAnimationDriver
+ size=16 align=8
+ base size=16 base align=8
+QAnimationDriver (0x0x7f234a57d618) 0
+ vptr=((& QAnimationDriver::_ZTV16QAnimationDriver) + 16)
+ QObject (0x0x7f234a5998a0) 0
+ primary-for QAnimationDriver (0x0x7f234a57d618)
+
+Class QEventLoop::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QEventLoop::QPrivateSignal (0x0x7f234a599b40) 0 empty
+
+Vtable for QEventLoop
+QEventLoop::_ZTV10QEventLoop: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI10QEventLoop)
+16 (int (*)(...))QEventLoop::metaObject
+24 (int (*)(...))QEventLoop::qt_metacast
+32 (int (*)(...))QEventLoop::qt_metacall
+40 (int (*)(...))QEventLoop::~QEventLoop
+48 (int (*)(...))QEventLoop::~QEventLoop
+56 (int (*)(...))QEventLoop::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QEventLoop
+ size=16 align=8
+ base size=16 base align=8
+QEventLoop (0x0x7f234a57d680) 0
+ vptr=((& QEventLoop::_ZTV10QEventLoop) + 16)
+ QObject (0x0x7f234a599ae0) 0
+ primary-for QEventLoop (0x0x7f234a57d680)
+
+Class QEventLoopLocker
+ size=8 align=8
+ base size=8 base align=8
+QEventLoopLocker (0x0x7f234a5ef420) 0
+
+Class QAbstractEventDispatcher::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QAbstractEventDispatcher::QPrivateSignal (0x0x7f234a5ef4e0) 0 empty
+
+Class QAbstractEventDispatcher::TimerInfo
+ size=12 align=4
+ base size=12 base align=4
+QAbstractEventDispatcher::TimerInfo (0x0x7f234a5ef540) 0
+
+Vtable for QAbstractEventDispatcher
+QAbstractEventDispatcher::_ZTV24QAbstractEventDispatcher: 28 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI24QAbstractEventDispatcher)
+16 (int (*)(...))QAbstractEventDispatcher::metaObject
+24 (int (*)(...))QAbstractEventDispatcher::qt_metacast
+32 (int (*)(...))QAbstractEventDispatcher::qt_metacall
+40 0
+48 0
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))__cxa_pure_virtual
+120 (int (*)(...))__cxa_pure_virtual
+128 (int (*)(...))__cxa_pure_virtual
+136 (int (*)(...))__cxa_pure_virtual
+144 (int (*)(...))__cxa_pure_virtual
+152 (int (*)(...))__cxa_pure_virtual
+160 (int (*)(...))__cxa_pure_virtual
+168 (int (*)(...))__cxa_pure_virtual
+176 (int (*)(...))__cxa_pure_virtual
+184 (int (*)(...))__cxa_pure_virtual
+192 (int (*)(...))__cxa_pure_virtual
+200 (int (*)(...))__cxa_pure_virtual
+208 (int (*)(...))QAbstractEventDispatcher::startingUp
+216 (int (*)(...))QAbstractEventDispatcher::closingDown
+
+Class QAbstractEventDispatcher
+ size=16 align=8
+ base size=16 base align=8
+QAbstractEventDispatcher (0x0x7f234a57d7b8) 0
+ vptr=((& QAbstractEventDispatcher::_ZTV24QAbstractEventDispatcher) + 16)
+ QObject (0x0x7f234a5ef480) 0
+ primary-for QAbstractEventDispatcher (0x0x7f234a57d7b8)
+
+Vtable for std::bad_function_call
+std::bad_function_call::_ZTVSt17bad_function_call: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt17bad_function_call)
+16 (int (*)(...))std::bad_function_call::~bad_function_call
+24 (int (*)(...))std::bad_function_call::~bad_function_call
+32 (int (*)(...))std::bad_function_call::what
+
+Class std::bad_function_call
+ size=8 align=8
+ base size=8 base align=8
+std::bad_function_call (0x0x7f234a297138) 0 nearly-empty
+ vptr=((& std::bad_function_call::_ZTVSt17bad_function_call) + 16)
+ std::exception (0x0x7f234a27fba0) 0 nearly-empty
+ primary-for std::bad_function_call (0x0x7f234a297138)
+
+Class std::_Nocopy_types
+ size=16 align=8
+ base size=16 base align=8
+std::_Nocopy_types (0x0x7f234a27fc60) 0
+
+Class std::_Any_data
+ size=16 align=8
+ base size=16 base align=8
+std::_Any_data (0x0x7f234a27fcc0) 0
+
+Class std::_Function_base
+ size=24 align=8
+ base size=24 base align=8
+std::_Function_base (0x0x7f234a2b5000) 0
+
+Class QMapNodeBase
+ size=24 align=8
+ base size=24 base align=8
+QMapNodeBase (0x0x7f234a06bf60) 0
+
+Class QMapDataBase
+ size=40 align=8
+ base size=40 base align=8
+QMapDataBase (0x0x7f234a0b7c00) 0
+
+Class QHashData::Node
+ size=16 align=8
+ base size=16 base align=8
+QHashData::Node (0x0x7f234a1a75a0) 0
+
+Class QHashData
+ size=48 align=8
+ base size=44 base align=8
+QHashData (0x0x7f234a1a7540) 0
+
+Class QHashDummyValue
+ size=1 align=1
+ base size=0 base align=1
+QHashDummyValue (0x0x7f234a1a7840) 0 empty
+
+Class QVariant::PrivateShared
+ size=16 align=8
+ base size=12 base align=8
+QVariant::PrivateShared (0x0x7f2349eafde0) 0
+
+Class QVariant::Private::Data
+ size=8 align=8
+ base size=8 base align=8
+QVariant::Private::Data (0x0x7f2349eafea0) 0
+
+Class QVariant::Private
+ size=16 align=8
+ base size=12 base align=8
+QVariant::Private (0x0x7f2349eafe40) 0
+
+Class QVariant::Handler
+ size=72 align=8
+ base size=72 base align=8
+QVariant::Handler (0x0x7f2349eaff00) 0
+
+Class QVariant
+ size=16 align=8
+ base size=16 base align=8
+QVariant (0x0x7f2349eafd80) 0
+
+Class QVariantComparisonHelper
+ size=8 align=8
+ base size=8 base align=8
+QVariantComparisonHelper (0x0x7f2349c2b1e0) 0
+
+Class QSequentialIterable::const_iterator
+ size=112 align=8
+ base size=112 base align=8
+QSequentialIterable::const_iterator (0x0x7f2349c6d840) 0
+
+Class QSequentialIterable
+ size=104 align=8
+ base size=104 base align=8
+QSequentialIterable (0x0x7f2349c6d7e0) 0
+
+Class QAssociativeIterable::const_iterator
+ size=120 align=8
+ base size=120 base align=8
+QAssociativeIterable::const_iterator (0x0x7f2349c6d960) 0
+
+Class QAssociativeIterable
+ size=112 align=8
+ base size=112 base align=8
+QAssociativeIterable (0x0x7f2349c6d900) 0
+
+Class QModelIndex
+ size=24 align=8
+ base size=24 base align=8
+QModelIndex (0x0x7f2349d37ae0) 0
+
+Class QPersistentModelIndex
+ size=8 align=8
+ base size=8 base align=8
+QPersistentModelIndex (0x0x7f2349daa720) 0
+
+Class QAbstractItemModel::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QAbstractItemModel::QPrivateSignal (0x0x7f2349a7b540) 0 empty
+
+Vtable for QAbstractItemModel
+QAbstractItemModel::_ZTV18QAbstractItemModel: 48 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI18QAbstractItemModel)
+16 (int (*)(...))QAbstractItemModel::metaObject
+24 (int (*)(...))QAbstractItemModel::qt_metacast
+32 (int (*)(...))QAbstractItemModel::qt_metacall
+40 0
+48 0
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))__cxa_pure_virtual
+120 (int (*)(...))__cxa_pure_virtual
+128 (int (*)(...))QAbstractItemModel::sibling
+136 (int (*)(...))__cxa_pure_virtual
+144 (int (*)(...))__cxa_pure_virtual
+152 (int (*)(...))QAbstractItemModel::hasChildren
+160 (int (*)(...))__cxa_pure_virtual
+168 (int (*)(...))QAbstractItemModel::setData
+176 (int (*)(...))QAbstractItemModel::headerData
+184 (int (*)(...))QAbstractItemModel::setHeaderData
+192 (int (*)(...))QAbstractItemModel::itemData
+200 (int (*)(...))QAbstractItemModel::setItemData
+208 (int (*)(...))QAbstractItemModel::mimeTypes
+216 (int (*)(...))QAbstractItemModel::mimeData
+224 (int (*)(...))QAbstractItemModel::canDropMimeData
+232 (int (*)(...))QAbstractItemModel::dropMimeData
+240 (int (*)(...))QAbstractItemModel::supportedDropActions
+248 (int (*)(...))QAbstractItemModel::supportedDragActions
+256 (int (*)(...))QAbstractItemModel::insertRows
+264 (int (*)(...))QAbstractItemModel::insertColumns
+272 (int (*)(...))QAbstractItemModel::removeRows
+280 (int (*)(...))QAbstractItemModel::removeColumns
+288 (int (*)(...))QAbstractItemModel::moveRows
+296 (int (*)(...))QAbstractItemModel::moveColumns
+304 (int (*)(...))QAbstractItemModel::fetchMore
+312 (int (*)(...))QAbstractItemModel::canFetchMore
+320 (int (*)(...))QAbstractItemModel::flags
+328 (int (*)(...))QAbstractItemModel::sort
+336 (int (*)(...))QAbstractItemModel::buddy
+344 (int (*)(...))QAbstractItemModel::match
+352 (int (*)(...))QAbstractItemModel::span
+360 (int (*)(...))QAbstractItemModel::roleNames
+368 (int (*)(...))QAbstractItemModel::submit
+376 (int (*)(...))QAbstractItemModel::revert
+
+Class QAbstractItemModel
+ size=16 align=8
+ base size=16 base align=8
+QAbstractItemModel (0x0x7f2349a6cd00) 0
+ vptr=((& QAbstractItemModel::_ZTV18QAbstractItemModel) + 16)
+ QObject (0x0x7f2349a7b4e0) 0
+ primary-for QAbstractItemModel (0x0x7f2349a6cd00)
+
+Class QAbstractTableModel::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QAbstractTableModel::QPrivateSignal (0x0x7f2349b32900) 0 empty
+
+Vtable for QAbstractTableModel
+QAbstractTableModel::_ZTV19QAbstractTableModel: 48 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI19QAbstractTableModel)
+16 (int (*)(...))QAbstractTableModel::metaObject
+24 (int (*)(...))QAbstractTableModel::qt_metacast
+32 (int (*)(...))QAbstractTableModel::qt_metacall
+40 0
+48 0
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QAbstractTableModel::index
+120 (int (*)(...))QAbstractTableModel::parent
+128 (int (*)(...))QAbstractTableModel::sibling
+136 (int (*)(...))__cxa_pure_virtual
+144 (int (*)(...))__cxa_pure_virtual
+152 (int (*)(...))QAbstractTableModel::hasChildren
+160 (int (*)(...))__cxa_pure_virtual
+168 (int (*)(...))QAbstractItemModel::setData
+176 (int (*)(...))QAbstractItemModel::headerData
+184 (int (*)(...))QAbstractItemModel::setHeaderData
+192 (int (*)(...))QAbstractItemModel::itemData
+200 (int (*)(...))QAbstractItemModel::setItemData
+208 (int (*)(...))QAbstractItemModel::mimeTypes
+216 (int (*)(...))QAbstractItemModel::mimeData
+224 (int (*)(...))QAbstractItemModel::canDropMimeData
+232 (int (*)(...))QAbstractTableModel::dropMimeData
+240 (int (*)(...))QAbstractItemModel::supportedDropActions
+248 (int (*)(...))QAbstractItemModel::supportedDragActions
+256 (int (*)(...))QAbstractItemModel::insertRows
+264 (int (*)(...))QAbstractItemModel::insertColumns
+272 (int (*)(...))QAbstractItemModel::removeRows
+280 (int (*)(...))QAbstractItemModel::removeColumns
+288 (int (*)(...))QAbstractItemModel::moveRows
+296 (int (*)(...))QAbstractItemModel::moveColumns
+304 (int (*)(...))QAbstractItemModel::fetchMore
+312 (int (*)(...))QAbstractItemModel::canFetchMore
+320 (int (*)(...))QAbstractTableModel::flags
+328 (int (*)(...))QAbstractItemModel::sort
+336 (int (*)(...))QAbstractItemModel::buddy
+344 (int (*)(...))QAbstractItemModel::match
+352 (int (*)(...))QAbstractItemModel::span
+360 (int (*)(...))QAbstractItemModel::roleNames
+368 (int (*)(...))QAbstractItemModel::submit
+376 (int (*)(...))QAbstractItemModel::revert
+
+Class QAbstractTableModel
+ size=16 align=8
+ base size=16 base align=8
+QAbstractTableModel (0x0x7f2349ac2340) 0
+ vptr=((& QAbstractTableModel::_ZTV19QAbstractTableModel) + 16)
+ QAbstractItemModel (0x0x7f2349ac23a8) 0
+ primary-for QAbstractTableModel (0x0x7f2349ac2340)
+ QObject (0x0x7f2349b328a0) 0
+ primary-for QAbstractItemModel (0x0x7f2349ac23a8)
+
+Class QAbstractListModel::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QAbstractListModel::QPrivateSignal (0x0x7f2349b32a80) 0 empty
+
+Vtable for QAbstractListModel
+QAbstractListModel::_ZTV18QAbstractListModel: 48 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI18QAbstractListModel)
+16 (int (*)(...))QAbstractListModel::metaObject
+24 (int (*)(...))QAbstractListModel::qt_metacast
+32 (int (*)(...))QAbstractListModel::qt_metacall
+40 0
+48 0
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QAbstractListModel::index
+120 (int (*)(...))QAbstractListModel::parent
+128 (int (*)(...))QAbstractListModel::sibling
+136 (int (*)(...))__cxa_pure_virtual
+144 (int (*)(...))QAbstractListModel::columnCount
+152 (int (*)(...))QAbstractListModel::hasChildren
+160 (int (*)(...))__cxa_pure_virtual
+168 (int (*)(...))QAbstractItemModel::setData
+176 (int (*)(...))QAbstractItemModel::headerData
+184 (int (*)(...))QAbstractItemModel::setHeaderData
+192 (int (*)(...))QAbstractItemModel::itemData
+200 (int (*)(...))QAbstractItemModel::setItemData
+208 (int (*)(...))QAbstractItemModel::mimeTypes
+216 (int (*)(...))QAbstractItemModel::mimeData
+224 (int (*)(...))QAbstractItemModel::canDropMimeData
+232 (int (*)(...))QAbstractListModel::dropMimeData
+240 (int (*)(...))QAbstractItemModel::supportedDropActions
+248 (int (*)(...))QAbstractItemModel::supportedDragActions
+256 (int (*)(...))QAbstractItemModel::insertRows
+264 (int (*)(...))QAbstractItemModel::insertColumns
+272 (int (*)(...))QAbstractItemModel::removeRows
+280 (int (*)(...))QAbstractItemModel::removeColumns
+288 (int (*)(...))QAbstractItemModel::moveRows
+296 (int (*)(...))QAbstractItemModel::moveColumns
+304 (int (*)(...))QAbstractItemModel::fetchMore
+312 (int (*)(...))QAbstractItemModel::canFetchMore
+320 (int (*)(...))QAbstractListModel::flags
+328 (int (*)(...))QAbstractItemModel::sort
+336 (int (*)(...))QAbstractItemModel::buddy
+344 (int (*)(...))QAbstractItemModel::match
+352 (int (*)(...))QAbstractItemModel::span
+360 (int (*)(...))QAbstractItemModel::roleNames
+368 (int (*)(...))QAbstractItemModel::submit
+376 (int (*)(...))QAbstractItemModel::revert
+
+Class QAbstractListModel
+ size=16 align=8
+ base size=16 base align=8
+QAbstractListModel (0x0x7f2349ac2410) 0
+ vptr=((& QAbstractListModel::_ZTV18QAbstractListModel) + 16)
+ QAbstractItemModel (0x0x7f2349ac2478) 0
+ primary-for QAbstractListModel (0x0x7f2349ac2410)
+ QObject (0x0x7f2349b32a20) 0
+ primary-for QAbstractItemModel (0x0x7f2349ac2478)
+
+Vtable for QAbstractNativeEventFilter
+QAbstractNativeEventFilter::_ZTV26QAbstractNativeEventFilter: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI26QAbstractNativeEventFilter)
+16 0
+24 0
+32 (int (*)(...))__cxa_pure_virtual
+
+Class QAbstractNativeEventFilter
+ size=16 align=8
+ base size=16 base align=8
+QAbstractNativeEventFilter (0x0x7f2349b771e0) 0
+ vptr=((& QAbstractNativeEventFilter::_ZTV26QAbstractNativeEventFilter) + 16)
+
+Class QAbstractProxyModel::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QAbstractProxyModel::QPrivateSignal (0x0x7f2349b772a0) 0 empty
+
+Vtable for QAbstractProxyModel
+QAbstractProxyModel::_ZTV19QAbstractProxyModel: 53 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI19QAbstractProxyModel)
+16 (int (*)(...))QAbstractProxyModel::metaObject
+24 (int (*)(...))QAbstractProxyModel::qt_metacast
+32 (int (*)(...))QAbstractProxyModel::qt_metacall
+40 0
+48 0
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))__cxa_pure_virtual
+120 (int (*)(...))__cxa_pure_virtual
+128 (int (*)(...))QAbstractProxyModel::sibling
+136 (int (*)(...))__cxa_pure_virtual
+144 (int (*)(...))__cxa_pure_virtual
+152 (int (*)(...))QAbstractProxyModel::hasChildren
+160 (int (*)(...))QAbstractProxyModel::data
+168 (int (*)(...))QAbstractProxyModel::setData
+176 (int (*)(...))QAbstractProxyModel::headerData
+184 (int (*)(...))QAbstractProxyModel::setHeaderData
+192 (int (*)(...))QAbstractProxyModel::itemData
+200 (int (*)(...))QAbstractProxyModel::setItemData
+208 (int (*)(...))QAbstractProxyModel::mimeTypes
+216 (int (*)(...))QAbstractProxyModel::mimeData
+224 (int (*)(...))QAbstractProxyModel::canDropMimeData
+232 (int (*)(...))QAbstractProxyModel::dropMimeData
+240 (int (*)(...))QAbstractProxyModel::supportedDropActions
+248 (int (*)(...))QAbstractProxyModel::supportedDragActions
+256 (int (*)(...))QAbstractItemModel::insertRows
+264 (int (*)(...))QAbstractItemModel::insertColumns
+272 (int (*)(...))QAbstractItemModel::removeRows
+280 (int (*)(...))QAbstractItemModel::removeColumns
+288 (int (*)(...))QAbstractItemModel::moveRows
+296 (int (*)(...))QAbstractItemModel::moveColumns
+304 (int (*)(...))QAbstractProxyModel::fetchMore
+312 (int (*)(...))QAbstractProxyModel::canFetchMore
+320 (int (*)(...))QAbstractProxyModel::flags
+328 (int (*)(...))QAbstractProxyModel::sort
+336 (int (*)(...))QAbstractProxyModel::buddy
+344 (int (*)(...))QAbstractItemModel::match
+352 (int (*)(...))QAbstractProxyModel::span
+360 (int (*)(...))QAbstractItemModel::roleNames
+368 (int (*)(...))QAbstractProxyModel::submit
+376 (int (*)(...))QAbstractProxyModel::revert
+384 (int (*)(...))QAbstractProxyModel::setSourceModel
+392 (int (*)(...))__cxa_pure_virtual
+400 (int (*)(...))__cxa_pure_virtual
+408 (int (*)(...))QAbstractProxyModel::mapSelectionToSource
+416 (int (*)(...))QAbstractProxyModel::mapSelectionFromSource
+
+Class QAbstractProxyModel
+ size=16 align=8
+ base size=16 base align=8
+QAbstractProxyModel (0x0x7f2349ac2548) 0
+ vptr=((& QAbstractProxyModel::_ZTV19QAbstractProxyModel) + 16)
+ QAbstractItemModel (0x0x7f2349ac25b0) 0
+ primary-for QAbstractProxyModel (0x0x7f2349ac2548)
+ QObject (0x0x7f2349b77240) 0
+ primary-for QAbstractItemModel (0x0x7f2349ac25b0)
+
+Class QAbstractState::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QAbstractState::QPrivateSignal (0x0x7f2349b774e0) 0 empty
+
+Vtable for QAbstractState
+QAbstractState::_ZTV14QAbstractState: 16 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI14QAbstractState)
+16 (int (*)(...))QAbstractState::metaObject
+24 (int (*)(...))QAbstractState::qt_metacast
+32 (int (*)(...))QAbstractState::qt_metacall
+40 0
+48 0
+56 (int (*)(...))QAbstractState::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))__cxa_pure_virtual
+120 (int (*)(...))__cxa_pure_virtual
+
+Class QAbstractState
+ size=16 align=8
+ base size=16 base align=8
+QAbstractState (0x0x7f2349ac2618) 0
+ vptr=((& QAbstractState::_ZTV14QAbstractState) + 16)
+ QObject (0x0x7f2349b77480) 0
+ primary-for QAbstractState (0x0x7f2349ac2618)
+
+Class QAbstractTransition::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QAbstractTransition::QPrivateSignal (0x0x7f2349b77720) 0 empty
+
+Vtable for QAbstractTransition
+QAbstractTransition::_ZTV19QAbstractTransition: 16 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI19QAbstractTransition)
+16 (int (*)(...))QAbstractTransition::metaObject
+24 (int (*)(...))QAbstractTransition::qt_metacast
+32 (int (*)(...))QAbstractTransition::qt_metacall
+40 0
+48 0
+56 (int (*)(...))QAbstractTransition::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))__cxa_pure_virtual
+120 (int (*)(...))__cxa_pure_virtual
+
+Class QAbstractTransition
+ size=16 align=8
+ base size=16 base align=8
+QAbstractTransition (0x0x7f2349ac2680) 0
+ vptr=((& QAbstractTransition::_ZTV19QAbstractTransition) + 16)
+ QObject (0x0x7f2349b776c0) 0
+ primary-for QAbstractTransition (0x0x7f2349ac2680)
+
+Class QAnimationGroup::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QAnimationGroup::QPrivateSignal (0x0x7f2349b77a20) 0 empty
+
+Vtable for QAnimationGroup
+QAnimationGroup::_ZTV15QAnimationGroup: 18 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI15QAnimationGroup)
+16 (int (*)(...))QAnimationGroup::metaObject
+24 (int (*)(...))QAnimationGroup::qt_metacast
+32 (int (*)(...))QAnimationGroup::qt_metacall
+40 0
+48 0
+56 (int (*)(...))QAnimationGroup::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))__cxa_pure_virtual
+120 (int (*)(...))__cxa_pure_virtual
+128 (int (*)(...))QAbstractAnimation::updateState
+136 (int (*)(...))QAbstractAnimation::updateDirection
+
+Class QAnimationGroup
+ size=16 align=8
+ base size=16 base align=8
+QAnimationGroup (0x0x7f2349ac26e8) 0
+ vptr=((& QAnimationGroup::_ZTV15QAnimationGroup) + 16)
+ QAbstractAnimation (0x0x7f2349ac2750) 0
+ primary-for QAnimationGroup (0x0x7f2349ac26e8)
+ QObject (0x0x7f2349b779c0) 0
+ primary-for QAbstractAnimation (0x0x7f2349ac2750)
+
+Class QBasicTimer
+ size=4 align=4
+ base size=4 base align=4
+QBasicTimer (0x0x7f234981fd80) 0
+
+Class QBitArray
+ size=8 align=8
+ base size=8 base align=8
+QBitArray (0x0x7f234987d180) 0
+
+Class QBitRef
+ size=16 align=8
+ base size=12 base align=8
+QBitRef (0x0x7f23498de600) 0
+
+Class QIODevice::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QIODevice::QPrivateSignal (0x0x7f23499289c0) 0 empty
+
+Vtable for QIODevice
+QIODevice::_ZTV9QIODevice: 30 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI9QIODevice)
+16 (int (*)(...))QIODevice::metaObject
+24 (int (*)(...))QIODevice::qt_metacast
+32 (int (*)(...))QIODevice::qt_metacall
+40 0
+48 0
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QIODevice::isSequential
+120 (int (*)(...))QIODevice::open
+128 (int (*)(...))QIODevice::close
+136 (int (*)(...))QIODevice::pos
+144 (int (*)(...))QIODevice::size
+152 (int (*)(...))QIODevice::seek
+160 (int (*)(...))QIODevice::atEnd
+168 (int (*)(...))QIODevice::reset
+176 (int (*)(...))QIODevice::bytesAvailable
+184 (int (*)(...))QIODevice::bytesToWrite
+192 (int (*)(...))QIODevice::canReadLine
+200 (int (*)(...))QIODevice::waitForReadyRead
+208 (int (*)(...))QIODevice::waitForBytesWritten
+216 (int (*)(...))__cxa_pure_virtual
+224 (int (*)(...))QIODevice::readLineData
+232 (int (*)(...))__cxa_pure_virtual
+
+Class QIODevice
+ size=16 align=8
+ base size=16 base align=8
+QIODevice (0x0x7f2349921c98) 0
+ vptr=((& QIODevice::_ZTV9QIODevice) + 16)
+ QObject (0x0x7f2349928960) 0
+ primary-for QIODevice (0x0x7f2349921c98)
+
+Class QBuffer::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QBuffer::QPrivateSignal (0x0x7f2349973360) 0 empty
+
+Vtable for QBuffer
+QBuffer::_ZTV7QBuffer: 30 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI7QBuffer)
+16 (int (*)(...))QBuffer::metaObject
+24 (int (*)(...))QBuffer::qt_metacast
+32 (int (*)(...))QBuffer::qt_metacall
+40 (int (*)(...))QBuffer::~QBuffer
+48 (int (*)(...))QBuffer::~QBuffer
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QBuffer::connectNotify
+104 (int (*)(...))QBuffer::disconnectNotify
+112 (int (*)(...))QIODevice::isSequential
+120 (int (*)(...))QBuffer::open
+128 (int (*)(...))QBuffer::close
+136 (int (*)(...))QBuffer::pos
+144 (int (*)(...))QBuffer::size
+152 (int (*)(...))QBuffer::seek
+160 (int (*)(...))QBuffer::atEnd
+168 (int (*)(...))QIODevice::reset
+176 (int (*)(...))QIODevice::bytesAvailable
+184 (int (*)(...))QIODevice::bytesToWrite
+192 (int (*)(...))QBuffer::canReadLine
+200 (int (*)(...))QIODevice::waitForReadyRead
+208 (int (*)(...))QIODevice::waitForBytesWritten
+216 (int (*)(...))QBuffer::readData
+224 (int (*)(...))QIODevice::readLineData
+232 (int (*)(...))QBuffer::writeData
+
+Class QBuffer
+ size=16 align=8
+ base size=16 base align=8
+QBuffer (0x0x7f2349921dd0) 0
+ vptr=((& QBuffer::_ZTV7QBuffer) + 16)
+ QIODevice (0x0x7f2349921e38) 0
+ primary-for QBuffer (0x0x7f2349921dd0)
+ QObject (0x0x7f2349973300) 0
+ primary-for QIODevice (0x0x7f2349921e38)
+
+Class QByteArrayMatcher::Data
+ size=272 align=8
+ base size=272 base align=8
+QByteArrayMatcher::Data (0x0x7f2349973600) 0
+
+Class QByteArrayMatcher
+ size=1040 align=8
+ base size=1040 base align=8
+QByteArrayMatcher (0x0x7f23499735a0) 0
+
+Class QStaticByteArrayMatcherBase::Skiptable
+ size=256 align=1
+ base size=256 base align=1
+QStaticByteArrayMatcherBase::Skiptable (0x0x7f2349973780) 0
+
+Class QStaticByteArrayMatcherBase
+ size=256 align=16
+ base size=256 base align=16
+QStaticByteArrayMatcherBase (0x0x7f2349973720) 0
+
+Class QSharedData
+ size=4 align=4
+ base size=4 base align=4
+QSharedData (0x0x7f23499bf660) 0
+
+Class QDate
+ size=8 align=8
+ base size=8 base align=8
+QDate (0x0x7f2349a00600) 0
+
+Class QTime
+ size=4 align=4
+ base size=4 base align=4
+QTime (0x0x7f2349656ea0) 0
+
+Class QDateTime::ShortData
+ size=8 align=8
+ base size=8 base align=8
+QDateTime::ShortData (0x0x7f23496bfb40) 0
+
+Class QDateTime::Data
+ size=8 align=8
+ base size=8 base align=8
+QDateTime::Data (0x0x7f23496bfba0) 0
+
+Class QDateTime
+ size=8 align=8
+ base size=8 base align=8
+QDateTime (0x0x7f23496bfae0) 0
+
+Class QLocale
+ size=8 align=8
+ base size=8 base align=8
+QLocale (0x0x7f23497b12a0) 0
+
+Vtable for QTextStream
+QTextStream::_ZTV11QTextStream: 4 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI11QTextStream)
+16 (int (*)(...))QTextStream::~QTextStream
+24 (int (*)(...))QTextStream::~QTextStream
+
+Class QTextStream
+ size=16 align=8
+ base size=16 base align=8
+QTextStream (0x0x7f23494ad840) 0
+ vptr=((& QTextStream::_ZTV11QTextStream) + 16)
+
+Class QTextStreamManipulator
+ size=40 align=8
+ base size=38 base align=8
+QTextStreamManipulator (0x0x7f2349514120) 0
+
+Class QContiguousCacheData
+ size=24 align=4
+ base size=24 base align=4
+QContiguousCacheData (0x0x7f2349587c00) 0
+
+Class QtSharedPointer::NormalDeleter
+ size=1 align=1
+ base size=0 base align=1
+QtSharedPointer::NormalDeleter (0x0x7f23495d28a0) 0 empty
+
+Class QtSharedPointer::ExternalRefCountData
+ size=16 align=8
+ base size=16 base align=8
+QtSharedPointer::ExternalRefCountData (0x0x7f23495d2a20) 0
+
+Class QDebug::Stream
+ size=80 align=8
+ base size=76 base align=8
+QDebug::Stream (0x0x7f234928d660) 0
+
+Class QDebug
+ size=8 align=8
+ base size=8 base align=8
+QDebug (0x0x7f234928d600) 0
+
+Class QDebugStateSaver
+ size=8 align=8
+ base size=8 base align=8
+QDebugStateSaver (0x0x7f234902e6c0) 0
+
+Class QNoDebug
+ size=1 align=1
+ base size=0 base align=1
+QNoDebug (0x0x7f234902e780) 0 empty
+
+Class QCborError
+ size=4 align=4
+ base size=4 base align=4
+QCborError (0x0x7f23490ada80) 0
+
+Class QRegularExpression
+ size=8 align=8
+ base size=8 base align=8
+QRegularExpression (0x0x7f23490e0240) 0
+
+Class QRegularExpressionMatch
+ size=8 align=8
+ base size=8 base align=8
+QRegularExpressionMatch (0x0x7f2349191120) 0
+
+Class QRegularExpressionMatchIterator
+ size=8 align=8
+ base size=8 base align=8
+QRegularExpressionMatchIterator (0x0x7f23491ceea0) 0
+
+Class QUrl
+ size=8 align=8
+ base size=8 base align=8
+QUrl (0x0x7f2348e51900) 0
+
+Class QUuid
+ size=16 align=4
+ base size=16 base align=4
+QUuid (0x0x7f2348f948a0) 0
+
+Class QCborParserError
+ size=16 align=8
+ base size=12 base align=8
+QCborParserError (0x0x7f2348c1c420) 0
+
+Class QCborValue
+ size=24 align=8
+ base size=20 base align=8
+QCborValue (0x0x7f2348c1c4e0) 0
+
+Class QCborValueRef
+ size=16 align=8
+ base size=16 base align=8
+QCborValueRef (0x0x7f2348a954e0) 0
+
+Class QCborArray::Iterator
+ size=16 align=8
+ base size=16 base align=8
+QCborArray::Iterator (0x0x7f2348b02f00) 0
+
+Class QCborArray::ConstIterator
+ size=16 align=8
+ base size=16 base align=8
+QCborArray::ConstIterator (0x0x7f2348b02f60) 0
+
+Class QCborArray
+ size=8 align=8
+ base size=8 base align=8
+QCborArray (0x0x7f2348b02ea0) 0
+
+Class QCborMap::Iterator
+ size=16 align=8
+ base size=16 base align=8
+QCborMap::Iterator (0x0x7f234881c960) 0
+
+Class QCborMap::ConstIterator
+ size=16 align=8
+ base size=16 base align=8
+QCborMap::ConstIterator (0x0x7f234881c9c0) 0
+
+Class QCborMap
+ size=8 align=8
+ base size=8 base align=8
+QCborMap (0x0x7f234881c900) 0
+
+Class qfloat16
+ size=2 align=2
+ base size=2 base align=2
+qfloat16 (0x0x7f234862e120) 0
+
+Class QCborStreamWriter
+ size=8 align=8
+ base size=8 base align=8
+QCborStreamWriter (0x0x7f23486f00c0) 0
+
+Class QCborStreamReader
+ size=24 align=8
+ base size=20 base align=8
+QCborStreamReader (0x0x7f23486f0de0) 0
+
+Class QCollatorSortKey
+ size=8 align=8
+ base size=8 base align=8
+QCollatorSortKey (0x0x7f2348786f00) 0
+
+Class QCollator
+ size=8 align=8
+ base size=8 base align=8
+QCollator (0x0x7f23487b0120) 0
+
+Class QCommandLineOption
+ size=8 align=8
+ base size=8 base align=8
+QCommandLineOption (0x0x7f234849c6c0) 0
+
+Vtable for QEvent
+QEvent::_ZTV6QEvent: 4 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI6QEvent)
+16 (int (*)(...))QEvent::~QEvent
+24 (int (*)(...))QEvent::~QEvent
+
+Class QEvent
+ size=24 align=8
+ base size=20 base align=8
+QEvent (0x0x7f23484f3de0) 0
+ vptr=((& QEvent::_ZTV6QEvent) + 16)
+
+Vtable for QTimerEvent
+QTimerEvent::_ZTV11QTimerEvent: 4 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI11QTimerEvent)
+16 (int (*)(...))QTimerEvent::~QTimerEvent
+24 (int (*)(...))QTimerEvent::~QTimerEvent
+
+Class QTimerEvent
+ size=24 align=8
+ base size=24 base align=8
+QTimerEvent (0x0x7f234853b000) 0
+ vptr=((& QTimerEvent::_ZTV11QTimerEvent) + 16)
+ QEvent (0x0x7f23485371e0) 0
+ primary-for QTimerEvent (0x0x7f234853b000)
+
+Vtable for QChildEvent
+QChildEvent::_ZTV11QChildEvent: 4 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI11QChildEvent)
+16 (int (*)(...))QChildEvent::~QChildEvent
+24 (int (*)(...))QChildEvent::~QChildEvent
+
+Class QChildEvent
+ size=32 align=8
+ base size=32 base align=8
+QChildEvent (0x0x7f234853b068) 0
+ vptr=((& QChildEvent::_ZTV11QChildEvent) + 16)
+ QEvent (0x0x7f23485372a0) 0
+ primary-for QChildEvent (0x0x7f234853b068)
+
+Vtable for QDynamicPropertyChangeEvent
+QDynamicPropertyChangeEvent::_ZTV27QDynamicPropertyChangeEvent: 4 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI27QDynamicPropertyChangeEvent)
+16 (int (*)(...))QDynamicPropertyChangeEvent::~QDynamicPropertyChangeEvent
+24 (int (*)(...))QDynamicPropertyChangeEvent::~QDynamicPropertyChangeEvent
+
+Class QDynamicPropertyChangeEvent
+ size=32 align=8
+ base size=32 base align=8
+QDynamicPropertyChangeEvent (0x0x7f234853b5b0) 0
+ vptr=((& QDynamicPropertyChangeEvent::_ZTV27QDynamicPropertyChangeEvent) + 16)
+ QEvent (0x0x7f2348537900) 0
+ primary-for QDynamicPropertyChangeEvent (0x0x7f234853b5b0)
+
+Vtable for QDeferredDeleteEvent
+QDeferredDeleteEvent::_ZTV20QDeferredDeleteEvent: 4 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI20QDeferredDeleteEvent)
+16 (int (*)(...))QDeferredDeleteEvent::~QDeferredDeleteEvent
+24 (int (*)(...))QDeferredDeleteEvent::~QDeferredDeleteEvent
+
+Class QDeferredDeleteEvent
+ size=24 align=8
+ base size=24 base align=8
+QDeferredDeleteEvent (0x0x7f234853b618) 0
+ vptr=((& QDeferredDeleteEvent::_ZTV20QDeferredDeleteEvent) + 16)
+ QEvent (0x0x7f23485379c0) 0
+ primary-for QDeferredDeleteEvent (0x0x7f234853b618)
+
+Class QCoreApplication::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QCoreApplication::QPrivateSignal (0x0x7f2348537ae0) 0 empty
+
+Vtable for QCoreApplication
+QCoreApplication::_ZTV16QCoreApplication: 16 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI16QCoreApplication)
+16 (int (*)(...))QCoreApplication::metaObject
+24 (int (*)(...))QCoreApplication::qt_metacast
+32 (int (*)(...))QCoreApplication::qt_metacall
+40 (int (*)(...))QCoreApplication::~QCoreApplication
+48 (int (*)(...))QCoreApplication::~QCoreApplication
+56 (int (*)(...))QCoreApplication::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QCoreApplication::notify
+120 (int (*)(...))QCoreApplication::compressEvent
+
+Class QCoreApplication
+ size=16 align=8
+ base size=16 base align=8
+QCoreApplication (0x0x7f234853b680) 0
+ vptr=((& QCoreApplication::_ZTV16QCoreApplication) + 16)
+ QObject (0x0x7f2348537a80) 0
+ primary-for QCoreApplication (0x0x7f234853b680)
+
+Class QCommandLineParser
+ size=8 align=8
+ base size=8 base align=8
+QCommandLineParser (0x0x7f2348537d20) 0
+
+Class QConcatenateTablesProxyModel::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QConcatenateTablesProxyModel::QPrivateSignal (0x0x7f2348537ea0) 0 empty
+
+Vtable for QConcatenateTablesProxyModel
+QConcatenateTablesProxyModel::_ZTV28QConcatenateTablesProxyModel: 48 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI28QConcatenateTablesProxyModel)
+16 (int (*)(...))QConcatenateTablesProxyModel::metaObject
+24 (int (*)(...))QConcatenateTablesProxyModel::qt_metacast
+32 (int (*)(...))QConcatenateTablesProxyModel::qt_metacall
+40 (int (*)(...))QConcatenateTablesProxyModel::~QConcatenateTablesProxyModel
+48 (int (*)(...))QConcatenateTablesProxyModel::~QConcatenateTablesProxyModel
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QConcatenateTablesProxyModel::index
+120 (int (*)(...))QConcatenateTablesProxyModel::parent
+128 (int (*)(...))QAbstractItemModel::sibling
+136 (int (*)(...))QConcatenateTablesProxyModel::rowCount
+144 (int (*)(...))QConcatenateTablesProxyModel::columnCount
+152 (int (*)(...))QAbstractItemModel::hasChildren
+160 (int (*)(...))QConcatenateTablesProxyModel::data
+168 (int (*)(...))QConcatenateTablesProxyModel::setData
+176 (int (*)(...))QConcatenateTablesProxyModel::headerData
+184 (int (*)(...))QAbstractItemModel::setHeaderData
+192 (int (*)(...))QConcatenateTablesProxyModel::itemData
+200 (int (*)(...))QConcatenateTablesProxyModel::setItemData
+208 (int (*)(...))QConcatenateTablesProxyModel::mimeTypes
+216 (int (*)(...))QConcatenateTablesProxyModel::mimeData
+224 (int (*)(...))QConcatenateTablesProxyModel::canDropMimeData
+232 (int (*)(...))QConcatenateTablesProxyModel::dropMimeData
+240 (int (*)(...))QAbstractItemModel::supportedDropActions
+248 (int (*)(...))QAbstractItemModel::supportedDragActions
+256 (int (*)(...))QAbstractItemModel::insertRows
+264 (int (*)(...))QAbstractItemModel::insertColumns
+272 (int (*)(...))QAbstractItemModel::removeRows
+280 (int (*)(...))QAbstractItemModel::removeColumns
+288 (int (*)(...))QAbstractItemModel::moveRows
+296 (int (*)(...))QAbstractItemModel::moveColumns
+304 (int (*)(...))QAbstractItemModel::fetchMore
+312 (int (*)(...))QAbstractItemModel::canFetchMore
+320 (int (*)(...))QConcatenateTablesProxyModel::flags
+328 (int (*)(...))QAbstractItemModel::sort
+336 (int (*)(...))QAbstractItemModel::buddy
+344 (int (*)(...))QAbstractItemModel::match
+352 (int (*)(...))QConcatenateTablesProxyModel::span
+360 (int (*)(...))QAbstractItemModel::roleNames
+368 (int (*)(...))QAbstractItemModel::submit
+376 (int (*)(...))QAbstractItemModel::revert
+
+Class QConcatenateTablesProxyModel
+ size=16 align=8
+ base size=16 base align=8
+QConcatenateTablesProxyModel (0x0x7f234853b6e8) 0
+ vptr=((& QConcatenateTablesProxyModel::_ZTV28QConcatenateTablesProxyModel) + 16)
+ QAbstractItemModel (0x0x7f234853b750) 0
+ primary-for QConcatenateTablesProxyModel (0x0x7f234853b6e8)
+ QObject (0x0x7f2348537e40) 0
+ primary-for QAbstractItemModel (0x0x7f234853b750)
+
+Class QCryptographicHash
+ size=8 align=8
+ base size=8 base align=8
+QCryptographicHash (0x0x7f234859d0c0) 0
+
+Class QDataStream
+ size=32 align=8
+ base size=32 base align=8
+QDataStream (0x0x7f234859d1e0) 0
+
+Class QtPrivate::StreamStateSaver
+ size=16 align=8
+ base size=12 base align=8
+QtPrivate::StreamStateSaver (0x0x7f234859d360) 0
+
+Class QElapsedTimer
+ size=16 align=8
+ base size=16 base align=8
+QElapsedTimer (0x0x7f23485f9a80) 0
+
+Class QDeadlineTimer
+ size=16 align=8
+ base size=16 base align=8
+QDeadlineTimer (0x0x7f234822b1e0) 0
+
+Class QFileDevice::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QFileDevice::QPrivateSignal (0x0x7f2348349f00) 0 empty
+
+Vtable for QFileDevice
+QFileDevice::_ZTV11QFileDevice: 34 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI11QFileDevice)
+16 (int (*)(...))QFileDevice::metaObject
+24 (int (*)(...))QFileDevice::qt_metacast
+32 (int (*)(...))QFileDevice::qt_metacall
+40 (int (*)(...))QFileDevice::~QFileDevice
+48 (int (*)(...))QFileDevice::~QFileDevice
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QFileDevice::isSequential
+120 (int (*)(...))QIODevice::open
+128 (int (*)(...))QFileDevice::close
+136 (int (*)(...))QFileDevice::pos
+144 (int (*)(...))QFileDevice::size
+152 (int (*)(...))QFileDevice::seek
+160 (int (*)(...))QFileDevice::atEnd
+168 (int (*)(...))QIODevice::reset
+176 (int (*)(...))QIODevice::bytesAvailable
+184 (int (*)(...))QIODevice::bytesToWrite
+192 (int (*)(...))QIODevice::canReadLine
+200 (int (*)(...))QIODevice::waitForReadyRead
+208 (int (*)(...))QIODevice::waitForBytesWritten
+216 (int (*)(...))QFileDevice::readData
+224 (int (*)(...))QFileDevice::readLineData
+232 (int (*)(...))QFileDevice::writeData
+240 (int (*)(...))QFileDevice::fileName
+248 (int (*)(...))QFileDevice::resize
+256 (int (*)(...))QFileDevice::permissions
+264 (int (*)(...))QFileDevice::setPermissions
+
+Class QFileDevice
+ size=16 align=8
+ base size=16 base align=8
+QFileDevice (0x0x7f2348353958) 0
+ vptr=((& QFileDevice::_ZTV11QFileDevice) + 16)
+ QIODevice (0x0x7f23483539c0) 0
+ primary-for QFileDevice (0x0x7f2348353958)
+ QObject (0x0x7f2348349ea0) 0
+ primary-for QIODevice (0x0x7f23483539c0)
+
+Class QFile::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QFile::QPrivateSignal (0x0x7f2348380840) 0 empty
+
+Vtable for QFile
+QFile::_ZTV5QFile: 34 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI5QFile)
+16 (int (*)(...))QFile::metaObject
+24 (int (*)(...))QFile::qt_metacast
+32 (int (*)(...))QFile::qt_metacall
+40 (int (*)(...))QFile::~QFile
+48 (int (*)(...))QFile::~QFile
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QFileDevice::isSequential
+120 (int (*)(...))QFile::open
+128 (int (*)(...))QFileDevice::close
+136 (int (*)(...))QFileDevice::pos
+144 (int (*)(...))QFile::size
+152 (int (*)(...))QFileDevice::seek
+160 (int (*)(...))QFileDevice::atEnd
+168 (int (*)(...))QIODevice::reset
+176 (int (*)(...))QIODevice::bytesAvailable
+184 (int (*)(...))QIODevice::bytesToWrite
+192 (int (*)(...))QIODevice::canReadLine
+200 (int (*)(...))QIODevice::waitForReadyRead
+208 (int (*)(...))QIODevice::waitForBytesWritten
+216 (int (*)(...))QFileDevice::readData
+224 (int (*)(...))QFileDevice::readLineData
+232 (int (*)(...))QFileDevice::writeData
+240 (int (*)(...))QFile::fileName
+248 (int (*)(...))QFile::resize
+256 (int (*)(...))QFile::permissions
+264 (int (*)(...))QFile::setPermissions
+
+Class QFile
+ size=16 align=8
+ base size=16 base align=8
+QFile (0x0x7f2348353af8) 0
+ vptr=((& QFile::_ZTV5QFile) + 16)
+ QFileDevice (0x0x7f2348353b60) 0
+ primary-for QFile (0x0x7f2348353af8)
+ QIODevice (0x0x7f2348353bc8) 0
+ primary-for QFileDevice (0x0x7f2348353b60)
+ QObject (0x0x7f23483807e0) 0
+ primary-for QIODevice (0x0x7f2348353bc8)
+
+Class QFileInfo
+ size=8 align=8
+ base size=8 base align=8
+QFileInfo (0x0x7f2348380ea0) 0
+
+Class QDir
+ size=8 align=8
+ base size=8 base align=8
+QDir (0x0x7f23480352a0) 0
+
+Class QDirIterator
+ size=8 align=8
+ base size=8 base align=8
+QDirIterator (0x0x7f23480df600) 0
+
+Class QEasingCurve
+ size=8 align=8
+ base size=8 base align=8
+QEasingCurve (0x0x7f23480dfd80) 0
+
+Class QEventTransition::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QEventTransition::QPrivateSignal (0x0x7f2347dd6ea0) 0 empty
+
+Vtable for QEventTransition
+QEventTransition::_ZTV16QEventTransition: 16 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI16QEventTransition)
+16 (int (*)(...))QEventTransition::metaObject
+24 (int (*)(...))QEventTransition::qt_metacast
+32 (int (*)(...))QEventTransition::qt_metacall
+40 (int (*)(...))QEventTransition::~QEventTransition
+48 (int (*)(...))QEventTransition::~QEventTransition
+56 (int (*)(...))QEventTransition::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QEventTransition::eventTest
+120 (int (*)(...))QEventTransition::onTransition
+
+Class QEventTransition
+ size=16 align=8
+ base size=16 base align=8
+QEventTransition (0x0x7f23481e0e38) 0
+ vptr=((& QEventTransition::_ZTV16QEventTransition) + 16)
+ QAbstractTransition (0x0x7f23481e0ea0) 0
+ primary-for QEventTransition (0x0x7f23481e0e38)
+ QObject (0x0x7f2347dd6e40) 0
+ primary-for QAbstractTransition (0x0x7f23481e0ea0)
+
+Vtable for QException
+QException::_ZTV10QException: 7 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI10QException)
+16 (int (*)(...))QException::~QException
+24 (int (*)(...))QException::~QException
+32 (int (*)(...))std::exception::what
+40 (int (*)(...))QException::raise
+48 (int (*)(...))QException::clone
+
+Class QException
+ size=8 align=8
+ base size=8 base align=8
+QException (0x0x7f23481e0f08) 0 nearly-empty
+ vptr=((& QException::_ZTV10QException) + 16)
+ std::exception (0x0x7f2347e0e0c0) 0 nearly-empty
+ primary-for QException (0x0x7f23481e0f08)
+
+Vtable for QUnhandledException
+QUnhandledException::_ZTV19QUnhandledException: 7 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI19QUnhandledException)
+16 (int (*)(...))QUnhandledException::~QUnhandledException
+24 (int (*)(...))QUnhandledException::~QUnhandledException
+32 (int (*)(...))std::exception::what
+40 (int (*)(...))QUnhandledException::raise
+48 (int (*)(...))QUnhandledException::clone
+
+Class QUnhandledException
+ size=8 align=8
+ base size=8 base align=8
+QUnhandledException (0x0x7f23481e0f70) 0 nearly-empty
+ vptr=((& QUnhandledException::_ZTV19QUnhandledException) + 16)
+ QException (0x0x7f2347e13000) 0 nearly-empty
+ primary-for QUnhandledException (0x0x7f23481e0f70)
+ std::exception (0x0x7f2347e0e120) 0 nearly-empty
+ primary-for QException (0x0x7f2347e13000)
+
+Class QtPrivate::ExceptionHolder
+ size=8 align=8
+ base size=8 base align=8
+QtPrivate::ExceptionHolder (0x0x7f2347e0e180) 0
+
+Class QtPrivate::ExceptionStore
+ size=8 align=8
+ base size=8 base align=8
+QtPrivate::ExceptionStore (0x0x7f2347e0e240) 0
+
+Vtable for QFactoryInterface
+QFactoryInterface::_ZTV17QFactoryInterface: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI17QFactoryInterface)
+16 0
+24 0
+32 (int (*)(...))__cxa_pure_virtual
+
+Class QFactoryInterface
+ size=8 align=8
+ base size=8 base align=8
+QFactoryInterface (0x0x7f2347e0e2a0) 0 nearly-empty
+ vptr=((& QFactoryInterface::_ZTV17QFactoryInterface) + 16)
+
+Class QFileSelector::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QFileSelector::QPrivateSignal (0x0x7f2347e0e4e0) 0 empty
+
+Vtable for QFileSelector
+QFileSelector::_ZTV13QFileSelector: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI13QFileSelector)
+16 (int (*)(...))QFileSelector::metaObject
+24 (int (*)(...))QFileSelector::qt_metacast
+32 (int (*)(...))QFileSelector::qt_metacall
+40 (int (*)(...))QFileSelector::~QFileSelector
+48 (int (*)(...))QFileSelector::~QFileSelector
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QFileSelector
+ size=16 align=8
+ base size=16 base align=8
+QFileSelector (0x0x7f2347e13068) 0
+ vptr=((& QFileSelector::_ZTV13QFileSelector) + 16)
+ QObject (0x0x7f2347e0e480) 0
+ primary-for QFileSelector (0x0x7f2347e13068)
+
+Class QFileSystemWatcher::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QFileSystemWatcher::QPrivateSignal (0x0x7f2347e0e720) 0 empty
+
+Vtable for QFileSystemWatcher
+QFileSystemWatcher::_ZTV18QFileSystemWatcher: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI18QFileSystemWatcher)
+16 (int (*)(...))QFileSystemWatcher::metaObject
+24 (int (*)(...))QFileSystemWatcher::qt_metacast
+32 (int (*)(...))QFileSystemWatcher::qt_metacall
+40 (int (*)(...))QFileSystemWatcher::~QFileSystemWatcher
+48 (int (*)(...))QFileSystemWatcher::~QFileSystemWatcher
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QFileSystemWatcher
+ size=16 align=8
+ base size=16 base align=8
+QFileSystemWatcher (0x0x7f2347e130d0) 0
+ vptr=((& QFileSystemWatcher::_ZTV18QFileSystemWatcher) + 16)
+ QObject (0x0x7f2347e0e6c0) 0
+ primary-for QFileSystemWatcher (0x0x7f2347e130d0)
+
+Class QFinalState::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QFinalState::QPrivateSignal (0x0x7f2347e0e960) 0 empty
+
+Vtable for QFinalState
+QFinalState::_ZTV11QFinalState: 16 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI11QFinalState)
+16 (int (*)(...))QFinalState::metaObject
+24 (int (*)(...))QFinalState::qt_metacast
+32 (int (*)(...))QFinalState::qt_metacall
+40 (int (*)(...))QFinalState::~QFinalState
+48 (int (*)(...))QFinalState::~QFinalState
+56 (int (*)(...))QFinalState::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QFinalState::onEntry
+120 (int (*)(...))QFinalState::onExit
+
+Class QFinalState
+ size=16 align=8
+ base size=16 base align=8
+QFinalState (0x0x7f2347e13138) 0
+ vptr=((& QFinalState::_ZTV11QFinalState) + 16)
+ QAbstractState (0x0x7f2347e131a0) 0
+ primary-for QFinalState (0x0x7f2347e13138)
+ QObject (0x0x7f2347e0e900) 0
+ primary-for QAbstractState (0x0x7f2347e131a0)
+
+Vtable for QRunnable
+QRunnable::_ZTV9QRunnable: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI9QRunnable)
+16 (int (*)(...))__cxa_pure_virtual
+24 0
+32 0
+
+Class QRunnable
+ size=16 align=8
+ base size=12 base align=8
+QRunnable (0x0x7f2347e0eb40) 0
+ vptr=((& QRunnable::_ZTV9QRunnable) + 16)
+
+Class QBasicMutex
+ size=8 align=8
+ base size=8 base align=8
+QBasicMutex (0x0x7f2347e0ede0) 0
+
+Class QMutex
+ size=8 align=8
+ base size=8 base align=8
+QMutex (0x0x7f2347e13270) 0
+ QBasicMutex (0x0x7f2347ebea80) 0
+
+Class QMutexLocker
+ size=8 align=8
+ base size=8 base align=8
+QMutexLocker (0x0x7f2347ebecc0) 0
+
+Class QtPrivate::ResultItem
+ size=16 align=8
+ base size=16 base align=8
+QtPrivate::ResultItem (0x0x7f2347ee2180) 0
+
+Class QtPrivate::ResultIteratorBase
+ size=16 align=8
+ base size=12 base align=8
+QtPrivate::ResultIteratorBase (0x0x7f2347ee2780) 0
+
+Vtable for QtPrivate::ResultStoreBase
+QtPrivate::ResultStoreBase::_ZTVN9QtPrivate15ResultStoreBaseE: 4 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTIN9QtPrivate15ResultStoreBaseE)
+16 (int (*)(...))QtPrivate::ResultStoreBase::~ResultStoreBase
+24 (int (*)(...))QtPrivate::ResultStoreBase::~ResultStoreBase
+
+Class QtPrivate::ResultStoreBase
+ size=48 align=8
+ base size=44 base align=8
+QtPrivate::ResultStoreBase (0x0x7f2347ee2960) 0
+ vptr=((& QtPrivate::ResultStoreBase::_ZTVN9QtPrivate15ResultStoreBaseE) + 16)
+
+Vtable for QFutureInterfaceBase
+QFutureInterfaceBase::_ZTV20QFutureInterfaceBase: 4 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI20QFutureInterfaceBase)
+16 (int (*)(...))QFutureInterfaceBase::~QFutureInterfaceBase
+24 (int (*)(...))QFutureInterfaceBase::~QFutureInterfaceBase
+
+Class QFutureInterfaceBase
+ size=16 align=8
+ base size=16 base align=8
+QFutureInterfaceBase (0x0x7f2347f6f180) 0
+ vptr=((& QFutureInterfaceBase::_ZTV20QFutureInterfaceBase) + 16)
+
+Class QFutureWatcherBase::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QFutureWatcherBase::QPrivateSignal (0x0x7f2347c1b480) 0 empty
+
+Vtable for QFutureWatcherBase
+QFutureWatcherBase::_ZTV18QFutureWatcherBase: 16 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI18QFutureWatcherBase)
+16 (int (*)(...))QFutureWatcherBase::metaObject
+24 (int (*)(...))QFutureWatcherBase::qt_metacast
+32 (int (*)(...))QFutureWatcherBase::qt_metacall
+40 0
+48 0
+56 (int (*)(...))QFutureWatcherBase::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QFutureWatcherBase::connectNotify
+104 (int (*)(...))QFutureWatcherBase::disconnectNotify
+112 (int (*)(...))__cxa_pure_virtual
+120 (int (*)(...))__cxa_pure_virtual
+
+Class QFutureWatcherBase
+ size=16 align=8
+ base size=16 base align=8
+QFutureWatcherBase (0x0x7f2347fa4888) 0
+ vptr=((& QFutureWatcherBase::_ZTV18QFutureWatcherBase) + 16)
+ QObject (0x0x7f2347c1b420) 0
+ primary-for QFutureWatcherBase (0x0x7f2347fa4888)
+
+Class QHistoryState::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QHistoryState::QPrivateSignal (0x0x7f2347c437e0) 0 empty
+
+Vtable for QHistoryState
+QHistoryState::_ZTV13QHistoryState: 16 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI13QHistoryState)
+16 (int (*)(...))QHistoryState::metaObject
+24 (int (*)(...))QHistoryState::qt_metacast
+32 (int (*)(...))QHistoryState::qt_metacall
+40 (int (*)(...))QHistoryState::~QHistoryState
+48 (int (*)(...))QHistoryState::~QHistoryState
+56 (int (*)(...))QHistoryState::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QHistoryState::onEntry
+120 (int (*)(...))QHistoryState::onExit
+
+Class QHistoryState
+ size=16 align=8
+ base size=16 base align=8
+QHistoryState (0x0x7f2347c4d0d0) 0
+ vptr=((& QHistoryState::_ZTV13QHistoryState) + 16)
+ QAbstractState (0x0x7f2347c4d138) 0
+ primary-for QHistoryState (0x0x7f2347c4d0d0)
+ QObject (0x0x7f2347c43780) 0
+ primary-for QAbstractState (0x0x7f2347c4d138)
+
+Class QIdentityProxyModel::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QIdentityProxyModel::QPrivateSignal (0x0x7f2347c43ae0) 0 empty
+
+Vtable for QIdentityProxyModel
+QIdentityProxyModel::_ZTV19QIdentityProxyModel: 53 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI19QIdentityProxyModel)
+16 (int (*)(...))QIdentityProxyModel::metaObject
+24 (int (*)(...))QIdentityProxyModel::qt_metacast
+32 (int (*)(...))QIdentityProxyModel::qt_metacall
+40 (int (*)(...))QIdentityProxyModel::~QIdentityProxyModel
+48 (int (*)(...))QIdentityProxyModel::~QIdentityProxyModel
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QIdentityProxyModel::index
+120 (int (*)(...))QIdentityProxyModel::parent
+128 (int (*)(...))QIdentityProxyModel::sibling
+136 (int (*)(...))QIdentityProxyModel::rowCount
+144 (int (*)(...))QIdentityProxyModel::columnCount
+152 (int (*)(...))QAbstractProxyModel::hasChildren
+160 (int (*)(...))QAbstractProxyModel::data
+168 (int (*)(...))QAbstractProxyModel::setData
+176 (int (*)(...))QIdentityProxyModel::headerData
+184 (int (*)(...))QAbstractProxyModel::setHeaderData
+192 (int (*)(...))QAbstractProxyModel::itemData
+200 (int (*)(...))QAbstractProxyModel::setItemData
+208 (int (*)(...))QAbstractProxyModel::mimeTypes
+216 (int (*)(...))QAbstractProxyModel::mimeData
+224 (int (*)(...))QAbstractProxyModel::canDropMimeData
+232 (int (*)(...))QIdentityProxyModel::dropMimeData
+240 (int (*)(...))QAbstractProxyModel::supportedDropActions
+248 (int (*)(...))QAbstractProxyModel::supportedDragActions
+256 (int (*)(...))QIdentityProxyModel::insertRows
+264 (int (*)(...))QIdentityProxyModel::insertColumns
+272 (int (*)(...))QIdentityProxyModel::removeRows
+280 (int (*)(...))QIdentityProxyModel::removeColumns
+288 (int (*)(...))QAbstractItemModel::moveRows
+296 (int (*)(...))QAbstractItemModel::moveColumns
+304 (int (*)(...))QAbstractProxyModel::fetchMore
+312 (int (*)(...))QAbstractProxyModel::canFetchMore
+320 (int (*)(...))QAbstractProxyModel::flags
+328 (int (*)(...))QAbstractProxyModel::sort
+336 (int (*)(...))QAbstractProxyModel::buddy
+344 (int (*)(...))QIdentityProxyModel::match
+352 (int (*)(...))QAbstractProxyModel::span
+360 (int (*)(...))QAbstractItemModel::roleNames
+368 (int (*)(...))QAbstractProxyModel::submit
+376 (int (*)(...))QAbstractProxyModel::revert
+384 (int (*)(...))QIdentityProxyModel::setSourceModel
+392 (int (*)(...))QIdentityProxyModel::mapToSource
+400 (int (*)(...))QIdentityProxyModel::mapFromSource
+408 (int (*)(...))QIdentityProxyModel::mapSelectionToSource
+416 (int (*)(...))QIdentityProxyModel::mapSelectionFromSource
+
+Class QIdentityProxyModel
+ size=16 align=8
+ base size=16 base align=8
+QIdentityProxyModel (0x0x7f2347c4d1a0) 0
+ vptr=((& QIdentityProxyModel::_ZTV19QIdentityProxyModel) + 16)
+ QAbstractProxyModel (0x0x7f2347c4d208) 0
+ primary-for QIdentityProxyModel (0x0x7f2347c4d1a0)
+ QAbstractItemModel (0x0x7f2347c4d270) 0
+ primary-for QAbstractProxyModel (0x0x7f2347c4d208)
+ QObject (0x0x7f2347c43a80) 0
+ primary-for QAbstractItemModel (0x0x7f2347c4d270)
+
+Class QItemSelectionRange
+ size=16 align=8
+ base size=16 base align=8
+QItemSelectionRange (0x0x7f2347c43cc0) 0
+
+Class QItemSelectionModel::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QItemSelectionModel::QPrivateSignal (0x0x7f2347d2b600) 0 empty
+
+Vtable for QItemSelectionModel
+QItemSelectionModel::_ZTV19QItemSelectionModel: 20 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI19QItemSelectionModel)
+16 (int (*)(...))QItemSelectionModel::metaObject
+24 (int (*)(...))QItemSelectionModel::qt_metacast
+32 (int (*)(...))QItemSelectionModel::qt_metacall
+40 (int (*)(...))QItemSelectionModel::~QItemSelectionModel
+48 (int (*)(...))QItemSelectionModel::~QItemSelectionModel
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QItemSelectionModel::setCurrentIndex
+120 (int (*)(...))QItemSelectionModel::select
+128 (int (*)(...))QItemSelectionModel::select
+136 (int (*)(...))QItemSelectionModel::clear
+144 (int (*)(...))QItemSelectionModel::reset
+152 (int (*)(...))QItemSelectionModel::clearCurrentIndex
+
+Class QItemSelectionModel
+ size=16 align=8
+ base size=16 base align=8
+QItemSelectionModel (0x0x7f2347d20bc8) 0
+ vptr=((& QItemSelectionModel::_ZTV19QItemSelectionModel) + 16)
+ QObject (0x0x7f2347d2b5a0) 0
+ primary-for QItemSelectionModel (0x0x7f2347d20bc8)
+
+Class QItemSelection
+ size=8 align=8
+ base size=8 base align=8
+QItemSelection (0x0x7f2347d20d68) 0
+ QList<QItemSelectionRange> (0x0x7f2347d20dd0) 0
+ QListSpecialMethods<QItemSelectionRange> (0x0x7f2347d73120) 0 empty
+
+Class QJsonValue
+ size=24 align=8
+ base size=20 base align=8
+QJsonValue (0x0x7f23479d7a20) 0
+
+Class QJsonValueRef
+ size=16 align=8
+ base size=12 base align=8
+QJsonValueRef (0x0x7f2347b29c00) 0
+
+Class QJsonValuePtr
+ size=24 align=8
+ base size=24 base align=8
+QJsonValuePtr (0x0x7f2347b62ba0) 0
+
+Class QJsonValueRefPtr
+ size=16 align=8
+ base size=16 base align=8
+QJsonValueRefPtr (0x0x7f2347b62e40) 0
+
+Class QJsonArray::iterator
+ size=16 align=8
+ base size=12 base align=8
+QJsonArray::iterator (0x0x7f23477db1e0) 0
+
+Class QJsonArray::const_iterator
+ size=16 align=8
+ base size=12 base align=8
+QJsonArray::const_iterator (0x0x7f23477db240) 0
+
+Class QJsonArray
+ size=16 align=8
+ base size=16 base align=8
+QJsonArray (0x0x7f23477db180) 0
+
+Class QJsonParseError
+ size=8 align=4
+ base size=8 base align=4
+QJsonParseError (0x0x7f2347907120) 0
+
+Class QJsonDocument
+ size=8 align=8
+ base size=8 base align=8
+QJsonDocument (0x0x7f2347907180) 0
+
+Class QJsonObject::iterator
+ size=16 align=8
+ base size=12 base align=8
+QJsonObject::iterator (0x0x7f234795c960) 0
+
+Class QJsonObject::const_iterator
+ size=16 align=8
+ base size=12 base align=8
+QJsonObject::const_iterator (0x0x7f234795c9c0) 0
+
+Class QJsonObject
+ size=16 align=8
+ base size=16 base align=8
+QJsonObject (0x0x7f234795c900) 0
+
+Class QLibrary::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QLibrary::QPrivateSignal (0x0x7f234766bd20) 0 empty
+
+Vtable for QLibrary
+QLibrary::_ZTV8QLibrary: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI8QLibrary)
+16 (int (*)(...))QLibrary::metaObject
+24 (int (*)(...))QLibrary::qt_metacast
+32 (int (*)(...))QLibrary::qt_metacall
+40 (int (*)(...))QLibrary::~QLibrary
+48 (int (*)(...))QLibrary::~QLibrary
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QLibrary
+ size=32 align=8
+ base size=25 base align=8
+QLibrary (0x0x7f234766ae38) 0
+ vptr=((& QLibrary::_ZTV8QLibrary) + 16)
+ QObject (0x0x7f234766bcc0) 0
+ primary-for QLibrary (0x0x7f234766ae38)
+
+Class QVersionNumber::SegmentStorage
+ size=8 align=8
+ base size=8 base align=8
+QVersionNumber::SegmentStorage (0x0x7f234769dba0) 0
+
+Class QVersionNumber
+ size=8 align=8
+ base size=8 base align=8
+QVersionNumber (0x0x7f234769d6c0) 0
+
+Class QLibraryInfo
+ size=1 align=1
+ base size=0 base align=1
+QLibraryInfo (0x0x7f234776e300) 0 empty
+
+Class QPoint
+ size=8 align=4
+ base size=8 base align=4
+QPoint (0x0x7f234776e360) 0
+
+Class QPointF
+ size=16 align=8
+ base size=16 base align=8
+QPointF (0x0x7f23473e3180) 0
+
+Class QLine
+ size=16 align=4
+ base size=16 base align=4
+QLine (0x0x7f2347456300) 0
+
+Class QLineF
+ size=32 align=8
+ base size=32 base align=8
+QLineF (0x0x7f23474be6c0) 0
+
+Class QLinkedListData
+ size=32 align=8
+ base size=25 base align=8
+QLinkedListData (0x0x7f2347539960) 0
+
+Class QLockFile
+ size=8 align=8
+ base size=8 base align=8
+QLockFile (0x0x7f23471dcae0) 0
+
+Class QLoggingCategory::AtomicBools
+ size=4 align=1
+ base size=4 base align=1
+QLoggingCategory::AtomicBools (0x0x7f23471dcd20) 0
+
+Class QLoggingCategory
+ size=24 align=8
+ base size=24 base align=8
+QLoggingCategory (0x0x7f23471dccc0) 0
+
+Class QMargins
+ size=16 align=4
+ base size=16 base align=4
+QMargins (0x0x7f234724b180) 0
+
+Class QMarginsF
+ size=32 align=8
+ base size=32 base align=8
+QMarginsF (0x0x7f23472c70c0) 0
+
+Class QMessageAuthenticationCode
+ size=8 align=8
+ base size=8 base align=8
+QMessageAuthenticationCode (0x0x7f234707a8a0) 0
+
+Class QMetaMethod
+ size=16 align=8
+ base size=12 base align=8
+QMetaMethod (0x0x7f234707a900) 0
+
+Class QMetaEnum
+ size=16 align=8
+ base size=12 base align=8
+QMetaEnum (0x0x7f2347105180) 0
+
+Class QMetaProperty
+ size=32 align=8
+ base size=32 base align=8
+QMetaProperty (0x0x7f2346d483c0) 0
+
+Class QMetaClassInfo
+ size=16 align=8
+ base size=12 base align=8
+QMetaClassInfo (0x0x7f2346d484e0) 0
+
+Class QMimeData::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QMimeData::QPrivateSignal (0x0x7f2346d88a80) 0 empty
+
+Vtable for QMimeData
+QMimeData::_ZTV9QMimeData: 17 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI9QMimeData)
+16 (int (*)(...))QMimeData::metaObject
+24 (int (*)(...))QMimeData::qt_metacast
+32 (int (*)(...))QMimeData::qt_metacall
+40 (int (*)(...))QMimeData::~QMimeData
+48 (int (*)(...))QMimeData::~QMimeData
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QMimeData::hasFormat
+120 (int (*)(...))QMimeData::formats
+128 (int (*)(...))QMimeData::retrieveData
+
+Class QMimeData
+ size=16 align=8
+ base size=16 base align=8
+QMimeData (0x0x7f2346d8ca90) 0
+ vptr=((& QMimeData::_ZTV9QMimeData) + 16)
+ QObject (0x0x7f2346d88a20) 0
+ primary-for QMimeData (0x0x7f2346d8ca90)
+
+Class QMimeType
+ size=8 align=8
+ base size=8 base align=8
+QMimeType (0x0x7f2346d88c60) 0
+
+Class QMimeDatabase
+ size=8 align=8
+ base size=8 base align=8
+QMimeDatabase (0x0x7f2346de2d80) 0
+
+Class QObjectCleanupHandler::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QObjectCleanupHandler::QPrivateSignal (0x0x7f2346de2e40) 0 empty
+
+Vtable for QObjectCleanupHandler
+QObjectCleanupHandler::_ZTV21QObjectCleanupHandler: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI21QObjectCleanupHandler)
+16 (int (*)(...))QObjectCleanupHandler::metaObject
+24 (int (*)(...))QObjectCleanupHandler::qt_metacast
+32 (int (*)(...))QObjectCleanupHandler::qt_metacall
+40 (int (*)(...))QObjectCleanupHandler::~QObjectCleanupHandler
+48 (int (*)(...))QObjectCleanupHandler::~QObjectCleanupHandler
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QObjectCleanupHandler
+ size=24 align=8
+ base size=24 base align=8
+QObjectCleanupHandler (0x0x7f2346de8dd0) 0
+ vptr=((& QObjectCleanupHandler::_ZTV21QObjectCleanupHandler) + 16)
+ QObject (0x0x7f2346de2de0) 0
+ primary-for QObjectCleanupHandler (0x0x7f2346de8dd0)
+
+Class QOperatingSystemVersion
+ size=16 align=4
+ base size=16 base align=4
+QOperatingSystemVersion (0x0x7f2346de2f60) 0
+
+Class QParallelAnimationGroup::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QParallelAnimationGroup::QPrivateSignal (0x0x7f2346e71720) 0 empty
+
+Vtable for QParallelAnimationGroup
+QParallelAnimationGroup::_ZTV23QParallelAnimationGroup: 18 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI23QParallelAnimationGroup)
+16 (int (*)(...))QParallelAnimationGroup::metaObject
+24 (int (*)(...))QParallelAnimationGroup::qt_metacast
+32 (int (*)(...))QParallelAnimationGroup::qt_metacall
+40 (int (*)(...))QParallelAnimationGroup::~QParallelAnimationGroup
+48 (int (*)(...))QParallelAnimationGroup::~QParallelAnimationGroup
+56 (int (*)(...))QParallelAnimationGroup::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QParallelAnimationGroup::duration
+120 (int (*)(...))QParallelAnimationGroup::updateCurrentTime
+128 (int (*)(...))QParallelAnimationGroup::updateState
+136 (int (*)(...))QParallelAnimationGroup::updateDirection
+
+Class QParallelAnimationGroup
+ size=16 align=8
+ base size=16 base align=8
+QParallelAnimationGroup (0x0x7f2346e75680) 0
+ vptr=((& QParallelAnimationGroup::_ZTV23QParallelAnimationGroup) + 16)
+ QAnimationGroup (0x0x7f2346e756e8) 0
+ primary-for QParallelAnimationGroup (0x0x7f2346e75680)
+ QAbstractAnimation (0x0x7f2346e75750) 0
+ primary-for QAnimationGroup (0x0x7f2346e756e8)
+ QObject (0x0x7f2346e716c0) 0
+ primary-for QAbstractAnimation (0x0x7f2346e75750)
+
+Class QPauseAnimation::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QPauseAnimation::QPrivateSignal (0x0x7f2346e71960) 0 empty
+
+Vtable for QPauseAnimation
+QPauseAnimation::_ZTV15QPauseAnimation: 18 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI15QPauseAnimation)
+16 (int (*)(...))QPauseAnimation::metaObject
+24 (int (*)(...))QPauseAnimation::qt_metacast
+32 (int (*)(...))QPauseAnimation::qt_metacall
+40 (int (*)(...))QPauseAnimation::~QPauseAnimation
+48 (int (*)(...))QPauseAnimation::~QPauseAnimation
+56 (int (*)(...))QPauseAnimation::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QPauseAnimation::duration
+120 (int (*)(...))QPauseAnimation::updateCurrentTime
+128 (int (*)(...))QAbstractAnimation::updateState
+136 (int (*)(...))QAbstractAnimation::updateDirection
+
+Class QPauseAnimation
+ size=16 align=8
+ base size=16 base align=8
+QPauseAnimation (0x0x7f2346e757b8) 0
+ vptr=((& QPauseAnimation::_ZTV15QPauseAnimation) + 16)
+ QAbstractAnimation (0x0x7f2346e75820) 0
+ primary-for QPauseAnimation (0x0x7f2346e757b8)
+ QObject (0x0x7f2346e71900) 0
+ primary-for QAbstractAnimation (0x0x7f2346e75820)
+
+Class QStaticPlugin
+ size=16 align=8
+ base size=16 base align=8
+QStaticPlugin (0x0x7f2346ea35a0) 0
+
+Class QPluginLoader::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QPluginLoader::QPrivateSignal (0x0x7f2346eea720) 0 empty
+
+Vtable for QPluginLoader
+QPluginLoader::_ZTV13QPluginLoader: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI13QPluginLoader)
+16 (int (*)(...))QPluginLoader::metaObject
+24 (int (*)(...))QPluginLoader::qt_metacast
+32 (int (*)(...))QPluginLoader::qt_metacall
+40 (int (*)(...))QPluginLoader::~QPluginLoader
+48 (int (*)(...))QPluginLoader::~QPluginLoader
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QPluginLoader
+ size=32 align=8
+ base size=25 base align=8
+QPluginLoader (0x0x7f2346edeb60) 0
+ vptr=((& QPluginLoader::_ZTV13QPluginLoader) + 16)
+ QObject (0x0x7f2346eea6c0) 0
+ primary-for QPluginLoader (0x0x7f2346edeb60)
+
+Class QProcessEnvironment
+ size=8 align=8
+ base size=8 base align=8
+QProcessEnvironment (0x0x7f2346eea840) 0
+
+Class QProcess::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QProcess::QPrivateSignal (0x0x7f2346b45ea0) 0 empty
+
+Vtable for QProcess
+QProcess::_ZTV8QProcess: 31 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI8QProcess)
+16 (int (*)(...))QProcess::metaObject
+24 (int (*)(...))QProcess::qt_metacast
+32 (int (*)(...))QProcess::qt_metacall
+40 (int (*)(...))QProcess::~QProcess
+48 (int (*)(...))QProcess::~QProcess
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QProcess::isSequential
+120 (int (*)(...))QProcess::open
+128 (int (*)(...))QProcess::close
+136 (int (*)(...))QIODevice::pos
+144 (int (*)(...))QIODevice::size
+152 (int (*)(...))QIODevice::seek
+160 (int (*)(...))QProcess::atEnd
+168 (int (*)(...))QIODevice::reset
+176 (int (*)(...))QProcess::bytesAvailable
+184 (int (*)(...))QProcess::bytesToWrite
+192 (int (*)(...))QProcess::canReadLine
+200 (int (*)(...))QProcess::waitForReadyRead
+208 (int (*)(...))QProcess::waitForBytesWritten
+216 (int (*)(...))QProcess::readData
+224 (int (*)(...))QIODevice::readLineData
+232 (int (*)(...))QProcess::writeData
+240 (int (*)(...))QProcess::setupChildProcess
+
+Class QProcess
+ size=16 align=8
+ base size=16 base align=8
+QProcess (0x0x7f2346b4f7b8) 0
+ vptr=((& QProcess::_ZTV8QProcess) + 16)
+ QIODevice (0x0x7f2346b4f820) 0
+ primary-for QProcess (0x0x7f2346b4f7b8)
+ QObject (0x0x7f2346b45e40) 0
+ primary-for QIODevice (0x0x7f2346b4f820)
+
+Class QVariantAnimation::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QVariantAnimation::QPrivateSignal (0x0x7f2346b885a0) 0 empty
+
+Vtable for QVariantAnimation
+QVariantAnimation::_ZTV17QVariantAnimation: 20 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI17QVariantAnimation)
+16 (int (*)(...))QVariantAnimation::metaObject
+24 (int (*)(...))QVariantAnimation::qt_metacast
+32 (int (*)(...))QVariantAnimation::qt_metacall
+40 (int (*)(...))QVariantAnimation::~QVariantAnimation
+48 (int (*)(...))QVariantAnimation::~QVariantAnimation
+56 (int (*)(...))QVariantAnimation::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QVariantAnimation::duration
+120 (int (*)(...))QVariantAnimation::updateCurrentTime
+128 (int (*)(...))QVariantAnimation::updateState
+136 (int (*)(...))QAbstractAnimation::updateDirection
+144 (int (*)(...))QVariantAnimation::updateCurrentValue
+152 (int (*)(...))QVariantAnimation::interpolated
+
+Class QVariantAnimation
+ size=16 align=8
+ base size=16 base align=8
+QVariantAnimation (0x0x7f2346b4f888) 0
+ vptr=((& QVariantAnimation::_ZTV17QVariantAnimation) + 16)
+ QAbstractAnimation (0x0x7f2346b4f8f0) 0
+ primary-for QVariantAnimation (0x0x7f2346b4f888)
+ QObject (0x0x7f2346b88540) 0
+ primary-for QAbstractAnimation (0x0x7f2346b4f8f0)
+
+Class QPropertyAnimation::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QPropertyAnimation::QPrivateSignal (0x0x7f2346b88840) 0 empty
+
+Vtable for QPropertyAnimation
+QPropertyAnimation::_ZTV18QPropertyAnimation: 20 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI18QPropertyAnimation)
+16 (int (*)(...))QPropertyAnimation::metaObject
+24 (int (*)(...))QPropertyAnimation::qt_metacast
+32 (int (*)(...))QPropertyAnimation::qt_metacall
+40 (int (*)(...))QPropertyAnimation::~QPropertyAnimation
+48 (int (*)(...))QPropertyAnimation::~QPropertyAnimation
+56 (int (*)(...))QPropertyAnimation::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QVariantAnimation::duration
+120 (int (*)(...))QVariantAnimation::updateCurrentTime
+128 (int (*)(...))QPropertyAnimation::updateState
+136 (int (*)(...))QAbstractAnimation::updateDirection
+144 (int (*)(...))QPropertyAnimation::updateCurrentValue
+152 (int (*)(...))QVariantAnimation::interpolated
+
+Class QPropertyAnimation
+ size=16 align=8
+ base size=16 base align=8
+QPropertyAnimation (0x0x7f2346b4f9c0) 0
+ vptr=((& QPropertyAnimation::_ZTV18QPropertyAnimation) + 16)
+ QVariantAnimation (0x0x7f2346b4fa28) 0
+ primary-for QPropertyAnimation (0x0x7f2346b4f9c0)
+ QAbstractAnimation (0x0x7f2346b4fa90) 0
+ primary-for QVariantAnimation (0x0x7f2346b4fa28)
+ QObject (0x0x7f2346b887e0) 0
+ primary-for QAbstractAnimation (0x0x7f2346b4fa90)
+
+Class std::random_device
+ size=5000 align=8
+ base size=5000 base align=8
+std::random_device (0x0x7f2346c07f60) 0
+
+Class std::bernoulli_distribution::param_type
+ size=8 align=8
+ base size=8 base align=8
+std::bernoulli_distribution::param_type (0x0x7f2346d07cc0) 0
+
+Class std::bernoulli_distribution
+ size=8 align=8
+ base size=8 base align=8
+std::bernoulli_distribution (0x0x7f2346d07c60) 0
+
+Class std::seed_seq
+ size=24 align=8
+ base size=24 base align=8
+std::seed_seq (0x0x7f2346b06a20) 0
+
+Class QRandomGenerator::Storage
+ size=2504 align=8
+ base size=2504 base align=8
+QRandomGenerator::Storage (0x0x7f23469316c0) 0
+
+Class QRandomGenerator
+ size=2512 align=8
+ base size=2512 base align=8
+QRandomGenerator (0x0x7f2346931660) 0
+
+Class QRandomGenerator64
+ size=2512 align=8
+ base size=2512 base align=8
+QRandomGenerator64 (0x0x7f234652e750) 0
+ QRandomGenerator (0x0x7f23465531e0) 0
+
+Class QReadWriteLock
+ size=8 align=8
+ base size=8 base align=8
+QReadWriteLock (0x0x7f2346553d80) 0
+
+Class QReadLocker
+ size=8 align=8
+ base size=8 base align=8
+QReadLocker (0x0x7f23465d9060) 0
+
+Class QWriteLocker
+ size=8 align=8
+ base size=8 base align=8
+QWriteLocker (0x0x7f23465d9540) 0
+
+Class QSize
+ size=8 align=4
+ base size=8 base align=4
+QSize (0x0x7f23465d9a20) 0
+
+Class QSizeF
+ size=16 align=8
+ base size=16 base align=8
+QSizeF (0x0x7f2346648840) 0
+
+Class QRect
+ size=16 align=4
+ base size=16 base align=4
+QRect (0x0x7f23466bf7e0) 0
+
+Class QRectF
+ size=32 align=8
+ base size=32 base align=8
+QRectF (0x0x7f2346372840) 0
+
+Class QResource
+ size=8 align=8
+ base size=8 base align=8
+QResource (0x0x7f2346432960) 0
+
+Class QSaveFile::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QSaveFile::QPrivateSignal (0x0x7f2346432c00) 0 empty
+
+Vtable for QSaveFile
+QSaveFile::_ZTV9QSaveFile: 34 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI9QSaveFile)
+16 (int (*)(...))QSaveFile::metaObject
+24 (int (*)(...))QSaveFile::qt_metacast
+32 (int (*)(...))QSaveFile::qt_metacall
+40 (int (*)(...))QSaveFile::~QSaveFile
+48 (int (*)(...))QSaveFile::~QSaveFile
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QFileDevice::isSequential
+120 (int (*)(...))QSaveFile::open
+128 (int (*)(...))QSaveFile::close
+136 (int (*)(...))QFileDevice::pos
+144 (int (*)(...))QFileDevice::size
+152 (int (*)(...))QFileDevice::seek
+160 (int (*)(...))QFileDevice::atEnd
+168 (int (*)(...))QIODevice::reset
+176 (int (*)(...))QIODevice::bytesAvailable
+184 (int (*)(...))QIODevice::bytesToWrite
+192 (int (*)(...))QIODevice::canReadLine
+200 (int (*)(...))QIODevice::waitForReadyRead
+208 (int (*)(...))QIODevice::waitForBytesWritten
+216 (int (*)(...))QFileDevice::readData
+224 (int (*)(...))QFileDevice::readLineData
+232 (int (*)(...))QSaveFile::writeData
+240 (int (*)(...))QSaveFile::fileName
+248 (int (*)(...))QFileDevice::resize
+256 (int (*)(...))QFileDevice::permissions
+264 (int (*)(...))QFileDevice::setPermissions
+
+Class QSaveFile
+ size=16 align=8
+ base size=16 base align=8
+QSaveFile (0x0x7f23463fc138) 0
+ vptr=((& QSaveFile::_ZTV9QSaveFile) + 16)
+ QFileDevice (0x0x7f23463fc1a0) 0
+ primary-for QSaveFile (0x0x7f23463fc138)
+ QIODevice (0x0x7f23463fc208) 0
+ primary-for QFileDevice (0x0x7f23463fc1a0)
+ QObject (0x0x7f2346432ba0) 0
+ primary-for QIODevice (0x0x7f23463fc208)
+
+Class QSemaphore
+ size=8 align=8
+ base size=8 base align=8
+QSemaphore (0x0x7f2346489240) 0
+
+Class QSemaphoreReleaser
+ size=16 align=8
+ base size=12 base align=8
+QSemaphoreReleaser (0x0x7f23464893c0) 0
+
+Class QSequentialAnimationGroup::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QSequentialAnimationGroup::QPrivateSignal (0x0x7f2346196660) 0 empty
+
+Vtable for QSequentialAnimationGroup
+QSequentialAnimationGroup::_ZTV25QSequentialAnimationGroup: 18 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI25QSequentialAnimationGroup)
+16 (int (*)(...))QSequentialAnimationGroup::metaObject
+24 (int (*)(...))QSequentialAnimationGroup::qt_metacast
+32 (int (*)(...))QSequentialAnimationGroup::qt_metacall
+40 (int (*)(...))QSequentialAnimationGroup::~QSequentialAnimationGroup
+48 (int (*)(...))QSequentialAnimationGroup::~QSequentialAnimationGroup
+56 (int (*)(...))QSequentialAnimationGroup::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QSequentialAnimationGroup::duration
+120 (int (*)(...))QSequentialAnimationGroup::updateCurrentTime
+128 (int (*)(...))QSequentialAnimationGroup::updateState
+136 (int (*)(...))QSequentialAnimationGroup::updateDirection
+
+Class QSequentialAnimationGroup
+ size=16 align=8
+ base size=16 base align=8
+QSequentialAnimationGroup (0x0x7f2346190f08) 0
+ vptr=((& QSequentialAnimationGroup::_ZTV25QSequentialAnimationGroup) + 16)
+ QAnimationGroup (0x0x7f2346190f70) 0
+ primary-for QSequentialAnimationGroup (0x0x7f2346190f08)
+ QAbstractAnimation (0x0x7f234619e000) 0
+ primary-for QAnimationGroup (0x0x7f2346190f70)
+ QObject (0x0x7f2346196600) 0
+ primary-for QAbstractAnimation (0x0x7f234619e000)
+
+Class QSettings::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QSettings::QPrivateSignal (0x0x7f23461968a0) 0 empty
+
+Vtable for QSettings
+QSettings::_ZTV9QSettings: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI9QSettings)
+16 (int (*)(...))QSettings::metaObject
+24 (int (*)(...))QSettings::qt_metacast
+32 (int (*)(...))QSettings::qt_metacall
+40 (int (*)(...))QSettings::~QSettings
+48 (int (*)(...))QSettings::~QSettings
+56 (int (*)(...))QSettings::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QSettings
+ size=16 align=8
+ base size=16 base align=8
+QSettings (0x0x7f234619e068) 0
+ vptr=((& QSettings::_ZTV9QSettings) + 16)
+ QObject (0x0x7f2346196840) 0
+ primary-for QSettings (0x0x7f234619e068)
+
+Class QSharedMemory::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QSharedMemory::QPrivateSignal (0x0x7f2346196d20) 0 empty
+
+Vtable for QSharedMemory
+QSharedMemory::_ZTV13QSharedMemory: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI13QSharedMemory)
+16 (int (*)(...))QSharedMemory::metaObject
+24 (int (*)(...))QSharedMemory::qt_metacast
+32 (int (*)(...))QSharedMemory::qt_metacall
+40 (int (*)(...))QSharedMemory::~QSharedMemory
+48 (int (*)(...))QSharedMemory::~QSharedMemory
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QSharedMemory
+ size=16 align=8
+ base size=16 base align=8
+QSharedMemory (0x0x7f234619e0d0) 0
+ vptr=((& QSharedMemory::_ZTV13QSharedMemory) + 16)
+ QObject (0x0x7f2346196cc0) 0
+ primary-for QSharedMemory (0x0x7f234619e0d0)
+
+Class QSignalMapper::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QSignalMapper::QPrivateSignal (0x0x7f2346196f60) 0 empty
+
+Vtable for QSignalMapper
+QSignalMapper::_ZTV13QSignalMapper: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI13QSignalMapper)
+16 (int (*)(...))QSignalMapper::metaObject
+24 (int (*)(...))QSignalMapper::qt_metacast
+32 (int (*)(...))QSignalMapper::qt_metacall
+40 (int (*)(...))QSignalMapper::~QSignalMapper
+48 (int (*)(...))QSignalMapper::~QSignalMapper
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QSignalMapper
+ size=16 align=8
+ base size=16 base align=8
+QSignalMapper (0x0x7f234619e138) 0
+ vptr=((& QSignalMapper::_ZTV13QSignalMapper) + 16)
+ QObject (0x0x7f2346196f00) 0
+ primary-for QSignalMapper (0x0x7f234619e138)
+
+Class QSignalTransition::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QSignalTransition::QPrivateSignal (0x0x7f23461ee1e0) 0 empty
+
+Vtable for QSignalTransition
+QSignalTransition::_ZTV17QSignalTransition: 16 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI17QSignalTransition)
+16 (int (*)(...))QSignalTransition::metaObject
+24 (int (*)(...))QSignalTransition::qt_metacast
+32 (int (*)(...))QSignalTransition::qt_metacall
+40 (int (*)(...))QSignalTransition::~QSignalTransition
+48 (int (*)(...))QSignalTransition::~QSignalTransition
+56 (int (*)(...))QSignalTransition::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QSignalTransition::eventTest
+120 (int (*)(...))QSignalTransition::onTransition
+
+Class QSignalTransition
+ size=16 align=8
+ base size=16 base align=8
+QSignalTransition (0x0x7f234619e1a0) 0
+ vptr=((& QSignalTransition::_ZTV17QSignalTransition) + 16)
+ QAbstractTransition (0x0x7f234619e208) 0
+ primary-for QSignalTransition (0x0x7f234619e1a0)
+ QObject (0x0x7f23461ee180) 0
+ primary-for QAbstractTransition (0x0x7f234619e208)
+
+Class QSocketNotifier::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QSocketNotifier::QPrivateSignal (0x0x7f23461ee480) 0 empty
+
+Vtable for QSocketNotifier
+QSocketNotifier::_ZTV15QSocketNotifier: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI15QSocketNotifier)
+16 (int (*)(...))QSocketNotifier::metaObject
+24 (int (*)(...))QSocketNotifier::qt_metacast
+32 (int (*)(...))QSocketNotifier::qt_metacall
+40 (int (*)(...))QSocketNotifier::~QSocketNotifier
+48 (int (*)(...))QSocketNotifier::~QSocketNotifier
+56 (int (*)(...))QSocketNotifier::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QSocketNotifier
+ size=16 align=8
+ base size=16 base align=8
+QSocketNotifier (0x0x7f234619e270) 0
+ vptr=((& QSocketNotifier::_ZTV15QSocketNotifier) + 16)
+ QObject (0x0x7f23461ee420) 0
+ primary-for QSocketNotifier (0x0x7f234619e270)
+
+Class QSortFilterProxyModel::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QSortFilterProxyModel::QPrivateSignal (0x0x7f23461ee6c0) 0 empty
+
+Vtable for QSortFilterProxyModel
+QSortFilterProxyModel::_ZTV21QSortFilterProxyModel: 56 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI21QSortFilterProxyModel)
+16 (int (*)(...))QSortFilterProxyModel::metaObject
+24 (int (*)(...))QSortFilterProxyModel::qt_metacast
+32 (int (*)(...))QSortFilterProxyModel::qt_metacall
+40 (int (*)(...))QSortFilterProxyModel::~QSortFilterProxyModel
+48 (int (*)(...))QSortFilterProxyModel::~QSortFilterProxyModel
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QSortFilterProxyModel::index
+120 (int (*)(...))QSortFilterProxyModel::parent
+128 (int (*)(...))QSortFilterProxyModel::sibling
+136 (int (*)(...))QSortFilterProxyModel::rowCount
+144 (int (*)(...))QSortFilterProxyModel::columnCount
+152 (int (*)(...))QSortFilterProxyModel::hasChildren
+160 (int (*)(...))QSortFilterProxyModel::data
+168 (int (*)(...))QSortFilterProxyModel::setData
+176 (int (*)(...))QSortFilterProxyModel::headerData
+184 (int (*)(...))QSortFilterProxyModel::setHeaderData
+192 (int (*)(...))QAbstractProxyModel::itemData
+200 (int (*)(...))QAbstractProxyModel::setItemData
+208 (int (*)(...))QSortFilterProxyModel::mimeTypes
+216 (int (*)(...))QSortFilterProxyModel::mimeData
+224 (int (*)(...))QAbstractProxyModel::canDropMimeData
+232 (int (*)(...))QSortFilterProxyModel::dropMimeData
+240 (int (*)(...))QSortFilterProxyModel::supportedDropActions
+248 (int (*)(...))QAbstractProxyModel::supportedDragActions
+256 (int (*)(...))QSortFilterProxyModel::insertRows
+264 (int (*)(...))QSortFilterProxyModel::insertColumns
+272 (int (*)(...))QSortFilterProxyModel::removeRows
+280 (int (*)(...))QSortFilterProxyModel::removeColumns
+288 (int (*)(...))QAbstractItemModel::moveRows
+296 (int (*)(...))QAbstractItemModel::moveColumns
+304 (int (*)(...))QSortFilterProxyModel::fetchMore
+312 (int (*)(...))QSortFilterProxyModel::canFetchMore
+320 (int (*)(...))QSortFilterProxyModel::flags
+328 (int (*)(...))QSortFilterProxyModel::sort
+336 (int (*)(...))QSortFilterProxyModel::buddy
+344 (int (*)(...))QSortFilterProxyModel::match
+352 (int (*)(...))QSortFilterProxyModel::span
+360 (int (*)(...))QAbstractItemModel::roleNames
+368 (int (*)(...))QAbstractProxyModel::submit
+376 (int (*)(...))QAbstractProxyModel::revert
+384 (int (*)(...))QSortFilterProxyModel::setSourceModel
+392 (int (*)(...))QSortFilterProxyModel::mapToSource
+400 (int (*)(...))QSortFilterProxyModel::mapFromSource
+408 (int (*)(...))QSortFilterProxyModel::mapSelectionToSource
+416 (int (*)(...))QSortFilterProxyModel::mapSelectionFromSource
+424 (int (*)(...))QSortFilterProxyModel::filterAcceptsRow
+432 (int (*)(...))QSortFilterProxyModel::filterAcceptsColumn
+440 (int (*)(...))QSortFilterProxyModel::lessThan
+
+Class QSortFilterProxyModel
+ size=16 align=8
+ base size=16 base align=8
+QSortFilterProxyModel (0x0x7f234619e2d8) 0
+ vptr=((& QSortFilterProxyModel::_ZTV21QSortFilterProxyModel) + 16)
+ QAbstractProxyModel (0x0x7f234619e340) 0
+ primary-for QSortFilterProxyModel (0x0x7f234619e2d8)
+ QAbstractItemModel (0x0x7f234619e3a8) 0
+ primary-for QAbstractProxyModel (0x0x7f234619e340)
+ QObject (0x0x7f23461ee660) 0
+ primary-for QAbstractItemModel (0x0x7f234619e3a8)
+
+Class QStandardPaths
+ size=1 align=1
+ base size=0 base align=1
+QStandardPaths (0x0x7f23461eeae0) 0 empty
+
+Class QState::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QState::QPrivateSignal (0x0x7f234625e420) 0 empty
+
+Vtable for QState
+QState::_ZTV6QState: 16 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI6QState)
+16 (int (*)(...))QState::metaObject
+24 (int (*)(...))QState::qt_metacast
+32 (int (*)(...))QState::qt_metacall
+40 (int (*)(...))QState::~QState
+48 (int (*)(...))QState::~QState
+56 (int (*)(...))QState::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QState::onEntry
+120 (int (*)(...))QState::onExit
+
+Class QState
+ size=16 align=8
+ base size=16 base align=8
+QState (0x0x7f234619e548) 0
+ vptr=((& QState::_ZTV6QState) + 16)
+ QAbstractState (0x0x7f234619e5b0) 0
+ primary-for QState (0x0x7f234619e548)
+ QObject (0x0x7f234625e3c0) 0
+ primary-for QAbstractState (0x0x7f234619e5b0)
+
+Class QStateMachine::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QStateMachine::QPrivateSignal (0x0x7f234625e8a0) 0 empty
+
+Vtable for QStateMachine::SignalEvent
+QStateMachine::SignalEvent::_ZTVN13QStateMachine11SignalEventE: 4 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTIN13QStateMachine11SignalEventE)
+16 (int (*)(...))QStateMachine::SignalEvent::~SignalEvent
+24 (int (*)(...))QStateMachine::SignalEvent::~SignalEvent
+
+Class QStateMachine::SignalEvent
+ size=48 align=8
+ base size=48 base align=8
+QStateMachine::SignalEvent (0x0x7f234619e750) 0
+ vptr=((& QStateMachine::SignalEvent::_ZTVN13QStateMachine11SignalEventE) + 16)
+ QEvent (0x0x7f234625e900) 0
+ primary-for QStateMachine::SignalEvent (0x0x7f234619e750)
+
+Vtable for QStateMachine::WrappedEvent
+QStateMachine::WrappedEvent::_ZTVN13QStateMachine12WrappedEventE: 4 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTIN13QStateMachine12WrappedEventE)
+16 (int (*)(...))QStateMachine::WrappedEvent::~WrappedEvent
+24 (int (*)(...))QStateMachine::WrappedEvent::~WrappedEvent
+
+Class QStateMachine::WrappedEvent
+ size=40 align=8
+ base size=40 base align=8
+QStateMachine::WrappedEvent (0x0x7f234619e7b8) 0
+ vptr=((& QStateMachine::WrappedEvent::_ZTVN13QStateMachine12WrappedEventE) + 16)
+ QEvent (0x0x7f234625e960) 0
+ primary-for QStateMachine::WrappedEvent (0x0x7f234619e7b8)
+
+Vtable for QStateMachine
+QStateMachine::_ZTV13QStateMachine: 20 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI13QStateMachine)
+16 (int (*)(...))QStateMachine::metaObject
+24 (int (*)(...))QStateMachine::qt_metacast
+32 (int (*)(...))QStateMachine::qt_metacall
+40 (int (*)(...))QStateMachine::~QStateMachine
+48 (int (*)(...))QStateMachine::~QStateMachine
+56 (int (*)(...))QStateMachine::event
+64 (int (*)(...))QStateMachine::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QStateMachine::onEntry
+120 (int (*)(...))QStateMachine::onExit
+128 (int (*)(...))QStateMachine::beginSelectTransitions
+136 (int (*)(...))QStateMachine::endSelectTransitions
+144 (int (*)(...))QStateMachine::beginMicrostep
+152 (int (*)(...))QStateMachine::endMicrostep
+
+Class QStateMachine
+ size=16 align=8
+ base size=16 base align=8
+QStateMachine (0x0x7f234619e618) 0
+ vptr=((& QStateMachine::_ZTV13QStateMachine) + 16)
+ QState (0x0x7f234619e680) 0
+ primary-for QStateMachine (0x0x7f234619e618)
+ QAbstractState (0x0x7f234619e6e8) 0
+ primary-for QState (0x0x7f234619e680)
+ QObject (0x0x7f234625e840) 0
+ primary-for QAbstractState (0x0x7f234619e6e8)
+
+Class QStorageInfo
+ size=8 align=8
+ base size=8 base align=8
+QStorageInfo (0x0x7f234625ed20) 0
+
+Class QAbstractConcatenable
+ size=1 align=1
+ base size=0 base align=1
+QAbstractConcatenable (0x0x7f2345f15d20) 0 empty
+
+Class QStringListModel::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QStringListModel::QPrivateSignal (0x0x7f2345fcb0c0) 0 empty
+
+Vtable for QStringListModel
+QStringListModel::_ZTV16QStringListModel: 48 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI16QStringListModel)
+16 (int (*)(...))QStringListModel::metaObject
+24 (int (*)(...))QStringListModel::qt_metacast
+32 (int (*)(...))QStringListModel::qt_metacall
+40 (int (*)(...))QStringListModel::~QStringListModel
+48 (int (*)(...))QStringListModel::~QStringListModel
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QAbstractListModel::index
+120 (int (*)(...))QAbstractListModel::parent
+128 (int (*)(...))QStringListModel::sibling
+136 (int (*)(...))QStringListModel::rowCount
+144 (int (*)(...))QAbstractListModel::columnCount
+152 (int (*)(...))QAbstractListModel::hasChildren
+160 (int (*)(...))QStringListModel::data
+168 (int (*)(...))QStringListModel::setData
+176 (int (*)(...))QAbstractItemModel::headerData
+184 (int (*)(...))QAbstractItemModel::setHeaderData
+192 (int (*)(...))QStringListModel::itemData
+200 (int (*)(...))QStringListModel::setItemData
+208 (int (*)(...))QAbstractItemModel::mimeTypes
+216 (int (*)(...))QAbstractItemModel::mimeData
+224 (int (*)(...))QAbstractItemModel::canDropMimeData
+232 (int (*)(...))QAbstractListModel::dropMimeData
+240 (int (*)(...))QStringListModel::supportedDropActions
+248 (int (*)(...))QAbstractItemModel::supportedDragActions
+256 (int (*)(...))QStringListModel::insertRows
+264 (int (*)(...))QAbstractItemModel::insertColumns
+272 (int (*)(...))QStringListModel::removeRows
+280 (int (*)(...))QAbstractItemModel::removeColumns
+288 (int (*)(...))QStringListModel::moveRows
+296 (int (*)(...))QAbstractItemModel::moveColumns
+304 (int (*)(...))QAbstractItemModel::fetchMore
+312 (int (*)(...))QAbstractItemModel::canFetchMore
+320 (int (*)(...))QStringListModel::flags
+328 (int (*)(...))QStringListModel::sort
+336 (int (*)(...))QAbstractItemModel::buddy
+344 (int (*)(...))QAbstractItemModel::match
+352 (int (*)(...))QAbstractItemModel::span
+360 (int (*)(...))QAbstractItemModel::roleNames
+368 (int (*)(...))QAbstractItemModel::submit
+376 (int (*)(...))QAbstractItemModel::revert
+
+Class QStringListModel
+ size=24 align=8
+ base size=24 base align=8
+QStringListModel (0x0x7f2345fa48f0) 0
+ vptr=((& QStringListModel::_ZTV16QStringListModel) + 16)
+ QAbstractListModel (0x0x7f2345fa4958) 0
+ primary-for QStringListModel (0x0x7f2345fa48f0)
+ QAbstractItemModel (0x0x7f2345fa49c0) 0
+ primary-for QAbstractListModel (0x0x7f2345fa4958)
+ QObject (0x0x7f2345fcb060) 0
+ primary-for QAbstractItemModel (0x0x7f2345fa49c0)
+
+Class QSystemSemaphore
+ size=8 align=8
+ base size=8 base align=8
+QSystemSemaphore (0x0x7f2345fcb1e0) 0
+
+Class QTemporaryDir
+ size=8 align=8
+ base size=8 base align=8
+QTemporaryDir (0x0x7f2345fcb2a0) 0
+
+Class QTemporaryFile::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QTemporaryFile::QPrivateSignal (0x0x7f2345fcb3c0) 0 empty
+
+Vtable for QTemporaryFile
+QTemporaryFile::_ZTV14QTemporaryFile: 34 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI14QTemporaryFile)
+16 (int (*)(...))QTemporaryFile::metaObject
+24 (int (*)(...))QTemporaryFile::qt_metacast
+32 (int (*)(...))QTemporaryFile::qt_metacall
+40 (int (*)(...))QTemporaryFile::~QTemporaryFile
+48 (int (*)(...))QTemporaryFile::~QTemporaryFile
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QFileDevice::isSequential
+120 (int (*)(...))QTemporaryFile::open
+128 (int (*)(...))QFileDevice::close
+136 (int (*)(...))QFileDevice::pos
+144 (int (*)(...))QFile::size
+152 (int (*)(...))QFileDevice::seek
+160 (int (*)(...))QFileDevice::atEnd
+168 (int (*)(...))QIODevice::reset
+176 (int (*)(...))QIODevice::bytesAvailable
+184 (int (*)(...))QIODevice::bytesToWrite
+192 (int (*)(...))QIODevice::canReadLine
+200 (int (*)(...))QIODevice::waitForReadyRead
+208 (int (*)(...))QIODevice::waitForBytesWritten
+216 (int (*)(...))QFileDevice::readData
+224 (int (*)(...))QFileDevice::readLineData
+232 (int (*)(...))QFileDevice::writeData
+240 (int (*)(...))QTemporaryFile::fileName
+248 (int (*)(...))QFile::resize
+256 (int (*)(...))QFile::permissions
+264 (int (*)(...))QFile::setPermissions
+
+Class QTemporaryFile
+ size=16 align=8
+ base size=16 base align=8
+QTemporaryFile (0x0x7f2345fa4a28) 0
+ vptr=((& QTemporaryFile::_ZTV14QTemporaryFile) + 16)
+ QFile (0x0x7f2345fa4a90) 0
+ primary-for QTemporaryFile (0x0x7f2345fa4a28)
+ QFileDevice (0x0x7f2345fa4af8) 0
+ primary-for QFile (0x0x7f2345fa4a90)
+ QIODevice (0x0x7f2345fa4b60) 0
+ primary-for QFileDevice (0x0x7f2345fa4af8)
+ QObject (0x0x7f2345fcb360) 0
+ primary-for QIODevice (0x0x7f2345fa4b60)
+
+Class QTextBoundaryFinder
+ size=48 align=8
+ base size=48 base align=8
+QTextBoundaryFinder (0x0x7f2345fcb720) 0
+
+Class QTextCodec::ConverterState
+ size=32 align=8
+ base size=32 base align=8
+QTextCodec::ConverterState (0x0x7f2345fcbf60) 0
+
+Vtable for QTextCodec
+QTextCodec::_ZTV10QTextCodec: 9 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI10QTextCodec)
+16 (int (*)(...))__cxa_pure_virtual
+24 (int (*)(...))QTextCodec::aliases
+32 (int (*)(...))__cxa_pure_virtual
+40 (int (*)(...))__cxa_pure_virtual
+48 (int (*)(...))__cxa_pure_virtual
+56 0
+64 0
+
+Class QTextCodec
+ size=8 align=8
+ base size=8 base align=8
+QTextCodec (0x0x7f2345fcbf00) 0 nearly-empty
+ vptr=((& QTextCodec::_ZTV10QTextCodec) + 16)
+
+Class QTextEncoder
+ size=40 align=8
+ base size=40 base align=8
+QTextEncoder (0x0x7f2346048960) 0
+
+Class QTextDecoder
+ size=40 align=8
+ base size=40 base align=8
+QTextDecoder (0x0x7f2346048b40) 0
+
+Class std::__mutex_base
+ size=40 align=8
+ base size=40 base align=8
+std::__mutex_base (0x0x7f2346048d20) 0
+
+Class std::mutex
+ size=40 align=8
+ base size=40 base align=8
+std::mutex (0x0x7f2345fa4d68) 0
+ std::__mutex_base (0x0x7f2346048d80) 0
+
+Class std::defer_lock_t
+ size=1 align=1
+ base size=0 base align=1
+std::defer_lock_t (0x0x7f2346048f60) 0 empty
+
+Class std::try_to_lock_t
+ size=1 align=1
+ base size=0 base align=1
+std::try_to_lock_t (0x0x7f23460a1000) 0 empty
+
+Class std::adopt_lock_t
+ size=1 align=1
+ base size=0 base align=1
+std::adopt_lock_t (0x0x7f23460a1060) 0 empty
+
+Class std::__recursive_mutex_base
+ size=40 align=8
+ base size=40 base align=8
+std::__recursive_mutex_base (0x0x7f23460a1a80) 0
+
+Class std::recursive_mutex
+ size=40 align=8
+ base size=40 base align=8
+std::recursive_mutex (0x0x7f2345fa4dd0) 0
+ std::__recursive_mutex_base (0x0x7f23460a1ae0) 0
+
+Class std::timed_mutex
+ size=40 align=8
+ base size=40 base align=8
+std::timed_mutex (0x0x7f2346095cb0) 0
+ std::__mutex_base (0x0x7f23460a1ea0) 0
+ std::__timed_mutex_impl<std::timed_mutex> (0x0x7f23460a1f00) 0 empty
+
+Class std::recursive_timed_mutex
+ size=40 align=8
+ base size=40 base align=8
+std::recursive_timed_mutex (0x0x7f2345cf0000) 0
+ std::__recursive_mutex_base (0x0x7f2345ce82a0) 0
+ std::__timed_mutex_impl<std::recursive_timed_mutex> (0x0x7f2345ce8300) 0 empty
+
+Class std::once_flag
+ size=4 align=4
+ base size=4 base align=4
+std::once_flag (0x0x7f2345ce8a20) 0
+
+Vtable for __gnu_cxx::__concurrence_lock_error
+__gnu_cxx::__concurrence_lock_error::_ZTVN9__gnu_cxx24__concurrence_lock_errorE: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTIN9__gnu_cxx24__concurrence_lock_errorE)
+16 (int (*)(...))__gnu_cxx::__concurrence_lock_error::~__concurrence_lock_error
+24 (int (*)(...))__gnu_cxx::__concurrence_lock_error::~__concurrence_lock_error
+32 (int (*)(...))__gnu_cxx::__concurrence_lock_error::what
+
+Class __gnu_cxx::__concurrence_lock_error
+ size=8 align=8
+ base size=8 base align=8
+__gnu_cxx::__concurrence_lock_error (0x0x7f2345fa4f08) 0 nearly-empty
+ vptr=((& __gnu_cxx::__concurrence_lock_error::_ZTVN9__gnu_cxx24__concurrence_lock_errorE) + 16)
+ std::exception (0x0x7f2345ce8f60) 0 nearly-empty
+ primary-for __gnu_cxx::__concurrence_lock_error (0x0x7f2345fa4f08)
+
+Vtable for __gnu_cxx::__concurrence_unlock_error
+__gnu_cxx::__concurrence_unlock_error::_ZTVN9__gnu_cxx26__concurrence_unlock_errorE: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTIN9__gnu_cxx26__concurrence_unlock_errorE)
+16 (int (*)(...))__gnu_cxx::__concurrence_unlock_error::~__concurrence_unlock_error
+24 (int (*)(...))__gnu_cxx::__concurrence_unlock_error::~__concurrence_unlock_error
+32 (int (*)(...))__gnu_cxx::__concurrence_unlock_error::what
+
+Class __gnu_cxx::__concurrence_unlock_error
+ size=8 align=8
+ base size=8 base align=8
+__gnu_cxx::__concurrence_unlock_error (0x0x7f2345fa4f70) 0 nearly-empty
+ vptr=((& __gnu_cxx::__concurrence_unlock_error::_ZTVN9__gnu_cxx26__concurrence_unlock_errorE) + 16)
+ std::exception (0x0x7f2345d1e0c0) 0 nearly-empty
+ primary-for __gnu_cxx::__concurrence_unlock_error (0x0x7f2345fa4f70)
+
+Vtable for __gnu_cxx::__concurrence_broadcast_error
+__gnu_cxx::__concurrence_broadcast_error::_ZTVN9__gnu_cxx29__concurrence_broadcast_errorE: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTIN9__gnu_cxx29__concurrence_broadcast_errorE)
+16 (int (*)(...))__gnu_cxx::__concurrence_broadcast_error::~__concurrence_broadcast_error
+24 (int (*)(...))__gnu_cxx::__concurrence_broadcast_error::~__concurrence_broadcast_error
+32 (int (*)(...))__gnu_cxx::__concurrence_broadcast_error::what
+
+Class __gnu_cxx::__concurrence_broadcast_error
+ size=8 align=8
+ base size=8 base align=8
+__gnu_cxx::__concurrence_broadcast_error (0x0x7f2345d25000) 0 nearly-empty
+ vptr=((& __gnu_cxx::__concurrence_broadcast_error::_ZTVN9__gnu_cxx29__concurrence_broadcast_errorE) + 16)
+ std::exception (0x0x7f2345d1e1e0) 0 nearly-empty
+ primary-for __gnu_cxx::__concurrence_broadcast_error (0x0x7f2345d25000)
+
+Vtable for __gnu_cxx::__concurrence_wait_error
+__gnu_cxx::__concurrence_wait_error::_ZTVN9__gnu_cxx24__concurrence_wait_errorE: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTIN9__gnu_cxx24__concurrence_wait_errorE)
+16 (int (*)(...))__gnu_cxx::__concurrence_wait_error::~__concurrence_wait_error
+24 (int (*)(...))__gnu_cxx::__concurrence_wait_error::~__concurrence_wait_error
+32 (int (*)(...))__gnu_cxx::__concurrence_wait_error::what
+
+Class __gnu_cxx::__concurrence_wait_error
+ size=8 align=8
+ base size=8 base align=8
+__gnu_cxx::__concurrence_wait_error (0x0x7f2345d250d0) 0 nearly-empty
+ vptr=((& __gnu_cxx::__concurrence_wait_error::_ZTVN9__gnu_cxx24__concurrence_wait_errorE) + 16)
+ std::exception (0x0x7f2345d1e300) 0 nearly-empty
+ primary-for __gnu_cxx::__concurrence_wait_error (0x0x7f2345d250d0)
+
+Class __gnu_cxx::__mutex
+ size=40 align=8
+ base size=40 base align=8
+__gnu_cxx::__mutex (0x0x7f2345d46360) 0
+
+Class __gnu_cxx::__recursive_mutex
+ size=40 align=8
+ base size=40 base align=8
+__gnu_cxx::__recursive_mutex (0x0x7f2345d46660) 0
+
+Class __gnu_cxx::__scoped_lock
+ size=8 align=8
+ base size=8 base align=8
+__gnu_cxx::__scoped_lock (0x0x7f2345d46960) 0
+
+Class __gnu_cxx::__cond
+ size=48 align=8
+ base size=48 base align=8
+__gnu_cxx::__cond (0x0x7f2345d46cc0) 0
+
+Vtable for std::bad_weak_ptr
+std::bad_weak_ptr::_ZTVSt12bad_weak_ptr: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt12bad_weak_ptr)
+16 (int (*)(...))std::bad_weak_ptr::~bad_weak_ptr
+24 (int (*)(...))std::bad_weak_ptr::~bad_weak_ptr
+32 (int (*)(...))std::bad_weak_ptr::what
+
+Class std::bad_weak_ptr
+ size=8 align=8
+ base size=8 base align=8
+std::bad_weak_ptr (0x0x7f2345d25138) 0 nearly-empty
+ vptr=((& std::bad_weak_ptr::_ZTVSt12bad_weak_ptr) + 16)
+ std::exception (0x0x7f2345dc2ea0) 0 nearly-empty
+ primary-for std::bad_weak_ptr (0x0x7f2345d25138)
+
+Class std::_Sp_make_shared_tag
+ size=1 align=1
+ base size=0 base align=1
+std::_Sp_make_shared_tag (0x0x7f2345e24e40) 0 empty
+
+Class std::__sp_array_delete
+ size=1 align=1
+ base size=0 base align=1
+std::__sp_array_delete (0x0x7f2345e522a0) 0 empty
+
+Class std::_Sp_locker
+ size=2 align=1
+ base size=2 base align=1
+std::_Sp_locker (0x0x7f2345b9a120) 0
+
+Vtable for std::thread::_State
+std::thread::_State::_ZTVNSt6thread6_StateE: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTINSt6thread6_StateE)
+16 0
+24 0
+32 (int (*)(...))__cxa_pure_virtual
+
+Class std::thread::_State
+ size=8 align=8
+ base size=8 base align=8
+std::thread::_State (0x0x7f2345bc55a0) 0 nearly-empty
+ vptr=((& std::thread::_State::_ZTVNSt6thread6_StateE) + 16)
+
+Class std::thread::id
+ size=8 align=8
+ base size=8 base align=8
+std::thread::id (0x0x7f2345bc5600) 0
+
+Class std::thread
+ size=8 align=8
+ base size=8 base align=8
+std::thread (0x0x7f2345bc5540) 0
+
+Class std::condition_variable
+ size=48 align=8
+ base size=48 base align=8
+std::condition_variable (0x0x7f2345a569c0) 0
+
+Class std::__at_thread_exit_elt
+ size=16 align=8
+ base size=16 base align=8
+std::__at_thread_exit_elt (0x0x7f2345a56d80) 0
+
+Class std::_V2::condition_variable_any
+ size=64 align=8
+ base size=64 base align=8
+std::_V2::condition_variable_any (0x0x7f2345a56de0) 0
+
+Class std::__atomic_futex_unsigned_base
+ size=1 align=1
+ base size=0 base align=1
+std::__atomic_futex_unsigned_base (0x0x7f2345812120) 0 empty
+
+Vtable for std::future_error
+std::future_error::_ZTVSt12future_error: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt12future_error)
+16 (int (*)(...))std::future_error::~future_error
+24 (int (*)(...))std::future_error::~future_error
+32 (int (*)(...))std::future_error::what
+
+Class std::future_error
+ size=32 align=8
+ base size=32 base align=8
+std::future_error (0x0x7f23457f89c0) 0
+ vptr=((& std::future_error::_ZTVSt12future_error) + 16)
+ std::logic_error (0x0x7f23457f8a28) 0
+ primary-for std::future_error (0x0x7f23457f89c0)
+ std::exception (0x0x7f2345812840) 0 nearly-empty
+ primary-for std::logic_error (0x0x7f23457f8a28)
+
+Class std::__future_base::_Result_base::_Deleter
+ size=1 align=1
+ base size=0 base align=1
+std::__future_base::_Result_base::_Deleter (0x0x7f2345812f60) 0 empty
+
+Vtable for std::__future_base::_Result_base
+std::__future_base::_Result_base::_ZTVNSt13__future_base12_Result_baseE: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTINSt13__future_base12_Result_baseE)
+16 (int (*)(...))__cxa_pure_virtual
+24 0
+32 0
+
+Class std::__future_base::_Result_base
+ size=16 align=8
+ base size=16 base align=8
+std::__future_base::_Result_base (0x0x7f2345812f00) 0
+ vptr=((& std::__future_base::_Result_base::_ZTVNSt13__future_base12_Result_baseE) + 16)
+
+Class std::__future_base::_State_baseV2::__exception_ptr_tag
+ size=1 align=1
+ base size=0 base align=1
+std::__future_base::_State_baseV2::__exception_ptr_tag (0x0x7f234561c6c0) 0 empty
+
+Class std::__future_base::_State_baseV2::_Make_ready
+ size=32 align=8
+ base size=32 base align=8
+std::__future_base::_State_baseV2::_Make_ready (0x0x7f234561f270) 0
+ std::__at_thread_exit_elt (0x0x7f234561c780) 0
+
+Vtable for std::__future_base::_State_baseV2
+std::__future_base::_State_baseV2::_ZTVNSt13__future_base13_State_baseV2E: 6 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTINSt13__future_base13_State_baseV2E)
+16 (int (*)(...))std::__future_base::_State_baseV2::~_State_baseV2
+24 (int (*)(...))std::__future_base::_State_baseV2::~_State_baseV2
+32 (int (*)(...))std::__future_base::_State_baseV2::_M_complete_async
+40 (int (*)(...))std::__future_base::_State_baseV2::_M_is_deferred_future
+
+Class std::__future_base::_State_baseV2
+ size=32 align=8
+ base size=28 base align=8
+std::__future_base::_State_baseV2 (0x0x7f2345846120) 0
+ vptr=((& std::__future_base::_State_baseV2::_ZTVNSt13__future_base13_State_baseV2E) + 16)
+
+Class std::__future_base
+ size=1 align=1
+ base size=0 base align=1
+std::__future_base (0x0x7f2345812ea0) 0 empty
+
+Vtable for std::__future_base::_Async_state_commonV2
+std::__future_base::_Async_state_commonV2::_ZTVNSt13__future_base21_Async_state_commonV2E: 6 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTINSt13__future_base21_Async_state_commonV2E)
+16 (int (*)(...))std::__future_base::_Async_state_commonV2::~_Async_state_commonV2
+24 (int (*)(...))std::__future_base::_Async_state_commonV2::~_Async_state_commonV2
+32 (int (*)(...))std::__future_base::_Async_state_commonV2::_M_complete_async
+40 (int (*)(...))std::__future_base::_State_baseV2::_M_is_deferred_future
+
+Class std::__future_base::_Async_state_commonV2
+ size=48 align=8
+ base size=44 base align=8
+std::__future_base::_Async_state_commonV2 (0x0x7f2344d7ef70) 0
+ vptr=((& std::__future_base::_Async_state_commonV2::_ZTVNSt13__future_base21_Async_state_commonV2E) + 16)
+ std::__future_base::_State_baseV2 (0x0x7f2344dcc780) 0
+ primary-for std::__future_base::_Async_state_commonV2 (0x0x7f2344d7ef70)
+
+Class QThread::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QThread::QPrivateSignal (0x0x7f2344df9060) 0 empty
+
+Vtable for QThread
+QThread::_ZTV7QThread: 15 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI7QThread)
+16 (int (*)(...))QThread::metaObject
+24 (int (*)(...))QThread::qt_metacast
+32 (int (*)(...))QThread::qt_metacall
+40 (int (*)(...))QThread::~QThread
+48 (int (*)(...))QThread::~QThread
+56 (int (*)(...))QThread::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QThread::run
+
+Class QThread
+ size=16 align=8
+ base size=16 base align=8
+QThread (0x0x7f2344de02d8) 0
+ vptr=((& QThread::_ZTV7QThread) + 16)
+ QObject (0x0x7f2344df9000) 0
+ primary-for QThread (0x0x7f2344de02d8)
+
+Class QThreadPool::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QThreadPool::QPrivateSignal (0x0x7f2344df9420) 0 empty
+
+Vtable for QThreadPool
+QThreadPool::_ZTV11QThreadPool: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI11QThreadPool)
+16 (int (*)(...))QThreadPool::metaObject
+24 (int (*)(...))QThreadPool::qt_metacast
+32 (int (*)(...))QThreadPool::qt_metacall
+40 (int (*)(...))QThreadPool::~QThreadPool
+48 (int (*)(...))QThreadPool::~QThreadPool
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QThreadPool
+ size=16 align=8
+ base size=16 base align=8
+QThreadPool (0x0x7f2344de0340) 0
+ vptr=((& QThreadPool::_ZTV11QThreadPool) + 16)
+ QObject (0x0x7f2344df93c0) 0
+ primary-for QThreadPool (0x0x7f2344de0340)
+
+Class QThreadStorageData
+ size=4 align=4
+ base size=4 base align=4
+QThreadStorageData (0x0x7f2344df9600) 0
+
+Class QTimeLine::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QTimeLine::QPrivateSignal (0x0x7f2344df9cc0) 0 empty
+
+Vtable for QTimeLine
+QTimeLine::_ZTV9QTimeLine: 15 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI9QTimeLine)
+16 (int (*)(...))QTimeLine::metaObject
+24 (int (*)(...))QTimeLine::qt_metacast
+32 (int (*)(...))QTimeLine::qt_metacall
+40 (int (*)(...))QTimeLine::~QTimeLine
+48 (int (*)(...))QTimeLine::~QTimeLine
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QTimeLine::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QTimeLine::valueForTime
+
+Class QTimeLine
+ size=16 align=8
+ base size=16 base align=8
+QTimeLine (0x0x7f2344de03a8) 0
+ vptr=((& QTimeLine::_ZTV9QTimeLine) + 16)
+ QObject (0x0x7f2344df9c60) 0
+ primary-for QTimeLine (0x0x7f2344de03a8)
+
+Class QTimer::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QTimer::QPrivateSignal (0x0x7f2344df9f00) 0 empty
+
+Vtable for QTimer
+QTimer::_ZTV6QTimer: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI6QTimer)
+16 (int (*)(...))QTimer::metaObject
+24 (int (*)(...))QTimer::qt_metacast
+32 (int (*)(...))QTimer::qt_metacall
+40 (int (*)(...))QTimer::~QTimer
+48 (int (*)(...))QTimer::~QTimer
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QTimer::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QTimer
+ size=32 align=8
+ base size=29 base align=8
+QTimer (0x0x7f2344de0410) 0
+ vptr=((& QTimer::_ZTV6QTimer) + 16)
+ QObject (0x0x7f2344df9ea0) 0
+ primary-for QTimer (0x0x7f2344de0410)
+
+Class QTimeZone::OffsetData
+ size=32 align=8
+ base size=28 base align=8
+QTimeZone::OffsetData (0x0x7f2344e7f8a0) 0
+
+Class QTimeZone
+ size=8 align=8
+ base size=8 base align=8
+QTimeZone (0x0x7f2344e7f840) 0
+
+Class QTranslator::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QTranslator::QPrivateSignal (0x0x7f2344b24960) 0 empty
+
+Vtable for QTranslator
+QTranslator::_ZTV11QTranslator: 16 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI11QTranslator)
+16 (int (*)(...))QTranslator::metaObject
+24 (int (*)(...))QTranslator::qt_metacast
+32 (int (*)(...))QTranslator::qt_metacall
+40 (int (*)(...))QTranslator::~QTranslator
+48 (int (*)(...))QTranslator::~QTranslator
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QTranslator::translate
+120 (int (*)(...))QTranslator::isEmpty
+
+Class QTranslator
+ size=16 align=8
+ base size=16 base align=8
+QTranslator (0x0x7f2344b21af8) 0
+ vptr=((& QTranslator::_ZTV11QTranslator) + 16)
+ QObject (0x0x7f2344b24900) 0
+ primary-for QTranslator (0x0x7f2344b21af8)
+
+Class QTransposeProxyModel::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QTransposeProxyModel::QPrivateSignal (0x0x7f2344b24ba0) 0 empty
+
+Vtable for QTransposeProxyModel
+QTransposeProxyModel::_ZTV20QTransposeProxyModel: 53 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI20QTransposeProxyModel)
+16 (int (*)(...))QTransposeProxyModel::metaObject
+24 (int (*)(...))QTransposeProxyModel::qt_metacast
+32 (int (*)(...))QTransposeProxyModel::qt_metacall
+40 (int (*)(...))QTransposeProxyModel::~QTransposeProxyModel
+48 (int (*)(...))QTransposeProxyModel::~QTransposeProxyModel
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QTransposeProxyModel::index
+120 (int (*)(...))QTransposeProxyModel::parent
+128 (int (*)(...))QAbstractProxyModel::sibling
+136 (int (*)(...))QTransposeProxyModel::rowCount
+144 (int (*)(...))QTransposeProxyModel::columnCount
+152 (int (*)(...))QAbstractProxyModel::hasChildren
+160 (int (*)(...))QAbstractProxyModel::data
+168 (int (*)(...))QAbstractProxyModel::setData
+176 (int (*)(...))QTransposeProxyModel::headerData
+184 (int (*)(...))QTransposeProxyModel::setHeaderData
+192 (int (*)(...))QTransposeProxyModel::itemData
+200 (int (*)(...))QTransposeProxyModel::setItemData
+208 (int (*)(...))QAbstractProxyModel::mimeTypes
+216 (int (*)(...))QAbstractProxyModel::mimeData
+224 (int (*)(...))QAbstractProxyModel::canDropMimeData
+232 (int (*)(...))QAbstractProxyModel::dropMimeData
+240 (int (*)(...))QAbstractProxyModel::supportedDropActions
+248 (int (*)(...))QAbstractProxyModel::supportedDragActions
+256 (int (*)(...))QTransposeProxyModel::insertRows
+264 (int (*)(...))QTransposeProxyModel::insertColumns
+272 (int (*)(...))QTransposeProxyModel::removeRows
+280 (int (*)(...))QTransposeProxyModel::removeColumns
+288 (int (*)(...))QTransposeProxyModel::moveRows
+296 (int (*)(...))QTransposeProxyModel::moveColumns
+304 (int (*)(...))QAbstractProxyModel::fetchMore
+312 (int (*)(...))QAbstractProxyModel::canFetchMore
+320 (int (*)(...))QAbstractProxyModel::flags
+328 (int (*)(...))QTransposeProxyModel::sort
+336 (int (*)(...))QAbstractProxyModel::buddy
+344 (int (*)(...))QAbstractItemModel::match
+352 (int (*)(...))QTransposeProxyModel::span
+360 (int (*)(...))QAbstractItemModel::roleNames
+368 (int (*)(...))QAbstractProxyModel::submit
+376 (int (*)(...))QAbstractProxyModel::revert
+384 (int (*)(...))QTransposeProxyModel::setSourceModel
+392 (int (*)(...))QTransposeProxyModel::mapToSource
+400 (int (*)(...))QTransposeProxyModel::mapFromSource
+408 (int (*)(...))QAbstractProxyModel::mapSelectionToSource
+416 (int (*)(...))QAbstractProxyModel::mapSelectionFromSource
+
+Class QTransposeProxyModel
+ size=16 align=8
+ base size=16 base align=8
+QTransposeProxyModel (0x0x7f2344b21b60) 0
+ vptr=((& QTransposeProxyModel::_ZTV20QTransposeProxyModel) + 16)
+ QAbstractProxyModel (0x0x7f2344b21bc8) 0
+ primary-for QTransposeProxyModel (0x0x7f2344b21b60)
+ QAbstractItemModel (0x0x7f2344b21c30) 0
+ primary-for QAbstractProxyModel (0x0x7f2344b21bc8)
+ QObject (0x0x7f2344b24b40) 0
+ primary-for QAbstractItemModel (0x0x7f2344b21c30)
+
+Class QUrlQuery
+ size=8 align=8
+ base size=8 base align=8
+QUrlQuery (0x0x7f2344b24d80) 0
+
+Class QWaitCondition
+ size=8 align=8
+ base size=8 base align=8
+QWaitCondition (0x0x7f2344bc6780) 0
+
+Class QXmlStreamStringRef
+ size=16 align=8
+ base size=16 base align=8
+QXmlStreamStringRef (0x0x7f2344bc68a0) 0
+
+Class QXmlStreamAttribute
+ size=80 align=8
+ base size=73 base align=8
+QXmlStreamAttribute (0x0x7f2344c55c60) 0
+
+Class QXmlStreamAttributes
+ size=8 align=8
+ base size=8 base align=8
+QXmlStreamAttributes (0x0x7f23448d32d8) 0
+ QVector<QXmlStreamAttribute> (0x0x7f23448d23c0) 0
+
+Class QXmlStreamNamespaceDeclaration
+ size=40 align=8
+ base size=40 base align=8
+QXmlStreamNamespaceDeclaration (0x0x7f23448d26c0) 0
+
+Class QXmlStreamNotationDeclaration
+ size=56 align=8
+ base size=56 base align=8
+QXmlStreamNotationDeclaration (0x0x7f2344956660) 0
+
+Class QXmlStreamEntityDeclaration
+ size=88 align=8
+ base size=88 base align=8
+QXmlStreamEntityDeclaration (0x0x7f23449b4660) 0
+
+Vtable for QXmlStreamEntityResolver
+QXmlStreamEntityResolver::_ZTV24QXmlStreamEntityResolver: 6 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI24QXmlStreamEntityResolver)
+16 (int (*)(...))QXmlStreamEntityResolver::~QXmlStreamEntityResolver
+24 (int (*)(...))QXmlStreamEntityResolver::~QXmlStreamEntityResolver
+32 (int (*)(...))QXmlStreamEntityResolver::resolveEntity
+40 (int (*)(...))QXmlStreamEntityResolver::resolveUndeclaredEntity
+
+Class QXmlStreamEntityResolver
+ size=8 align=8
+ base size=8 base align=8
+QXmlStreamEntityResolver (0x0x7f2344a1e720) 0 nearly-empty
+ vptr=((& QXmlStreamEntityResolver::_ZTV24QXmlStreamEntityResolver) + 16)
+
+Class QXmlStreamReader
+ size=8 align=8
+ base size=8 base align=8
+QXmlStreamReader (0x0x7f2344a1e780) 0
+
+Class QXmlStreamWriter
+ size=8 align=8
+ base size=8 base align=8
+QXmlStreamWriter (0x0x7f2344a7a660) 0
+
+Class QNetworkRequest
+ size=8 align=8
+ base size=8 base align=8
+QNetworkRequest (0x0x7f2344a7a840) 0
+
+Class QNetworkCacheMetaData
+ size=8 align=8
+ base size=8 base align=8
+QNetworkCacheMetaData (0x0x7f23447001e0) 0
+
+Class QAbstractNetworkCache::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QAbstractNetworkCache::QPrivateSignal (0x0x7f234475f780) 0 empty
+
+Vtable for QAbstractNetworkCache
+QAbstractNetworkCache::_ZTV21QAbstractNetworkCache: 22 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI21QAbstractNetworkCache)
+16 (int (*)(...))QAbstractNetworkCache::metaObject
+24 (int (*)(...))QAbstractNetworkCache::qt_metacast
+32 (int (*)(...))QAbstractNetworkCache::qt_metacall
+40 0
+48 0
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))__cxa_pure_virtual
+120 (int (*)(...))__cxa_pure_virtual
+128 (int (*)(...))__cxa_pure_virtual
+136 (int (*)(...))__cxa_pure_virtual
+144 (int (*)(...))__cxa_pure_virtual
+152 (int (*)(...))__cxa_pure_virtual
+160 (int (*)(...))__cxa_pure_virtual
+168 (int (*)(...))__cxa_pure_virtual
+
+Class QAbstractNetworkCache
+ size=16 align=8
+ base size=16 base align=8
+QAbstractNetworkCache (0x0x7f234474fb60) 0
+ vptr=((& QAbstractNetworkCache::_ZTV21QAbstractNetworkCache) + 16)
+ QObject (0x0x7f234475f720) 0
+ primary-for QAbstractNetworkCache (0x0x7f234474fb60)
+
+Class QAbstractSocket::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QAbstractSocket::QPrivateSignal (0x0x7f234475f9c0) 0 empty
+
+Vtable for QAbstractSocket
+QAbstractSocket::_ZTV15QAbstractSocket: 41 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI15QAbstractSocket)
+16 (int (*)(...))QAbstractSocket::metaObject
+24 (int (*)(...))QAbstractSocket::qt_metacast
+32 (int (*)(...))QAbstractSocket::qt_metacall
+40 (int (*)(...))QAbstractSocket::~QAbstractSocket
+48 (int (*)(...))QAbstractSocket::~QAbstractSocket
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QAbstractSocket::isSequential
+120 (int (*)(...))QIODevice::open
+128 (int (*)(...))QAbstractSocket::close
+136 (int (*)(...))QIODevice::pos
+144 (int (*)(...))QIODevice::size
+152 (int (*)(...))QIODevice::seek
+160 (int (*)(...))QAbstractSocket::atEnd
+168 (int (*)(...))QIODevice::reset
+176 (int (*)(...))QAbstractSocket::bytesAvailable
+184 (int (*)(...))QAbstractSocket::bytesToWrite
+192 (int (*)(...))QAbstractSocket::canReadLine
+200 (int (*)(...))QAbstractSocket::waitForReadyRead
+208 (int (*)(...))QAbstractSocket::waitForBytesWritten
+216 (int (*)(...))QAbstractSocket::readData
+224 (int (*)(...))QAbstractSocket::readLineData
+232 (int (*)(...))QAbstractSocket::writeData
+240 (int (*)(...))QAbstractSocket::resume
+248 (int (*)(...))QAbstractSocket::connectToHost
+256 (int (*)(...))QAbstractSocket::connectToHost
+264 (int (*)(...))QAbstractSocket::disconnectFromHost
+272 (int (*)(...))QAbstractSocket::setReadBufferSize
+280 (int (*)(...))QAbstractSocket::socketDescriptor
+288 (int (*)(...))QAbstractSocket::setSocketDescriptor
+296 (int (*)(...))QAbstractSocket::setSocketOption
+304 (int (*)(...))QAbstractSocket::socketOption
+312 (int (*)(...))QAbstractSocket::waitForConnected
+320 (int (*)(...))QAbstractSocket::waitForDisconnected
+
+Class QAbstractSocket
+ size=16 align=8
+ base size=16 base align=8
+QAbstractSocket (0x0x7f234474fbc8) 0
+ vptr=((& QAbstractSocket::_ZTV15QAbstractSocket) + 16)
+ QIODevice (0x0x7f234474fc30) 0
+ primary-for QAbstractSocket (0x0x7f234474fbc8)
+ QObject (0x0x7f234475f960) 0
+ primary-for QIODevice (0x0x7f234474fc30)
+
+Class QAuthenticator
+ size=8 align=8
+ base size=8 base align=8
+QAuthenticator (0x0x7f2344805120) 0
+
+Class QDnsDomainNameRecord
+ size=8 align=8
+ base size=8 base align=8
+QDnsDomainNameRecord (0x0x7f23448051e0) 0
+
+Class QDnsHostAddressRecord
+ size=8 align=8
+ base size=8 base align=8
+QDnsHostAddressRecord (0x0x7f234485b360) 0
+
+Class QDnsMailExchangeRecord
+ size=8 align=8
+ base size=8 base align=8
+QDnsMailExchangeRecord (0x0x7f23448a54e0) 0
+
+Class QDnsServiceRecord
+ size=8 align=8
+ base size=8 base align=8
+QDnsServiceRecord (0x0x7f23443f55a0) 0
+
+Class QDnsTextRecord
+ size=8 align=8
+ base size=8 base align=8
+QDnsTextRecord (0x0x7f234443f840) 0
+
+Class QDnsLookup::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QDnsLookup::QPrivateSignal (0x0x7f2344480d80) 0 empty
+
+Vtable for QDnsLookup
+QDnsLookup::_ZTV10QDnsLookup: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI10QDnsLookup)
+16 (int (*)(...))QDnsLookup::metaObject
+24 (int (*)(...))QDnsLookup::qt_metacast
+32 (int (*)(...))QDnsLookup::qt_metacall
+40 (int (*)(...))QDnsLookup::~QDnsLookup
+48 (int (*)(...))QDnsLookup::~QDnsLookup
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QDnsLookup
+ size=16 align=8
+ base size=16 base align=8
+QDnsLookup (0x0x7f2344496208) 0
+ vptr=((& QDnsLookup::_ZTV10QDnsLookup) + 16)
+ QObject (0x0x7f2344480d20) 0
+ primary-for QDnsLookup (0x0x7f2344496208)
+
+Class QTcpSocket::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QTcpSocket::QPrivateSignal (0x0x7f23444b1180) 0 empty
+
+Vtable for QTcpSocket
+QTcpSocket::_ZTV10QTcpSocket: 41 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI10QTcpSocket)
+16 (int (*)(...))QTcpSocket::metaObject
+24 (int (*)(...))QTcpSocket::qt_metacast
+32 (int (*)(...))QTcpSocket::qt_metacall
+40 (int (*)(...))QTcpSocket::~QTcpSocket
+48 (int (*)(...))QTcpSocket::~QTcpSocket
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QAbstractSocket::isSequential
+120 (int (*)(...))QIODevice::open
+128 (int (*)(...))QAbstractSocket::close
+136 (int (*)(...))QIODevice::pos
+144 (int (*)(...))QIODevice::size
+152 (int (*)(...))QIODevice::seek
+160 (int (*)(...))QAbstractSocket::atEnd
+168 (int (*)(...))QIODevice::reset
+176 (int (*)(...))QAbstractSocket::bytesAvailable
+184 (int (*)(...))QAbstractSocket::bytesToWrite
+192 (int (*)(...))QAbstractSocket::canReadLine
+200 (int (*)(...))QAbstractSocket::waitForReadyRead
+208 (int (*)(...))QAbstractSocket::waitForBytesWritten
+216 (int (*)(...))QAbstractSocket::readData
+224 (int (*)(...))QAbstractSocket::readLineData
+232 (int (*)(...))QAbstractSocket::writeData
+240 (int (*)(...))QAbstractSocket::resume
+248 (int (*)(...))QAbstractSocket::connectToHost
+256 (int (*)(...))QAbstractSocket::connectToHost
+264 (int (*)(...))QAbstractSocket::disconnectFromHost
+272 (int (*)(...))QAbstractSocket::setReadBufferSize
+280 (int (*)(...))QAbstractSocket::socketDescriptor
+288 (int (*)(...))QAbstractSocket::setSocketDescriptor
+296 (int (*)(...))QAbstractSocket::setSocketOption
+304 (int (*)(...))QAbstractSocket::socketOption
+312 (int (*)(...))QAbstractSocket::waitForConnected
+320 (int (*)(...))QAbstractSocket::waitForDisconnected
+
+Class QTcpSocket
+ size=16 align=8
+ base size=16 base align=8
+QTcpSocket (0x0x7f2344496270) 0
+ vptr=((& QTcpSocket::_ZTV10QTcpSocket) + 16)
+ QAbstractSocket (0x0x7f23444962d8) 0
+ primary-for QTcpSocket (0x0x7f2344496270)
+ QIODevice (0x0x7f2344496340) 0
+ primary-for QAbstractSocket (0x0x7f23444962d8)
+ QObject (0x0x7f23444b1120) 0
+ primary-for QIODevice (0x0x7f2344496340)
+
+Class QSslCertificate
+ size=8 align=8
+ base size=8 base align=8
+QSslCertificate (0x0x7f23444b1a20) 0
+
+Class QSslError
+ size=8 align=8
+ base size=8 base align=8
+QSslError (0x0x7f23445511e0) 0
+
+Class QSslSocket::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QSslSocket::QPrivateSignal (0x0x7f23442193c0) 0 empty
+
+Vtable for QSslSocket
+QSslSocket::_ZTV10QSslSocket: 41 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI10QSslSocket)
+16 (int (*)(...))QSslSocket::metaObject
+24 (int (*)(...))QSslSocket::qt_metacast
+32 (int (*)(...))QSslSocket::qt_metacall
+40 (int (*)(...))QSslSocket::~QSslSocket
+48 (int (*)(...))QSslSocket::~QSslSocket
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QAbstractSocket::isSequential
+120 (int (*)(...))QIODevice::open
+128 (int (*)(...))QSslSocket::close
+136 (int (*)(...))QIODevice::pos
+144 (int (*)(...))QIODevice::size
+152 (int (*)(...))QIODevice::seek
+160 (int (*)(...))QSslSocket::atEnd
+168 (int (*)(...))QIODevice::reset
+176 (int (*)(...))QSslSocket::bytesAvailable
+184 (int (*)(...))QSslSocket::bytesToWrite
+192 (int (*)(...))QSslSocket::canReadLine
+200 (int (*)(...))QSslSocket::waitForReadyRead
+208 (int (*)(...))QSslSocket::waitForBytesWritten
+216 (int (*)(...))QSslSocket::readData
+224 (int (*)(...))QAbstractSocket::readLineData
+232 (int (*)(...))QSslSocket::writeData
+240 (int (*)(...))QSslSocket::resume
+248 (int (*)(...))QSslSocket::connectToHost
+256 (int (*)(...))QAbstractSocket::connectToHost
+264 (int (*)(...))QSslSocket::disconnectFromHost
+272 (int (*)(...))QSslSocket::setReadBufferSize
+280 (int (*)(...))QAbstractSocket::socketDescriptor
+288 (int (*)(...))QSslSocket::setSocketDescriptor
+296 (int (*)(...))QSslSocket::setSocketOption
+304 (int (*)(...))QSslSocket::socketOption
+312 (int (*)(...))QSslSocket::waitForConnected
+320 (int (*)(...))QSslSocket::waitForDisconnected
+
+Class QSslSocket
+ size=16 align=8
+ base size=16 base align=8
+QSslSocket (0x0x7f234420e680) 0
+ vptr=((& QSslSocket::_ZTV10QSslSocket) + 16)
+ QTcpSocket (0x0x7f234420e6e8) 0
+ primary-for QSslSocket (0x0x7f234420e680)
+ QAbstractSocket (0x0x7f234420e750) 0
+ primary-for QTcpSocket (0x0x7f234420e6e8)
+ QIODevice (0x0x7f234420e7b8) 0
+ primary-for QAbstractSocket (0x0x7f234420e750)
+ QObject (0x0x7f2344219360) 0
+ primary-for QIODevice (0x0x7f234420e7b8)
+
+Class QDtlsClientVerifier::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QDtlsClientVerifier::QPrivateSignal (0x0x7f2344219600) 0 empty
+
+Class QDtlsClientVerifier::GeneratorParameters
+ size=16 align=8
+ base size=16 base align=8
+QDtlsClientVerifier::GeneratorParameters (0x0x7f2344219660) 0
+
+Vtable for QDtlsClientVerifier
+QDtlsClientVerifier::_ZTV19QDtlsClientVerifier: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI19QDtlsClientVerifier)
+16 (int (*)(...))QDtlsClientVerifier::metaObject
+24 (int (*)(...))QDtlsClientVerifier::qt_metacast
+32 (int (*)(...))QDtlsClientVerifier::qt_metacall
+40 (int (*)(...))QDtlsClientVerifier::~QDtlsClientVerifier
+48 (int (*)(...))QDtlsClientVerifier::~QDtlsClientVerifier
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QDtlsClientVerifier
+ size=16 align=8
+ base size=16 base align=8
+QDtlsClientVerifier (0x0x7f234420e820) 0
+ vptr=((& QDtlsClientVerifier::_ZTV19QDtlsClientVerifier) + 16)
+ QObject (0x0x7f23442195a0) 0
+ primary-for QDtlsClientVerifier (0x0x7f234420e820)
+
+Class QDtls::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QDtls::QPrivateSignal (0x0x7f23442198a0) 0 empty
+
+Vtable for QDtls
+QDtls::_ZTV5QDtls: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI5QDtls)
+16 (int (*)(...))QDtls::metaObject
+24 (int (*)(...))QDtls::qt_metacast
+32 (int (*)(...))QDtls::qt_metacall
+40 (int (*)(...))QDtls::~QDtls
+48 (int (*)(...))QDtls::~QDtls
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QDtls
+ size=16 align=8
+ base size=16 base align=8
+QDtls (0x0x7f234420e888) 0
+ vptr=((& QDtls::_ZTV5QDtls) + 16)
+ QObject (0x0x7f2344219840) 0
+ primary-for QDtls (0x0x7f234420e888)
+
+Class QIPv6Address
+ size=16 align=1
+ base size=16 base align=1
+QIPv6Address (0x0x7f2344219ae0) 0
+
+Class QHostAddress
+ size=8 align=8
+ base size=8 base align=8
+QHostAddress (0x0x7f2344219c00) 0
+
+Class QHostInfo
+ size=8 align=8
+ base size=8 base align=8
+QHostInfo (0x0x7f234431f9c0) 0
+
+Class QHstsPolicy
+ size=8 align=8
+ base size=8 base align=8
+QHstsPolicy (0x0x7f2343fef7e0) 0
+
+Class QHttpPart
+ size=8 align=8
+ base size=8 base align=8
+QHttpPart (0x0x7f234407e420) 0
+
+Class QHttpMultiPart::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QHttpMultiPart::QPrivateSignal (0x0x7f23440cf5a0) 0 empty
+
+Vtable for QHttpMultiPart
+QHttpMultiPart::_ZTV14QHttpMultiPart: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI14QHttpMultiPart)
+16 (int (*)(...))QHttpMultiPart::metaObject
+24 (int (*)(...))QHttpMultiPart::qt_metacast
+32 (int (*)(...))QHttpMultiPart::qt_metacall
+40 (int (*)(...))QHttpMultiPart::~QHttpMultiPart
+48 (int (*)(...))QHttpMultiPart::~QHttpMultiPart
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QHttpMultiPart
+ size=16 align=8
+ base size=16 base align=8
+QHttpMultiPart (0x0x7f23440cc680) 0
+ vptr=((& QHttpMultiPart::_ZTV14QHttpMultiPart) + 16)
+ QObject (0x0x7f23440cf540) 0
+ primary-for QHttpMultiPart (0x0x7f23440cc680)
+
+Class QLocalServer::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QLocalServer::QPrivateSignal (0x0x7f23440cf7e0) 0 empty
+
+Vtable for QLocalServer
+QLocalServer::_ZTV12QLocalServer: 17 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI12QLocalServer)
+16 (int (*)(...))QLocalServer::metaObject
+24 (int (*)(...))QLocalServer::qt_metacast
+32 (int (*)(...))QLocalServer::qt_metacall
+40 (int (*)(...))QLocalServer::~QLocalServer
+48 (int (*)(...))QLocalServer::~QLocalServer
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QLocalServer::hasPendingConnections
+120 (int (*)(...))QLocalServer::nextPendingConnection
+128 (int (*)(...))QLocalServer::incomingConnection
+
+Class QLocalServer
+ size=16 align=8
+ base size=16 base align=8
+QLocalServer (0x0x7f23440cc6e8) 0
+ vptr=((& QLocalServer::_ZTV12QLocalServer) + 16)
+ QObject (0x0x7f23440cf780) 0
+ primary-for QLocalServer (0x0x7f23440cc6e8)
+
+Class QLocalSocket::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QLocalSocket::QPrivateSignal (0x0x7f234411c2a0) 0 empty
+
+Vtable for QLocalSocket
+QLocalSocket::_ZTV12QLocalSocket: 30 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI12QLocalSocket)
+16 (int (*)(...))QLocalSocket::metaObject
+24 (int (*)(...))QLocalSocket::qt_metacast
+32 (int (*)(...))QLocalSocket::qt_metacall
+40 (int (*)(...))QLocalSocket::~QLocalSocket
+48 (int (*)(...))QLocalSocket::~QLocalSocket
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QLocalSocket::isSequential
+120 (int (*)(...))QLocalSocket::open
+128 (int (*)(...))QLocalSocket::close
+136 (int (*)(...))QIODevice::pos
+144 (int (*)(...))QIODevice::size
+152 (int (*)(...))QIODevice::seek
+160 (int (*)(...))QIODevice::atEnd
+168 (int (*)(...))QIODevice::reset
+176 (int (*)(...))QLocalSocket::bytesAvailable
+184 (int (*)(...))QLocalSocket::bytesToWrite
+192 (int (*)(...))QLocalSocket::canReadLine
+200 (int (*)(...))QLocalSocket::waitForReadyRead
+208 (int (*)(...))QLocalSocket::waitForBytesWritten
+216 (int (*)(...))QLocalSocket::readData
+224 (int (*)(...))QIODevice::readLineData
+232 (int (*)(...))QLocalSocket::writeData
+
+Class QLocalSocket
+ size=16 align=8
+ base size=16 base align=8
+QLocalSocket (0x0x7f23440cc888) 0
+ vptr=((& QLocalSocket::_ZTV12QLocalSocket) + 16)
+ QIODevice (0x0x7f23440cc8f0) 0
+ primary-for QLocalSocket (0x0x7f23440cc888)
+ QObject (0x0x7f234411c240) 0
+ primary-for QIODevice (0x0x7f23440cc8f0)
+
+Class QSslConfiguration
+ size=8 align=8
+ base size=8 base align=8
+QSslConfiguration (0x0x7f234411c480) 0
+
+Class QSslPreSharedKeyAuthenticator
+ size=8 align=8
+ base size=8 base align=8
+QSslPreSharedKeyAuthenticator (0x0x7f23441add20) 0
+
+Class QNetworkAccessManager::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QNetworkAccessManager::QPrivateSignal (0x0x7f2343e29300) 0 empty
+
+Vtable for QNetworkAccessManager
+QNetworkAccessManager::_ZTV21QNetworkAccessManager: 15 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI21QNetworkAccessManager)
+16 (int (*)(...))QNetworkAccessManager::metaObject
+24 (int (*)(...))QNetworkAccessManager::qt_metacast
+32 (int (*)(...))QNetworkAccessManager::qt_metacall
+40 (int (*)(...))QNetworkAccessManager::~QNetworkAccessManager
+48 (int (*)(...))QNetworkAccessManager::~QNetworkAccessManager
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QNetworkAccessManager::createRequest
+
+Class QNetworkAccessManager
+ size=16 align=8
+ base size=16 base align=8
+QNetworkAccessManager (0x0x7f2343e168f0) 0
+ vptr=((& QNetworkAccessManager::_ZTV21QNetworkAccessManager) + 16)
+ QObject (0x0x7f2343e292a0) 0
+ primary-for QNetworkAccessManager (0x0x7f2343e168f0)
+
+Class QNetworkConfiguration
+ size=8 align=8
+ base size=8 base align=8
+QNetworkConfiguration (0x0x7f2343e295a0) 0
+
+Class QNetworkConfigurationManager::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QNetworkConfigurationManager::QPrivateSignal (0x0x7f2343ea49c0) 0 empty
+
+Vtable for QNetworkConfigurationManager
+QNetworkConfigurationManager::_ZTV28QNetworkConfigurationManager: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI28QNetworkConfigurationManager)
+16 (int (*)(...))QNetworkConfigurationManager::metaObject
+24 (int (*)(...))QNetworkConfigurationManager::qt_metacast
+32 (int (*)(...))QNetworkConfigurationManager::qt_metacall
+40 (int (*)(...))QNetworkConfigurationManager::~QNetworkConfigurationManager
+48 (int (*)(...))QNetworkConfigurationManager::~QNetworkConfigurationManager
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QNetworkConfigurationManager
+ size=16 align=8
+ base size=16 base align=8
+QNetworkConfigurationManager (0x0x7f2343e93d00) 0
+ vptr=((& QNetworkConfigurationManager::_ZTV28QNetworkConfigurationManager) + 16)
+ QObject (0x0x7f2343ea4960) 0
+ primary-for QNetworkConfigurationManager (0x0x7f2343e93d00)
+
+Class QNetworkCookie
+ size=8 align=8
+ base size=8 base align=8
+QNetworkCookie (0x0x7f2343ef5540) 0
+
+Class QNetworkCookieJar::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QNetworkCookieJar::QPrivateSignal (0x0x7f2343f6d060) 0 empty
+
+Vtable for QNetworkCookieJar
+QNetworkCookieJar::_ZTV17QNetworkCookieJar: 20 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI17QNetworkCookieJar)
+16 (int (*)(...))QNetworkCookieJar::metaObject
+24 (int (*)(...))QNetworkCookieJar::qt_metacast
+32 (int (*)(...))QNetworkCookieJar::qt_metacall
+40 (int (*)(...))QNetworkCookieJar::~QNetworkCookieJar
+48 (int (*)(...))QNetworkCookieJar::~QNetworkCookieJar
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QNetworkCookieJar::cookiesForUrl
+120 (int (*)(...))QNetworkCookieJar::setCookiesFromUrl
+128 (int (*)(...))QNetworkCookieJar::insertCookie
+136 (int (*)(...))QNetworkCookieJar::updateCookie
+144 (int (*)(...))QNetworkCookieJar::deleteCookie
+152 (int (*)(...))QNetworkCookieJar::validateCookie
+
+Class QNetworkCookieJar
+ size=16 align=8
+ base size=16 base align=8
+QNetworkCookieJar (0x0x7f2343f59750) 0
+ vptr=((& QNetworkCookieJar::_ZTV17QNetworkCookieJar) + 16)
+ QObject (0x0x7f2343f6d000) 0
+ primary-for QNetworkCookieJar (0x0x7f2343f59750)
+
+Class QNetworkDatagram
+ size=8 align=8
+ base size=8 base align=8
+QNetworkDatagram (0x0x7f2343f6d240) 0
+
+Class QNetworkDiskCache::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QNetworkDiskCache::QPrivateSignal (0x0x7f2343c24d80) 0 empty
+
+Vtable for QNetworkDiskCache
+QNetworkDiskCache::_ZTV17QNetworkDiskCache: 23 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI17QNetworkDiskCache)
+16 (int (*)(...))QNetworkDiskCache::metaObject
+24 (int (*)(...))QNetworkDiskCache::qt_metacast
+32 (int (*)(...))QNetworkDiskCache::qt_metacall
+40 (int (*)(...))QNetworkDiskCache::~QNetworkDiskCache
+48 (int (*)(...))QNetworkDiskCache::~QNetworkDiskCache
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QNetworkDiskCache::metaData
+120 (int (*)(...))QNetworkDiskCache::updateMetaData
+128 (int (*)(...))QNetworkDiskCache::data
+136 (int (*)(...))QNetworkDiskCache::remove
+144 (int (*)(...))QNetworkDiskCache::cacheSize
+152 (int (*)(...))QNetworkDiskCache::prepare
+160 (int (*)(...))QNetworkDiskCache::insert
+168 (int (*)(...))QNetworkDiskCache::clear
+176 (int (*)(...))QNetworkDiskCache::expire
+
+Class QNetworkDiskCache
+ size=16 align=8
+ base size=16 base align=8
+QNetworkDiskCache (0x0x7f2343c32618) 0
+ vptr=((& QNetworkDiskCache::_ZTV17QNetworkDiskCache) + 16)
+ QAbstractNetworkCache (0x0x7f2343c32680) 0
+ primary-for QNetworkDiskCache (0x0x7f2343c32618)
+ QObject (0x0x7f2343c24d20) 0
+ primary-for QAbstractNetworkCache (0x0x7f2343c32680)
+
+Class QNetworkAddressEntry
+ size=8 align=8
+ base size=8 base align=8
+QNetworkAddressEntry (0x0x7f2343c24f60) 0
+
+Class QNetworkInterface
+ size=8 align=8
+ base size=8 base align=8
+QNetworkInterface (0x0x7f2343cf8f00) 0
+
+Class QNetworkProxyQuery
+ size=8 align=8
+ base size=8 base align=8
+QNetworkProxyQuery (0x0x7f2343d76de0) 0
+
+Class QNetworkProxy
+ size=8 align=8
+ base size=8 base align=8
+QNetworkProxy (0x0x7f2343a09300) 0
+
+Vtable for QNetworkProxyFactory
+QNetworkProxyFactory::_ZTV20QNetworkProxyFactory: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI20QNetworkProxyFactory)
+16 0
+24 0
+32 (int (*)(...))__cxa_pure_virtual
+
+Class QNetworkProxyFactory
+ size=8 align=8
+ base size=8 base align=8
+QNetworkProxyFactory (0x0x7f2343a99060) 0 nearly-empty
+ vptr=((& QNetworkProxyFactory::_ZTV20QNetworkProxyFactory) + 16)
+
+Class QNetworkReply::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QNetworkReply::QPrivateSignal (0x0x7f2343a99300) 0 empty
+
+Vtable for QNetworkReply
+QNetworkReply::_ZTV13QNetworkReply: 36 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI13QNetworkReply)
+16 (int (*)(...))QNetworkReply::metaObject
+24 (int (*)(...))QNetworkReply::qt_metacast
+32 (int (*)(...))QNetworkReply::qt_metacall
+40 0
+48 0
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QNetworkReply::isSequential
+120 (int (*)(...))QIODevice::open
+128 (int (*)(...))QNetworkReply::close
+136 (int (*)(...))QIODevice::pos
+144 (int (*)(...))QIODevice::size
+152 (int (*)(...))QIODevice::seek
+160 (int (*)(...))QIODevice::atEnd
+168 (int (*)(...))QIODevice::reset
+176 (int (*)(...))QIODevice::bytesAvailable
+184 (int (*)(...))QIODevice::bytesToWrite
+192 (int (*)(...))QIODevice::canReadLine
+200 (int (*)(...))QIODevice::waitForReadyRead
+208 (int (*)(...))QIODevice::waitForBytesWritten
+216 (int (*)(...))__cxa_pure_virtual
+224 (int (*)(...))QIODevice::readLineData
+232 (int (*)(...))QNetworkReply::writeData
+240 (int (*)(...))QNetworkReply::setReadBufferSize
+248 (int (*)(...))__cxa_pure_virtual
+256 (int (*)(...))QNetworkReply::ignoreSslErrors
+264 (int (*)(...))QNetworkReply::sslConfigurationImplementation
+272 (int (*)(...))QNetworkReply::setSslConfigurationImplementation
+280 (int (*)(...))QNetworkReply::ignoreSslErrorsImplementation
+
+Class QNetworkReply
+ size=16 align=8
+ base size=16 base align=8
+QNetworkReply (0x0x7f2343a6e208) 0
+ vptr=((& QNetworkReply::_ZTV13QNetworkReply) + 16)
+ QIODevice (0x0x7f2343a6e270) 0
+ primary-for QNetworkReply (0x0x7f2343a6e208)
+ QObject (0x0x7f2343a992a0) 0
+ primary-for QIODevice (0x0x7f2343a6e270)
+
+Class QNetworkSession::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QNetworkSession::QPrivateSignal (0x0x7f2343a997e0) 0 empty
+
+Vtable for QNetworkSession
+QNetworkSession::_ZTV15QNetworkSession: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI15QNetworkSession)
+16 (int (*)(...))QNetworkSession::metaObject
+24 (int (*)(...))QNetworkSession::qt_metacast
+32 (int (*)(...))QNetworkSession::qt_metacall
+40 (int (*)(...))QNetworkSession::~QNetworkSession
+48 (int (*)(...))QNetworkSession::~QNetworkSession
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QNetworkSession::connectNotify
+104 (int (*)(...))QNetworkSession::disconnectNotify
+
+Class QNetworkSession
+ size=24 align=8
+ base size=24 base align=8
+QNetworkSession (0x0x7f2343a6e2d8) 0
+ vptr=((& QNetworkSession::_ZTV15QNetworkSession) + 16)
+ QObject (0x0x7f2343a99780) 0
+ primary-for QNetworkSession (0x0x7f2343a6e2d8)
+
+Class QOcspResponse
+ size=8 align=8
+ base size=8 base align=8
+QOcspResponse (0x0x7f2343b04060) 0
+
+Class QTcpServer::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QTcpServer::QPrivateSignal (0x0x7f2343b558a0) 0 empty
+
+Vtable for QTcpServer
+QTcpServer::_ZTV10QTcpServer: 17 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI10QTcpServer)
+16 (int (*)(...))QTcpServer::metaObject
+24 (int (*)(...))QTcpServer::qt_metacast
+32 (int (*)(...))QTcpServer::qt_metacall
+40 (int (*)(...))QTcpServer::~QTcpServer
+48 (int (*)(...))QTcpServer::~QTcpServer
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QTcpServer::hasPendingConnections
+120 (int (*)(...))QTcpServer::nextPendingConnection
+128 (int (*)(...))QTcpServer::incomingConnection
+
+Class QTcpServer
+ size=16 align=8
+ base size=16 base align=8
+QTcpServer (0x0x7f2343b49b60) 0
+ vptr=((& QTcpServer::_ZTV10QTcpServer) + 16)
+ QObject (0x0x7f2343b55840) 0
+ primary-for QTcpServer (0x0x7f2343b49b60)
+
+Class QSslCertificateExtension
+ size=8 align=8
+ base size=8 base align=8
+QSslCertificateExtension (0x0x7f2343b55a80) 0
+
+Class QSslCipher
+ size=8 align=8
+ base size=8 base align=8
+QSslCipher (0x0x7f2343bbb840) 0
+
+Class QSslDiffieHellmanParameters
+ size=8 align=8
+ base size=8 base align=8
+QSslDiffieHellmanParameters (0x0x7f234387a900) 0
+
+Class QSslEllipticCurve
+ size=4 align=4
+ base size=4 base align=4
+QSslEllipticCurve (0x0x7f234393f660) 0
+
+Class QSslKey
+ size=8 align=8
+ base size=8 base align=8
+QSslKey (0x0x7f23439a5000) 0
+
+Class QUdpSocket::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QUdpSocket::QPrivateSignal (0x0x7f23435fb300) 0 empty
+
+Vtable for QUdpSocket
+QUdpSocket::_ZTV10QUdpSocket: 41 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI10QUdpSocket)
+16 (int (*)(...))QUdpSocket::metaObject
+24 (int (*)(...))QUdpSocket::qt_metacast
+32 (int (*)(...))QUdpSocket::qt_metacall
+40 (int (*)(...))QUdpSocket::~QUdpSocket
+48 (int (*)(...))QUdpSocket::~QUdpSocket
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QAbstractSocket::isSequential
+120 (int (*)(...))QIODevice::open
+128 (int (*)(...))QAbstractSocket::close
+136 (int (*)(...))QIODevice::pos
+144 (int (*)(...))QIODevice::size
+152 (int (*)(...))QIODevice::seek
+160 (int (*)(...))QAbstractSocket::atEnd
+168 (int (*)(...))QIODevice::reset
+176 (int (*)(...))QAbstractSocket::bytesAvailable
+184 (int (*)(...))QAbstractSocket::bytesToWrite
+192 (int (*)(...))QAbstractSocket::canReadLine
+200 (int (*)(...))QAbstractSocket::waitForReadyRead
+208 (int (*)(...))QAbstractSocket::waitForBytesWritten
+216 (int (*)(...))QAbstractSocket::readData
+224 (int (*)(...))QAbstractSocket::readLineData
+232 (int (*)(...))QAbstractSocket::writeData
+240 (int (*)(...))QAbstractSocket::resume
+248 (int (*)(...))QAbstractSocket::connectToHost
+256 (int (*)(...))QAbstractSocket::connectToHost
+264 (int (*)(...))QAbstractSocket::disconnectFromHost
+272 (int (*)(...))QAbstractSocket::setReadBufferSize
+280 (int (*)(...))QAbstractSocket::socketDescriptor
+288 (int (*)(...))QAbstractSocket::setSocketDescriptor
+296 (int (*)(...))QAbstractSocket::setSocketOption
+304 (int (*)(...))QAbstractSocket::socketOption
+312 (int (*)(...))QAbstractSocket::waitForConnected
+320 (int (*)(...))QAbstractSocket::waitForDisconnected
+
+Class QUdpSocket
+ size=16 align=8
+ base size=16 base align=8
+QUdpSocket (0x0x7f23435fc0d0) 0
+ vptr=((& QUdpSocket::_ZTV10QUdpSocket) + 16)
+ QAbstractSocket (0x0x7f23435fc138) 0
+ primary-for QUdpSocket (0x0x7f23435fc0d0)
+ QIODevice (0x0x7f23435fc1a0) 0
+ primary-for QAbstractSocket (0x0x7f23435fc138)
+ QObject (0x0x7f23435fb2a0) 0
+ primary-for QIODevice (0x0x7f23435fc1a0)
+
+Class QRemoteObjectSourceLocationInfo
+ size=16 align=8
+ base size=16 base align=8
+QRemoteObjectSourceLocationInfo (0x0x7f23435fb540) 0
+
+Class QAbstractItemModelReplica::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QAbstractItemModelReplica::QPrivateSignal (0x0x7f23436e85a0) 0 empty
+
+Vtable for QAbstractItemModelReplica
+QAbstractItemModelReplica::_ZTV25QAbstractItemModelReplica: 48 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI25QAbstractItemModelReplica)
+16 (int (*)(...))QAbstractItemModelReplica::metaObject
+24 (int (*)(...))QAbstractItemModelReplica::qt_metacast
+32 (int (*)(...))QAbstractItemModelReplica::qt_metacall
+40 (int (*)(...))QAbstractItemModelReplica::~QAbstractItemModelReplica
+48 (int (*)(...))QAbstractItemModelReplica::~QAbstractItemModelReplica
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QAbstractItemModelReplica::index
+120 (int (*)(...))QAbstractItemModelReplica::parent
+128 (int (*)(...))QAbstractItemModel::sibling
+136 (int (*)(...))QAbstractItemModelReplica::rowCount
+144 (int (*)(...))QAbstractItemModelReplica::columnCount
+152 (int (*)(...))QAbstractItemModelReplica::hasChildren
+160 (int (*)(...))QAbstractItemModelReplica::data
+168 (int (*)(...))QAbstractItemModelReplica::setData
+176 (int (*)(...))QAbstractItemModelReplica::headerData
+184 (int (*)(...))QAbstractItemModel::setHeaderData
+192 (int (*)(...))QAbstractItemModel::itemData
+200 (int (*)(...))QAbstractItemModel::setItemData
+208 (int (*)(...))QAbstractItemModel::mimeTypes
+216 (int (*)(...))QAbstractItemModel::mimeData
+224 (int (*)(...))QAbstractItemModel::canDropMimeData
+232 (int (*)(...))QAbstractItemModel::dropMimeData
+240 (int (*)(...))QAbstractItemModel::supportedDropActions
+248 (int (*)(...))QAbstractItemModel::supportedDragActions
+256 (int (*)(...))QAbstractItemModel::insertRows
+264 (int (*)(...))QAbstractItemModel::insertColumns
+272 (int (*)(...))QAbstractItemModel::removeRows
+280 (int (*)(...))QAbstractItemModel::removeColumns
+288 (int (*)(...))QAbstractItemModel::moveRows
+296 (int (*)(...))QAbstractItemModel::moveColumns
+304 (int (*)(...))QAbstractItemModel::fetchMore
+312 (int (*)(...))QAbstractItemModel::canFetchMore
+320 (int (*)(...))QAbstractItemModelReplica::flags
+328 (int (*)(...))QAbstractItemModel::sort
+336 (int (*)(...))QAbstractItemModel::buddy
+344 (int (*)(...))QAbstractItemModel::match
+352 (int (*)(...))QAbstractItemModel::span
+360 (int (*)(...))QAbstractItemModelReplica::roleNames
+368 (int (*)(...))QAbstractItemModel::submit
+376 (int (*)(...))QAbstractItemModel::revert
+
+Class QAbstractItemModelReplica
+ size=24 align=8
+ base size=24 base align=8
+QAbstractItemModelReplica (0x0x7f23436438f0) 0
+ vptr=((& QAbstractItemModelReplica::_ZTV25QAbstractItemModelReplica) + 16)
+ QAbstractItemModel (0x0x7f2343643958) 0
+ primary-for QAbstractItemModelReplica (0x0x7f23436438f0)
+ QObject (0x0x7f23436e8540) 0
+ primary-for QAbstractItemModel (0x0x7f2343643958)
+
+Class ModelIndex
+ size=8 align=4
+ base size=8 base align=4
+ModelIndex (0x0x7f23436e8720) 0
+
+Class IndexValuePair
+ size=40 align=8
+ base size=40 base align=8
+IndexValuePair (0x0x7f2343726000) 0
+
+Class DataEntries
+ size=8 align=8
+ base size=8 base align=8
+DataEntries (0x0x7f23437aa840) 0
+
+Class MetaAndDataEntries
+ size=24 align=8
+ base size=24 base align=8
+MetaAndDataEntries (0x0x7f23437b8d68) 0
+ DataEntries (0x0x7f23433d32a0) 0
+
+Class QRemoteObjectReplica::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QRemoteObjectReplica::QPrivateSignal (0x0x7f23434d85a0) 0 empty
+
+Vtable for QRemoteObjectReplica
+QRemoteObjectReplica::_ZTV20QRemoteObjectReplica: 16 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI20QRemoteObjectReplica)
+16 (int (*)(...))QRemoteObjectReplica::metaObject
+24 (int (*)(...))QRemoteObjectReplica::qt_metacast
+32 (int (*)(...))QRemoteObjectReplica::qt_metacall
+40 (int (*)(...))QRemoteObjectReplica::~QRemoteObjectReplica
+48 (int (*)(...))QRemoteObjectReplica::~QRemoteObjectReplica
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QRemoteObjectReplica::setNode
+120 (int (*)(...))QRemoteObjectReplica::initialize
+
+Class QRemoteObjectReplica
+ size=32 align=8
+ base size=32 base align=8
+QRemoteObjectReplica (0x0x7f2343435f70) 0
+ vptr=((& QRemoteObjectReplica::_ZTV20QRemoteObjectReplica) + 16)
+ QObject (0x0x7f23434d8540) 0
+ primary-for QRemoteObjectReplica (0x0x7f2343435f70)
+
+Vtable for QRemoteObjectDynamicReplica
+QRemoteObjectDynamicReplica::_ZTV27QRemoteObjectDynamicReplica: 16 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI27QRemoteObjectDynamicReplica)
+16 (int (*)(...))QRemoteObjectDynamicReplica::metaObject
+24 (int (*)(...))QRemoteObjectDynamicReplica::qt_metacast
+32 (int (*)(...))QRemoteObjectDynamicReplica::qt_metacall
+40 (int (*)(...))QRemoteObjectDynamicReplica::~QRemoteObjectDynamicReplica
+48 (int (*)(...))QRemoteObjectDynamicReplica::~QRemoteObjectDynamicReplica
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QRemoteObjectReplica::setNode
+120 (int (*)(...))QRemoteObjectReplica::initialize
+
+Class QRemoteObjectDynamicReplica
+ size=32 align=8
+ base size=32 base align=8
+QRemoteObjectDynamicReplica (0x0x7f234351f000) 0
+ vptr=((& QRemoteObjectDynamicReplica::_ZTV27QRemoteObjectDynamicReplica) + 16)
+ QRemoteObjectReplica (0x0x7f234351f068) 0
+ primary-for QRemoteObjectDynamicReplica (0x0x7f234351f000)
+ QObject (0x0x7f23434d87e0) 0
+ primary-for QRemoteObjectReplica (0x0x7f234351f068)
+
+Class QRemoteObjectRegistry::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QRemoteObjectRegistry::QPrivateSignal (0x0x7f23434d88a0) 0 empty
+
+Vtable for QRemoteObjectRegistry
+QRemoteObjectRegistry::_ZTV21QRemoteObjectRegistry: 16 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI21QRemoteObjectRegistry)
+16 (int (*)(...))QRemoteObjectRegistry::metaObject
+24 (int (*)(...))QRemoteObjectRegistry::qt_metacast
+32 (int (*)(...))QRemoteObjectRegistry::qt_metacall
+40 (int (*)(...))QRemoteObjectRegistry::~QRemoteObjectRegistry
+48 (int (*)(...))QRemoteObjectRegistry::~QRemoteObjectRegistry
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QRemoteObjectReplica::setNode
+120 (int (*)(...))QRemoteObjectRegistry::initialize
+
+Class QRemoteObjectRegistry
+ size=32 align=8
+ base size=32 base align=8
+QRemoteObjectRegistry (0x0x7f234351f0d0) 0
+ vptr=((& QRemoteObjectRegistry::_ZTV21QRemoteObjectRegistry) + 16)
+ QRemoteObjectReplica (0x0x7f234351f138) 0
+ primary-for QRemoteObjectRegistry (0x0x7f234351f0d0)
+ QObject (0x0x7f23434d8840) 0
+ primary-for QRemoteObjectReplica (0x0x7f234351f138)
+
+Class QRemoteObjectAbstractPersistedStore::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QRemoteObjectAbstractPersistedStore::QPrivateSignal (0x0x7f23434d8ae0) 0 empty
+
+Vtable for QRemoteObjectAbstractPersistedStore
+QRemoteObjectAbstractPersistedStore::_ZTV35QRemoteObjectAbstractPersistedStore: 16 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI35QRemoteObjectAbstractPersistedStore)
+16 (int (*)(...))QRemoteObjectAbstractPersistedStore::metaObject
+24 (int (*)(...))QRemoteObjectAbstractPersistedStore::qt_metacast
+32 (int (*)(...))QRemoteObjectAbstractPersistedStore::qt_metacall
+40 0
+48 0
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))__cxa_pure_virtual
+120 (int (*)(...))__cxa_pure_virtual
+
+Class QRemoteObjectAbstractPersistedStore
+ size=16 align=8
+ base size=16 base align=8
+QRemoteObjectAbstractPersistedStore (0x0x7f234351f1a0) 0
+ vptr=((& QRemoteObjectAbstractPersistedStore::_ZTV35QRemoteObjectAbstractPersistedStore) + 16)
+ QObject (0x0x7f23434d8a80) 0
+ primary-for QRemoteObjectAbstractPersistedStore (0x0x7f234351f1a0)
+
+Class QRemoteObjectNode::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QRemoteObjectNode::QPrivateSignal (0x0x7f23434d8d80) 0 empty
+
+Vtable for QRemoteObjectNode
+QRemoteObjectNode::_ZTV17QRemoteObjectNode: 16 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI17QRemoteObjectNode)
+16 (int (*)(...))QRemoteObjectNode::metaObject
+24 (int (*)(...))QRemoteObjectNode::qt_metacast
+32 (int (*)(...))QRemoteObjectNode::qt_metacall
+40 (int (*)(...))QRemoteObjectNode::~QRemoteObjectNode
+48 (int (*)(...))QRemoteObjectNode::~QRemoteObjectNode
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QRemoteObjectNode::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QRemoteObjectNode::setName
+120 (int (*)(...))QRemoteObjectNode::setRegistryUrl
+
+Class QRemoteObjectNode
+ size=16 align=8
+ base size=16 base align=8
+QRemoteObjectNode (0x0x7f234351f208) 0
+ vptr=((& QRemoteObjectNode::_ZTV17QRemoteObjectNode) + 16)
+ QObject (0x0x7f23434d8d20) 0
+ primary-for QRemoteObjectNode (0x0x7f234351f208)
+
+Class QRemoteObjectHostBase::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QRemoteObjectHostBase::QPrivateSignal (0x0x7f234355a180) 0 empty
+
+Vtable for QRemoteObjectHostBase
+QRemoteObjectHostBase::_ZTV21QRemoteObjectHostBase: 18 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI21QRemoteObjectHostBase)
+16 (int (*)(...))QRemoteObjectHostBase::metaObject
+24 (int (*)(...))QRemoteObjectHostBase::qt_metacast
+32 (int (*)(...))QRemoteObjectHostBase::qt_metacall
+40 (int (*)(...))QRemoteObjectHostBase::~QRemoteObjectHostBase
+48 (int (*)(...))QRemoteObjectHostBase::~QRemoteObjectHostBase
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QRemoteObjectNode::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QRemoteObjectHostBase::setName
+120 (int (*)(...))QRemoteObjectNode::setRegistryUrl
+128 (int (*)(...))QRemoteObjectHostBase::hostUrl
+136 (int (*)(...))QRemoteObjectHostBase::setHostUrl
+
+Class QRemoteObjectHostBase
+ size=16 align=8
+ base size=16 base align=8
+QRemoteObjectHostBase (0x0x7f234351f270) 0
+ vptr=((& QRemoteObjectHostBase::_ZTV21QRemoteObjectHostBase) + 16)
+ QRemoteObjectNode (0x0x7f234351f2d8) 0
+ primary-for QRemoteObjectHostBase (0x0x7f234351f270)
+ QObject (0x0x7f234355a120) 0
+ primary-for QRemoteObjectNode (0x0x7f234351f2d8)
+
+Class QRemoteObjectHost::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QRemoteObjectHost::QPrivateSignal (0x0x7f23431e73c0) 0 empty
+
+Vtable for QRemoteObjectHost
+QRemoteObjectHost::_ZTV17QRemoteObjectHost: 18 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI17QRemoteObjectHost)
+16 (int (*)(...))QRemoteObjectHost::metaObject
+24 (int (*)(...))QRemoteObjectHost::qt_metacast
+32 (int (*)(...))QRemoteObjectHost::qt_metacall
+40 (int (*)(...))QRemoteObjectHost::~QRemoteObjectHost
+48 (int (*)(...))QRemoteObjectHost::~QRemoteObjectHost
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QRemoteObjectNode::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QRemoteObjectHostBase::setName
+120 (int (*)(...))QRemoteObjectNode::setRegistryUrl
+128 (int (*)(...))QRemoteObjectHost::hostUrl
+136 (int (*)(...))QRemoteObjectHost::setHostUrl
+
+Class QRemoteObjectHost
+ size=16 align=8
+ base size=16 base align=8
+QRemoteObjectHost (0x0x7f23431cfb60) 0
+ vptr=((& QRemoteObjectHost::_ZTV17QRemoteObjectHost) + 16)
+ QRemoteObjectHostBase (0x0x7f23431cfbc8) 0
+ primary-for QRemoteObjectHost (0x0x7f23431cfb60)
+ QRemoteObjectNode (0x0x7f23431cfc30) 0
+ primary-for QRemoteObjectHostBase (0x0x7f23431cfbc8)
+ QObject (0x0x7f23431e7360) 0
+ primary-for QRemoteObjectNode (0x0x7f23431cfc30)
+
+Class QRemoteObjectRegistryHost::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QRemoteObjectRegistryHost::QPrivateSignal (0x0x7f23431e7600) 0 empty
+
+Vtable for QRemoteObjectRegistryHost
+QRemoteObjectRegistryHost::_ZTV25QRemoteObjectRegistryHost: 18 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI25QRemoteObjectRegistryHost)
+16 (int (*)(...))QRemoteObjectRegistryHost::metaObject
+24 (int (*)(...))QRemoteObjectRegistryHost::qt_metacast
+32 (int (*)(...))QRemoteObjectRegistryHost::qt_metacall
+40 (int (*)(...))QRemoteObjectRegistryHost::~QRemoteObjectRegistryHost
+48 (int (*)(...))QRemoteObjectRegistryHost::~QRemoteObjectRegistryHost
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QRemoteObjectNode::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QRemoteObjectHostBase::setName
+120 (int (*)(...))QRemoteObjectRegistryHost::setRegistryUrl
+128 (int (*)(...))QRemoteObjectHostBase::hostUrl
+136 (int (*)(...))QRemoteObjectHostBase::setHostUrl
+
+Class QRemoteObjectRegistryHost
+ size=16 align=8
+ base size=16 base align=8
+QRemoteObjectRegistryHost (0x0x7f23431cfc98) 0
+ vptr=((& QRemoteObjectRegistryHost::_ZTV25QRemoteObjectRegistryHost) + 16)
+ QRemoteObjectHostBase (0x0x7f23431cfd00) 0
+ primary-for QRemoteObjectRegistryHost (0x0x7f23431cfc98)
+ QRemoteObjectNode (0x0x7f23431cfd68) 0
+ primary-for QRemoteObjectHostBase (0x0x7f23431cfd00)
+ QObject (0x0x7f23431e75a0) 0
+ primary-for QRemoteObjectNode (0x0x7f23431cfd68)
+
+Class QRemoteObjectPendingCall
+ size=8 align=8
+ base size=8 base align=8
+QRemoteObjectPendingCall (0x0x7f23431e77e0) 0
+
+Class QRemoteObjectPendingCallWatcher::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QRemoteObjectPendingCallWatcher::QPrivateSignal (0x0x7f23431e7b40) 0 empty
+
+Vtable for QRemoteObjectPendingCallWatcher
+QRemoteObjectPendingCallWatcher::_ZTV31QRemoteObjectPendingCallWatcher: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI31QRemoteObjectPendingCallWatcher)
+16 (int (*)(...))QRemoteObjectPendingCallWatcher::metaObject
+24 (int (*)(...))QRemoteObjectPendingCallWatcher::qt_metacast
+32 (int (*)(...))QRemoteObjectPendingCallWatcher::qt_metacall
+40 (int (*)(...))QRemoteObjectPendingCallWatcher::~QRemoteObjectPendingCallWatcher
+48 (int (*)(...))QRemoteObjectPendingCallWatcher::~QRemoteObjectPendingCallWatcher
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QRemoteObjectPendingCallWatcher
+ size=24 align=8
+ base size=24 base align=8
+QRemoteObjectPendingCallWatcher (0x0x7f23432220e0) 0
+ vptr=((& QRemoteObjectPendingCallWatcher::_ZTV31QRemoteObjectPendingCallWatcher) + 16)
+ QObject (0x0x7f23431e7a80) 0
+ primary-for QRemoteObjectPendingCallWatcher (0x0x7f23432220e0)
+ QRemoteObjectPendingCall (0x0x7f23431e7ae0) 16
+
+Class QRemoteObjectSettingsStore::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QRemoteObjectSettingsStore::QPrivateSignal (0x0x7f2343236060) 0 empty
+
+Vtable for QRemoteObjectSettingsStore
+QRemoteObjectSettingsStore::_ZTV26QRemoteObjectSettingsStore: 16 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI26QRemoteObjectSettingsStore)
+16 (int (*)(...))QRemoteObjectSettingsStore::metaObject
+24 (int (*)(...))QRemoteObjectSettingsStore::qt_metacast
+32 (int (*)(...))QRemoteObjectSettingsStore::qt_metacall
+40 (int (*)(...))QRemoteObjectSettingsStore::~QRemoteObjectSettingsStore
+48 (int (*)(...))QRemoteObjectSettingsStore::~QRemoteObjectSettingsStore
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QRemoteObjectSettingsStore::saveProperties
+120 (int (*)(...))QRemoteObjectSettingsStore::restoreProperties
+
+Class QRemoteObjectSettingsStore
+ size=16 align=8
+ base size=16 base align=8
+QRemoteObjectSettingsStore (0x0x7f2343235000) 0
+ vptr=((& QRemoteObjectSettingsStore::_ZTV26QRemoteObjectSettingsStore) + 16)
+ QRemoteObjectAbstractPersistedStore (0x0x7f2343235068) 0
+ primary-for QRemoteObjectSettingsStore (0x0x7f2343235000)
+ QObject (0x0x7f2343236000) 0
+ primary-for QRemoteObjectAbstractPersistedStore (0x0x7f2343235068)
+
+Class QtPrivate::QMetaObjectPrivate
+ size=56 align=4
+ base size=56 base align=4
+QtPrivate::QMetaObjectPrivate (0x0x7f23432365a0) 0
+
+Class ModelInfo
+ size=24 align=8
+ base size=24 base align=8
+ModelInfo (0x0x7f23432842a0) 0
+
+Vtable for SourceApiMap
+SourceApiMap::_ZTV12SourceApiMap: 32 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI12SourceApiMap)
+16 0
+24 0
+32 (int (*)(...))__cxa_pure_virtual
+40 (int (*)(...))__cxa_pure_virtual
+48 (int (*)(...))SourceApiMap::className
+56 (int (*)(...))__cxa_pure_virtual
+64 (int (*)(...))__cxa_pure_virtual
+72 (int (*)(...))__cxa_pure_virtual
+80 (int (*)(...))__cxa_pure_virtual
+88 (int (*)(...))__cxa_pure_virtual
+96 (int (*)(...))__cxa_pure_virtual
+104 (int (*)(...))__cxa_pure_virtual
+112 (int (*)(...))__cxa_pure_virtual
+120 (int (*)(...))__cxa_pure_virtual
+128 (int (*)(...))__cxa_pure_virtual
+136 (int (*)(...))__cxa_pure_virtual
+144 (int (*)(...))__cxa_pure_virtual
+152 (int (*)(...))__cxa_pure_virtual
+160 (int (*)(...))__cxa_pure_virtual
+168 (int (*)(...))__cxa_pure_virtual
+176 (int (*)(...))__cxa_pure_virtual
+184 (int (*)(...))__cxa_pure_virtual
+192 (int (*)(...))__cxa_pure_virtual
+200 (int (*)(...))__cxa_pure_virtual
+208 (int (*)(...))__cxa_pure_virtual
+216 (int (*)(...))__cxa_pure_virtual
+224 (int (*)(...))SourceApiMap::isDynamic
+232 (int (*)(...))SourceApiMap::isAdapterSignal
+240 (int (*)(...))SourceApiMap::isAdapterMethod
+248 (int (*)(...))SourceApiMap::isAdapterProperty
+
+Class SourceApiMap
+ size=24 align=8
+ base size=24 base align=8
+SourceApiMap (0x0x7f2343284300) 0
+ vptr=((& SourceApiMap::_ZTV12SourceApiMap) + 16)
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long int; _Ret = int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Save_errno
+ size=4 align=4
+ base size=4 base align=4
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long int; _Ret = int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Save_errno (0x0x7f234331bcc0) 0
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long int; _Ret = int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Range_chk
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long int; _Ret = int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Range_chk (0x0x7f2343339060) 0 empty
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long int; _Ret = long int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Save_errno
+ size=4 align=4
+ base size=4 base align=4
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long int; _Ret = long int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Save_errno (0x0x7f2343339240) 0
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long int; _Ret = long int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Range_chk
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long int; _Ret = long int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Range_chk (0x0x7f23433395a0) 0 empty
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long unsigned int; _Ret = long unsigned int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Save_errno
+ size=4 align=4
+ base size=4 base align=4
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long unsigned int; _Ret = long unsigned int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Save_errno (0x0x7f2343339780) 0
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long unsigned int; _Ret = long unsigned int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Range_chk
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long unsigned int; _Ret = long unsigned int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Range_chk (0x0x7f2343339ae0) 0 empty
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long long int; _Ret = long long int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Save_errno
+ size=4 align=4
+ base size=4 base align=4
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long long int; _Ret = long long int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Save_errno (0x0x7f2343339cc0) 0
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long long int; _Ret = long long int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Range_chk
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long long int; _Ret = long long int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Range_chk (0x0x7f234337b060) 0 empty
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long long unsigned int; _Ret = long long unsigned int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Save_errno
+ size=4 align=4
+ base size=4 base align=4
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long long unsigned int; _Ret = long long unsigned int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Save_errno (0x0x7f234337b240) 0
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long long unsigned int; _Ret = long long unsigned int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Range_chk
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long long unsigned int; _Ret = long long unsigned int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Range_chk (0x0x7f234337b5a0) 0 empty
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = float; _Ret = float; _CharT = char; _Base = {}; std::size_t = long unsigned int]::_Save_errno
+ size=4 align=4
+ base size=4 base align=4
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = float; _Ret = float; _CharT = char; _Base = {}; std::size_t = long unsigned int]::_Save_errno (0x0x7f234337b780) 0
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = float; _Ret = float; _CharT = char; _Base = {}; std::size_t = long unsigned int]::_Range_chk
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = float; _Ret = float; _CharT = char; _Base = {}; std::size_t = long unsigned int]::_Range_chk (0x0x7f234337bae0) 0 empty
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = double; _Ret = double; _CharT = char; _Base = {}; std::size_t = long unsigned int]::_Save_errno
+ size=4 align=4
+ base size=4 base align=4
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = double; _Ret = double; _CharT = char; _Base = {}; std::size_t = long unsigned int]::_Save_errno (0x0x7f234337bcc0) 0
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = double; _Ret = double; _CharT = char; _Base = {}; std::size_t = long unsigned int]::_Range_chk
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = double; _Ret = double; _CharT = char; _Base = {}; std::size_t = long unsigned int]::_Range_chk (0x0x7f23433af060) 0 empty
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long double; _Ret = long double; _CharT = char; _Base = {}; std::size_t = long unsigned int]::_Save_errno
+ size=4 align=4
+ base size=4 base align=4
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long double; _Ret = long double; _CharT = char; _Base = {}; std::size_t = long unsigned int]::_Save_errno (0x0x7f23433af240) 0
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long double; _Ret = long double; _CharT = char; _Base = {}; std::size_t = long unsigned int]::_Range_chk
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long double; _Ret = long double; _CharT = char; _Base = {}; std::size_t = long unsigned int]::_Range_chk (0x0x7f23433af5a0) 0 empty
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long int; _Ret = int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Save_errno
+ size=4 align=4
+ base size=4 base align=4
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long int; _Ret = int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Save_errno (0x0x7f2342fe0a80) 0
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long int; _Ret = int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Range_chk
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long int; _Ret = int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Range_chk (0x0x7f2342fe0de0) 0 empty
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long int; _Ret = long int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Save_errno
+ size=4 align=4
+ base size=4 base align=4
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long int; _Ret = long int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Save_errno (0x0x7f2342fe0f60) 0
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long int; _Ret = long int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Range_chk
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long int; _Ret = long int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Range_chk (0x0x7f2343011300) 0 empty
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long unsigned int; _Ret = long unsigned int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Save_errno
+ size=4 align=4
+ base size=4 base align=4
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long unsigned int; _Ret = long unsigned int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Save_errno (0x0x7f2343011480) 0
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long unsigned int; _Ret = long unsigned int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Range_chk
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long unsigned int; _Ret = long unsigned int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Range_chk (0x0x7f23430117e0) 0 empty
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long long int; _Ret = long long int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Save_errno
+ size=4 align=4
+ base size=4 base align=4
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long long int; _Ret = long long int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Save_errno (0x0x7f2343011960) 0
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long long int; _Ret = long long int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Range_chk
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long long int; _Ret = long long int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Range_chk (0x0x7f2343011cc0) 0 empty
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long long unsigned int; _Ret = long long unsigned int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Save_errno
+ size=4 align=4
+ base size=4 base align=4
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long long unsigned int; _Ret = long long unsigned int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Save_errno (0x0x7f2343011e40) 0
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long long unsigned int; _Ret = long long unsigned int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Range_chk
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long long unsigned int; _Ret = long long unsigned int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Range_chk (0x0x7f23430411e0) 0 empty
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = float; _Ret = float; _CharT = wchar_t; _Base = {}; std::size_t = long unsigned int]::_Save_errno
+ size=4 align=4
+ base size=4 base align=4
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = float; _Ret = float; _CharT = wchar_t; _Base = {}; std::size_t = long unsigned int]::_Save_errno (0x0x7f2343041360) 0
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = float; _Ret = float; _CharT = wchar_t; _Base = {}; std::size_t = long unsigned int]::_Range_chk
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = float; _Ret = float; _CharT = wchar_t; _Base = {}; std::size_t = long unsigned int]::_Range_chk (0x0x7f23430416c0) 0 empty
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = double; _Ret = double; _CharT = wchar_t; _Base = {}; std::size_t = long unsigned int]::_Save_errno
+ size=4 align=4
+ base size=4 base align=4
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = double; _Ret = double; _CharT = wchar_t; _Base = {}; std::size_t = long unsigned int]::_Save_errno (0x0x7f2343041840) 0
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = double; _Ret = double; _CharT = wchar_t; _Base = {}; std::size_t = long unsigned int]::_Range_chk
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = double; _Ret = double; _CharT = wchar_t; _Base = {}; std::size_t = long unsigned int]::_Range_chk (0x0x7f2343041ba0) 0 empty
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long double; _Ret = long double; _CharT = wchar_t; _Base = {}; std::size_t = long unsigned int]::_Save_errno
+ size=4 align=4
+ base size=4 base align=4
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long double; _Ret = long double; _CharT = wchar_t; _Base = {}; std::size_t = long unsigned int]::_Save_errno (0x0x7f2343041d20) 0
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long double; _Ret = long double; _CharT = wchar_t; _Base = {}; std::size_t = long unsigned int]::_Range_chk
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long double; _Ret = long double; _CharT = wchar_t; _Base = {}; std::size_t = long unsigned int]::_Range_chk (0x0x7f234306e0c0) 0 empty
+
--- /dev/null
+Class std::__failure_type
+ size=1 align=1
+ base size=0 base align=1
+std::__failure_type (0x0x7f3bffb14720) 0 empty
+
+Class std::__do_is_destructible_impl
+ size=1 align=1
+ base size=0 base align=1
+std::__do_is_destructible_impl (0x0x7f3bffb77ea0) 0 empty
+
+Class std::__do_is_nt_destructible_impl
+ size=1 align=1
+ base size=0 base align=1
+std::__do_is_nt_destructible_impl (0x0x7f3bffba4120) 0 empty
+
+Class std::__do_is_default_constructible_impl
+ size=1 align=1
+ base size=0 base align=1
+std::__do_is_default_constructible_impl (0x0x7f3bffba4360) 0 empty
+
+Class std::__do_is_static_castable_impl
+ size=1 align=1
+ base size=0 base align=1
+std::__do_is_static_castable_impl (0x0x7f3bffba45a0) 0 empty
+
+Class std::__do_is_direct_constructible_impl
+ size=1 align=1
+ base size=0 base align=1
+std::__do_is_direct_constructible_impl (0x0x7f3bffba4720) 0 empty
+
+Class std::__do_is_nary_constructible_impl
+ size=1 align=1
+ base size=0 base align=1
+std::__do_is_nary_constructible_impl (0x0x7f3bffba4ae0) 0 empty
+
+Class std::__do_is_implicitly_default_constructible_impl
+ size=1 align=1
+ base size=0 base align=1
+std::__do_is_implicitly_default_constructible_impl (0x0x7f3bffbe0c00) 0 empty
+
+Class std::__do_common_type_impl
+ size=1 align=1
+ base size=0 base align=1
+std::__do_common_type_impl (0x0x7f3bff862300) 0 empty
+
+Class std::__do_member_type_wrapper
+ size=1 align=1
+ base size=0 base align=1
+std::__do_member_type_wrapper (0x0x7f3bff8623c0) 0 empty
+
+Class std::__invoke_memfun_ref
+ size=1 align=1
+ base size=0 base align=1
+std::__invoke_memfun_ref (0x0x7f3bff862780) 0 empty
+
+Class std::__invoke_memfun_deref
+ size=1 align=1
+ base size=0 base align=1
+std::__invoke_memfun_deref (0x0x7f3bff8627e0) 0 empty
+
+Class std::__invoke_memobj_ref
+ size=1 align=1
+ base size=0 base align=1
+std::__invoke_memobj_ref (0x0x7f3bff862840) 0 empty
+
+Class std::__invoke_memobj_deref
+ size=1 align=1
+ base size=0 base align=1
+std::__invoke_memobj_deref (0x0x7f3bff8628a0) 0 empty
+
+Class std::__invoke_other
+ size=1 align=1
+ base size=0 base align=1
+std::__invoke_other (0x0x7f3bff862900) 0 empty
+
+Class std::__result_of_memfun_ref_impl
+ size=1 align=1
+ base size=0 base align=1
+std::__result_of_memfun_ref_impl (0x0x7f3bff8629c0) 0 empty
+
+Class std::__result_of_memfun_deref_impl
+ size=1 align=1
+ base size=0 base align=1
+std::__result_of_memfun_deref_impl (0x0x7f3bff862a80) 0 empty
+
+Class std::__result_of_memobj_ref_impl
+ size=1 align=1
+ base size=0 base align=1
+std::__result_of_memobj_ref_impl (0x0x7f3bff862b40) 0 empty
+
+Class std::__result_of_memobj_deref_impl
+ size=1 align=1
+ base size=0 base align=1
+std::__result_of_memobj_deref_impl (0x0x7f3bff862c00) 0 empty
+
+Class std::__result_of_other_impl
+ size=1 align=1
+ base size=0 base align=1
+std::__result_of_other_impl (0x0x7f3bff862f60) 0 empty
+
+Class std::__swappable_details::__do_is_swappable_impl
+ size=1 align=1
+ base size=0 base align=1
+std::__swappable_details::__do_is_swappable_impl (0x0x7f3bff89f300) 0 empty
+
+Class std::__swappable_details::__do_is_nothrow_swappable_impl
+ size=1 align=1
+ base size=0 base align=1
+std::__swappable_details::__do_is_nothrow_swappable_impl (0x0x7f3bff89f360) 0 empty
+
+Class std::__nonesuch
+ size=1 align=1
+ base size=0 base align=1
+std::__nonesuch (0x0x7f3bff89f900) 0 empty
+
+Class std::piecewise_construct_t
+ size=1 align=1
+ base size=0 base align=1
+std::piecewise_construct_t (0x0x7f3bff89ff60) 0 empty
+
+Class std::__nonesuch_no_braces
+ size=1 align=1
+ base size=1 base align=1
+std::__nonesuch_no_braces (0x0x7f3bff8ba270) 0 empty
+ std::__nonesuch (0x0x7f3bff8e3480) 0 empty
+
+Class std::__true_type
+ size=1 align=1
+ base size=0 base align=1
+std::__true_type (0x0x7f3bff92fde0) 0 empty
+
+Class std::__false_type
+ size=1 align=1
+ base size=0 base align=1
+std::__false_type (0x0x7f3bff92fe40) 0 empty
+
+Class std::input_iterator_tag
+ size=1 align=1
+ base size=0 base align=1
+std::input_iterator_tag (0x0x7f3bff992b40) 0 empty
+
+Class std::output_iterator_tag
+ size=1 align=1
+ base size=0 base align=1
+std::output_iterator_tag (0x0x7f3bff992ba0) 0 empty
+
+Class std::forward_iterator_tag
+ size=1 align=1
+ base size=1 base align=1
+std::forward_iterator_tag (0x0x7f3bff8ba750) 0 empty
+ std::input_iterator_tag (0x0x7f3bff992c00) 0 empty
+
+Class std::bidirectional_iterator_tag
+ size=1 align=1
+ base size=1 base align=1
+std::bidirectional_iterator_tag (0x0x7f3bff8ba7b8) 0 empty
+ std::forward_iterator_tag (0x0x7f3bff8ba820) 0 empty
+ std::input_iterator_tag (0x0x7f3bff992c60) 0 empty
+
+Class std::random_access_iterator_tag
+ size=1 align=1
+ base size=1 base align=1
+std::random_access_iterator_tag (0x0x7f3bff8ba888) 0 empty
+ std::bidirectional_iterator_tag (0x0x7f3bff8ba8f0) 0 empty
+ std::forward_iterator_tag (0x0x7f3bff8ba958) 0 empty
+ std::input_iterator_tag (0x0x7f3bff992cc0) 0 empty
+
+Class __gnu_cxx::__ops::_Iter_less_iter
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__ops::_Iter_less_iter (0x0x7f3bffa417e0) 0 empty
+
+Class __gnu_cxx::__ops::_Iter_less_val
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__ops::_Iter_less_val (0x0x7f3bffa41900) 0 empty
+
+Class __gnu_cxx::__ops::_Val_less_iter
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__ops::_Val_less_iter (0x0x7f3bffa41c00) 0 empty
+
+Class __gnu_cxx::__ops::_Iter_equal_to_iter
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__ops::_Iter_equal_to_iter (0x0x7f3bffa41f00) 0 empty
+
+Class __gnu_cxx::__ops::_Iter_equal_to_val
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__ops::_Iter_equal_to_val (0x0x7f3bff670060) 0 empty
+
+Class __locale_struct
+ size=232 align=8
+ base size=232 base align=8
+__locale_struct (0x0x7f3bff6fc360) 0
+
+Class timeval
+ size=16 align=8
+ base size=16 base align=8
+timeval (0x0x7f3bff6fc660) 0
+
+Class timespec
+ size=16 align=8
+ base size=16 base align=8
+timespec (0x0x7f3bff6fc6c0) 0
+
+Class __pthread_rwlock_arch_t
+ size=56 align=8
+ base size=56 base align=8
+__pthread_rwlock_arch_t (0x0x7f3bff6fc780) 0
+
+Class __pthread_internal_list
+ size=16 align=8
+ base size=16 base align=8
+__pthread_internal_list (0x0x7f3bff6fc7e0) 0
+
+Class __pthread_mutex_s
+ size=40 align=8
+ base size=40 base align=8
+__pthread_mutex_s (0x0x7f3bff6fc840) 0
+
+Class __pthread_cond_s
+ size=48 align=8
+ base size=48 base align=8
+__pthread_cond_s (0x0x7f3bff6fc8a0) 0
+
+Class pthread_attr_t
+ size=56 align=8
+ base size=56 base align=8
+pthread_attr_t (0x0x7f3bff6fcb40) 0
+
+Class random_data
+ size=48 align=8
+ base size=48 base align=8
+random_data (0x0x7f3bff6fcde0) 0
+
+Class drand48_data
+ size=24 align=8
+ base size=24 base align=8
+drand48_data (0x0x7f3bff6fce40) 0
+
+Vtable for std::exception
+std::exception::_ZTVSt9exception: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt9exception)
+16 (int (*)(...))std::exception::~exception
+24 (int (*)(...))std::exception::~exception
+32 (int (*)(...))std::exception::what
+
+Class std::exception
+ size=8 align=8
+ base size=8 base align=8
+std::exception (0x0x7f3bff755c00) 0 nearly-empty
+ vptr=((& std::exception::_ZTVSt9exception) + 16)
+
+Vtable for std::bad_exception
+std::bad_exception::_ZTVSt13bad_exception: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt13bad_exception)
+16 (int (*)(...))std::bad_exception::~bad_exception
+24 (int (*)(...))std::bad_exception::~bad_exception
+32 (int (*)(...))std::bad_exception::what
+
+Class std::bad_exception
+ size=8 align=8
+ base size=8 base align=8
+std::bad_exception (0x0x7f3bff8bac98) 0 nearly-empty
+ vptr=((& std::bad_exception::_ZTVSt13bad_exception) + 16)
+ std::exception (0x0x7f3bff755de0) 0 nearly-empty
+ primary-for std::bad_exception (0x0x7f3bff8bac98)
+
+Vtable for std::type_info
+std::type_info::_ZTVSt9type_info: 8 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt9type_info)
+16 (int (*)(...))std::type_info::~type_info
+24 (int (*)(...))std::type_info::~type_info
+32 (int (*)(...))std::type_info::__is_pointer_p
+40 (int (*)(...))std::type_info::__is_function_p
+48 (int (*)(...))std::type_info::__do_catch
+56 (int (*)(...))std::type_info::__do_upcast
+
+Class std::type_info
+ size=16 align=8
+ base size=16 base align=8
+std::type_info (0x0x7f3bff7f8000) 0
+ vptr=((& std::type_info::_ZTVSt9type_info) + 16)
+
+Vtable for std::bad_cast
+std::bad_cast::_ZTVSt8bad_cast: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt8bad_cast)
+16 (int (*)(...))std::bad_cast::~bad_cast
+24 (int (*)(...))std::bad_cast::~bad_cast
+32 (int (*)(...))std::bad_cast::what
+
+Class std::bad_cast
+ size=8 align=8
+ base size=8 base align=8
+std::bad_cast (0x0x7f3bff8bad00) 0 nearly-empty
+ vptr=((& std::bad_cast::_ZTVSt8bad_cast) + 16)
+ std::exception (0x0x7f3bff7f83c0) 0 nearly-empty
+ primary-for std::bad_cast (0x0x7f3bff8bad00)
+
+Vtable for std::bad_typeid
+std::bad_typeid::_ZTVSt10bad_typeid: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt10bad_typeid)
+16 (int (*)(...))std::bad_typeid::~bad_typeid
+24 (int (*)(...))std::bad_typeid::~bad_typeid
+32 (int (*)(...))std::bad_typeid::what
+
+Class std::bad_typeid
+ size=8 align=8
+ base size=8 base align=8
+std::bad_typeid (0x0x7f3bff8bad68) 0 nearly-empty
+ vptr=((& std::bad_typeid::_ZTVSt10bad_typeid) + 16)
+ std::exception (0x0x7f3bff7f85a0) 0 nearly-empty
+ primary-for std::bad_typeid (0x0x7f3bff8bad68)
+
+Class std::__exception_ptr::exception_ptr
+ size=8 align=8
+ base size=8 base align=8
+std::__exception_ptr::exception_ptr (0x0x7f3bff7f8780) 0
+
+Vtable for std::nested_exception
+std::nested_exception::_ZTVSt16nested_exception: 4 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt16nested_exception)
+16 (int (*)(...))std::nested_exception::~nested_exception
+24 (int (*)(...))std::nested_exception::~nested_exception
+
+Class std::nested_exception
+ size=16 align=8
+ base size=16 base align=8
+std::nested_exception (0x0x7f3bff7f8d20) 0
+ vptr=((& std::nested_exception::_ZTVSt16nested_exception) + 16)
+
+Vtable for std::bad_alloc
+std::bad_alloc::_ZTVSt9bad_alloc: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt9bad_alloc)
+16 (int (*)(...))std::bad_alloc::~bad_alloc
+24 (int (*)(...))std::bad_alloc::~bad_alloc
+32 (int (*)(...))std::bad_alloc::what
+
+Class std::bad_alloc
+ size=8 align=8
+ base size=8 base align=8
+std::bad_alloc (0x0x7f3bff8badd0) 0 nearly-empty
+ vptr=((& std::bad_alloc::_ZTVSt9bad_alloc) + 16)
+ std::exception (0x0x7f3bff82d420) 0 nearly-empty
+ primary-for std::bad_alloc (0x0x7f3bff8badd0)
+
+Vtable for std::bad_array_new_length
+std::bad_array_new_length::_ZTVSt20bad_array_new_length: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt20bad_array_new_length)
+16 (int (*)(...))std::bad_array_new_length::~bad_array_new_length
+24 (int (*)(...))std::bad_array_new_length::~bad_array_new_length
+32 (int (*)(...))std::bad_array_new_length::what
+
+Class std::bad_array_new_length
+ size=8 align=8
+ base size=8 base align=8
+std::bad_array_new_length (0x0x7f3bff8bae38) 0 nearly-empty
+ vptr=((& std::bad_array_new_length::_ZTVSt20bad_array_new_length) + 16)
+ std::bad_alloc (0x0x7f3bff8baea0) 0 nearly-empty
+ primary-for std::bad_array_new_length (0x0x7f3bff8bae38)
+ std::exception (0x0x7f3bff82d600) 0 nearly-empty
+ primary-for std::bad_alloc (0x0x7f3bff8baea0)
+
+Class std::nothrow_t
+ size=1 align=1
+ base size=0 base align=1
+std::nothrow_t (0x0x7f3bff82d7e0) 0 empty
+
+Class std::__allocator_traits_base
+ size=1 align=1
+ base size=0 base align=1
+std::__allocator_traits_base (0x0x7f3bff82d9c0) 0 empty
+
+Class std::__numeric_limits_base
+ size=1 align=1
+ base size=0 base align=1
+std::__numeric_limits_base (0x0x7f3bff4abea0) 0 empty
+
+Class QSysInfo
+ size=1 align=1
+ base size=0 base align=1
+QSysInfo (0x0x7f3bff145420) 0 empty
+
+Class QMessageLogContext
+ size=32 align=8
+ base size=32 base align=8
+QMessageLogContext (0x0x7f3bff145540) 0
+
+Class QMessageLogger
+ size=32 align=8
+ base size=32 base align=8
+QMessageLogger (0x0x7f3bff145720) 0
+
+Class QFlag
+ size=4 align=4
+ base size=4 base align=4
+QFlag (0x0x7f3bff145de0) 0
+
+Class QIncompatibleFlag
+ size=4 align=4
+ base size=4 base align=4
+QIncompatibleFlag (0x0x7f3bff1c05a0) 0
+
+Class std::__atomic_flag_base
+ size=1 align=1
+ base size=1 base align=1
+std::__atomic_flag_base (0x0x7f3bfee56a80) 0
+
+Class std::atomic_flag
+ size=1 align=1
+ base size=1 base align=1
+std::atomic_flag (0x0x7f3bff1f6d00) 0
+ std::__atomic_flag_base (0x0x7f3bfee56ae0) 0
+
+Class QAtomicInt
+ size=4 align=4
+ base size=4 base align=4
+QAtomicInt (0x0x7f3bff002478) 0
+ QAtomicInteger<int> (0x0x7f3bff0024e0) 0
+ QBasicAtomicInteger<int> (0x0x7f3bfed8cd20) 0
+
+Class QInternal
+ size=1 align=1
+ base size=0 base align=1
+QInternal (0x0x7f3bfe9e08a0) 0 empty
+
+Class QtPrivate::QSlotObjectBase
+ size=16 align=8
+ base size=16 base align=8
+QtPrivate::QSlotObjectBase (0x0x7f3bfea10e40) 0
+
+Class QGenericArgument
+ size=16 align=8
+ base size=16 base align=8
+QGenericArgument (0x0x7f3bfe65d5a0) 0
+
+Class QGenericReturnArgument
+ size=16 align=8
+ base size=16 base align=8
+QGenericReturnArgument (0x0x7f3bfe666138) 0
+ QGenericArgument (0x0x7f3bfe65d840) 0
+
+Class QMetaObject::SuperData
+ size=8 align=8
+ base size=8 base align=8
+QMetaObject::SuperData (0x0x7f3bfe65dcc0) 0
+
+Class QMetaObject
+ size=48 align=8
+ base size=48 base align=8
+QMetaObject (0x0x7f3bfe65dc60) 0
+
+Class QMetaObject::Connection
+ size=8 align=8
+ base size=8 base align=8
+QMetaObject::Connection (0x0x7f3bfe6b25a0) 0
+
+Class QLatin1Char
+ size=1 align=1
+ base size=1 base align=1
+QLatin1Char (0x0x7f3bfe7350c0) 0
+
+Class QChar
+ size=2 align=2
+ base size=2 base align=2
+QChar (0x0x7f3bfe7357e0) 0
+
+Class QtPrivate::RefCount
+ size=4 align=4
+ base size=4 base align=4
+QtPrivate::RefCount (0x0x7f3bfe804600) 0
+
+Class QArrayData
+ size=24 align=8
+ base size=24 base align=8
+QArrayData (0x0x7f3bfe804960) 0
+
+Class QtPrivate::QContainerImplHelper
+ size=1 align=1
+ base size=0 base align=1
+QtPrivate::QContainerImplHelper (0x0x7f3bfe461c60) 0 empty
+
+Class lconv
+ size=96 align=8
+ base size=96 base align=8
+lconv (0x0x7f3bfe5574e0) 0
+
+Vtable for __cxxabiv1::__forced_unwind
+__cxxabiv1::__forced_unwind::_ZTVN10__cxxabiv115__forced_unwindE: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTIN10__cxxabiv115__forced_unwindE)
+16 0
+24 0
+32 (int (*)(...))__cxa_pure_virtual
+
+Class __cxxabiv1::__forced_unwind
+ size=8 align=8
+ base size=8 base align=8
+__cxxabiv1::__forced_unwind (0x0x7f3bfe5575a0) 0 nearly-empty
+ vptr=((& __cxxabiv1::__forced_unwind::_ZTVN10__cxxabiv115__forced_unwindE) + 16)
+
+Class sched_param
+ size=4 align=4
+ base size=4 base align=4
+sched_param (0x0x7f3bfe60c6c0) 0
+
+Class timex
+ size=208 align=8
+ base size=208 base align=8
+timex (0x0x7f3bfe60c780) 0
+
+Class tm
+ size=56 align=8
+ base size=56 base align=8
+tm (0x0x7f3bfe60c7e0) 0
+
+Class itimerspec
+ size=32 align=8
+ base size=32 base align=8
+itimerspec (0x0x7f3bfe60c840) 0
+
+Class _pthread_cleanup_buffer
+ size=32 align=8
+ base size=32 base align=8
+_pthread_cleanup_buffer (0x0x7f3bfe60c8a0) 0
+
+Class __pthread_cleanup_frame
+ size=24 align=8
+ base size=24 base align=8
+__pthread_cleanup_frame (0x0x7f3bfe60c9c0) 0
+
+Class __pthread_cleanup_class
+ size=24 align=8
+ base size=24 base align=8
+__pthread_cleanup_class (0x0x7f3bfe60ca20) 0
+
+Class _IO_marker
+ size=24 align=8
+ base size=24 base align=8
+_IO_marker (0x0x7f3bfe34d9c0) 0
+
+Class _IO_FILE
+ size=216 align=8
+ base size=216 base align=8
+_IO_FILE (0x0x7f3bfe34da20) 0
+
+Class std::_Hash_impl
+ size=1 align=1
+ base size=0 base align=1
+std::_Hash_impl (0x0x7f3bfe106a80) 0 empty
+
+Class std::_Fnv_hash_impl
+ size=1 align=1
+ base size=0 base align=1
+std::_Fnv_hash_impl (0x0x7f3bfe106c00) 0 empty
+
+Class std::locale
+ size=8 align=8
+ base size=8 base align=8
+std::locale (0x0x7f3bfde7bd80) 0
+
+Vtable for std::locale::facet
+std::locale::facet::_ZTVNSt6locale5facetE: 4 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTINSt6locale5facetE)
+16 (int (*)(...))std::locale::facet::~facet
+24 (int (*)(...))std::locale::facet::~facet
+
+Class std::locale::facet
+ size=16 align=8
+ base size=12 base align=8
+std::locale::facet (0x0x7f3bfdec7180) 0
+ vptr=((& std::locale::facet::_ZTVNSt6locale5facetE) + 16)
+
+Class std::locale::id
+ size=8 align=8
+ base size=8 base align=8
+std::locale::id (0x0x7f3bfdec7420) 0
+
+Class std::locale::_Impl
+ size=40 align=8
+ base size=40 base align=8
+std::locale::_Impl (0x0x7f3bfdec7600) 0
+
+Class std::__cow_string
+ size=8 align=8
+ base size=8 base align=8
+std::__cow_string (0x0x7f3bfdf14600) 0
+
+Vtable for std::logic_error
+std::logic_error::_ZTVSt11logic_error: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt11logic_error)
+16 (int (*)(...))std::logic_error::~logic_error
+24 (int (*)(...))std::logic_error::~logic_error
+32 (int (*)(...))std::logic_error::what
+
+Class std::logic_error
+ size=16 align=8
+ base size=16 base align=8
+std::logic_error (0x0x7f3bfdf250d0) 0
+ vptr=((& std::logic_error::_ZTVSt11logic_error) + 16)
+ std::exception (0x0x7f3bfdf146c0) 0 nearly-empty
+ primary-for std::logic_error (0x0x7f3bfdf250d0)
+
+Vtable for std::domain_error
+std::domain_error::_ZTVSt12domain_error: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt12domain_error)
+16 (int (*)(...))std::domain_error::~domain_error
+24 (int (*)(...))std::domain_error::~domain_error
+32 (int (*)(...))std::logic_error::what
+
+Class std::domain_error
+ size=16 align=8
+ base size=16 base align=8
+std::domain_error (0x0x7f3bfdf25138) 0
+ vptr=((& std::domain_error::_ZTVSt12domain_error) + 16)
+ std::logic_error (0x0x7f3bfdf251a0) 0
+ primary-for std::domain_error (0x0x7f3bfdf25138)
+ std::exception (0x0x7f3bfdf14720) 0 nearly-empty
+ primary-for std::logic_error (0x0x7f3bfdf251a0)
+
+Vtable for std::invalid_argument
+std::invalid_argument::_ZTVSt16invalid_argument: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt16invalid_argument)
+16 (int (*)(...))std::invalid_argument::~invalid_argument
+24 (int (*)(...))std::invalid_argument::~invalid_argument
+32 (int (*)(...))std::logic_error::what
+
+Class std::invalid_argument
+ size=16 align=8
+ base size=16 base align=8
+std::invalid_argument (0x0x7f3bfdf25208) 0
+ vptr=((& std::invalid_argument::_ZTVSt16invalid_argument) + 16)
+ std::logic_error (0x0x7f3bfdf25270) 0
+ primary-for std::invalid_argument (0x0x7f3bfdf25208)
+ std::exception (0x0x7f3bfdf14780) 0 nearly-empty
+ primary-for std::logic_error (0x0x7f3bfdf25270)
+
+Vtable for std::length_error
+std::length_error::_ZTVSt12length_error: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt12length_error)
+16 (int (*)(...))std::length_error::~length_error
+24 (int (*)(...))std::length_error::~length_error
+32 (int (*)(...))std::logic_error::what
+
+Class std::length_error
+ size=16 align=8
+ base size=16 base align=8
+std::length_error (0x0x7f3bfdf252d8) 0
+ vptr=((& std::length_error::_ZTVSt12length_error) + 16)
+ std::logic_error (0x0x7f3bfdf25340) 0
+ primary-for std::length_error (0x0x7f3bfdf252d8)
+ std::exception (0x0x7f3bfdf147e0) 0 nearly-empty
+ primary-for std::logic_error (0x0x7f3bfdf25340)
+
+Vtable for std::out_of_range
+std::out_of_range::_ZTVSt12out_of_range: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt12out_of_range)
+16 (int (*)(...))std::out_of_range::~out_of_range
+24 (int (*)(...))std::out_of_range::~out_of_range
+32 (int (*)(...))std::logic_error::what
+
+Class std::out_of_range
+ size=16 align=8
+ base size=16 base align=8
+std::out_of_range (0x0x7f3bfdf253a8) 0
+ vptr=((& std::out_of_range::_ZTVSt12out_of_range) + 16)
+ std::logic_error (0x0x7f3bfdf25410) 0
+ primary-for std::out_of_range (0x0x7f3bfdf253a8)
+ std::exception (0x0x7f3bfdf14840) 0 nearly-empty
+ primary-for std::logic_error (0x0x7f3bfdf25410)
+
+Vtable for std::runtime_error
+std::runtime_error::_ZTVSt13runtime_error: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt13runtime_error)
+16 (int (*)(...))std::runtime_error::~runtime_error
+24 (int (*)(...))std::runtime_error::~runtime_error
+32 (int (*)(...))std::runtime_error::what
+
+Class std::runtime_error
+ size=16 align=8
+ base size=16 base align=8
+std::runtime_error (0x0x7f3bfdf25478) 0
+ vptr=((& std::runtime_error::_ZTVSt13runtime_error) + 16)
+ std::exception (0x0x7f3bfdf148a0) 0 nearly-empty
+ primary-for std::runtime_error (0x0x7f3bfdf25478)
+
+Vtable for std::range_error
+std::range_error::_ZTVSt11range_error: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt11range_error)
+16 (int (*)(...))std::range_error::~range_error
+24 (int (*)(...))std::range_error::~range_error
+32 (int (*)(...))std::runtime_error::what
+
+Class std::range_error
+ size=16 align=8
+ base size=16 base align=8
+std::range_error (0x0x7f3bfdf254e0) 0
+ vptr=((& std::range_error::_ZTVSt11range_error) + 16)
+ std::runtime_error (0x0x7f3bfdf25548) 0
+ primary-for std::range_error (0x0x7f3bfdf254e0)
+ std::exception (0x0x7f3bfdf14900) 0 nearly-empty
+ primary-for std::runtime_error (0x0x7f3bfdf25548)
+
+Vtable for std::overflow_error
+std::overflow_error::_ZTVSt14overflow_error: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt14overflow_error)
+16 (int (*)(...))std::overflow_error::~overflow_error
+24 (int (*)(...))std::overflow_error::~overflow_error
+32 (int (*)(...))std::runtime_error::what
+
+Class std::overflow_error
+ size=16 align=8
+ base size=16 base align=8
+std::overflow_error (0x0x7f3bfdf255b0) 0
+ vptr=((& std::overflow_error::_ZTVSt14overflow_error) + 16)
+ std::runtime_error (0x0x7f3bfdf25618) 0
+ primary-for std::overflow_error (0x0x7f3bfdf255b0)
+ std::exception (0x0x7f3bfdf14960) 0 nearly-empty
+ primary-for std::runtime_error (0x0x7f3bfdf25618)
+
+Vtable for std::underflow_error
+std::underflow_error::_ZTVSt15underflow_error: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt15underflow_error)
+16 (int (*)(...))std::underflow_error::~underflow_error
+24 (int (*)(...))std::underflow_error::~underflow_error
+32 (int (*)(...))std::runtime_error::what
+
+Class std::underflow_error
+ size=16 align=8
+ base size=16 base align=8
+std::underflow_error (0x0x7f3bfdf25680) 0
+ vptr=((& std::underflow_error::_ZTVSt15underflow_error) + 16)
+ std::runtime_error (0x0x7f3bfdf256e8) 0
+ primary-for std::underflow_error (0x0x7f3bfdf25680)
+ std::exception (0x0x7f3bfdf149c0) 0 nearly-empty
+ primary-for std::runtime_error (0x0x7f3bfdf256e8)
+
+Vtable for std::_V2::error_category
+std::_V2::error_category::_ZTVNSt3_V214error_categoryE: 10 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTINSt3_V214error_categoryE)
+16 0
+24 0
+32 (int (*)(...))__cxa_pure_virtual
+40 (int (*)(...))std::_V2::error_category::_M_message
+48 (int (*)(...))__cxa_pure_virtual
+56 (int (*)(...))std::_V2::error_category::default_error_condition
+64 (int (*)(...))std::_V2::error_category::equivalent
+72 (int (*)(...))std::_V2::error_category::equivalent
+
+Class std::_V2::error_category
+ size=8 align=8
+ base size=8 base align=8
+std::_V2::error_category (0x0x7f3bfdf14b40) 0 nearly-empty
+ vptr=((& std::_V2::error_category::_ZTVNSt3_V214error_categoryE) + 16)
+
+Class std::error_code
+ size=16 align=8
+ base size=16 base align=8
+std::error_code (0x0x7f3bfdf14ea0) 0
+
+Class std::error_condition
+ size=16 align=8
+ base size=16 base align=8
+std::error_condition (0x0x7f3bfdf72720) 0
+
+Vtable for std::system_error
+std::system_error::_ZTVSt12system_error: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt12system_error)
+16 (int (*)(...))std::system_error::~system_error
+24 (int (*)(...))std::system_error::~system_error
+32 (int (*)(...))std::runtime_error::what
+
+Class std::system_error
+ size=32 align=8
+ base size=32 base align=8
+std::system_error (0x0x7f3bfdf25af8) 0
+ vptr=((& std::system_error::_ZTVSt12system_error) + 16)
+ std::runtime_error (0x0x7f3bfdf25b60) 0
+ primary-for std::system_error (0x0x7f3bfdf25af8)
+ std::exception (0x0x7f3bfdf99300) 0 nearly-empty
+ primary-for std::runtime_error (0x0x7f3bfdf25b60)
+
+Vtable for std::ios_base::failure
+std::ios_base::failure::_ZTVNSt8ios_base7failureB5cxx11E: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTINSt8ios_base7failureB5cxx11E)
+16 (int (*)(...))std::ios_base::failure::~failure
+24 (int (*)(...))std::ios_base::failure::~failure
+32 (int (*)(...))std::ios_base::failure::what
+
+Class std::ios_base::failure
+ size=32 align=8
+ base size=32 base align=8
+std::ios_base::failure (0x0x7f3bfdf25dd0) 0
+ vptr=((& std::ios_base::failure::_ZTVNSt8ios_base7failureB5cxx11E) + 16)
+ std::system_error (0x0x7f3bfdf25e38) 0
+ primary-for std::ios_base::failure (0x0x7f3bfdf25dd0)
+ std::runtime_error (0x0x7f3bfdf25ea0) 0
+ primary-for std::system_error (0x0x7f3bfdf25e38)
+ std::exception (0x0x7f3bfdfca8a0) 0 nearly-empty
+ primary-for std::runtime_error (0x0x7f3bfdf25ea0)
+
+Class std::ios_base::_Callback_list
+ size=24 align=8
+ base size=24 base align=8
+std::ios_base::_Callback_list (0x0x7f3bfdfca900) 0
+
+Class std::ios_base::_Words
+ size=16 align=8
+ base size=16 base align=8
+std::ios_base::_Words (0x0x7f3bfdfca960) 0
+
+Class std::ios_base::Init
+ size=1 align=1
+ base size=0 base align=1
+std::ios_base::Init (0x0x7f3bfdfca9c0) 0 empty
+
+Vtable for std::ios_base
+std::ios_base::_ZTVSt8ios_base: 4 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt8ios_base)
+16 (int (*)(...))std::ios_base::~ios_base
+24 (int (*)(...))std::ios_base::~ios_base
+
+Class std::ios_base
+ size=216 align=8
+ base size=216 base align=8
+std::ios_base (0x0x7f3bfdfca840) 0
+ vptr=((& std::ios_base::_ZTVSt8ios_base) + 16)
+
+Class std::ctype_base
+ size=1 align=1
+ base size=0 base align=1
+std::ctype_base (0x0x7f3bfdcbe300) 0 empty
+
+Class std::__num_base
+ size=1 align=1
+ base size=0 base align=1
+std::__num_base (0x0x7f3bfdd8a4e0) 0 empty
+
+VTT for std::basic_ostream<char>
+std::basic_ostream<char>::_ZTTSo: 2 entries
+0 ((& std::basic_ostream<char>::_ZTVSo) + 24)
+8 ((& std::basic_ostream<char>::_ZTVSo) + 64)
+
+VTT for std::basic_ostream<wchar_t>
+std::basic_ostream<wchar_t>::_ZTTSt13basic_ostreamIwSt11char_traitsIwEE: 2 entries
+0 ((& std::basic_ostream<wchar_t>::_ZTVSt13basic_ostreamIwSt11char_traitsIwEE) + 24)
+8 ((& std::basic_ostream<wchar_t>::_ZTVSt13basic_ostreamIwSt11char_traitsIwEE) + 64)
+
+VTT for std::basic_istream<char>
+std::basic_istream<char>::_ZTTSi: 2 entries
+0 ((& std::basic_istream<char>::_ZTVSi) + 24)
+8 ((& std::basic_istream<char>::_ZTVSi) + 64)
+
+VTT for std::basic_istream<wchar_t>
+std::basic_istream<wchar_t>::_ZTTSt13basic_istreamIwSt11char_traitsIwEE: 2 entries
+0 ((& std::basic_istream<wchar_t>::_ZTVSt13basic_istreamIwSt11char_traitsIwEE) + 24)
+8 ((& std::basic_istream<wchar_t>::_ZTVSt13basic_istreamIwSt11char_traitsIwEE) + 64)
+
+Construction vtable for std::basic_istream<char> (0x0x7f3bfd9265b0 instance) in std::basic_iostream<char>
+std::basic_iostream<char>::_ZTCSd0_Si: 10 entries
+0 24
+8 (int (*)(...))0
+16 (int (*)(...))(& _ZTISi)
+24 0
+32 0
+40 18446744073709551592
+48 (int (*)(...))-24
+56 (int (*)(...))(& _ZTISi)
+64 0
+72 0
+
+Construction vtable for std::basic_ostream<char> (0x0x7f3bfd926680 instance) in std::basic_iostream<char>
+std::basic_iostream<char>::_ZTCSd16_So: 10 entries
+0 8
+8 (int (*)(...))0
+16 (int (*)(...))(& _ZTISo)
+24 0
+32 0
+40 18446744073709551608
+48 (int (*)(...))-8
+56 (int (*)(...))(& _ZTISo)
+64 0
+72 0
+
+VTT for std::basic_iostream<char>
+std::basic_iostream<char>::_ZTTSd: 7 entries
+0 ((& std::basic_iostream<char>::_ZTVSd) + 24)
+8 ((& std::basic_iostream<char>::_ZTCSd0_Si) + 24)
+16 ((& std::basic_iostream<char>::_ZTCSd0_Si) + 64)
+24 ((& std::basic_iostream<char>::_ZTCSd16_So) + 24)
+32 ((& std::basic_iostream<char>::_ZTCSd16_So) + 64)
+40 ((& std::basic_iostream<char>::_ZTVSd) + 104)
+48 ((& std::basic_iostream<char>::_ZTVSd) + 64)
+
+Construction vtable for std::basic_istream<wchar_t> (0x0x7f3bfd967340 instance) in std::basic_iostream<wchar_t>
+std::basic_iostream<wchar_t>::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE0_St13basic_istreamIwS1_E: 10 entries
+0 24
+8 (int (*)(...))0
+16 (int (*)(...))(& _ZTISt13basic_istreamIwSt11char_traitsIwEE)
+24 0
+32 0
+40 18446744073709551592
+48 (int (*)(...))-24
+56 (int (*)(...))(& _ZTISt13basic_istreamIwSt11char_traitsIwEE)
+64 0
+72 0
+
+Construction vtable for std::basic_ostream<wchar_t> (0x0x7f3bfd967410 instance) in std::basic_iostream<wchar_t>
+std::basic_iostream<wchar_t>::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE16_St13basic_ostreamIwS1_E: 10 entries
+0 8
+8 (int (*)(...))0
+16 (int (*)(...))(& _ZTISt13basic_ostreamIwSt11char_traitsIwEE)
+24 0
+32 0
+40 18446744073709551608
+48 (int (*)(...))-8
+56 (int (*)(...))(& _ZTISt13basic_ostreamIwSt11char_traitsIwEE)
+64 0
+72 0
+
+VTT for std::basic_iostream<wchar_t>
+std::basic_iostream<wchar_t>::_ZTTSt14basic_iostreamIwSt11char_traitsIwEE: 7 entries
+0 ((& std::basic_iostream<wchar_t>::_ZTVSt14basic_iostreamIwSt11char_traitsIwEE) + 24)
+8 ((& std::basic_iostream<wchar_t>::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE0_St13basic_istreamIwS1_E) + 24)
+16 ((& std::basic_iostream<wchar_t>::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE0_St13basic_istreamIwS1_E) + 64)
+24 ((& std::basic_iostream<wchar_t>::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE16_St13basic_ostreamIwS1_E) + 24)
+32 ((& std::basic_iostream<wchar_t>::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE16_St13basic_ostreamIwS1_E) + 64)
+40 ((& std::basic_iostream<wchar_t>::_ZTVSt14basic_iostreamIwSt11char_traitsIwEE) + 104)
+48 ((& std::basic_iostream<wchar_t>::_ZTVSt14basic_iostreamIwSt11char_traitsIwEE) + 64)
+
+Class QByteArrayDataPtr
+ size=8 align=8
+ base size=8 base align=8
+QByteArrayDataPtr (0x0x7f3bfd963e40) 0
+
+Class QByteArray
+ size=8 align=8
+ base size=8 base align=8
+QByteArray (0x0x7f3bfd963ea0) 0
+
+Class QByteRef
+ size=16 align=8
+ base size=12 base align=8
+QByteRef (0x0x7f3bfd6d02a0) 0
+
+Class QStringDataPtr
+ size=8 align=8
+ base size=8 base align=8
+QStringDataPtr (0x0x7f3bfd76c120) 0
+
+Class QStringView
+ size=16 align=8
+ base size=16 base align=8
+QStringView (0x0x7f3bfd76c5a0) 0
+
+Class QLatin1String
+ size=16 align=8
+ base size=16 base align=8
+QLatin1String (0x0x7f3bfd455660) 0
+
+Class QString::Null
+ size=1 align=1
+ base size=0 base align=1
+QString::Null (0x0x7f3bfd505600) 0 empty
+
+Class QString
+ size=8 align=8
+ base size=8 base align=8
+QString (0x0x7f3bfd5054e0) 0
+
+Class QCharRef
+ size=16 align=8
+ base size=12 base align=8
+QCharRef (0x0x7f3bfd3df480) 0
+
+Class QStringRef
+ size=16 align=8
+ base size=16 base align=8
+QStringRef (0x0x7f3bfd162060) 0
+
+Class QtPrivate::ArgBase
+ size=1 align=1
+ base size=1 base align=1
+QtPrivate::ArgBase (0x0x7f3bfcec0e40) 0
+
+Class QtPrivate::QStringViewArg
+ size=24 align=8
+ base size=24 base align=8
+QtPrivate::QStringViewArg (0x0x7f3bfd1f5270) 0
+ QtPrivate::ArgBase (0x0x7f3bfcec0ea0) 0
+
+Class QtPrivate::QLatin1StringArg
+ size=24 align=8
+ base size=24 base align=8
+QtPrivate::QLatin1StringArg (0x0x7f3bfd1f52d8) 0
+ QtPrivate::ArgBase (0x0x7f3bfcefd0c0) 0
+
+Class std::__erased_type
+ size=1 align=1
+ base size=0 base align=1
+std::__erased_type (0x0x7f3bfcfbc000) 0 empty
+
+Class std::allocator_arg_t
+ size=1 align=1
+ base size=0 base align=1
+std::allocator_arg_t (0x0x7f3bfcfbc060) 0 empty
+
+Class std::__uses_alloc_base
+ size=1 align=1
+ base size=0 base align=1
+std::__uses_alloc_base (0x0x7f3bfcfbc1e0) 0 empty
+
+Class std::__uses_alloc0::_Sink
+ size=1 align=1
+ base size=0 base align=1
+std::__uses_alloc0::_Sink (0x0x7f3bfcfbc2a0) 0 empty
+
+Class std::__uses_alloc0
+ size=1 align=1
+ base size=1 base align=1
+std::__uses_alloc0 (0x0x7f3bfd1f5680) 0
+ std::__uses_alloc_base (0x0x7f3bfcfbc240) 0 empty
+
+Class std::_Swallow_assign
+ size=1 align=1
+ base size=0 base align=1
+std::_Swallow_assign (0x0x7f3bfcd27600) 0 empty
+
+Vtable for std::bad_function_call
+std::bad_function_call::_ZTVSt17bad_function_call: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt17bad_function_call)
+16 (int (*)(...))std::bad_function_call::~bad_function_call
+24 (int (*)(...))std::bad_function_call::~bad_function_call
+32 (int (*)(...))std::bad_function_call::what
+
+Class std::bad_function_call
+ size=8 align=8
+ base size=8 base align=8
+std::bad_function_call (0x0x7f3bfcd6f8f0) 0 nearly-empty
+ vptr=((& std::bad_function_call::_ZTVSt17bad_function_call) + 16)
+ std::exception (0x0x7f3bfcd6cf00) 0 nearly-empty
+ primary-for std::bad_function_call (0x0x7f3bfcd6f8f0)
+
+Class std::_Nocopy_types
+ size=16 align=8
+ base size=16 base align=8
+std::_Nocopy_types (0x0x7f3bfcda0000) 0
+
+Class std::_Any_data
+ size=16 align=8
+ base size=16 base align=8
+std::_Any_data (0x0x7f3bfcda0060) 0
+
+Class std::_Function_base
+ size=24 align=8
+ base size=24 base align=8
+std::_Function_base (0x0x7f3bfcda0360) 0
+
+Class QtPrivate::QHashCombine
+ size=1 align=1
+ base size=0 base align=1
+QtPrivate::QHashCombine (0x0x7f3bfcb927e0) 0 empty
+
+Class QtPrivate::QHashCombineCommutative
+ size=1 align=1
+ base size=0 base align=1
+QtPrivate::QHashCombineCommutative (0x0x7f3bfcb928a0) 0 empty
+
+Class std::_Bit_reference
+ size=16 align=8
+ base size=16 base align=8
+std::_Bit_reference (0x0x7f3bfc8c5000) 0
+
+Class std::_Bit_iterator_base
+ size=16 align=8
+ base size=12 base align=8
+std::_Bit_iterator_base (0x0x7f3bfcbf2478) 0
+ std::iterator<std::random_access_iterator_tag, bool> (0x0x7f3bfc8c5720) 0 empty
+
+Class std::_Bit_iterator
+ size=16 align=8
+ base size=12 base align=8
+std::_Bit_iterator (0x0x7f3bfcbf25b0) 0
+ std::_Bit_iterator_base (0x0x7f3bfcbf2618) 0
+ std::iterator<std::random_access_iterator_tag, bool> (0x0x7f3bfc8c5d80) 0 empty
+
+Class std::_Bit_const_iterator
+ size=16 align=8
+ base size=12 base align=8
+std::_Bit_const_iterator (0x0x7f3bfcbf2680) 0
+ std::_Bit_iterator_base (0x0x7f3bfcbf26e8) 0
+ std::iterator<std::random_access_iterator_tag, bool> (0x0x7f3bfc8fd5a0) 0 empty
+
+Class std::__detail::_List_node_base
+ size=16 align=8
+ base size=16 base align=8
+std::__detail::_List_node_base (0x0x7f3bfc748120) 0
+
+Class QListData::NotArrayCompatibleLayout
+ size=1 align=1
+ base size=0 base align=1
+QListData::NotArrayCompatibleLayout (0x0x7f3bfc7f5ea0) 0 empty
+
+Class QListData::NotIndirectLayout
+ size=1 align=1
+ base size=0 base align=1
+QListData::NotIndirectLayout (0x0x7f3bfc7f5f00) 0 empty
+
+Class QListData::ArrayCompatibleLayout
+ size=1 align=1
+ base size=1 base align=1
+QListData::ArrayCompatibleLayout (0x0x7f3bfc744208) 0 empty
+ QListData::NotIndirectLayout (0x0x7f3bfc7f5f60) 0 empty
+
+Class QListData::InlineWithPaddingLayout
+ size=1 align=1
+ base size=1 base align=1
+QListData::InlineWithPaddingLayout (0x0x7f3bfc738af0) 0 empty
+ QListData::NotArrayCompatibleLayout (0x0x7f3bfc80d000) 0 empty
+ QListData::NotIndirectLayout (0x0x7f3bfc80d060) 0 empty
+
+Class QListData::IndirectLayout
+ size=1 align=1
+ base size=1 base align=1
+QListData::IndirectLayout (0x0x7f3bfc744270) 0 empty
+ QListData::NotArrayCompatibleLayout (0x0x7f3bfc80d0c0) 0 empty
+
+Class QListData::Data
+ size=24 align=8
+ base size=24 base align=8
+QListData::Data (0x0x7f3bfc80d120) 0
+
+Class QListData
+ size=8 align=8
+ base size=8 base align=8
+QListData (0x0x7f3bfc7f5e40) 0
+
+Class QRegExp
+ size=8 align=8
+ base size=8 base align=8
+QRegExp (0x0x7f3bfc5072a0) 0
+
+Class QStringMatcher::Data
+ size=272 align=8
+ base size=272 base align=8
+QStringMatcher::Data (0x0x7f3bfc5e57e0) 0
+
+Class QStringMatcher
+ size=1048 align=8
+ base size=1048 base align=8
+QStringMatcher (0x0x7f3bfc5e5780) 0
+
+Class QStringList
+ size=8 align=8
+ base size=8 base align=8
+QStringList (0x0x7f3bfc5d5f08) 0
+ QList<QString> (0x0x7f3bfc5d5f70) 0
+ QListSpecialMethods<QString> (0x0x7f3bfc5e5a20) 0 empty
+
+Class QScopedPointerPodDeleter
+ size=1 align=1
+ base size=0 base align=1
+QScopedPointerPodDeleter (0x0x7f3bfc2b4960) 0 empty
+
+Class std::_Rb_tree_node_base
+ size=32 align=8
+ base size=32 base align=8
+std::_Rb_tree_node_base (0x0x7f3bfc34aba0) 0
+
+Class std::_Rb_tree_header
+ size=40 align=8
+ base size=40 base align=8
+std::_Rb_tree_header (0x0x7f3bfc34af00) 0
+
+Class QtPrivate::AbstractDebugStreamFunction
+ size=16 align=8
+ base size=16 base align=8
+QtPrivate::AbstractDebugStreamFunction (0x0x7f3bfc0a6540) 0
+
+Class QtPrivate::AbstractComparatorFunction
+ size=24 align=8
+ base size=24 base align=8
+QtPrivate::AbstractComparatorFunction (0x0x7f3bfc0a68a0) 0
+
+Class QtPrivate::AbstractConverterFunction
+ size=8 align=8
+ base size=8 base align=8
+QtPrivate::AbstractConverterFunction (0x0x7f3bfc0a6de0) 0
+
+Class QMetaType
+ size=80 align=8
+ base size=80 base align=8
+QMetaType (0x0x7f3bfc0cb360) 0
+
+Class QtMetaTypePrivate::VariantData
+ size=24 align=8
+ base size=20 base align=8
+QtMetaTypePrivate::VariantData (0x0x7f3bfc136540) 0
+
+Class QtMetaTypePrivate::VectorBoolElements
+ size=1 align=1
+ base size=0 base align=1
+QtMetaTypePrivate::VectorBoolElements (0x0x7f3bfc136c00) 0 empty
+
+Class QtMetaTypePrivate::QSequentialIterableImpl
+ size=104 align=8
+ base size=104 base align=8
+QtMetaTypePrivate::QSequentialIterableImpl (0x0x7f3bfbd8da80) 0
+
+Class QtMetaTypePrivate::QAssociativeIterableImpl
+ size=112 align=8
+ base size=112 base align=8
+QtMetaTypePrivate::QAssociativeIterableImpl (0x0x7f3bfbe46180) 0
+
+Class QtMetaTypePrivate::QPairVariantInterfaceImpl
+ size=40 align=8
+ base size=40 base align=8
+QtMetaTypePrivate::QPairVariantInterfaceImpl (0x0x7f3bfbe9d6c0) 0
+
+Class std::chrono::_V2::system_clock
+ size=1 align=1
+ base size=0 base align=1
+std::chrono::_V2::system_clock (0x0x7f3bfbd30c60) 0 empty
+
+Class std::chrono::_V2::steady_clock
+ size=1 align=1
+ base size=0 base align=1
+std::chrono::_V2::steady_clock (0x0x7f3bfba65720) 0 empty
+
+Vtable for QObjectData
+QObjectData::_ZTV11QObjectData: 4 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI11QObjectData)
+16 (int (*)(...))__cxa_pure_virtual
+24 (int (*)(...))__cxa_pure_virtual
+
+Class QObjectData
+ size=48 align=8
+ base size=48 base align=8
+QObjectData (0x0x7f3bfba65780) 0
+ vptr=((& QObjectData::_ZTV11QObjectData) + 16)
+
+Class QObject::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QObject::QPrivateSignal (0x0x7f3bfba65960) 0 empty
+
+Vtable for QObject
+QObject::_ZTV7QObject: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI7QObject)
+16 (int (*)(...))QObject::metaObject
+24 (int (*)(...))QObject::qt_metacast
+32 (int (*)(...))QObject::qt_metacall
+40 (int (*)(...))QObject::~QObject
+48 (int (*)(...))QObject::~QObject
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QObject
+ size=16 align=8
+ base size=16 base align=8
+QObject (0x0x7f3bfba65900) 0
+ vptr=((& QObject::_ZTV7QObject) + 16)
+
+Vtable for QObjectUserData
+QObjectUserData::_ZTV15QObjectUserData: 4 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI15QObjectUserData)
+16 (int (*)(...))QObjectUserData::~QObjectUserData
+24 (int (*)(...))QObjectUserData::~QObjectUserData
+
+Class QObjectUserData
+ size=8 align=8
+ base size=8 base align=8
+QObjectUserData (0x0x7f3bfb749780) 0 nearly-empty
+ vptr=((& QObjectUserData::_ZTV15QObjectUserData) + 16)
+
+Class QSignalBlocker
+ size=16 align=8
+ base size=10 base align=8
+QSignalBlocker (0x0x7f3bfb749900) 0
+
+Class QAbstractAnimation::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QAbstractAnimation::QPrivateSignal (0x0x7f3bfb76c1e0) 0 empty
+
+Vtable for QAbstractAnimation
+QAbstractAnimation::_ZTV18QAbstractAnimation: 18 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI18QAbstractAnimation)
+16 (int (*)(...))QAbstractAnimation::metaObject
+24 (int (*)(...))QAbstractAnimation::qt_metacast
+32 (int (*)(...))QAbstractAnimation::qt_metacall
+40 0
+48 0
+56 (int (*)(...))QAbstractAnimation::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))__cxa_pure_virtual
+120 (int (*)(...))__cxa_pure_virtual
+128 (int (*)(...))QAbstractAnimation::updateState
+136 (int (*)(...))QAbstractAnimation::updateDirection
+
+Class QAbstractAnimation
+ size=16 align=8
+ base size=16 base align=8
+QAbstractAnimation (0x0x7f3bfb74d0d0) 0
+ vptr=((& QAbstractAnimation::_ZTV18QAbstractAnimation) + 16)
+ QObject (0x0x7f3bfb76c180) 0
+ primary-for QAbstractAnimation (0x0x7f3bfb74d0d0)
+
+Class QAnimationDriver::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QAnimationDriver::QPrivateSignal (0x0x7f3bfb76c5a0) 0 empty
+
+Vtable for QAnimationDriver
+QAnimationDriver::_ZTV16QAnimationDriver: 18 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI16QAnimationDriver)
+16 (int (*)(...))QAnimationDriver::metaObject
+24 (int (*)(...))QAnimationDriver::qt_metacast
+32 (int (*)(...))QAnimationDriver::qt_metacall
+40 (int (*)(...))QAnimationDriver::~QAnimationDriver
+48 (int (*)(...))QAnimationDriver::~QAnimationDriver
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QAnimationDriver::advance
+120 (int (*)(...))QAnimationDriver::elapsed
+128 (int (*)(...))QAnimationDriver::start
+136 (int (*)(...))QAnimationDriver::stop
+
+Class QAnimationDriver
+ size=16 align=8
+ base size=16 base align=8
+QAnimationDriver (0x0x7f3bfb74d138) 0
+ vptr=((& QAnimationDriver::_ZTV16QAnimationDriver) + 16)
+ QObject (0x0x7f3bfb76c540) 0
+ primary-for QAnimationDriver (0x0x7f3bfb74d138)
+
+Class QEventLoop::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QEventLoop::QPrivateSignal (0x0x7f3bfb76c7e0) 0 empty
+
+Vtable for QEventLoop
+QEventLoop::_ZTV10QEventLoop: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI10QEventLoop)
+16 (int (*)(...))QEventLoop::metaObject
+24 (int (*)(...))QEventLoop::qt_metacast
+32 (int (*)(...))QEventLoop::qt_metacall
+40 (int (*)(...))QEventLoop::~QEventLoop
+48 (int (*)(...))QEventLoop::~QEventLoop
+56 (int (*)(...))QEventLoop::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QEventLoop
+ size=16 align=8
+ base size=16 base align=8
+QEventLoop (0x0x7f3bfb74d1a0) 0
+ vptr=((& QEventLoop::_ZTV10QEventLoop) + 16)
+ QObject (0x0x7f3bfb76c780) 0
+ primary-for QEventLoop (0x0x7f3bfb74d1a0)
+
+Class QEventLoopLocker
+ size=8 align=8
+ base size=8 base align=8
+QEventLoopLocker (0x0x7f3bfb7ca0c0) 0
+
+Class QAbstractEventDispatcher::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QAbstractEventDispatcher::QPrivateSignal (0x0x7f3bfb7ca180) 0 empty
+
+Class QAbstractEventDispatcher::TimerInfo
+ size=12 align=4
+ base size=12 base align=4
+QAbstractEventDispatcher::TimerInfo (0x0x7f3bfb7ca1e0) 0
+
+Vtable for QAbstractEventDispatcher
+QAbstractEventDispatcher::_ZTV24QAbstractEventDispatcher: 28 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI24QAbstractEventDispatcher)
+16 (int (*)(...))QAbstractEventDispatcher::metaObject
+24 (int (*)(...))QAbstractEventDispatcher::qt_metacast
+32 (int (*)(...))QAbstractEventDispatcher::qt_metacall
+40 0
+48 0
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))__cxa_pure_virtual
+120 (int (*)(...))__cxa_pure_virtual
+128 (int (*)(...))__cxa_pure_virtual
+136 (int (*)(...))__cxa_pure_virtual
+144 (int (*)(...))__cxa_pure_virtual
+152 (int (*)(...))__cxa_pure_virtual
+160 (int (*)(...))__cxa_pure_virtual
+168 (int (*)(...))__cxa_pure_virtual
+176 (int (*)(...))__cxa_pure_virtual
+184 (int (*)(...))__cxa_pure_virtual
+192 (int (*)(...))__cxa_pure_virtual
+200 (int (*)(...))__cxa_pure_virtual
+208 (int (*)(...))QAbstractEventDispatcher::startingUp
+216 (int (*)(...))QAbstractEventDispatcher::closingDown
+
+Class QAbstractEventDispatcher
+ size=16 align=8
+ base size=16 base align=8
+QAbstractEventDispatcher (0x0x7f3bfb74d2d8) 0
+ vptr=((& QAbstractEventDispatcher::_ZTV24QAbstractEventDispatcher) + 16)
+ QObject (0x0x7f3bfb7ca120) 0
+ primary-for QAbstractEventDispatcher (0x0x7f3bfb74d2d8)
+
+Class QMapNodeBase
+ size=24 align=8
+ base size=24 base align=8
+QMapNodeBase (0x0x7f3bfb8201e0) 0
+
+Class QMapDataBase
+ size=40 align=8
+ base size=40 base align=8
+QMapDataBase (0x0x7f3bfb820e40) 0
+
+Class QHashData::Node
+ size=16 align=8
+ base size=16 base align=8
+QHashData::Node (0x0x7f3bfb9117e0) 0
+
+Class QHashData
+ size=48 align=8
+ base size=44 base align=8
+QHashData (0x0x7f3bfb911780) 0
+
+Class QHashDummyValue
+ size=1 align=1
+ base size=0 base align=1
+QHashDummyValue (0x0x7f3bfb911a80) 0 empty
+
+Class QVariant::PrivateShared
+ size=16 align=8
+ base size=12 base align=8
+QVariant::PrivateShared (0x0x7f3bfb6451e0) 0
+
+Class QVariant::Private::Data
+ size=8 align=8
+ base size=8 base align=8
+QVariant::Private::Data (0x0x7f3bfb6452a0) 0
+
+Class QVariant::Private
+ size=16 align=8
+ base size=12 base align=8
+QVariant::Private (0x0x7f3bfb645240) 0
+
+Class QVariant::Handler
+ size=72 align=8
+ base size=72 base align=8
+QVariant::Handler (0x0x7f3bfb645300) 0
+
+Class QVariant
+ size=16 align=8
+ base size=16 base align=8
+QVariant (0x0x7f3bfb645180) 0
+
+Class QVariantComparisonHelper
+ size=8 align=8
+ base size=8 base align=8
+QVariantComparisonHelper (0x0x7f3bfb3a55a0) 0
+
+Class QSequentialIterable::const_iterator
+ size=112 align=8
+ base size=112 base align=8
+QSequentialIterable::const_iterator (0x0x7f3bfb3e7c00) 0
+
+Class QSequentialIterable
+ size=104 align=8
+ base size=104 base align=8
+QSequentialIterable (0x0x7f3bfb3e7ba0) 0
+
+Class QAssociativeIterable::const_iterator
+ size=120 align=8
+ base size=120 base align=8
+QAssociativeIterable::const_iterator (0x0x7f3bfb3e7d20) 0
+
+Class QAssociativeIterable
+ size=112 align=8
+ base size=112 base align=8
+QAssociativeIterable (0x0x7f3bfb3e7cc0) 0
+
+Class QModelIndex
+ size=24 align=8
+ base size=24 base align=8
+QModelIndex (0x0x7f3bfb4b3ea0) 0
+
+Class QPersistentModelIndex
+ size=8 align=8
+ base size=8 base align=8
+QPersistentModelIndex (0x0x7f3bfb526ae0) 0
+
+Class QAbstractItemModel::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QAbstractItemModel::QPrivateSignal (0x0x7f3bfb1f5900) 0 empty
+
+Vtable for QAbstractItemModel
+QAbstractItemModel::_ZTV18QAbstractItemModel: 48 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI18QAbstractItemModel)
+16 (int (*)(...))QAbstractItemModel::metaObject
+24 (int (*)(...))QAbstractItemModel::qt_metacast
+32 (int (*)(...))QAbstractItemModel::qt_metacall
+40 0
+48 0
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))__cxa_pure_virtual
+120 (int (*)(...))__cxa_pure_virtual
+128 (int (*)(...))QAbstractItemModel::sibling
+136 (int (*)(...))__cxa_pure_virtual
+144 (int (*)(...))__cxa_pure_virtual
+152 (int (*)(...))QAbstractItemModel::hasChildren
+160 (int (*)(...))__cxa_pure_virtual
+168 (int (*)(...))QAbstractItemModel::setData
+176 (int (*)(...))QAbstractItemModel::headerData
+184 (int (*)(...))QAbstractItemModel::setHeaderData
+192 (int (*)(...))QAbstractItemModel::itemData
+200 (int (*)(...))QAbstractItemModel::setItemData
+208 (int (*)(...))QAbstractItemModel::mimeTypes
+216 (int (*)(...))QAbstractItemModel::mimeData
+224 (int (*)(...))QAbstractItemModel::canDropMimeData
+232 (int (*)(...))QAbstractItemModel::dropMimeData
+240 (int (*)(...))QAbstractItemModel::supportedDropActions
+248 (int (*)(...))QAbstractItemModel::supportedDragActions
+256 (int (*)(...))QAbstractItemModel::insertRows
+264 (int (*)(...))QAbstractItemModel::insertColumns
+272 (int (*)(...))QAbstractItemModel::removeRows
+280 (int (*)(...))QAbstractItemModel::removeColumns
+288 (int (*)(...))QAbstractItemModel::moveRows
+296 (int (*)(...))QAbstractItemModel::moveColumns
+304 (int (*)(...))QAbstractItemModel::fetchMore
+312 (int (*)(...))QAbstractItemModel::canFetchMore
+320 (int (*)(...))QAbstractItemModel::flags
+328 (int (*)(...))QAbstractItemModel::sort
+336 (int (*)(...))QAbstractItemModel::buddy
+344 (int (*)(...))QAbstractItemModel::match
+352 (int (*)(...))QAbstractItemModel::span
+360 (int (*)(...))QAbstractItemModel::roleNames
+368 (int (*)(...))QAbstractItemModel::submit
+376 (int (*)(...))QAbstractItemModel::revert
+
+Class QAbstractItemModel
+ size=16 align=8
+ base size=16 base align=8
+QAbstractItemModel (0x0x7f3bfb203478) 0
+ vptr=((& QAbstractItemModel::_ZTV18QAbstractItemModel) + 16)
+ QObject (0x0x7f3bfb1f58a0) 0
+ primary-for QAbstractItemModel (0x0x7f3bfb203478)
+
+Class QAbstractTableModel::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QAbstractTableModel::QPrivateSignal (0x0x7f3bfb26ecc0) 0 empty
+
+Vtable for QAbstractTableModel
+QAbstractTableModel::_ZTV19QAbstractTableModel: 48 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI19QAbstractTableModel)
+16 (int (*)(...))QAbstractTableModel::metaObject
+24 (int (*)(...))QAbstractTableModel::qt_metacast
+32 (int (*)(...))QAbstractTableModel::qt_metacall
+40 0
+48 0
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QAbstractTableModel::index
+120 (int (*)(...))QAbstractTableModel::parent
+128 (int (*)(...))QAbstractTableModel::sibling
+136 (int (*)(...))__cxa_pure_virtual
+144 (int (*)(...))__cxa_pure_virtual
+152 (int (*)(...))QAbstractTableModel::hasChildren
+160 (int (*)(...))__cxa_pure_virtual
+168 (int (*)(...))QAbstractItemModel::setData
+176 (int (*)(...))QAbstractItemModel::headerData
+184 (int (*)(...))QAbstractItemModel::setHeaderData
+192 (int (*)(...))QAbstractItemModel::itemData
+200 (int (*)(...))QAbstractItemModel::setItemData
+208 (int (*)(...))QAbstractItemModel::mimeTypes
+216 (int (*)(...))QAbstractItemModel::mimeData
+224 (int (*)(...))QAbstractItemModel::canDropMimeData
+232 (int (*)(...))QAbstractTableModel::dropMimeData
+240 (int (*)(...))QAbstractItemModel::supportedDropActions
+248 (int (*)(...))QAbstractItemModel::supportedDragActions
+256 (int (*)(...))QAbstractItemModel::insertRows
+264 (int (*)(...))QAbstractItemModel::insertColumns
+272 (int (*)(...))QAbstractItemModel::removeRows
+280 (int (*)(...))QAbstractItemModel::removeColumns
+288 (int (*)(...))QAbstractItemModel::moveRows
+296 (int (*)(...))QAbstractItemModel::moveColumns
+304 (int (*)(...))QAbstractItemModel::fetchMore
+312 (int (*)(...))QAbstractItemModel::canFetchMore
+320 (int (*)(...))QAbstractTableModel::flags
+328 (int (*)(...))QAbstractItemModel::sort
+336 (int (*)(...))QAbstractItemModel::buddy
+344 (int (*)(...))QAbstractItemModel::match
+352 (int (*)(...))QAbstractItemModel::span
+360 (int (*)(...))QAbstractItemModel::roleNames
+368 (int (*)(...))QAbstractItemModel::submit
+376 (int (*)(...))QAbstractItemModel::revert
+
+Class QAbstractTableModel
+ size=16 align=8
+ base size=16 base align=8
+QAbstractTableModel (0x0x7f3bfb203a90) 0
+ vptr=((& QAbstractTableModel::_ZTV19QAbstractTableModel) + 16)
+ QAbstractItemModel (0x0x7f3bfb203af8) 0
+ primary-for QAbstractTableModel (0x0x7f3bfb203a90)
+ QObject (0x0x7f3bfb26ec60) 0
+ primary-for QAbstractItemModel (0x0x7f3bfb203af8)
+
+Class QAbstractListModel::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QAbstractListModel::QPrivateSignal (0x0x7f3bfb26ee40) 0 empty
+
+Vtable for QAbstractListModel
+QAbstractListModel::_ZTV18QAbstractListModel: 48 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI18QAbstractListModel)
+16 (int (*)(...))QAbstractListModel::metaObject
+24 (int (*)(...))QAbstractListModel::qt_metacast
+32 (int (*)(...))QAbstractListModel::qt_metacall
+40 0
+48 0
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QAbstractListModel::index
+120 (int (*)(...))QAbstractListModel::parent
+128 (int (*)(...))QAbstractListModel::sibling
+136 (int (*)(...))__cxa_pure_virtual
+144 (int (*)(...))QAbstractListModel::columnCount
+152 (int (*)(...))QAbstractListModel::hasChildren
+160 (int (*)(...))__cxa_pure_virtual
+168 (int (*)(...))QAbstractItemModel::setData
+176 (int (*)(...))QAbstractItemModel::headerData
+184 (int (*)(...))QAbstractItemModel::setHeaderData
+192 (int (*)(...))QAbstractItemModel::itemData
+200 (int (*)(...))QAbstractItemModel::setItemData
+208 (int (*)(...))QAbstractItemModel::mimeTypes
+216 (int (*)(...))QAbstractItemModel::mimeData
+224 (int (*)(...))QAbstractItemModel::canDropMimeData
+232 (int (*)(...))QAbstractListModel::dropMimeData
+240 (int (*)(...))QAbstractItemModel::supportedDropActions
+248 (int (*)(...))QAbstractItemModel::supportedDragActions
+256 (int (*)(...))QAbstractItemModel::insertRows
+264 (int (*)(...))QAbstractItemModel::insertColumns
+272 (int (*)(...))QAbstractItemModel::removeRows
+280 (int (*)(...))QAbstractItemModel::removeColumns
+288 (int (*)(...))QAbstractItemModel::moveRows
+296 (int (*)(...))QAbstractItemModel::moveColumns
+304 (int (*)(...))QAbstractItemModel::fetchMore
+312 (int (*)(...))QAbstractItemModel::canFetchMore
+320 (int (*)(...))QAbstractListModel::flags
+328 (int (*)(...))QAbstractItemModel::sort
+336 (int (*)(...))QAbstractItemModel::buddy
+344 (int (*)(...))QAbstractItemModel::match
+352 (int (*)(...))QAbstractItemModel::span
+360 (int (*)(...))QAbstractItemModel::roleNames
+368 (int (*)(...))QAbstractItemModel::submit
+376 (int (*)(...))QAbstractItemModel::revert
+
+Class QAbstractListModel
+ size=16 align=8
+ base size=16 base align=8
+QAbstractListModel (0x0x7f3bfb203b60) 0
+ vptr=((& QAbstractListModel::_ZTV18QAbstractListModel) + 16)
+ QAbstractItemModel (0x0x7f3bfb203bc8) 0
+ primary-for QAbstractListModel (0x0x7f3bfb203b60)
+ QObject (0x0x7f3bfb26ede0) 0
+ primary-for QAbstractItemModel (0x0x7f3bfb203bc8)
+
+Vtable for QAbstractNativeEventFilter
+QAbstractNativeEventFilter::_ZTV26QAbstractNativeEventFilter: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI26QAbstractNativeEventFilter)
+16 0
+24 0
+32 (int (*)(...))__cxa_pure_virtual
+
+Class QAbstractNativeEventFilter
+ size=16 align=8
+ base size=16 base align=8
+QAbstractNativeEventFilter (0x0x7f3bfb2f65a0) 0
+ vptr=((& QAbstractNativeEventFilter::_ZTV26QAbstractNativeEventFilter) + 16)
+
+Class QAbstractProxyModel::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QAbstractProxyModel::QPrivateSignal (0x0x7f3bfb2f6660) 0 empty
+
+Vtable for QAbstractProxyModel
+QAbstractProxyModel::_ZTV19QAbstractProxyModel: 53 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI19QAbstractProxyModel)
+16 (int (*)(...))QAbstractProxyModel::metaObject
+24 (int (*)(...))QAbstractProxyModel::qt_metacast
+32 (int (*)(...))QAbstractProxyModel::qt_metacall
+40 0
+48 0
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))__cxa_pure_virtual
+120 (int (*)(...))__cxa_pure_virtual
+128 (int (*)(...))QAbstractProxyModel::sibling
+136 (int (*)(...))__cxa_pure_virtual
+144 (int (*)(...))__cxa_pure_virtual
+152 (int (*)(...))QAbstractProxyModel::hasChildren
+160 (int (*)(...))QAbstractProxyModel::data
+168 (int (*)(...))QAbstractProxyModel::setData
+176 (int (*)(...))QAbstractProxyModel::headerData
+184 (int (*)(...))QAbstractProxyModel::setHeaderData
+192 (int (*)(...))QAbstractProxyModel::itemData
+200 (int (*)(...))QAbstractProxyModel::setItemData
+208 (int (*)(...))QAbstractProxyModel::mimeTypes
+216 (int (*)(...))QAbstractProxyModel::mimeData
+224 (int (*)(...))QAbstractProxyModel::canDropMimeData
+232 (int (*)(...))QAbstractProxyModel::dropMimeData
+240 (int (*)(...))QAbstractProxyModel::supportedDropActions
+248 (int (*)(...))QAbstractProxyModel::supportedDragActions
+256 (int (*)(...))QAbstractItemModel::insertRows
+264 (int (*)(...))QAbstractItemModel::insertColumns
+272 (int (*)(...))QAbstractItemModel::removeRows
+280 (int (*)(...))QAbstractItemModel::removeColumns
+288 (int (*)(...))QAbstractItemModel::moveRows
+296 (int (*)(...))QAbstractItemModel::moveColumns
+304 (int (*)(...))QAbstractProxyModel::fetchMore
+312 (int (*)(...))QAbstractProxyModel::canFetchMore
+320 (int (*)(...))QAbstractProxyModel::flags
+328 (int (*)(...))QAbstractProxyModel::sort
+336 (int (*)(...))QAbstractProxyModel::buddy
+344 (int (*)(...))QAbstractItemModel::match
+352 (int (*)(...))QAbstractProxyModel::span
+360 (int (*)(...))QAbstractItemModel::roleNames
+368 (int (*)(...))QAbstractProxyModel::submit
+376 (int (*)(...))QAbstractProxyModel::revert
+384 (int (*)(...))QAbstractProxyModel::setSourceModel
+392 (int (*)(...))__cxa_pure_virtual
+400 (int (*)(...))__cxa_pure_virtual
+408 (int (*)(...))QAbstractProxyModel::mapSelectionToSource
+416 (int (*)(...))QAbstractProxyModel::mapSelectionFromSource
+
+Class QAbstractProxyModel
+ size=16 align=8
+ base size=16 base align=8
+QAbstractProxyModel (0x0x7f3bfb203c98) 0
+ vptr=((& QAbstractProxyModel::_ZTV19QAbstractProxyModel) + 16)
+ QAbstractItemModel (0x0x7f3bfb203d00) 0
+ primary-for QAbstractProxyModel (0x0x7f3bfb203c98)
+ QObject (0x0x7f3bfb2f6600) 0
+ primary-for QAbstractItemModel (0x0x7f3bfb203d00)
+
+Class QAbstractState::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QAbstractState::QPrivateSignal (0x0x7f3bfb2f68a0) 0 empty
+
+Vtable for QAbstractState
+QAbstractState::_ZTV14QAbstractState: 16 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI14QAbstractState)
+16 (int (*)(...))QAbstractState::metaObject
+24 (int (*)(...))QAbstractState::qt_metacast
+32 (int (*)(...))QAbstractState::qt_metacall
+40 0
+48 0
+56 (int (*)(...))QAbstractState::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))__cxa_pure_virtual
+120 (int (*)(...))__cxa_pure_virtual
+
+Class QAbstractState
+ size=16 align=8
+ base size=16 base align=8
+QAbstractState (0x0x7f3bfb203d68) 0
+ vptr=((& QAbstractState::_ZTV14QAbstractState) + 16)
+ QObject (0x0x7f3bfb2f6840) 0
+ primary-for QAbstractState (0x0x7f3bfb203d68)
+
+Class QAbstractTransition::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QAbstractTransition::QPrivateSignal (0x0x7f3bfb2f6ae0) 0 empty
+
+Vtable for QAbstractTransition
+QAbstractTransition::_ZTV19QAbstractTransition: 16 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI19QAbstractTransition)
+16 (int (*)(...))QAbstractTransition::metaObject
+24 (int (*)(...))QAbstractTransition::qt_metacast
+32 (int (*)(...))QAbstractTransition::qt_metacall
+40 0
+48 0
+56 (int (*)(...))QAbstractTransition::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))__cxa_pure_virtual
+120 (int (*)(...))__cxa_pure_virtual
+
+Class QAbstractTransition
+ size=16 align=8
+ base size=16 base align=8
+QAbstractTransition (0x0x7f3bfb203dd0) 0
+ vptr=((& QAbstractTransition::_ZTV19QAbstractTransition) + 16)
+ QObject (0x0x7f3bfb2f6a80) 0
+ primary-for QAbstractTransition (0x0x7f3bfb203dd0)
+
+Class QAnimationGroup::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QAnimationGroup::QPrivateSignal (0x0x7f3bfb2f6de0) 0 empty
+
+Vtable for QAnimationGroup
+QAnimationGroup::_ZTV15QAnimationGroup: 18 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI15QAnimationGroup)
+16 (int (*)(...))QAnimationGroup::metaObject
+24 (int (*)(...))QAnimationGroup::qt_metacast
+32 (int (*)(...))QAnimationGroup::qt_metacall
+40 0
+48 0
+56 (int (*)(...))QAnimationGroup::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))__cxa_pure_virtual
+120 (int (*)(...))__cxa_pure_virtual
+128 (int (*)(...))QAbstractAnimation::updateState
+136 (int (*)(...))QAbstractAnimation::updateDirection
+
+Class QAnimationGroup
+ size=16 align=8
+ base size=16 base align=8
+QAnimationGroup (0x0x7f3bfb203e38) 0
+ vptr=((& QAnimationGroup::_ZTV15QAnimationGroup) + 16)
+ QAbstractAnimation (0x0x7f3bfb203ea0) 0
+ primary-for QAnimationGroup (0x0x7f3bfb203e38)
+ QObject (0x0x7f3bfb2f6d80) 0
+ primary-for QAbstractAnimation (0x0x7f3bfb203ea0)
+
+Class QBasicTimer
+ size=4 align=4
+ base size=4 base align=4
+QBasicTimer (0x0x7f3bfafc9120) 0
+
+Class QBitArray
+ size=8 align=8
+ base size=8 base align=8
+QBitArray (0x0x7f3bfb04aa80) 0
+
+Class QBitRef
+ size=16 align=8
+ base size=12 base align=8
+QBitRef (0x0x7f3bfb099f00) 0
+
+Class QIODevice::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QIODevice::QPrivateSignal (0x0x7f3bfb10f1e0) 0 empty
+
+Vtable for QIODevice
+QIODevice::_ZTV9QIODevice: 30 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI9QIODevice)
+16 (int (*)(...))QIODevice::metaObject
+24 (int (*)(...))QIODevice::qt_metacast
+32 (int (*)(...))QIODevice::qt_metacall
+40 0
+48 0
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QIODevice::isSequential
+120 (int (*)(...))QIODevice::open
+128 (int (*)(...))QIODevice::close
+136 (int (*)(...))QIODevice::pos
+144 (int (*)(...))QIODevice::size
+152 (int (*)(...))QIODevice::seek
+160 (int (*)(...))QIODevice::atEnd
+168 (int (*)(...))QIODevice::reset
+176 (int (*)(...))QIODevice::bytesAvailable
+184 (int (*)(...))QIODevice::bytesToWrite
+192 (int (*)(...))QIODevice::canReadLine
+200 (int (*)(...))QIODevice::waitForReadyRead
+208 (int (*)(...))QIODevice::waitForBytesWritten
+216 (int (*)(...))__cxa_pure_virtual
+224 (int (*)(...))QIODevice::readLineData
+232 (int (*)(...))__cxa_pure_virtual
+
+Class QIODevice
+ size=16 align=8
+ base size=16 base align=8
+QIODevice (0x0x7f3bfb108478) 0
+ vptr=((& QIODevice::_ZTV9QIODevice) + 16)
+ QObject (0x0x7f3bfb10f180) 0
+ primary-for QIODevice (0x0x7f3bfb108478)
+
+Class QBuffer::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QBuffer::QPrivateSignal (0x0x7f3bfb10fb40) 0 empty
+
+Vtable for QBuffer
+QBuffer::_ZTV7QBuffer: 30 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI7QBuffer)
+16 (int (*)(...))QBuffer::metaObject
+24 (int (*)(...))QBuffer::qt_metacast
+32 (int (*)(...))QBuffer::qt_metacall
+40 (int (*)(...))QBuffer::~QBuffer
+48 (int (*)(...))QBuffer::~QBuffer
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QBuffer::connectNotify
+104 (int (*)(...))QBuffer::disconnectNotify
+112 (int (*)(...))QIODevice::isSequential
+120 (int (*)(...))QBuffer::open
+128 (int (*)(...))QBuffer::close
+136 (int (*)(...))QBuffer::pos
+144 (int (*)(...))QBuffer::size
+152 (int (*)(...))QBuffer::seek
+160 (int (*)(...))QBuffer::atEnd
+168 (int (*)(...))QIODevice::reset
+176 (int (*)(...))QIODevice::bytesAvailable
+184 (int (*)(...))QIODevice::bytesToWrite
+192 (int (*)(...))QBuffer::canReadLine
+200 (int (*)(...))QIODevice::waitForReadyRead
+208 (int (*)(...))QIODevice::waitForBytesWritten
+216 (int (*)(...))QBuffer::readData
+224 (int (*)(...))QIODevice::readLineData
+232 (int (*)(...))QBuffer::writeData
+
+Class QBuffer
+ size=16 align=8
+ base size=16 base align=8
+QBuffer (0x0x7f3bfb1085b0) 0
+ vptr=((& QBuffer::_ZTV7QBuffer) + 16)
+ QIODevice (0x0x7f3bfb108618) 0
+ primary-for QBuffer (0x0x7f3bfb1085b0)
+ QObject (0x0x7f3bfb10fae0) 0
+ primary-for QIODevice (0x0x7f3bfb108618)
+
+Class QByteArrayMatcher::Data
+ size=272 align=8
+ base size=272 base align=8
+QByteArrayMatcher::Data (0x0x7f3bfb10fde0) 0
+
+Class QByteArrayMatcher
+ size=1040 align=8
+ base size=1040 base align=8
+QByteArrayMatcher (0x0x7f3bfb10fd80) 0
+
+Class QStaticByteArrayMatcherBase::Skiptable
+ size=256 align=1
+ base size=256 base align=1
+QStaticByteArrayMatcherBase::Skiptable (0x0x7f3bfb10ff60) 0
+
+Class QStaticByteArrayMatcherBase
+ size=256 align=16
+ base size=256 base align=16
+QStaticByteArrayMatcherBase (0x0x7f3bfb10ff00) 0
+
+Class QSharedData
+ size=4 align=4
+ base size=4 base align=4
+QSharedData (0x0x7f3bfad77e40) 0
+
+Class QLocale
+ size=8 align=8
+ base size=8 base align=8
+QLocale (0x0x7f3bfadd4d20) 0
+
+Class QCalendar::YearMonthDay
+ size=12 align=4
+ base size=12 base align=4
+QCalendar::YearMonthDay (0x0x7f3bfab61240) 0
+
+Class QCalendar
+ size=8 align=8
+ base size=8 base align=8
+QCalendar (0x0x7f3bfab611e0) 0
+
+Class QDate
+ size=8 align=8
+ base size=8 base align=8
+QDate (0x0x7f3bfab61a20) 0
+
+Class QTime
+ size=4 align=4
+ base size=4 base align=4
+QTime (0x0x7f3bfabe8300) 0
+
+Class QDateTime::ShortData
+ size=8 align=8
+ base size=8 base align=8
+QDateTime::ShortData (0x0x7f3bfac37f60) 0
+
+Class QDateTime::Data
+ size=8 align=8
+ base size=8 base align=8
+QDateTime::Data (0x0x7f3bfac51000) 0
+
+Class QDateTime
+ size=8 align=8
+ base size=8 base align=8
+QDateTime (0x0x7f3bfac37f00) 0
+
+Vtable for QTextStream
+QTextStream::_ZTV11QTextStream: 4 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI11QTextStream)
+16 (int (*)(...))QTextStream::~QTextStream
+24 (int (*)(...))QTextStream::~QTextStream
+
+Class QTextStream
+ size=16 align=8
+ base size=16 base align=8
+QTextStream (0x0x7f3bfad296c0) 0
+ vptr=((& QTextStream::_ZTV11QTextStream) + 16)
+
+Class QTextStreamManipulator
+ size=40 align=8
+ base size=38 base align=8
+QTextStreamManipulator (0x0x7f3bfad29f60) 0
+
+Class QContiguousCacheData
+ size=24 align=4
+ base size=24 base align=4
+QContiguousCacheData (0x0x7f3bfa9fac00) 0
+
+Vtable for __gnu_cxx::__concurrence_lock_error
+__gnu_cxx::__concurrence_lock_error::_ZTVN9__gnu_cxx24__concurrence_lock_errorE: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTIN9__gnu_cxx24__concurrence_lock_errorE)
+16 (int (*)(...))__gnu_cxx::__concurrence_lock_error::~__concurrence_lock_error
+24 (int (*)(...))__gnu_cxx::__concurrence_lock_error::~__concurrence_lock_error
+32 (int (*)(...))__gnu_cxx::__concurrence_lock_error::what
+
+Class __gnu_cxx::__concurrence_lock_error
+ size=8 align=8
+ base size=8 base align=8
+__gnu_cxx::__concurrence_lock_error (0x0x7f3bfad2a618) 0 nearly-empty
+ vptr=((& __gnu_cxx::__concurrence_lock_error::_ZTVN9__gnu_cxx24__concurrence_lock_errorE) + 16)
+ std::exception (0x0x7f3bfaa43a80) 0 nearly-empty
+ primary-for __gnu_cxx::__concurrence_lock_error (0x0x7f3bfad2a618)
+
+Vtable for __gnu_cxx::__concurrence_unlock_error
+__gnu_cxx::__concurrence_unlock_error::_ZTVN9__gnu_cxx26__concurrence_unlock_errorE: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTIN9__gnu_cxx26__concurrence_unlock_errorE)
+16 (int (*)(...))__gnu_cxx::__concurrence_unlock_error::~__concurrence_unlock_error
+24 (int (*)(...))__gnu_cxx::__concurrence_unlock_error::~__concurrence_unlock_error
+32 (int (*)(...))__gnu_cxx::__concurrence_unlock_error::what
+
+Class __gnu_cxx::__concurrence_unlock_error
+ size=8 align=8
+ base size=8 base align=8
+__gnu_cxx::__concurrence_unlock_error (0x0x7f3bfad2a680) 0 nearly-empty
+ vptr=((& __gnu_cxx::__concurrence_unlock_error::_ZTVN9__gnu_cxx26__concurrence_unlock_errorE) + 16)
+ std::exception (0x0x7f3bfaa43ba0) 0 nearly-empty
+ primary-for __gnu_cxx::__concurrence_unlock_error (0x0x7f3bfad2a680)
+
+Vtable for __gnu_cxx::__concurrence_broadcast_error
+__gnu_cxx::__concurrence_broadcast_error::_ZTVN9__gnu_cxx29__concurrence_broadcast_errorE: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTIN9__gnu_cxx29__concurrence_broadcast_errorE)
+16 (int (*)(...))__gnu_cxx::__concurrence_broadcast_error::~__concurrence_broadcast_error
+24 (int (*)(...))__gnu_cxx::__concurrence_broadcast_error::~__concurrence_broadcast_error
+32 (int (*)(...))__gnu_cxx::__concurrence_broadcast_error::what
+
+Class __gnu_cxx::__concurrence_broadcast_error
+ size=8 align=8
+ base size=8 base align=8
+__gnu_cxx::__concurrence_broadcast_error (0x0x7f3bfad2a6e8) 0 nearly-empty
+ vptr=((& __gnu_cxx::__concurrence_broadcast_error::_ZTVN9__gnu_cxx29__concurrence_broadcast_errorE) + 16)
+ std::exception (0x0x7f3bfaa43cc0) 0 nearly-empty
+ primary-for __gnu_cxx::__concurrence_broadcast_error (0x0x7f3bfad2a6e8)
+
+Vtable for __gnu_cxx::__concurrence_wait_error
+__gnu_cxx::__concurrence_wait_error::_ZTVN9__gnu_cxx24__concurrence_wait_errorE: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTIN9__gnu_cxx24__concurrence_wait_errorE)
+16 (int (*)(...))__gnu_cxx::__concurrence_wait_error::~__concurrence_wait_error
+24 (int (*)(...))__gnu_cxx::__concurrence_wait_error::~__concurrence_wait_error
+32 (int (*)(...))__gnu_cxx::__concurrence_wait_error::what
+
+Class __gnu_cxx::__concurrence_wait_error
+ size=8 align=8
+ base size=8 base align=8
+__gnu_cxx::__concurrence_wait_error (0x0x7f3bfad2a7b8) 0 nearly-empty
+ vptr=((& __gnu_cxx::__concurrence_wait_error::_ZTVN9__gnu_cxx24__concurrence_wait_errorE) + 16)
+ std::exception (0x0x7f3bfaa43de0) 0 nearly-empty
+ primary-for __gnu_cxx::__concurrence_wait_error (0x0x7f3bfad2a7b8)
+
+Class __gnu_cxx::__mutex
+ size=40 align=8
+ base size=40 base align=8
+__gnu_cxx::__mutex (0x0x7f3bfaa6fe40) 0
+
+Class __gnu_cxx::__recursive_mutex
+ size=40 align=8
+ base size=40 base align=8
+__gnu_cxx::__recursive_mutex (0x0x7f3bfaa96180) 0
+
+Class __gnu_cxx::__scoped_lock
+ size=8 align=8
+ base size=8 base align=8
+__gnu_cxx::__scoped_lock (0x0x7f3bfaa96480) 0
+
+Class __gnu_cxx::__cond
+ size=48 align=8
+ base size=48 base align=8
+__gnu_cxx::__cond (0x0x7f3bfaa967e0) 0
+
+Vtable for std::bad_weak_ptr
+std::bad_weak_ptr::_ZTVSt12bad_weak_ptr: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt12bad_weak_ptr)
+16 (int (*)(...))std::bad_weak_ptr::~bad_weak_ptr
+24 (int (*)(...))std::bad_weak_ptr::~bad_weak_ptr
+32 (int (*)(...))std::bad_weak_ptr::what
+
+Class std::bad_weak_ptr
+ size=8 align=8
+ base size=8 base align=8
+std::bad_weak_ptr (0x0x7f3bfad2a820) 0 nearly-empty
+ vptr=((& std::bad_weak_ptr::_ZTVSt12bad_weak_ptr) + 16)
+ std::exception (0x0x7f3bfab0a9c0) 0 nearly-empty
+ primary-for std::bad_weak_ptr (0x0x7f3bfad2a820)
+
+Class std::_Sp_make_shared_tag
+ size=1 align=1
+ base size=0 base align=1
+std::_Sp_make_shared_tag (0x0x7f3bfa776960) 0 empty
+
+Class std::__sp_array_delete
+ size=1 align=1
+ base size=0 base align=1
+std::__sp_array_delete (0x0x7f3bfa776d80) 0 empty
+
+Class std::_Sp_locker
+ size=2 align=1
+ base size=2 base align=1
+std::_Sp_locker (0x0x7f3bfa8acc00) 0
+
+Class QtSharedPointer::NormalDeleter
+ size=1 align=1
+ base size=0 base align=1
+QtSharedPointer::NormalDeleter (0x0x7f3bfa90d120) 0 empty
+
+Class QtSharedPointer::ExternalRefCountData
+ size=16 align=8
+ base size=16 base align=8
+QtSharedPointer::ExternalRefCountData (0x0x7f3bfa90d2a0) 0
+
+Class QtPrivate::EnableInternalData
+ size=1 align=1
+ base size=0 base align=1
+QtPrivate::EnableInternalData (0x0x7f3bfa56eba0) 0 empty
+
+Class QDebug::Stream
+ size=80 align=8
+ base size=76 base align=8
+QDebug::Stream (0x0x7f3bfa5c82a0) 0
+
+Class QDebug
+ size=8 align=8
+ base size=8 base align=8
+QDebug (0x0x7f3bfa5c8240) 0
+
+Class QDebugStateSaver
+ size=8 align=8
+ base size=8 base align=8
+QDebugStateSaver (0x0x7f3bfa738ba0) 0
+
+Class QNoDebug
+ size=1 align=1
+ base size=0 base align=1
+QNoDebug (0x0x7f3bfa738c60) 0 empty
+
+Class QCborError
+ size=4 align=4
+ base size=4 base align=4
+QCborError (0x0x7f3bfa3b7ea0) 0
+
+Class QRegularExpression
+ size=8 align=8
+ base size=8 base align=8
+QRegularExpression (0x0x7f3bfa3ea660) 0
+
+Class QRegularExpressionMatch
+ size=8 align=8
+ base size=8 base align=8
+QRegularExpressionMatch (0x0x7f3bfa499540) 0
+
+Class QRegularExpressionMatchIterator
+ size=8 align=8
+ base size=8 base align=8
+QRegularExpressionMatchIterator (0x0x7f3bfa500300) 0
+
+Class QUrl
+ size=8 align=8
+ base size=8 base align=8
+QUrl (0x0x7f3bfa153d20) 0
+
+Class QUuid
+ size=16 align=4
+ base size=16 base align=4
+QUuid (0x0x7f3bfa29acc0) 0
+
+Class QCborParserError
+ size=16 align=8
+ base size=12 base align=8
+QCborParserError (0x0x7f3bfa32f840) 0
+
+Class QCborValue
+ size=24 align=8
+ base size=20 base align=8
+QCborValue (0x0x7f3bfa32f900) 0
+
+Class QCborValueRef
+ size=16 align=8
+ base size=16 base align=8
+QCborValueRef (0x0x7f3bf9daf4e0) 0
+
+Class QCborArray::Iterator
+ size=16 align=8
+ base size=16 base align=8
+QCborArray::Iterator (0x0x7f3bf9e1ff00) 0
+
+Class QCborArray::ConstIterator
+ size=16 align=8
+ base size=16 base align=8
+QCborArray::ConstIterator (0x0x7f3bf9e1ff60) 0
+
+Class QCborArray
+ size=8 align=8
+ base size=8 base align=8
+QCborArray (0x0x7f3bf9e1fea0) 0
+
+Class QCborMap::Iterator
+ size=16 align=8
+ base size=16 base align=8
+QCborMap::Iterator (0x0x7f3bf9ba2b40) 0
+
+Class QCborMap::ConstIterator
+ size=16 align=8
+ base size=16 base align=8
+QCborMap::ConstIterator (0x0x7f3bf9ba2ba0) 0
+
+Class QCborMap
+ size=8 align=8
+ base size=8 base align=8
+QCborMap (0x0x7f3bf9ba2ae0) 0
+
+Class qfloat16::Wrap
+ size=2 align=2
+ base size=2 base align=2
+qfloat16::Wrap (0x0x7f3bf99bf360) 0
+
+Class qfloat16
+ size=2 align=2
+ base size=2 base align=2
+qfloat16 (0x0x7f3bf99bf300) 0
+
+Class QCborStreamWriter
+ size=8 align=8
+ base size=8 base align=8
+QCborStreamWriter (0x0x7f3bf9aaf000) 0
+
+Class QCborStreamReader
+ size=24 align=8
+ base size=20 base align=8
+QCborStreamReader (0x0x7f3bf9aafd20) 0
+
+Class QCollatorSortKey
+ size=8 align=8
+ base size=8 base align=8
+QCollatorSortKey (0x0x7f3bf9740e40) 0
+
+Class QCollator
+ size=8 align=8
+ base size=8 base align=8
+QCollator (0x0x7f3bf976d060) 0
+
+Class QCommandLineOption
+ size=8 align=8
+ base size=8 base align=8
+QCommandLineOption (0x0x7f3bf985d660) 0
+
+Vtable for QEvent
+QEvent::_ZTV6QEvent: 4 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI6QEvent)
+16 (int (*)(...))QEvent::~QEvent
+24 (int (*)(...))QEvent::~QEvent
+
+Class QEvent
+ size=24 align=8
+ base size=20 base align=8
+QEvent (0x0x7f3bf991fba0) 0
+ vptr=((& QEvent::_ZTV6QEvent) + 16)
+
+Vtable for QTimerEvent
+QTimerEvent::_ZTV11QTimerEvent: 4 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI11QTimerEvent)
+16 (int (*)(...))QTimerEvent::~QTimerEvent
+24 (int (*)(...))QTimerEvent::~QTimerEvent
+
+Class QTimerEvent
+ size=24 align=8
+ base size=24 base align=8
+QTimerEvent (0x0x7f3bf9926270) 0
+ vptr=((& QTimerEvent::_ZTV11QTimerEvent) + 16)
+ QEvent (0x0x7f3bf991ff60) 0
+ primary-for QTimerEvent (0x0x7f3bf9926270)
+
+Vtable for QChildEvent
+QChildEvent::_ZTV11QChildEvent: 4 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI11QChildEvent)
+16 (int (*)(...))QChildEvent::~QChildEvent
+24 (int (*)(...))QChildEvent::~QChildEvent
+
+Class QChildEvent
+ size=32 align=8
+ base size=32 base align=8
+QChildEvent (0x0x7f3bf99262d8) 0
+ vptr=((& QChildEvent::_ZTV11QChildEvent) + 16)
+ QEvent (0x0x7f3bf9566060) 0
+ primary-for QChildEvent (0x0x7f3bf99262d8)
+
+Vtable for QDynamicPropertyChangeEvent
+QDynamicPropertyChangeEvent::_ZTV27QDynamicPropertyChangeEvent: 4 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI27QDynamicPropertyChangeEvent)
+16 (int (*)(...))QDynamicPropertyChangeEvent::~QDynamicPropertyChangeEvent
+24 (int (*)(...))QDynamicPropertyChangeEvent::~QDynamicPropertyChangeEvent
+
+Class QDynamicPropertyChangeEvent
+ size=32 align=8
+ base size=32 base align=8
+QDynamicPropertyChangeEvent (0x0x7f3bf9926820) 0
+ vptr=((& QDynamicPropertyChangeEvent::_ZTV27QDynamicPropertyChangeEvent) + 16)
+ QEvent (0x0x7f3bf95666c0) 0
+ primary-for QDynamicPropertyChangeEvent (0x0x7f3bf9926820)
+
+Vtable for QDeferredDeleteEvent
+QDeferredDeleteEvent::_ZTV20QDeferredDeleteEvent: 4 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI20QDeferredDeleteEvent)
+16 (int (*)(...))QDeferredDeleteEvent::~QDeferredDeleteEvent
+24 (int (*)(...))QDeferredDeleteEvent::~QDeferredDeleteEvent
+
+Class QDeferredDeleteEvent
+ size=24 align=8
+ base size=24 base align=8
+QDeferredDeleteEvent (0x0x7f3bf9926888) 0
+ vptr=((& QDeferredDeleteEvent::_ZTV20QDeferredDeleteEvent) + 16)
+ QEvent (0x0x7f3bf9566780) 0
+ primary-for QDeferredDeleteEvent (0x0x7f3bf9926888)
+
+Class QCoreApplication::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QCoreApplication::QPrivateSignal (0x0x7f3bf95668a0) 0 empty
+
+Vtable for QCoreApplication
+QCoreApplication::_ZTV16QCoreApplication: 16 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI16QCoreApplication)
+16 (int (*)(...))QCoreApplication::metaObject
+24 (int (*)(...))QCoreApplication::qt_metacast
+32 (int (*)(...))QCoreApplication::qt_metacall
+40 (int (*)(...))QCoreApplication::~QCoreApplication
+48 (int (*)(...))QCoreApplication::~QCoreApplication
+56 (int (*)(...))QCoreApplication::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QCoreApplication::notify
+120 (int (*)(...))QCoreApplication::compressEvent
+
+Class QCoreApplication
+ size=16 align=8
+ base size=16 base align=8
+QCoreApplication (0x0x7f3bf99268f0) 0
+ vptr=((& QCoreApplication::_ZTV16QCoreApplication) + 16)
+ QObject (0x0x7f3bf9566840) 0
+ primary-for QCoreApplication (0x0x7f3bf99268f0)
+
+Class QCommandLineParser
+ size=8 align=8
+ base size=8 base align=8
+QCommandLineParser (0x0x7f3bf9566ae0) 0
+
+Class QConcatenateTablesProxyModel::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QConcatenateTablesProxyModel::QPrivateSignal (0x0x7f3bf9566c60) 0 empty
+
+Vtable for QConcatenateTablesProxyModel
+QConcatenateTablesProxyModel::_ZTV28QConcatenateTablesProxyModel: 48 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI28QConcatenateTablesProxyModel)
+16 (int (*)(...))QConcatenateTablesProxyModel::metaObject
+24 (int (*)(...))QConcatenateTablesProxyModel::qt_metacast
+32 (int (*)(...))QConcatenateTablesProxyModel::qt_metacall
+40 (int (*)(...))QConcatenateTablesProxyModel::~QConcatenateTablesProxyModel
+48 (int (*)(...))QConcatenateTablesProxyModel::~QConcatenateTablesProxyModel
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QConcatenateTablesProxyModel::index
+120 (int (*)(...))QConcatenateTablesProxyModel::parent
+128 (int (*)(...))QAbstractItemModel::sibling
+136 (int (*)(...))QConcatenateTablesProxyModel::rowCount
+144 (int (*)(...))QConcatenateTablesProxyModel::columnCount
+152 (int (*)(...))QAbstractItemModel::hasChildren
+160 (int (*)(...))QConcatenateTablesProxyModel::data
+168 (int (*)(...))QConcatenateTablesProxyModel::setData
+176 (int (*)(...))QConcatenateTablesProxyModel::headerData
+184 (int (*)(...))QAbstractItemModel::setHeaderData
+192 (int (*)(...))QConcatenateTablesProxyModel::itemData
+200 (int (*)(...))QConcatenateTablesProxyModel::setItemData
+208 (int (*)(...))QConcatenateTablesProxyModel::mimeTypes
+216 (int (*)(...))QConcatenateTablesProxyModel::mimeData
+224 (int (*)(...))QConcatenateTablesProxyModel::canDropMimeData
+232 (int (*)(...))QConcatenateTablesProxyModel::dropMimeData
+240 (int (*)(...))QAbstractItemModel::supportedDropActions
+248 (int (*)(...))QAbstractItemModel::supportedDragActions
+256 (int (*)(...))QAbstractItemModel::insertRows
+264 (int (*)(...))QAbstractItemModel::insertColumns
+272 (int (*)(...))QAbstractItemModel::removeRows
+280 (int (*)(...))QAbstractItemModel::removeColumns
+288 (int (*)(...))QAbstractItemModel::moveRows
+296 (int (*)(...))QAbstractItemModel::moveColumns
+304 (int (*)(...))QAbstractItemModel::fetchMore
+312 (int (*)(...))QAbstractItemModel::canFetchMore
+320 (int (*)(...))QConcatenateTablesProxyModel::flags
+328 (int (*)(...))QAbstractItemModel::sort
+336 (int (*)(...))QAbstractItemModel::buddy
+344 (int (*)(...))QAbstractItemModel::match
+352 (int (*)(...))QConcatenateTablesProxyModel::span
+360 (int (*)(...))QAbstractItemModel::roleNames
+368 (int (*)(...))QAbstractItemModel::submit
+376 (int (*)(...))QAbstractItemModel::revert
+
+Class QConcatenateTablesProxyModel
+ size=16 align=8
+ base size=16 base align=8
+QConcatenateTablesProxyModel (0x0x7f3bf9926958) 0
+ vptr=((& QConcatenateTablesProxyModel::_ZTV28QConcatenateTablesProxyModel) + 16)
+ QAbstractItemModel (0x0x7f3bf99269c0) 0
+ primary-for QConcatenateTablesProxyModel (0x0x7f3bf9926958)
+ QObject (0x0x7f3bf9566c00) 0
+ primary-for QAbstractItemModel (0x0x7f3bf99269c0)
+
+Class QCryptographicHash
+ size=8 align=8
+ base size=8 base align=8
+QCryptographicHash (0x0x7f3bf9566e40) 0
+
+Class QDataStream
+ size=32 align=8
+ base size=32 base align=8
+QDataStream (0x0x7f3bf9566f60) 0
+
+Class QtPrivate::StreamStateSaver
+ size=16 align=8
+ base size=12 base align=8
+QtPrivate::StreamStateSaver (0x0x7f3bf95ed120) 0
+
+Class QElapsedTimer
+ size=16 align=8
+ base size=16 base align=8
+QElapsedTimer (0x0x7f3bf962a840) 0
+
+Class QDeadlineTimer
+ size=16 align=8
+ base size=16 base align=8
+QDeadlineTimer (0x0x7f3bf962af60) 0
+
+Class QFileDevice::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QFileDevice::QPrivateSignal (0x0x7f3bf937ec60) 0 empty
+
+Vtable for QFileDevice
+QFileDevice::_ZTV11QFileDevice: 34 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI11QFileDevice)
+16 (int (*)(...))QFileDevice::metaObject
+24 (int (*)(...))QFileDevice::qt_metacast
+32 (int (*)(...))QFileDevice::qt_metacall
+40 (int (*)(...))QFileDevice::~QFileDevice
+48 (int (*)(...))QFileDevice::~QFileDevice
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QFileDevice::isSequential
+120 (int (*)(...))QIODevice::open
+128 (int (*)(...))QFileDevice::close
+136 (int (*)(...))QFileDevice::pos
+144 (int (*)(...))QFileDevice::size
+152 (int (*)(...))QFileDevice::seek
+160 (int (*)(...))QFileDevice::atEnd
+168 (int (*)(...))QIODevice::reset
+176 (int (*)(...))QIODevice::bytesAvailable
+184 (int (*)(...))QIODevice::bytesToWrite
+192 (int (*)(...))QIODevice::canReadLine
+200 (int (*)(...))QIODevice::waitForReadyRead
+208 (int (*)(...))QIODevice::waitForBytesWritten
+216 (int (*)(...))QFileDevice::readData
+224 (int (*)(...))QFileDevice::readLineData
+232 (int (*)(...))QFileDevice::writeData
+240 (int (*)(...))QFileDevice::fileName
+248 (int (*)(...))QFileDevice::resize
+256 (int (*)(...))QFileDevice::permissions
+264 (int (*)(...))QFileDevice::setPermissions
+
+Class QFileDevice
+ size=16 align=8
+ base size=16 base align=8
+QFileDevice (0x0x7f3bf9375bc8) 0
+ vptr=((& QFileDevice::_ZTV11QFileDevice) + 16)
+ QIODevice (0x0x7f3bf9375c30) 0
+ primary-for QFileDevice (0x0x7f3bf9375bc8)
+ QObject (0x0x7f3bf937ec00) 0
+ primary-for QIODevice (0x0x7f3bf9375c30)
+
+Class QFile::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QFile::QPrivateSignal (0x0x7f3bf93c55a0) 0 empty
+
+Vtable for QFile
+QFile::_ZTV5QFile: 34 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI5QFile)
+16 (int (*)(...))QFile::metaObject
+24 (int (*)(...))QFile::qt_metacast
+32 (int (*)(...))QFile::qt_metacall
+40 (int (*)(...))QFile::~QFile
+48 (int (*)(...))QFile::~QFile
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QFileDevice::isSequential
+120 (int (*)(...))QFile::open
+128 (int (*)(...))QFileDevice::close
+136 (int (*)(...))QFileDevice::pos
+144 (int (*)(...))QFile::size
+152 (int (*)(...))QFileDevice::seek
+160 (int (*)(...))QFileDevice::atEnd
+168 (int (*)(...))QIODevice::reset
+176 (int (*)(...))QIODevice::bytesAvailable
+184 (int (*)(...))QIODevice::bytesToWrite
+192 (int (*)(...))QIODevice::canReadLine
+200 (int (*)(...))QIODevice::waitForReadyRead
+208 (int (*)(...))QIODevice::waitForBytesWritten
+216 (int (*)(...))QFileDevice::readData
+224 (int (*)(...))QFileDevice::readLineData
+232 (int (*)(...))QFileDevice::writeData
+240 (int (*)(...))QFile::fileName
+248 (int (*)(...))QFile::resize
+256 (int (*)(...))QFile::permissions
+264 (int (*)(...))QFile::setPermissions
+
+Class QFile
+ size=16 align=8
+ base size=16 base align=8
+QFile (0x0x7f3bf9375d68) 0
+ vptr=((& QFile::_ZTV5QFile) + 16)
+ QFileDevice (0x0x7f3bf9375dd0) 0
+ primary-for QFile (0x0x7f3bf9375d68)
+ QIODevice (0x0x7f3bf9375e38) 0
+ primary-for QFileDevice (0x0x7f3bf9375dd0)
+ QObject (0x0x7f3bf93c5540) 0
+ primary-for QIODevice (0x0x7f3bf9375e38)
+
+Class QFileInfo
+ size=8 align=8
+ base size=8 base align=8
+QFileInfo (0x0x7f3bf93c5c00) 0
+
+Class QDir
+ size=8 align=8
+ base size=8 base align=8
+QDir (0x0x7f3bf94bdae0) 0
+
+Class QDirIterator
+ size=8 align=8
+ base size=8 base align=8
+QDirIterator (0x0x7f3bf9156ae0) 0
+
+Class QEasingCurve
+ size=8 align=8
+ base size=8 base align=8
+QEasingCurve (0x0x7f3bf91b02a0) 0
+
+Class QEventTransition::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QEventTransition::QPrivateSignal (0x0x7f3bf92af3c0) 0 empty
+
+Vtable for QEventTransition
+QEventTransition::_ZTV16QEventTransition: 16 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI16QEventTransition)
+16 (int (*)(...))QEventTransition::metaObject
+24 (int (*)(...))QEventTransition::qt_metacast
+32 (int (*)(...))QEventTransition::qt_metacall
+40 (int (*)(...))QEventTransition::~QEventTransition
+48 (int (*)(...))QEventTransition::~QEventTransition
+56 (int (*)(...))QEventTransition::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QEventTransition::eventTest
+120 (int (*)(...))QEventTransition::onTransition
+
+Class QEventTransition
+ size=16 align=8
+ base size=16 base align=8
+QEventTransition (0x0x7f3bf9269af8) 0
+ vptr=((& QEventTransition::_ZTV16QEventTransition) + 16)
+ QAbstractTransition (0x0x7f3bf9269b60) 0
+ primary-for QEventTransition (0x0x7f3bf9269af8)
+ QObject (0x0x7f3bf92af360) 0
+ primary-for QAbstractTransition (0x0x7f3bf9269b60)
+
+Vtable for QException
+QException::_ZTV10QException: 7 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI10QException)
+16 (int (*)(...))QException::~QException
+24 (int (*)(...))QException::~QException
+32 (int (*)(...))std::exception::what
+40 (int (*)(...))QException::raise
+48 (int (*)(...))QException::clone
+
+Class QException
+ size=8 align=8
+ base size=8 base align=8
+QException (0x0x7f3bf9269bc8) 0 nearly-empty
+ vptr=((& QException::_ZTV10QException) + 16)
+ std::exception (0x0x7f3bf92af5a0) 0 nearly-empty
+ primary-for QException (0x0x7f3bf9269bc8)
+
+Vtable for QUnhandledException
+QUnhandledException::_ZTV19QUnhandledException: 7 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI19QUnhandledException)
+16 (int (*)(...))QUnhandledException::~QUnhandledException
+24 (int (*)(...))QUnhandledException::~QUnhandledException
+32 (int (*)(...))std::exception::what
+40 (int (*)(...))QUnhandledException::raise
+48 (int (*)(...))QUnhandledException::clone
+
+Class QUnhandledException
+ size=8 align=8
+ base size=8 base align=8
+QUnhandledException (0x0x7f3bf9269c30) 0 nearly-empty
+ vptr=((& QUnhandledException::_ZTV19QUnhandledException) + 16)
+ QException (0x0x7f3bf9269c98) 0 nearly-empty
+ primary-for QUnhandledException (0x0x7f3bf9269c30)
+ std::exception (0x0x7f3bf92af600) 0 nearly-empty
+ primary-for QException (0x0x7f3bf9269c98)
+
+Class QtPrivate::ExceptionHolder
+ size=8 align=8
+ base size=8 base align=8
+QtPrivate::ExceptionHolder (0x0x7f3bf92af660) 0
+
+Class QtPrivate::ExceptionStore
+ size=8 align=8
+ base size=8 base align=8
+QtPrivate::ExceptionStore (0x0x7f3bf92af720) 0
+
+Vtable for QFactoryInterface
+QFactoryInterface::_ZTV17QFactoryInterface: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI17QFactoryInterface)
+16 0
+24 0
+32 (int (*)(...))__cxa_pure_virtual
+
+Class QFactoryInterface
+ size=8 align=8
+ base size=8 base align=8
+QFactoryInterface (0x0x7f3bf92af780) 0 nearly-empty
+ vptr=((& QFactoryInterface::_ZTV17QFactoryInterface) + 16)
+
+Class QFileSelector::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QFileSelector::QPrivateSignal (0x0x7f3bf92af9c0) 0 empty
+
+Vtable for QFileSelector
+QFileSelector::_ZTV13QFileSelector: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI13QFileSelector)
+16 (int (*)(...))QFileSelector::metaObject
+24 (int (*)(...))QFileSelector::qt_metacast
+32 (int (*)(...))QFileSelector::qt_metacall
+40 (int (*)(...))QFileSelector::~QFileSelector
+48 (int (*)(...))QFileSelector::~QFileSelector
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QFileSelector
+ size=16 align=8
+ base size=16 base align=8
+QFileSelector (0x0x7f3bf9269d00) 0
+ vptr=((& QFileSelector::_ZTV13QFileSelector) + 16)
+ QObject (0x0x7f3bf92af960) 0
+ primary-for QFileSelector (0x0x7f3bf9269d00)
+
+Class QFileSystemWatcher::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QFileSystemWatcher::QPrivateSignal (0x0x7f3bf92afc00) 0 empty
+
+Vtable for QFileSystemWatcher
+QFileSystemWatcher::_ZTV18QFileSystemWatcher: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI18QFileSystemWatcher)
+16 (int (*)(...))QFileSystemWatcher::metaObject
+24 (int (*)(...))QFileSystemWatcher::qt_metacast
+32 (int (*)(...))QFileSystemWatcher::qt_metacall
+40 (int (*)(...))QFileSystemWatcher::~QFileSystemWatcher
+48 (int (*)(...))QFileSystemWatcher::~QFileSystemWatcher
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QFileSystemWatcher
+ size=16 align=8
+ base size=16 base align=8
+QFileSystemWatcher (0x0x7f3bf9269d68) 0
+ vptr=((& QFileSystemWatcher::_ZTV18QFileSystemWatcher) + 16)
+ QObject (0x0x7f3bf92afba0) 0
+ primary-for QFileSystemWatcher (0x0x7f3bf9269d68)
+
+Class QFinalState::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QFinalState::QPrivateSignal (0x0x7f3bf92afe40) 0 empty
+
+Vtable for QFinalState
+QFinalState::_ZTV11QFinalState: 16 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI11QFinalState)
+16 (int (*)(...))QFinalState::metaObject
+24 (int (*)(...))QFinalState::qt_metacast
+32 (int (*)(...))QFinalState::qt_metacall
+40 (int (*)(...))QFinalState::~QFinalState
+48 (int (*)(...))QFinalState::~QFinalState
+56 (int (*)(...))QFinalState::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QFinalState::onEntry
+120 (int (*)(...))QFinalState::onExit
+
+Class QFinalState
+ size=16 align=8
+ base size=16 base align=8
+QFinalState (0x0x7f3bf9269dd0) 0
+ vptr=((& QFinalState::_ZTV11QFinalState) + 16)
+ QAbstractState (0x0x7f3bf9269e38) 0
+ primary-for QFinalState (0x0x7f3bf9269dd0)
+ QObject (0x0x7f3bf92afde0) 0
+ primary-for QAbstractState (0x0x7f3bf9269e38)
+
+Vtable for QRunnable
+QRunnable::_ZTV9QRunnable: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI9QRunnable)
+16 (int (*)(...))__cxa_pure_virtual
+24 0
+32 0
+
+Class QRunnable
+ size=16 align=8
+ base size=12 base align=8
+QRunnable (0x0x7f3bf8ecd060) 0
+ vptr=((& QRunnable::_ZTV9QRunnable) + 16)
+
+Class QBasicMutex
+ size=8 align=8
+ base size=8 base align=8
+QBasicMutex (0x0x7f3bf8ecd300) 0
+
+Class QMutex
+ size=8 align=8
+ base size=8 base align=8
+QMutex (0x0x7f3bf9269f08) 0
+ QBasicMutex (0x0x7f3bf8ecdf60) 0
+
+Class QRecursiveMutex
+ size=8 align=8
+ base size=8 base align=8
+QRecursiveMutex (0x0x7f3bf9269f70) 0
+ QMutex (0x0x7f3bf8f5c000) 0
+ QBasicMutex (0x0x7f3bf8f571e0) 0
+
+Class QMutexLocker
+ size=8 align=8
+ base size=8 base align=8
+QMutexLocker (0x0x7f3bf8f57240) 0
+
+Class QtPrivate::ResultItem
+ size=16 align=8
+ base size=16 base align=8
+QtPrivate::ResultItem (0x0x7f3bf8f57840) 0
+
+Class QtPrivate::ResultIteratorBase
+ size=16 align=8
+ base size=12 base align=8
+QtPrivate::ResultIteratorBase (0x0x7f3bf8f57e40) 0
+
+Vtable for QtPrivate::ResultStoreBase
+QtPrivate::ResultStoreBase::_ZTVN9QtPrivate15ResultStoreBaseE: 4 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTIN9QtPrivate15ResultStoreBaseE)
+16 (int (*)(...))QtPrivate::ResultStoreBase::~ResultStoreBase
+24 (int (*)(...))QtPrivate::ResultStoreBase::~ResultStoreBase
+
+Class QtPrivate::ResultStoreBase
+ size=48 align=8
+ base size=44 base align=8
+QtPrivate::ResultStoreBase (0x0x7f3bf8fa7060) 0
+ vptr=((& QtPrivate::ResultStoreBase::_ZTVN9QtPrivate15ResultStoreBaseE) + 16)
+
+Class std::__mutex_base
+ size=40 align=8
+ base size=40 base align=8
+std::__mutex_base (0x0x7f3bf8ff3840) 0
+
+Class std::mutex
+ size=40 align=8
+ base size=40 base align=8
+std::mutex (0x0x7f3bf8ff18f0) 0
+ std::__mutex_base (0x0x7f3bf8ff38a0) 0
+
+Class std::defer_lock_t
+ size=1 align=1
+ base size=0 base align=1
+std::defer_lock_t (0x0x7f3bf8ff3a80) 0 empty
+
+Class std::try_to_lock_t
+ size=1 align=1
+ base size=0 base align=1
+std::try_to_lock_t (0x0x7f3bf8ff3ae0) 0 empty
+
+Class std::adopt_lock_t
+ size=1 align=1
+ base size=0 base align=1
+std::adopt_lock_t (0x0x7f3bf8ff3b40) 0 empty
+
+Class std::__recursive_mutex_base
+ size=40 align=8
+ base size=40 base align=8
+std::__recursive_mutex_base (0x0x7f3bf90265a0) 0
+
+Class std::recursive_mutex
+ size=40 align=8
+ base size=40 base align=8
+std::recursive_mutex (0x0x7f3bf8ff1958) 0
+ std::__recursive_mutex_base (0x0x7f3bf9026600) 0
+
+Class std::timed_mutex
+ size=40 align=8
+ base size=40 base align=8
+std::timed_mutex (0x0x7f3bf8ffdd20) 0
+ std::__mutex_base (0x0x7f3bf90269c0) 0
+ std::__timed_mutex_impl<std::timed_mutex> (0x0x7f3bf9026a20) 0 empty
+
+Class std::recursive_timed_mutex
+ size=40 align=8
+ base size=40 base align=8
+std::recursive_timed_mutex (0x0x7f3bf9051070) 0
+ std::__recursive_mutex_base (0x0x7f3bf9026d80) 0
+ std::__timed_mutex_impl<std::recursive_timed_mutex> (0x0x7f3bf9026de0) 0 empty
+
+Class std::once_flag
+ size=4 align=4
+ base size=4 base align=4
+std::once_flag (0x0x7f3bf9067540) 0
+
+Vtable for QFutureInterfaceBase
+QFutureInterfaceBase::_ZTV20QFutureInterfaceBase: 4 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI20QFutureInterfaceBase)
+16 (int (*)(...))QFutureInterfaceBase::~QFutureInterfaceBase
+24 (int (*)(...))QFutureInterfaceBase::~QFutureInterfaceBase
+
+Class QFutureInterfaceBase
+ size=16 align=8
+ base size=16 base align=8
+QFutureInterfaceBase (0x0x7f3bf9067780) 0
+ vptr=((& QFutureInterfaceBase::_ZTV20QFutureInterfaceBase) + 16)
+
+Class QFutureWatcherBase::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QFutureWatcherBase::QPrivateSignal (0x0x7f3bf8d15ae0) 0 empty
+
+Vtable for QFutureWatcherBase
+QFutureWatcherBase::_ZTV18QFutureWatcherBase: 16 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI18QFutureWatcherBase)
+16 (int (*)(...))QFutureWatcherBase::metaObject
+24 (int (*)(...))QFutureWatcherBase::qt_metacast
+32 (int (*)(...))QFutureWatcherBase::qt_metacall
+40 0
+48 0
+56 (int (*)(...))QFutureWatcherBase::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QFutureWatcherBase::connectNotify
+104 (int (*)(...))QFutureWatcherBase::disconnectNotify
+112 (int (*)(...))__cxa_pure_virtual
+120 (int (*)(...))__cxa_pure_virtual
+
+Class QFutureWatcherBase
+ size=16 align=8
+ base size=16 base align=8
+QFutureWatcherBase (0x0x7f3bf8cb3750) 0
+ vptr=((& QFutureWatcherBase::_ZTV18QFutureWatcherBase) + 16)
+ QObject (0x0x7f3bf8d15a80) 0
+ primary-for QFutureWatcherBase (0x0x7f3bf8cb3750)
+
+Class QHistoryState::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QHistoryState::QPrivateSignal (0x0x7f3bf8d40e40) 0 empty
+
+Vtable for QHistoryState
+QHistoryState::_ZTV13QHistoryState: 16 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI13QHistoryState)
+16 (int (*)(...))QHistoryState::metaObject
+24 (int (*)(...))QHistoryState::qt_metacast
+32 (int (*)(...))QHistoryState::qt_metacall
+40 (int (*)(...))QHistoryState::~QHistoryState
+48 (int (*)(...))QHistoryState::~QHistoryState
+56 (int (*)(...))QHistoryState::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QHistoryState::onEntry
+120 (int (*)(...))QHistoryState::onExit
+
+Class QHistoryState
+ size=16 align=8
+ base size=16 base align=8
+QHistoryState (0x0x7f3bf8cb3f70) 0
+ vptr=((& QHistoryState::_ZTV13QHistoryState) + 16)
+ QAbstractState (0x0x7f3bf8d60000) 0
+ primary-for QHistoryState (0x0x7f3bf8cb3f70)
+ QObject (0x0x7f3bf8d40de0) 0
+ primary-for QAbstractState (0x0x7f3bf8d60000)
+
+Class QIdentityProxyModel::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QIdentityProxyModel::QPrivateSignal (0x0x7f3bf8d6e180) 0 empty
+
+Vtable for QIdentityProxyModel
+QIdentityProxyModel::_ZTV19QIdentityProxyModel: 53 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI19QIdentityProxyModel)
+16 (int (*)(...))QIdentityProxyModel::metaObject
+24 (int (*)(...))QIdentityProxyModel::qt_metacast
+32 (int (*)(...))QIdentityProxyModel::qt_metacall
+40 (int (*)(...))QIdentityProxyModel::~QIdentityProxyModel
+48 (int (*)(...))QIdentityProxyModel::~QIdentityProxyModel
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QIdentityProxyModel::index
+120 (int (*)(...))QIdentityProxyModel::parent
+128 (int (*)(...))QIdentityProxyModel::sibling
+136 (int (*)(...))QIdentityProxyModel::rowCount
+144 (int (*)(...))QIdentityProxyModel::columnCount
+152 (int (*)(...))QAbstractProxyModel::hasChildren
+160 (int (*)(...))QAbstractProxyModel::data
+168 (int (*)(...))QAbstractProxyModel::setData
+176 (int (*)(...))QIdentityProxyModel::headerData
+184 (int (*)(...))QAbstractProxyModel::setHeaderData
+192 (int (*)(...))QAbstractProxyModel::itemData
+200 (int (*)(...))QAbstractProxyModel::setItemData
+208 (int (*)(...))QAbstractProxyModel::mimeTypes
+216 (int (*)(...))QAbstractProxyModel::mimeData
+224 (int (*)(...))QAbstractProxyModel::canDropMimeData
+232 (int (*)(...))QIdentityProxyModel::dropMimeData
+240 (int (*)(...))QAbstractProxyModel::supportedDropActions
+248 (int (*)(...))QAbstractProxyModel::supportedDragActions
+256 (int (*)(...))QIdentityProxyModel::insertRows
+264 (int (*)(...))QIdentityProxyModel::insertColumns
+272 (int (*)(...))QIdentityProxyModel::removeRows
+280 (int (*)(...))QIdentityProxyModel::removeColumns
+288 (int (*)(...))QAbstractItemModel::moveRows
+296 (int (*)(...))QAbstractItemModel::moveColumns
+304 (int (*)(...))QAbstractProxyModel::fetchMore
+312 (int (*)(...))QAbstractProxyModel::canFetchMore
+320 (int (*)(...))QAbstractProxyModel::flags
+328 (int (*)(...))QAbstractProxyModel::sort
+336 (int (*)(...))QAbstractProxyModel::buddy
+344 (int (*)(...))QIdentityProxyModel::match
+352 (int (*)(...))QAbstractProxyModel::span
+360 (int (*)(...))QAbstractItemModel::roleNames
+368 (int (*)(...))QAbstractProxyModel::submit
+376 (int (*)(...))QAbstractProxyModel::revert
+384 (int (*)(...))QIdentityProxyModel::setSourceModel
+392 (int (*)(...))QIdentityProxyModel::mapToSource
+400 (int (*)(...))QIdentityProxyModel::mapFromSource
+408 (int (*)(...))QIdentityProxyModel::mapSelectionToSource
+416 (int (*)(...))QIdentityProxyModel::mapSelectionFromSource
+
+Class QIdentityProxyModel
+ size=16 align=8
+ base size=16 base align=8
+QIdentityProxyModel (0x0x7f3bf8d60068) 0
+ vptr=((& QIdentityProxyModel::_ZTV19QIdentityProxyModel) + 16)
+ QAbstractProxyModel (0x0x7f3bf8d600d0) 0
+ primary-for QIdentityProxyModel (0x0x7f3bf8d60068)
+ QAbstractItemModel (0x0x7f3bf8d60138) 0
+ primary-for QAbstractProxyModel (0x0x7f3bf8d600d0)
+ QObject (0x0x7f3bf8d6e120) 0
+ primary-for QAbstractItemModel (0x0x7f3bf8d60138)
+
+Class QItemSelectionRange
+ size=16 align=8
+ base size=16 base align=8
+QItemSelectionRange (0x0x7f3bf8d6e360) 0
+
+Class QItemSelectionModel::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QItemSelectionModel::QPrivateSignal (0x0x7f3bf8e29c60) 0 empty
+
+Vtable for QItemSelectionModel
+QItemSelectionModel::_ZTV19QItemSelectionModel: 20 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI19QItemSelectionModel)
+16 (int (*)(...))QItemSelectionModel::metaObject
+24 (int (*)(...))QItemSelectionModel::qt_metacast
+32 (int (*)(...))QItemSelectionModel::qt_metacall
+40 (int (*)(...))QItemSelectionModel::~QItemSelectionModel
+48 (int (*)(...))QItemSelectionModel::~QItemSelectionModel
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QItemSelectionModel::setCurrentIndex
+120 (int (*)(...))QItemSelectionModel::select
+128 (int (*)(...))QItemSelectionModel::select
+136 (int (*)(...))QItemSelectionModel::clear
+144 (int (*)(...))QItemSelectionModel::reset
+152 (int (*)(...))QItemSelectionModel::clearCurrentIndex
+
+Class QItemSelectionModel
+ size=16 align=8
+ base size=16 base align=8
+QItemSelectionModel (0x0x7f3bf8e33a90) 0
+ vptr=((& QItemSelectionModel::_ZTV19QItemSelectionModel) + 16)
+ QObject (0x0x7f3bf8e29c00) 0
+ primary-for QItemSelectionModel (0x0x7f3bf8e33a90)
+
+Class QItemSelection
+ size=8 align=8
+ base size=8 base align=8
+QItemSelection (0x0x7f3bf8e33c30) 0
+ QList<QItemSelectionRange> (0x0x7f3bf8e33c98) 0
+ QListSpecialMethods<QItemSelectionRange> (0x0x7f3bf8e6b780) 0 empty
+
+Class QJsonValue
+ size=24 align=8
+ base size=20 base align=8
+QJsonValue (0x0x7f3bf8aff0c0) 0
+
+Class QJsonValueRef
+ size=16 align=8
+ base size=12 base align=8
+QJsonValueRef (0x0x7f3bf8c2bd20) 0
+
+Class QJsonValuePtr
+ size=24 align=8
+ base size=24 base align=8
+QJsonValuePtr (0x0x7f3bf8c68cc0) 0
+
+Class QJsonValueRefPtr
+ size=16 align=8
+ base size=16 base align=8
+QJsonValueRefPtr (0x0x7f3bf8c68f60) 0
+
+Class QJsonArray::iterator
+ size=16 align=8
+ base size=12 base align=8
+QJsonArray::iterator (0x0x7f3bf88e4300) 0
+
+Class QJsonArray::const_iterator
+ size=16 align=8
+ base size=12 base align=8
+QJsonArray::const_iterator (0x0x7f3bf88e4360) 0
+
+Class QJsonArray
+ size=16 align=8
+ base size=16 base align=8
+QJsonArray (0x0x7f3bf88e42a0) 0
+
+Class QJsonParseError
+ size=8 align=4
+ base size=8 base align=4
+QJsonParseError (0x0x7f3bf8a14240) 0
+
+Class QJsonDocument
+ size=8 align=8
+ base size=8 base align=8
+QJsonDocument (0x0x7f3bf8a142a0) 0
+
+Class QJsonObject::iterator
+ size=16 align=8
+ base size=12 base align=8
+QJsonObject::iterator (0x0x7f3bf8a65a80) 0
+
+Class QJsonObject::const_iterator
+ size=16 align=8
+ base size=12 base align=8
+QJsonObject::const_iterator (0x0x7f3bf8a65ae0) 0
+
+Class QJsonObject
+ size=16 align=8
+ base size=16 base align=8
+QJsonObject (0x0x7f3bf8a65a20) 0
+
+Class QLibrary::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QLibrary::QPrivateSignal (0x0x7f3bf8781f00) 0 empty
+
+Vtable for QLibrary
+QLibrary::_ZTV8QLibrary: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI8QLibrary)
+16 (int (*)(...))QLibrary::metaObject
+24 (int (*)(...))QLibrary::qt_metacast
+32 (int (*)(...))QLibrary::qt_metacall
+40 (int (*)(...))QLibrary::~QLibrary
+48 (int (*)(...))QLibrary::~QLibrary
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QLibrary
+ size=32 align=8
+ base size=25 base align=8
+QLibrary (0x0x7f3bf878e8f0) 0
+ vptr=((& QLibrary::_ZTV8QLibrary) + 16)
+ QObject (0x0x7f3bf8781ea0) 0
+ primary-for QLibrary (0x0x7f3bf878e8f0)
+
+Class QVersionNumber::SegmentStorage
+ size=8 align=8
+ base size=8 base align=8
+QVersionNumber::SegmentStorage (0x0x7f3bf87b1d80) 0
+
+Class QVersionNumber
+ size=8 align=8
+ base size=8 base align=8
+QVersionNumber (0x0x7f3bf87b18a0) 0
+
+Class QLibraryInfo
+ size=1 align=1
+ base size=0 base align=1
+QLibraryInfo (0x0x7f3bf8481540) 0 empty
+
+Class QPoint
+ size=8 align=4
+ base size=8 base align=4
+QPoint (0x0x7f3bf84815a0) 0
+
+Class QPointF
+ size=16 align=8
+ base size=16 base align=8
+QPointF (0x0x7f3bf84f9420) 0
+
+Class QLine
+ size=16 align=4
+ base size=16 base align=4
+QLine (0x0x7f3bf856a600) 0
+
+Class QLineF
+ size=32 align=8
+ base size=32 base align=8
+QLineF (0x0x7f3bf85d79c0) 0
+
+Class QLinkedListData
+ size=32 align=8
+ base size=25 base align=8
+QLinkedListData (0x0x7f3bf8654c60) 0
+
+Class QLockFile
+ size=8 align=8
+ base size=8 base align=8
+QLockFile (0x0x7f3bf831a1e0) 0
+
+Class QLoggingCategory::AtomicBools
+ size=4 align=1
+ base size=4 base align=1
+QLoggingCategory::AtomicBools (0x0x7f3bf831a420) 0
+
+Class QLoggingCategory
+ size=24 align=8
+ base size=24 base align=8
+QLoggingCategory (0x0x7f3bf831a3c0) 0
+
+Class QMargins
+ size=16 align=4
+ base size=16 base align=4
+QMargins (0x0x7f3bf831a840) 0
+
+Class QMarginsF
+ size=32 align=8
+ base size=32 base align=8
+QMarginsF (0x0x7f3bf83d8780) 0
+
+Class QMessageAuthenticationCode
+ size=8 align=8
+ base size=8 base align=8
+QMessageAuthenticationCode (0x0x7f3bf8221f60) 0
+
+Class QMetaMethod
+ size=16 align=8
+ base size=12 base align=8
+QMetaMethod (0x0x7f3bf8248000) 0
+
+Class QMetaEnum
+ size=16 align=8
+ base size=12 base align=8
+QMetaEnum (0x0x7f3bf7e2a840) 0
+
+Class QMetaProperty
+ size=32 align=8
+ base size=32 base align=8
+QMetaProperty (0x0x7f3bf7e6ea20) 0
+
+Class QMetaClassInfo
+ size=16 align=8
+ base size=12 base align=8
+QMetaClassInfo (0x0x7f3bf7e6eb40) 0
+
+Class QMimeData::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QMimeData::QPrivateSignal (0x0x7f3bf7ec9120) 0 empty
+
+Vtable for QMimeData
+QMimeData::_ZTV9QMimeData: 17 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI9QMimeData)
+16 (int (*)(...))QMimeData::metaObject
+24 (int (*)(...))QMimeData::qt_metacast
+32 (int (*)(...))QMimeData::qt_metacall
+40 (int (*)(...))QMimeData::~QMimeData
+48 (int (*)(...))QMimeData::~QMimeData
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QMimeData::hasFormat
+120 (int (*)(...))QMimeData::formats
+128 (int (*)(...))QMimeData::retrieveData
+
+Class QMimeData
+ size=16 align=8
+ base size=16 base align=8
+QMimeData (0x0x7f3bf7ec1548) 0
+ vptr=((& QMimeData::_ZTV9QMimeData) + 16)
+ QObject (0x0x7f3bf7ec90c0) 0
+ primary-for QMimeData (0x0x7f3bf7ec1548)
+
+Class QMimeType
+ size=8 align=8
+ base size=8 base align=8
+QMimeType (0x0x7f3bf7ec9300) 0
+
+Class QMimeDatabase
+ size=8 align=8
+ base size=8 base align=8
+QMimeDatabase (0x0x7f3bf7f97240) 0
+
+Class QObjectCleanupHandler::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QObjectCleanupHandler::QPrivateSignal (0x0x7f3bf7f97300) 0 empty
+
+Vtable for QObjectCleanupHandler
+QObjectCleanupHandler::_ZTV21QObjectCleanupHandler: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI21QObjectCleanupHandler)
+16 (int (*)(...))QObjectCleanupHandler::metaObject
+24 (int (*)(...))QObjectCleanupHandler::qt_metacast
+32 (int (*)(...))QObjectCleanupHandler::qt_metacall
+40 (int (*)(...))QObjectCleanupHandler::~QObjectCleanupHandler
+48 (int (*)(...))QObjectCleanupHandler::~QObjectCleanupHandler
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QObjectCleanupHandler
+ size=24 align=8
+ base size=24 base align=8
+QObjectCleanupHandler (0x0x7f3bf7f990d0) 0
+ vptr=((& QObjectCleanupHandler::_ZTV21QObjectCleanupHandler) + 16)
+ QObject (0x0x7f3bf7f972a0) 0
+ primary-for QObjectCleanupHandler (0x0x7f3bf7f990d0)
+
+Class QOperatingSystemVersion
+ size=16 align=4
+ base size=16 base align=4
+QOperatingSystemVersion (0x0x7f3bf7f97420) 0
+
+Class QParallelAnimationGroup::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QParallelAnimationGroup::QPrivateSignal (0x0x7f3bf7c08ba0) 0 empty
+
+Vtable for QParallelAnimationGroup
+QParallelAnimationGroup::_ZTV23QParallelAnimationGroup: 18 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI23QParallelAnimationGroup)
+16 (int (*)(...))QParallelAnimationGroup::metaObject
+24 (int (*)(...))QParallelAnimationGroup::qt_metacast
+32 (int (*)(...))QParallelAnimationGroup::qt_metacall
+40 (int (*)(...))QParallelAnimationGroup::~QParallelAnimationGroup
+48 (int (*)(...))QParallelAnimationGroup::~QParallelAnimationGroup
+56 (int (*)(...))QParallelAnimationGroup::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QParallelAnimationGroup::duration
+120 (int (*)(...))QParallelAnimationGroup::updateCurrentTime
+128 (int (*)(...))QParallelAnimationGroup::updateState
+136 (int (*)(...))QParallelAnimationGroup::updateDirection
+
+Class QParallelAnimationGroup
+ size=16 align=8
+ base size=16 base align=8
+QParallelAnimationGroup (0x0x7f3bf7c12958) 0
+ vptr=((& QParallelAnimationGroup::_ZTV23QParallelAnimationGroup) + 16)
+ QAnimationGroup (0x0x7f3bf7c129c0) 0
+ primary-for QParallelAnimationGroup (0x0x7f3bf7c12958)
+ QAbstractAnimation (0x0x7f3bf7c12a28) 0
+ primary-for QAnimationGroup (0x0x7f3bf7c129c0)
+ QObject (0x0x7f3bf7c08b40) 0
+ primary-for QAbstractAnimation (0x0x7f3bf7c12a28)
+
+Class QPauseAnimation::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QPauseAnimation::QPrivateSignal (0x0x7f3bf7c08de0) 0 empty
+
+Vtable for QPauseAnimation
+QPauseAnimation::_ZTV15QPauseAnimation: 18 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI15QPauseAnimation)
+16 (int (*)(...))QPauseAnimation::metaObject
+24 (int (*)(...))QPauseAnimation::qt_metacast
+32 (int (*)(...))QPauseAnimation::qt_metacall
+40 (int (*)(...))QPauseAnimation::~QPauseAnimation
+48 (int (*)(...))QPauseAnimation::~QPauseAnimation
+56 (int (*)(...))QPauseAnimation::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QPauseAnimation::duration
+120 (int (*)(...))QPauseAnimation::updateCurrentTime
+128 (int (*)(...))QAbstractAnimation::updateState
+136 (int (*)(...))QAbstractAnimation::updateDirection
+
+Class QPauseAnimation
+ size=16 align=8
+ base size=16 base align=8
+QPauseAnimation (0x0x7f3bf7c12a90) 0
+ vptr=((& QPauseAnimation::_ZTV15QPauseAnimation) + 16)
+ QAbstractAnimation (0x0x7f3bf7c12af8) 0
+ primary-for QPauseAnimation (0x0x7f3bf7c12a90)
+ QObject (0x0x7f3bf7c08d80) 0
+ primary-for QAbstractAnimation (0x0x7f3bf7c12af8)
+
+Class QStaticPlugin
+ size=16 align=8
+ base size=16 base align=8
+QStaticPlugin (0x0x7f3bf7c39960) 0
+
+Class QPluginLoader::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QPluginLoader::QPrivateSignal (0x0x7f3bf7c84ae0) 0 empty
+
+Vtable for QPluginLoader
+QPluginLoader::_ZTV13QPluginLoader: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI13QPluginLoader)
+16 (int (*)(...))QPluginLoader::metaObject
+24 (int (*)(...))QPluginLoader::qt_metacast
+32 (int (*)(...))QPluginLoader::qt_metacall
+40 (int (*)(...))QPluginLoader::~QPluginLoader
+48 (int (*)(...))QPluginLoader::~QPluginLoader
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QPluginLoader
+ size=32 align=8
+ base size=25 base align=8
+QPluginLoader (0x0x7f3bf7c79e38) 0
+ vptr=((& QPluginLoader::_ZTV13QPluginLoader) + 16)
+ QObject (0x0x7f3bf7c84a80) 0
+ primary-for QPluginLoader (0x0x7f3bf7c79e38)
+
+Class QProcessEnvironment
+ size=8 align=8
+ base size=8 base align=8
+QProcessEnvironment (0x0x7f3bf7c84c00) 0
+
+Class QProcess::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QProcess::QPrivateSignal (0x0x7f3bf7d670c0) 0 empty
+
+Vtable for QProcess
+QProcess::_ZTV8QProcess: 31 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI8QProcess)
+16 (int (*)(...))QProcess::metaObject
+24 (int (*)(...))QProcess::qt_metacast
+32 (int (*)(...))QProcess::qt_metacall
+40 (int (*)(...))QProcess::~QProcess
+48 (int (*)(...))QProcess::~QProcess
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QProcess::isSequential
+120 (int (*)(...))QProcess::open
+128 (int (*)(...))QProcess::close
+136 (int (*)(...))QIODevice::pos
+144 (int (*)(...))QIODevice::size
+152 (int (*)(...))QIODevice::seek
+160 (int (*)(...))QProcess::atEnd
+168 (int (*)(...))QIODevice::reset
+176 (int (*)(...))QProcess::bytesAvailable
+184 (int (*)(...))QProcess::bytesToWrite
+192 (int (*)(...))QProcess::canReadLine
+200 (int (*)(...))QProcess::waitForReadyRead
+208 (int (*)(...))QProcess::waitForBytesWritten
+216 (int (*)(...))QProcess::readData
+224 (int (*)(...))QIODevice::readLineData
+232 (int (*)(...))QProcess::writeData
+240 (int (*)(...))QProcess::setupChildProcess
+
+Class QProcess
+ size=16 align=8
+ base size=16 base align=8
+QProcess (0x0x7f3bf7d632d8) 0
+ vptr=((& QProcess::_ZTV8QProcess) + 16)
+ QIODevice (0x0x7f3bf7d63340) 0
+ primary-for QProcess (0x0x7f3bf7d632d8)
+ QObject (0x0x7f3bf7d67060) 0
+ primary-for QIODevice (0x0x7f3bf7d63340)
+
+Class QVariantAnimation::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QVariantAnimation::QPrivateSignal (0x0x7f3bf7d67780) 0 empty
+
+Vtable for QVariantAnimation
+QVariantAnimation::_ZTV17QVariantAnimation: 20 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI17QVariantAnimation)
+16 (int (*)(...))QVariantAnimation::metaObject
+24 (int (*)(...))QVariantAnimation::qt_metacast
+32 (int (*)(...))QVariantAnimation::qt_metacall
+40 (int (*)(...))QVariantAnimation::~QVariantAnimation
+48 (int (*)(...))QVariantAnimation::~QVariantAnimation
+56 (int (*)(...))QVariantAnimation::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QVariantAnimation::duration
+120 (int (*)(...))QVariantAnimation::updateCurrentTime
+128 (int (*)(...))QVariantAnimation::updateState
+136 (int (*)(...))QAbstractAnimation::updateDirection
+144 (int (*)(...))QVariantAnimation::updateCurrentValue
+152 (int (*)(...))QVariantAnimation::interpolated
+
+Class QVariantAnimation
+ size=16 align=8
+ base size=16 base align=8
+QVariantAnimation (0x0x7f3bf7d633a8) 0
+ vptr=((& QVariantAnimation::_ZTV17QVariantAnimation) + 16)
+ QAbstractAnimation (0x0x7f3bf7d63410) 0
+ primary-for QVariantAnimation (0x0x7f3bf7d633a8)
+ QObject (0x0x7f3bf7d67720) 0
+ primary-for QAbstractAnimation (0x0x7f3bf7d63410)
+
+Class QPropertyAnimation::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QPropertyAnimation::QPrivateSignal (0x0x7f3bf7d67a20) 0 empty
+
+Vtable for QPropertyAnimation
+QPropertyAnimation::_ZTV18QPropertyAnimation: 20 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI18QPropertyAnimation)
+16 (int (*)(...))QPropertyAnimation::metaObject
+24 (int (*)(...))QPropertyAnimation::qt_metacast
+32 (int (*)(...))QPropertyAnimation::qt_metacall
+40 (int (*)(...))QPropertyAnimation::~QPropertyAnimation
+48 (int (*)(...))QPropertyAnimation::~QPropertyAnimation
+56 (int (*)(...))QPropertyAnimation::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QVariantAnimation::duration
+120 (int (*)(...))QVariantAnimation::updateCurrentTime
+128 (int (*)(...))QPropertyAnimation::updateState
+136 (int (*)(...))QAbstractAnimation::updateDirection
+144 (int (*)(...))QPropertyAnimation::updateCurrentValue
+152 (int (*)(...))QVariantAnimation::interpolated
+
+Class QPropertyAnimation
+ size=16 align=8
+ base size=16 base align=8
+QPropertyAnimation (0x0x7f3bf7d634e0) 0
+ vptr=((& QPropertyAnimation::_ZTV18QPropertyAnimation) + 16)
+ QVariantAnimation (0x0x7f3bf7d63548) 0
+ primary-for QPropertyAnimation (0x0x7f3bf7d634e0)
+ QAbstractAnimation (0x0x7f3bf7d635b0) 0
+ primary-for QVariantAnimation (0x0x7f3bf7d63548)
+ QObject (0x0x7f3bf7d679c0) 0
+ primary-for QAbstractAnimation (0x0x7f3bf7d635b0)
+
+Class std::random_device
+ size=5000 align=8
+ base size=5000 base align=8
+std::random_device (0x0x7f3bf7a39180) 0
+
+Class std::bernoulli_distribution::param_type
+ size=8 align=8
+ base size=8 base align=8
+std::bernoulli_distribution::param_type (0x0x7f3bf7b13ea0) 0
+
+Class std::bernoulli_distribution
+ size=8 align=8
+ base size=8 base align=8
+std::bernoulli_distribution (0x0x7f3bf7b13e40) 0
+
+Class std::seed_seq
+ size=24 align=8
+ base size=24 base align=8
+std::seed_seq (0x0x7f3bf7901c00) 0
+
+Class QRandomGenerator::Storage
+ size=2504 align=8
+ base size=2504 base align=8
+QRandomGenerator::Storage (0x0x7f3bf773f8a0) 0
+
+Class QRandomGenerator
+ size=2512 align=8
+ base size=2512 base align=8
+QRandomGenerator (0x0x7f3bf773f840) 0
+
+Class QRandomGenerator64
+ size=2512 align=8
+ base size=2512 base align=8
+QRandomGenerator64 (0x0x7f3bf77d6270) 0
+ QRandomGenerator (0x0x7f3bf77e93c0) 0
+
+Class QReadWriteLock
+ size=8 align=8
+ base size=8 base align=8
+QReadWriteLock (0x0x7f3bf77e9f60) 0
+
+Class QReadLocker
+ size=8 align=8
+ base size=8 base align=8
+QReadLocker (0x0x7f3bf7408240) 0
+
+Class QWriteLocker
+ size=8 align=8
+ base size=8 base align=8
+QWriteLocker (0x0x7f3bf7408720) 0
+
+Class QSize
+ size=8 align=4
+ base size=8 base align=4
+QSize (0x0x7f3bf7408c00) 0
+
+Class QSizeF
+ size=16 align=8
+ base size=16 base align=8
+QSizeF (0x0x7f3bf74ddae0) 0
+
+Class QRect
+ size=16 align=4
+ base size=16 base align=4
+QRect (0x0x7f3bf7558b40) 0
+
+Class QRectF
+ size=32 align=8
+ base size=32 base align=8
+QRectF (0x0x7f3bf7208ba0) 0
+
+Class QResource
+ size=8 align=8
+ base size=8 base align=8
+QResource (0x0x7f3bf72c9cc0) 0
+
+Class QSaveFile::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QSaveFile::QPrivateSignal (0x0x7f3bf72c9f60) 0 empty
+
+Vtable for QSaveFile
+QSaveFile::_ZTV9QSaveFile: 34 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI9QSaveFile)
+16 (int (*)(...))QSaveFile::metaObject
+24 (int (*)(...))QSaveFile::qt_metacast
+32 (int (*)(...))QSaveFile::qt_metacall
+40 (int (*)(...))QSaveFile::~QSaveFile
+48 (int (*)(...))QSaveFile::~QSaveFile
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QFileDevice::isSequential
+120 (int (*)(...))QSaveFile::open
+128 (int (*)(...))QSaveFile::close
+136 (int (*)(...))QFileDevice::pos
+144 (int (*)(...))QFileDevice::size
+152 (int (*)(...))QFileDevice::seek
+160 (int (*)(...))QFileDevice::atEnd
+168 (int (*)(...))QIODevice::reset
+176 (int (*)(...))QIODevice::bytesAvailable
+184 (int (*)(...))QIODevice::bytesToWrite
+192 (int (*)(...))QIODevice::canReadLine
+200 (int (*)(...))QIODevice::waitForReadyRead
+208 (int (*)(...))QIODevice::waitForBytesWritten
+216 (int (*)(...))QFileDevice::readData
+224 (int (*)(...))QFileDevice::readLineData
+232 (int (*)(...))QSaveFile::writeData
+240 (int (*)(...))QSaveFile::fileName
+248 (int (*)(...))QFileDevice::resize
+256 (int (*)(...))QFileDevice::permissions
+264 (int (*)(...))QFileDevice::setPermissions
+
+Class QSaveFile
+ size=16 align=8
+ base size=16 base align=8
+QSaveFile (0x0x7f3bf7289c30) 0
+ vptr=((& QSaveFile::_ZTV9QSaveFile) + 16)
+ QFileDevice (0x0x7f3bf7289c98) 0
+ primary-for QSaveFile (0x0x7f3bf7289c30)
+ QIODevice (0x0x7f3bf7289d00) 0
+ primary-for QFileDevice (0x0x7f3bf7289c98)
+ QObject (0x0x7f3bf72c9f00) 0
+ primary-for QIODevice (0x0x7f3bf7289d00)
+
+Class QSemaphore
+ size=8 align=8
+ base size=8 base align=8
+QSemaphore (0x0x7f3bf73205a0) 0
+
+Class QSemaphoreReleaser
+ size=16 align=8
+ base size=12 base align=8
+QSemaphoreReleaser (0x0x7f3bf7320720) 0
+
+Class QSequentialAnimationGroup::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QSequentialAnimationGroup::QPrivateSignal (0x0x7f3bf73f3360) 0 empty
+
+Vtable for QSequentialAnimationGroup
+QSequentialAnimationGroup::_ZTV25QSequentialAnimationGroup: 18 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI25QSequentialAnimationGroup)
+16 (int (*)(...))QSequentialAnimationGroup::metaObject
+24 (int (*)(...))QSequentialAnimationGroup::qt_metacast
+32 (int (*)(...))QSequentialAnimationGroup::qt_metacall
+40 (int (*)(...))QSequentialAnimationGroup::~QSequentialAnimationGroup
+48 (int (*)(...))QSequentialAnimationGroup::~QSequentialAnimationGroup
+56 (int (*)(...))QSequentialAnimationGroup::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QSequentialAnimationGroup::duration
+120 (int (*)(...))QSequentialAnimationGroup::updateCurrentTime
+128 (int (*)(...))QSequentialAnimationGroup::updateState
+136 (int (*)(...))QSequentialAnimationGroup::updateDirection
+
+Class QSequentialAnimationGroup
+ size=16 align=8
+ base size=16 base align=8
+QSequentialAnimationGroup (0x0x7f3bf73f54e0) 0
+ vptr=((& QSequentialAnimationGroup::_ZTV25QSequentialAnimationGroup) + 16)
+ QAnimationGroup (0x0x7f3bf73f5548) 0
+ primary-for QSequentialAnimationGroup (0x0x7f3bf73f54e0)
+ QAbstractAnimation (0x0x7f3bf73f55b0) 0
+ primary-for QAnimationGroup (0x0x7f3bf73f5548)
+ QObject (0x0x7f3bf73f3300) 0
+ primary-for QAbstractAnimation (0x0x7f3bf73f55b0)
+
+Class QSettings::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QSettings::QPrivateSignal (0x0x7f3bf73f35a0) 0 empty
+
+Vtable for QSettings
+QSettings::_ZTV9QSettings: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI9QSettings)
+16 (int (*)(...))QSettings::metaObject
+24 (int (*)(...))QSettings::qt_metacast
+32 (int (*)(...))QSettings::qt_metacall
+40 (int (*)(...))QSettings::~QSettings
+48 (int (*)(...))QSettings::~QSettings
+56 (int (*)(...))QSettings::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QSettings
+ size=16 align=8
+ base size=16 base align=8
+QSettings (0x0x7f3bf73f5618) 0
+ vptr=((& QSettings::_ZTV9QSettings) + 16)
+ QObject (0x0x7f3bf73f3540) 0
+ primary-for QSettings (0x0x7f3bf73f5618)
+
+Class QSharedMemory::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QSharedMemory::QPrivateSignal (0x0x7f3bf73f3a20) 0 empty
+
+Vtable for QSharedMemory
+QSharedMemory::_ZTV13QSharedMemory: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI13QSharedMemory)
+16 (int (*)(...))QSharedMemory::metaObject
+24 (int (*)(...))QSharedMemory::qt_metacast
+32 (int (*)(...))QSharedMemory::qt_metacall
+40 (int (*)(...))QSharedMemory::~QSharedMemory
+48 (int (*)(...))QSharedMemory::~QSharedMemory
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QSharedMemory
+ size=16 align=8
+ base size=16 base align=8
+QSharedMemory (0x0x7f3bf73f5680) 0
+ vptr=((& QSharedMemory::_ZTV13QSharedMemory) + 16)
+ QObject (0x0x7f3bf73f39c0) 0
+ primary-for QSharedMemory (0x0x7f3bf73f5680)
+
+Class QSignalMapper::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QSignalMapper::QPrivateSignal (0x0x7f3bf73f3c60) 0 empty
+
+Vtable for QSignalMapper
+QSignalMapper::_ZTV13QSignalMapper: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI13QSignalMapper)
+16 (int (*)(...))QSignalMapper::metaObject
+24 (int (*)(...))QSignalMapper::qt_metacast
+32 (int (*)(...))QSignalMapper::qt_metacall
+40 (int (*)(...))QSignalMapper::~QSignalMapper
+48 (int (*)(...))QSignalMapper::~QSignalMapper
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QSignalMapper
+ size=16 align=8
+ base size=16 base align=8
+QSignalMapper (0x0x7f3bf73f56e8) 0
+ vptr=((& QSignalMapper::_ZTV13QSignalMapper) + 16)
+ QObject (0x0x7f3bf73f3c00) 0
+ primary-for QSignalMapper (0x0x7f3bf73f56e8)
+
+Class QSignalTransition::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QSignalTransition::QPrivateSignal (0x0x7f3bf73f3ea0) 0 empty
+
+Vtable for QSignalTransition
+QSignalTransition::_ZTV17QSignalTransition: 16 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI17QSignalTransition)
+16 (int (*)(...))QSignalTransition::metaObject
+24 (int (*)(...))QSignalTransition::qt_metacast
+32 (int (*)(...))QSignalTransition::qt_metacall
+40 (int (*)(...))QSignalTransition::~QSignalTransition
+48 (int (*)(...))QSignalTransition::~QSignalTransition
+56 (int (*)(...))QSignalTransition::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QSignalTransition::eventTest
+120 (int (*)(...))QSignalTransition::onTransition
+
+Class QSignalTransition
+ size=16 align=8
+ base size=16 base align=8
+QSignalTransition (0x0x7f3bf73f5750) 0
+ vptr=((& QSignalTransition::_ZTV17QSignalTransition) + 16)
+ QAbstractTransition (0x0x7f3bf73f57b8) 0
+ primary-for QSignalTransition (0x0x7f3bf73f5750)
+ QObject (0x0x7f3bf73f3e40) 0
+ primary-for QAbstractTransition (0x0x7f3bf73f57b8)
+
+Class QSocketNotifier::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QSocketNotifier::QPrivateSignal (0x0x7f3bf705c180) 0 empty
+
+Vtable for QSocketNotifier
+QSocketNotifier::_ZTV15QSocketNotifier: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI15QSocketNotifier)
+16 (int (*)(...))QSocketNotifier::metaObject
+24 (int (*)(...))QSocketNotifier::qt_metacast
+32 (int (*)(...))QSocketNotifier::qt_metacall
+40 (int (*)(...))QSocketNotifier::~QSocketNotifier
+48 (int (*)(...))QSocketNotifier::~QSocketNotifier
+56 (int (*)(...))QSocketNotifier::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QSocketNotifier
+ size=16 align=8
+ base size=16 base align=8
+QSocketNotifier (0x0x7f3bf73f5820) 0
+ vptr=((& QSocketNotifier::_ZTV15QSocketNotifier) + 16)
+ QObject (0x0x7f3bf705c120) 0
+ primary-for QSocketNotifier (0x0x7f3bf73f5820)
+
+Class QSortFilterProxyModel::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QSortFilterProxyModel::QPrivateSignal (0x0x7f3bf705c3c0) 0 empty
+
+Vtable for QSortFilterProxyModel
+QSortFilterProxyModel::_ZTV21QSortFilterProxyModel: 56 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI21QSortFilterProxyModel)
+16 (int (*)(...))QSortFilterProxyModel::metaObject
+24 (int (*)(...))QSortFilterProxyModel::qt_metacast
+32 (int (*)(...))QSortFilterProxyModel::qt_metacall
+40 (int (*)(...))QSortFilterProxyModel::~QSortFilterProxyModel
+48 (int (*)(...))QSortFilterProxyModel::~QSortFilterProxyModel
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QSortFilterProxyModel::index
+120 (int (*)(...))QSortFilterProxyModel::parent
+128 (int (*)(...))QSortFilterProxyModel::sibling
+136 (int (*)(...))QSortFilterProxyModel::rowCount
+144 (int (*)(...))QSortFilterProxyModel::columnCount
+152 (int (*)(...))QSortFilterProxyModel::hasChildren
+160 (int (*)(...))QSortFilterProxyModel::data
+168 (int (*)(...))QSortFilterProxyModel::setData
+176 (int (*)(...))QSortFilterProxyModel::headerData
+184 (int (*)(...))QSortFilterProxyModel::setHeaderData
+192 (int (*)(...))QAbstractProxyModel::itemData
+200 (int (*)(...))QAbstractProxyModel::setItemData
+208 (int (*)(...))QSortFilterProxyModel::mimeTypes
+216 (int (*)(...))QSortFilterProxyModel::mimeData
+224 (int (*)(...))QAbstractProxyModel::canDropMimeData
+232 (int (*)(...))QSortFilterProxyModel::dropMimeData
+240 (int (*)(...))QSortFilterProxyModel::supportedDropActions
+248 (int (*)(...))QAbstractProxyModel::supportedDragActions
+256 (int (*)(...))QSortFilterProxyModel::insertRows
+264 (int (*)(...))QSortFilterProxyModel::insertColumns
+272 (int (*)(...))QSortFilterProxyModel::removeRows
+280 (int (*)(...))QSortFilterProxyModel::removeColumns
+288 (int (*)(...))QAbstractItemModel::moveRows
+296 (int (*)(...))QAbstractItemModel::moveColumns
+304 (int (*)(...))QSortFilterProxyModel::fetchMore
+312 (int (*)(...))QSortFilterProxyModel::canFetchMore
+320 (int (*)(...))QSortFilterProxyModel::flags
+328 (int (*)(...))QSortFilterProxyModel::sort
+336 (int (*)(...))QSortFilterProxyModel::buddy
+344 (int (*)(...))QSortFilterProxyModel::match
+352 (int (*)(...))QSortFilterProxyModel::span
+360 (int (*)(...))QAbstractItemModel::roleNames
+368 (int (*)(...))QAbstractProxyModel::submit
+376 (int (*)(...))QAbstractProxyModel::revert
+384 (int (*)(...))QSortFilterProxyModel::setSourceModel
+392 (int (*)(...))QSortFilterProxyModel::mapToSource
+400 (int (*)(...))QSortFilterProxyModel::mapFromSource
+408 (int (*)(...))QSortFilterProxyModel::mapSelectionToSource
+416 (int (*)(...))QSortFilterProxyModel::mapSelectionFromSource
+424 (int (*)(...))QSortFilterProxyModel::filterAcceptsRow
+432 (int (*)(...))QSortFilterProxyModel::filterAcceptsColumn
+440 (int (*)(...))QSortFilterProxyModel::lessThan
+
+Class QSortFilterProxyModel
+ size=16 align=8
+ base size=16 base align=8
+QSortFilterProxyModel (0x0x7f3bf73f5888) 0
+ vptr=((& QSortFilterProxyModel::_ZTV21QSortFilterProxyModel) + 16)
+ QAbstractProxyModel (0x0x7f3bf73f58f0) 0
+ primary-for QSortFilterProxyModel (0x0x7f3bf73f5888)
+ QAbstractItemModel (0x0x7f3bf73f5958) 0
+ primary-for QAbstractProxyModel (0x0x7f3bf73f58f0)
+ QObject (0x0x7f3bf705c360) 0
+ primary-for QAbstractItemModel (0x0x7f3bf73f5958)
+
+Class QStandardPaths
+ size=1 align=1
+ base size=0 base align=1
+QStandardPaths (0x0x7f3bf705c7e0) 0 empty
+
+Class QState::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QState::QPrivateSignal (0x0x7f3bf70c7120) 0 empty
+
+Vtable for QState
+QState::_ZTV6QState: 16 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI6QState)
+16 (int (*)(...))QState::metaObject
+24 (int (*)(...))QState::qt_metacast
+32 (int (*)(...))QState::qt_metacall
+40 (int (*)(...))QState::~QState
+48 (int (*)(...))QState::~QState
+56 (int (*)(...))QState::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QState::onEntry
+120 (int (*)(...))QState::onExit
+
+Class QState
+ size=16 align=8
+ base size=16 base align=8
+QState (0x0x7f3bf73f5af8) 0
+ vptr=((& QState::_ZTV6QState) + 16)
+ QAbstractState (0x0x7f3bf73f5b60) 0
+ primary-for QState (0x0x7f3bf73f5af8)
+ QObject (0x0x7f3bf70c70c0) 0
+ primary-for QAbstractState (0x0x7f3bf73f5b60)
+
+Class QStateMachine::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QStateMachine::QPrivateSignal (0x0x7f3bf70c75a0) 0 empty
+
+Vtable for QStateMachine::SignalEvent
+QStateMachine::SignalEvent::_ZTVN13QStateMachine11SignalEventE: 4 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTIN13QStateMachine11SignalEventE)
+16 (int (*)(...))QStateMachine::SignalEvent::~SignalEvent
+24 (int (*)(...))QStateMachine::SignalEvent::~SignalEvent
+
+Class QStateMachine::SignalEvent
+ size=48 align=8
+ base size=48 base align=8
+QStateMachine::SignalEvent (0x0x7f3bf73f5d00) 0
+ vptr=((& QStateMachine::SignalEvent::_ZTVN13QStateMachine11SignalEventE) + 16)
+ QEvent (0x0x7f3bf70c7600) 0
+ primary-for QStateMachine::SignalEvent (0x0x7f3bf73f5d00)
+
+Vtable for QStateMachine::WrappedEvent
+QStateMachine::WrappedEvent::_ZTVN13QStateMachine12WrappedEventE: 4 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTIN13QStateMachine12WrappedEventE)
+16 (int (*)(...))QStateMachine::WrappedEvent::~WrappedEvent
+24 (int (*)(...))QStateMachine::WrappedEvent::~WrappedEvent
+
+Class QStateMachine::WrappedEvent
+ size=40 align=8
+ base size=40 base align=8
+QStateMachine::WrappedEvent (0x0x7f3bf73f5d68) 0
+ vptr=((& QStateMachine::WrappedEvent::_ZTVN13QStateMachine12WrappedEventE) + 16)
+ QEvent (0x0x7f3bf70c7660) 0
+ primary-for QStateMachine::WrappedEvent (0x0x7f3bf73f5d68)
+
+Vtable for QStateMachine
+QStateMachine::_ZTV13QStateMachine: 20 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI13QStateMachine)
+16 (int (*)(...))QStateMachine::metaObject
+24 (int (*)(...))QStateMachine::qt_metacast
+32 (int (*)(...))QStateMachine::qt_metacall
+40 (int (*)(...))QStateMachine::~QStateMachine
+48 (int (*)(...))QStateMachine::~QStateMachine
+56 (int (*)(...))QStateMachine::event
+64 (int (*)(...))QStateMachine::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QStateMachine::onEntry
+120 (int (*)(...))QStateMachine::onExit
+128 (int (*)(...))QStateMachine::beginSelectTransitions
+136 (int (*)(...))QStateMachine::endSelectTransitions
+144 (int (*)(...))QStateMachine::beginMicrostep
+152 (int (*)(...))QStateMachine::endMicrostep
+
+Class QStateMachine
+ size=16 align=8
+ base size=16 base align=8
+QStateMachine (0x0x7f3bf73f5bc8) 0
+ vptr=((& QStateMachine::_ZTV13QStateMachine) + 16)
+ QState (0x0x7f3bf73f5c30) 0
+ primary-for QStateMachine (0x0x7f3bf73f5bc8)
+ QAbstractState (0x0x7f3bf73f5c98) 0
+ primary-for QState (0x0x7f3bf73f5c30)
+ QObject (0x0x7f3bf70c7540) 0
+ primary-for QAbstractState (0x0x7f3bf73f5c98)
+
+Class QStorageInfo
+ size=8 align=8
+ base size=8 base align=8
+QStorageInfo (0x0x7f3bf70c7a20) 0
+
+Class QAbstractConcatenable
+ size=1 align=1
+ base size=0 base align=1
+QAbstractConcatenable (0x0x7f3bf71ea7e0) 0 empty
+
+Class QStringListModel::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QStringListModel::QPrivateSignal (0x0x7f3bf6e72b40) 0 empty
+
+Vtable for QStringListModel
+QStringListModel::_ZTV16QStringListModel: 48 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI16QStringListModel)
+16 (int (*)(...))QStringListModel::metaObject
+24 (int (*)(...))QStringListModel::qt_metacast
+32 (int (*)(...))QStringListModel::qt_metacall
+40 (int (*)(...))QStringListModel::~QStringListModel
+48 (int (*)(...))QStringListModel::~QStringListModel
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QAbstractListModel::index
+120 (int (*)(...))QAbstractListModel::parent
+128 (int (*)(...))QStringListModel::sibling
+136 (int (*)(...))QStringListModel::rowCount
+144 (int (*)(...))QAbstractListModel::columnCount
+152 (int (*)(...))QAbstractListModel::hasChildren
+160 (int (*)(...))QStringListModel::data
+168 (int (*)(...))QStringListModel::setData
+176 (int (*)(...))QAbstractItemModel::headerData
+184 (int (*)(...))QAbstractItemModel::setHeaderData
+192 (int (*)(...))QStringListModel::itemData
+200 (int (*)(...))QStringListModel::setItemData
+208 (int (*)(...))QAbstractItemModel::mimeTypes
+216 (int (*)(...))QAbstractItemModel::mimeData
+224 (int (*)(...))QAbstractItemModel::canDropMimeData
+232 (int (*)(...))QAbstractListModel::dropMimeData
+240 (int (*)(...))QStringListModel::supportedDropActions
+248 (int (*)(...))QAbstractItemModel::supportedDragActions
+256 (int (*)(...))QStringListModel::insertRows
+264 (int (*)(...))QAbstractItemModel::insertColumns
+272 (int (*)(...))QStringListModel::removeRows
+280 (int (*)(...))QAbstractItemModel::removeColumns
+288 (int (*)(...))QStringListModel::moveRows
+296 (int (*)(...))QAbstractItemModel::moveColumns
+304 (int (*)(...))QAbstractItemModel::fetchMore
+312 (int (*)(...))QAbstractItemModel::canFetchMore
+320 (int (*)(...))QStringListModel::flags
+328 (int (*)(...))QStringListModel::sort
+336 (int (*)(...))QAbstractItemModel::buddy
+344 (int (*)(...))QAbstractItemModel::match
+352 (int (*)(...))QAbstractItemModel::span
+360 (int (*)(...))QAbstractItemModel::roleNames
+368 (int (*)(...))QAbstractItemModel::submit
+376 (int (*)(...))QAbstractItemModel::revert
+
+Class QStringListModel
+ size=24 align=8
+ base size=24 base align=8
+QStringListModel (0x0x7f3bf6e6f680) 0
+ vptr=((& QStringListModel::_ZTV16QStringListModel) + 16)
+ QAbstractListModel (0x0x7f3bf6e6f6e8) 0
+ primary-for QStringListModel (0x0x7f3bf6e6f680)
+ QAbstractItemModel (0x0x7f3bf6e6f750) 0
+ primary-for QAbstractListModel (0x0x7f3bf6e6f6e8)
+ QObject (0x0x7f3bf6e72ae0) 0
+ primary-for QAbstractItemModel (0x0x7f3bf6e6f750)
+
+Class QSystemSemaphore
+ size=8 align=8
+ base size=8 base align=8
+QSystemSemaphore (0x0x7f3bf6e72c60) 0
+
+Class QTemporaryDir
+ size=8 align=8
+ base size=8 base align=8
+QTemporaryDir (0x0x7f3bf6e72d20) 0
+
+Class QTemporaryFile::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QTemporaryFile::QPrivateSignal (0x0x7f3bf6e72e40) 0 empty
+
+Vtable for QTemporaryFile
+QTemporaryFile::_ZTV14QTemporaryFile: 34 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI14QTemporaryFile)
+16 (int (*)(...))QTemporaryFile::metaObject
+24 (int (*)(...))QTemporaryFile::qt_metacast
+32 (int (*)(...))QTemporaryFile::qt_metacall
+40 (int (*)(...))QTemporaryFile::~QTemporaryFile
+48 (int (*)(...))QTemporaryFile::~QTemporaryFile
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QFileDevice::isSequential
+120 (int (*)(...))QTemporaryFile::open
+128 (int (*)(...))QFileDevice::close
+136 (int (*)(...))QFileDevice::pos
+144 (int (*)(...))QFile::size
+152 (int (*)(...))QFileDevice::seek
+160 (int (*)(...))QFileDevice::atEnd
+168 (int (*)(...))QIODevice::reset
+176 (int (*)(...))QIODevice::bytesAvailable
+184 (int (*)(...))QIODevice::bytesToWrite
+192 (int (*)(...))QIODevice::canReadLine
+200 (int (*)(...))QIODevice::waitForReadyRead
+208 (int (*)(...))QIODevice::waitForBytesWritten
+216 (int (*)(...))QFileDevice::readData
+224 (int (*)(...))QFileDevice::readLineData
+232 (int (*)(...))QFileDevice::writeData
+240 (int (*)(...))QTemporaryFile::fileName
+248 (int (*)(...))QFile::resize
+256 (int (*)(...))QFile::permissions
+264 (int (*)(...))QFile::setPermissions
+
+Class QTemporaryFile
+ size=16 align=8
+ base size=16 base align=8
+QTemporaryFile (0x0x7f3bf6e6f7b8) 0
+ vptr=((& QTemporaryFile::_ZTV14QTemporaryFile) + 16)
+ QFile (0x0x7f3bf6e6f820) 0
+ primary-for QTemporaryFile (0x0x7f3bf6e6f7b8)
+ QFileDevice (0x0x7f3bf6e6f888) 0
+ primary-for QFile (0x0x7f3bf6e6f820)
+ QIODevice (0x0x7f3bf6e6f8f0) 0
+ primary-for QFileDevice (0x0x7f3bf6e6f888)
+ QObject (0x0x7f3bf6e72de0) 0
+ primary-for QIODevice (0x0x7f3bf6e6f8f0)
+
+Class QTextBoundaryFinder
+ size=48 align=8
+ base size=48 base align=8
+QTextBoundaryFinder (0x0x7f3bf6ed01e0) 0
+
+Class QTextCodec::ConverterState
+ size=32 align=8
+ base size=32 base align=8
+QTextCodec::ConverterState (0x0x7f3bf6ed0a20) 0
+
+Vtable for QTextCodec
+QTextCodec::_ZTV10QTextCodec: 9 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI10QTextCodec)
+16 (int (*)(...))__cxa_pure_virtual
+24 (int (*)(...))QTextCodec::aliases
+32 (int (*)(...))__cxa_pure_virtual
+40 (int (*)(...))__cxa_pure_virtual
+48 (int (*)(...))__cxa_pure_virtual
+56 0
+64 0
+
+Class QTextCodec
+ size=8 align=8
+ base size=8 base align=8
+QTextCodec (0x0x7f3bf6ed09c0) 0 nearly-empty
+ vptr=((& QTextCodec::_ZTV10QTextCodec) + 16)
+
+Class QTextEncoder
+ size=40 align=8
+ base size=40 base align=8
+QTextEncoder (0x0x7f3bf6f35420) 0
+
+Class QTextDecoder
+ size=40 align=8
+ base size=40 base align=8
+QTextDecoder (0x0x7f3bf6f35600) 0
+
+Vtable for std::thread::_State
+std::thread::_State::_ZTVNSt6thread6_StateE: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTINSt6thread6_StateE)
+16 0
+24 0
+32 (int (*)(...))__cxa_pure_virtual
+
+Class std::thread::_State
+ size=8 align=8
+ base size=8 base align=8
+std::thread::_State (0x0x7f3bf6f35840) 0 nearly-empty
+ vptr=((& std::thread::_State::_ZTVNSt6thread6_StateE) + 16)
+
+Class std::thread::id
+ size=8 align=8
+ base size=8 base align=8
+std::thread::id (0x0x7f3bf6f358a0) 0
+
+Class std::thread
+ size=8 align=8
+ base size=8 base align=8
+std::thread (0x0x7f3bf6f357e0) 0
+
+Class std::condition_variable
+ size=48 align=8
+ base size=48 base align=8
+std::condition_variable (0x0x7f3bf6dd9c60) 0
+
+Class std::__at_thread_exit_elt
+ size=16 align=8
+ base size=16 base align=8
+std::__at_thread_exit_elt (0x0x7f3bf6a26060) 0
+
+Class std::_V2::condition_variable_any
+ size=64 align=8
+ base size=64 base align=8
+std::_V2::condition_variable_any (0x0x7f3bf6a260c0) 0
+
+Class std::__atomic_futex_unsigned_base
+ size=1 align=1
+ base size=0 base align=1
+std::__atomic_futex_unsigned_base (0x0x7f3bf6b8a3c0) 0 empty
+
+Vtable for std::future_error
+std::future_error::_ZTVSt12future_error: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTISt12future_error)
+16 (int (*)(...))std::future_error::~future_error
+24 (int (*)(...))std::future_error::~future_error
+32 (int (*)(...))std::future_error::what
+
+Class std::future_error
+ size=32 align=8
+ base size=32 base align=8
+std::future_error (0x0x7f3bf6b7bc98) 0
+ vptr=((& std::future_error::_ZTVSt12future_error) + 16)
+ std::logic_error (0x0x7f3bf6b7bd00) 0
+ primary-for std::future_error (0x0x7f3bf6b7bc98)
+ std::exception (0x0x7f3bf6b8aae0) 0 nearly-empty
+ primary-for std::logic_error (0x0x7f3bf6b7bd00)
+
+Class std::__future_base::_Result_base::_Deleter
+ size=1 align=1
+ base size=0 base align=1
+std::__future_base::_Result_base::_Deleter (0x0x7f3bf6bbd240) 0 empty
+
+Vtable for std::__future_base::_Result_base
+std::__future_base::_Result_base::_ZTVNSt13__future_base12_Result_baseE: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTINSt13__future_base12_Result_baseE)
+16 (int (*)(...))__cxa_pure_virtual
+24 0
+32 0
+
+Class std::__future_base::_Result_base
+ size=16 align=8
+ base size=16 base align=8
+std::__future_base::_Result_base (0x0x7f3bf6bbd1e0) 0
+ vptr=((& std::__future_base::_Result_base::_ZTVNSt13__future_base12_Result_baseE) + 16)
+
+Class std::__future_base::_State_baseV2::__exception_ptr_tag
+ size=1 align=1
+ base size=0 base align=1
+std::__future_base::_State_baseV2::__exception_ptr_tag (0x0x7f3bf699a960) 0 empty
+
+Class std::__future_base::_State_baseV2::_Make_ready
+ size=32 align=8
+ base size=32 base align=8
+std::__future_base::_State_baseV2::_Make_ready (0x0x7f3bf69a0548) 0
+ std::__at_thread_exit_elt (0x0x7f3bf699aa20) 0
+
+Vtable for std::__future_base::_State_baseV2
+std::__future_base::_State_baseV2::_ZTVNSt13__future_base13_State_baseV2E: 6 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTINSt13__future_base13_State_baseV2E)
+16 (int (*)(...))std::__future_base::_State_baseV2::~_State_baseV2
+24 (int (*)(...))std::__future_base::_State_baseV2::~_State_baseV2
+32 (int (*)(...))std::__future_base::_State_baseV2::_M_complete_async
+40 (int (*)(...))std::__future_base::_State_baseV2::_M_is_deferred_future
+
+Class std::__future_base::_State_baseV2
+ size=32 align=8
+ base size=28 base align=8
+std::__future_base::_State_baseV2 (0x0x7f3bf6bbd3c0) 0
+ vptr=((& std::__future_base::_State_baseV2::_ZTVNSt13__future_base13_State_baseV2E) + 16)
+
+Class std::__future_base
+ size=1 align=1
+ base size=0 base align=1
+std::__future_base (0x0x7f3bf6bbd180) 0 empty
+
+Vtable for std::__future_base::_Async_state_commonV2
+std::__future_base::_Async_state_commonV2::_ZTVNSt13__future_base21_Async_state_commonV2E: 6 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTINSt13__future_base21_Async_state_commonV2E)
+16 (int (*)(...))std::__future_base::_Async_state_commonV2::~_Async_state_commonV2
+24 (int (*)(...))std::__future_base::_Async_state_commonV2::~_Async_state_commonV2
+32 (int (*)(...))std::__future_base::_Async_state_commonV2::_M_complete_async
+40 (int (*)(...))std::__future_base::_State_baseV2::_M_is_deferred_future
+
+Class std::__future_base::_Async_state_commonV2
+ size=48 align=8
+ base size=44 base align=8
+std::__future_base::_Async_state_commonV2 (0x0x7f3bf6136270) 0
+ vptr=((& std::__future_base::_Async_state_commonV2::_ZTVNSt13__future_base21_Async_state_commonV2E) + 16)
+ std::__future_base::_State_baseV2 (0x0x7f3bf6143a20) 0
+ primary-for std::__future_base::_Async_state_commonV2 (0x0x7f3bf6136270)
+
+Class QThread::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QThread::QPrivateSignal (0x0x7f3bf6177300) 0 empty
+
+Vtable for QThread
+QThread::_ZTV7QThread: 15 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI7QThread)
+16 (int (*)(...))QThread::metaObject
+24 (int (*)(...))QThread::qt_metacast
+32 (int (*)(...))QThread::qt_metacall
+40 (int (*)(...))QThread::~QThread
+48 (int (*)(...))QThread::~QThread
+56 (int (*)(...))QThread::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QThread::run
+
+Class QThread
+ size=16 align=8
+ base size=16 base align=8
+QThread (0x0x7f3bf61365b0) 0
+ vptr=((& QThread::_ZTV7QThread) + 16)
+ QObject (0x0x7f3bf61772a0) 0
+ primary-for QThread (0x0x7f3bf61365b0)
+
+Class QThreadPool::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QThreadPool::QPrivateSignal (0x0x7f3bf61776c0) 0 empty
+
+Vtable for QThreadPool
+QThreadPool::_ZTV11QThreadPool: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI11QThreadPool)
+16 (int (*)(...))QThreadPool::metaObject
+24 (int (*)(...))QThreadPool::qt_metacast
+32 (int (*)(...))QThreadPool::qt_metacall
+40 (int (*)(...))QThreadPool::~QThreadPool
+48 (int (*)(...))QThreadPool::~QThreadPool
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QThreadPool
+ size=16 align=8
+ base size=16 base align=8
+QThreadPool (0x0x7f3bf6136618) 0
+ vptr=((& QThreadPool::_ZTV11QThreadPool) + 16)
+ QObject (0x0x7f3bf6177660) 0
+ primary-for QThreadPool (0x0x7f3bf6136618)
+
+Class QThreadStorageData
+ size=4 align=4
+ base size=4 base align=4
+QThreadStorageData (0x0x7f3bf61778a0) 0
+
+Class QTimeLine::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QTimeLine::QPrivateSignal (0x0x7f3bf6177f60) 0 empty
+
+Vtable for QTimeLine
+QTimeLine::_ZTV9QTimeLine: 15 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI9QTimeLine)
+16 (int (*)(...))QTimeLine::metaObject
+24 (int (*)(...))QTimeLine::qt_metacast
+32 (int (*)(...))QTimeLine::qt_metacall
+40 (int (*)(...))QTimeLine::~QTimeLine
+48 (int (*)(...))QTimeLine::~QTimeLine
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QTimeLine::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QTimeLine::valueForTime
+
+Class QTimeLine
+ size=16 align=8
+ base size=16 base align=8
+QTimeLine (0x0x7f3bf6136680) 0
+ vptr=((& QTimeLine::_ZTV9QTimeLine) + 16)
+ QObject (0x0x7f3bf6177f00) 0
+ primary-for QTimeLine (0x0x7f3bf6136680)
+
+Class QTimer::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QTimer::QPrivateSignal (0x0x7f3bf61ca1e0) 0 empty
+
+Vtable for QTimer
+QTimer::_ZTV6QTimer: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI6QTimer)
+16 (int (*)(...))QTimer::metaObject
+24 (int (*)(...))QTimer::qt_metacast
+32 (int (*)(...))QTimer::qt_metacall
+40 (int (*)(...))QTimer::~QTimer
+48 (int (*)(...))QTimer::~QTimer
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QTimer::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QTimer
+ size=32 align=8
+ base size=29 base align=8
+QTimer (0x0x7f3bf61366e8) 0
+ vptr=((& QTimer::_ZTV6QTimer) + 16)
+ QObject (0x0x7f3bf61ca180) 0
+ primary-for QTimer (0x0x7f3bf61366e8)
+
+Class QTimeZone::OffsetData
+ size=32 align=8
+ base size=28 base align=8
+QTimeZone::OffsetData (0x0x7f3bf5e02b40) 0
+
+Class QTimeZone
+ size=8 align=8
+ base size=8 base align=8
+QTimeZone (0x0x7f3bf5e02ae0) 0
+
+Class QTranslator::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QTranslator::QPrivateSignal (0x0x7f3bf5ea0c00) 0 empty
+
+Vtable for QTranslator
+QTranslator::_ZTV11QTranslator: 16 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI11QTranslator)
+16 (int (*)(...))QTranslator::metaObject
+24 (int (*)(...))QTranslator::qt_metacast
+32 (int (*)(...))QTranslator::qt_metacall
+40 (int (*)(...))QTranslator::~QTranslator
+48 (int (*)(...))QTranslator::~QTranslator
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QTranslator::translate
+120 (int (*)(...))QTranslator::isEmpty
+
+Class QTranslator
+ size=16 align=8
+ base size=16 base align=8
+QTranslator (0x0x7f3bf5e9fdd0) 0
+ vptr=((& QTranslator::_ZTV11QTranslator) + 16)
+ QObject (0x0x7f3bf5ea0ba0) 0
+ primary-for QTranslator (0x0x7f3bf5e9fdd0)
+
+Class QTransposeProxyModel::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QTransposeProxyModel::QPrivateSignal (0x0x7f3bf5ea0e40) 0 empty
+
+Vtable for QTransposeProxyModel
+QTransposeProxyModel::_ZTV20QTransposeProxyModel: 53 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI20QTransposeProxyModel)
+16 (int (*)(...))QTransposeProxyModel::metaObject
+24 (int (*)(...))QTransposeProxyModel::qt_metacast
+32 (int (*)(...))QTransposeProxyModel::qt_metacall
+40 (int (*)(...))QTransposeProxyModel::~QTransposeProxyModel
+48 (int (*)(...))QTransposeProxyModel::~QTransposeProxyModel
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QTransposeProxyModel::index
+120 (int (*)(...))QTransposeProxyModel::parent
+128 (int (*)(...))QAbstractProxyModel::sibling
+136 (int (*)(...))QTransposeProxyModel::rowCount
+144 (int (*)(...))QTransposeProxyModel::columnCount
+152 (int (*)(...))QAbstractProxyModel::hasChildren
+160 (int (*)(...))QAbstractProxyModel::data
+168 (int (*)(...))QAbstractProxyModel::setData
+176 (int (*)(...))QTransposeProxyModel::headerData
+184 (int (*)(...))QTransposeProxyModel::setHeaderData
+192 (int (*)(...))QTransposeProxyModel::itemData
+200 (int (*)(...))QTransposeProxyModel::setItemData
+208 (int (*)(...))QAbstractProxyModel::mimeTypes
+216 (int (*)(...))QAbstractProxyModel::mimeData
+224 (int (*)(...))QAbstractProxyModel::canDropMimeData
+232 (int (*)(...))QAbstractProxyModel::dropMimeData
+240 (int (*)(...))QAbstractProxyModel::supportedDropActions
+248 (int (*)(...))QAbstractProxyModel::supportedDragActions
+256 (int (*)(...))QTransposeProxyModel::insertRows
+264 (int (*)(...))QTransposeProxyModel::insertColumns
+272 (int (*)(...))QTransposeProxyModel::removeRows
+280 (int (*)(...))QTransposeProxyModel::removeColumns
+288 (int (*)(...))QTransposeProxyModel::moveRows
+296 (int (*)(...))QTransposeProxyModel::moveColumns
+304 (int (*)(...))QAbstractProxyModel::fetchMore
+312 (int (*)(...))QAbstractProxyModel::canFetchMore
+320 (int (*)(...))QAbstractProxyModel::flags
+328 (int (*)(...))QTransposeProxyModel::sort
+336 (int (*)(...))QAbstractProxyModel::buddy
+344 (int (*)(...))QAbstractItemModel::match
+352 (int (*)(...))QTransposeProxyModel::span
+360 (int (*)(...))QAbstractItemModel::roleNames
+368 (int (*)(...))QAbstractProxyModel::submit
+376 (int (*)(...))QAbstractProxyModel::revert
+384 (int (*)(...))QTransposeProxyModel::setSourceModel
+392 (int (*)(...))QTransposeProxyModel::mapToSource
+400 (int (*)(...))QTransposeProxyModel::mapFromSource
+408 (int (*)(...))QAbstractProxyModel::mapSelectionToSource
+416 (int (*)(...))QAbstractProxyModel::mapSelectionFromSource
+
+Class QTransposeProxyModel
+ size=16 align=8
+ base size=16 base align=8
+QTransposeProxyModel (0x0x7f3bf5e9fe38) 0
+ vptr=((& QTransposeProxyModel::_ZTV20QTransposeProxyModel) + 16)
+ QAbstractProxyModel (0x0x7f3bf5e9fea0) 0
+ primary-for QTransposeProxyModel (0x0x7f3bf5e9fe38)
+ QAbstractItemModel (0x0x7f3bf5e9ff08) 0
+ primary-for QAbstractProxyModel (0x0x7f3bf5e9fea0)
+ QObject (0x0x7f3bf5ea0de0) 0
+ primary-for QAbstractItemModel (0x0x7f3bf5e9ff08)
+
+Class QUrlQuery
+ size=8 align=8
+ base size=8 base align=8
+QUrlQuery (0x0x7f3bf5ee2060) 0
+
+Class QWaitCondition
+ size=8 align=8
+ base size=8 base align=8
+QWaitCondition (0x0x7f3bf5fbd540) 0
+
+Class QXmlStreamStringRef
+ size=16 align=8
+ base size=16 base align=8
+QXmlStreamStringRef (0x0x7f3bf5fbd660) 0
+
+Class QXmlStreamAttribute
+ size=80 align=8
+ base size=73 base align=8
+QXmlStreamAttribute (0x0x7f3bf5b50a20) 0
+
+Class QXmlStreamAttributes
+ size=8 align=8
+ base size=8 base align=8
+QXmlStreamAttributes (0x0x7f3bf5bc6208) 0
+ QVector<QXmlStreamAttribute> (0x0x7f3bf5bc7180) 0
+
+Class QXmlStreamNamespaceDeclaration
+ size=40 align=8
+ base size=40 base align=8
+QXmlStreamNamespaceDeclaration (0x0x7f3bf5bc7480) 0
+
+Class QXmlStreamNotationDeclaration
+ size=56 align=8
+ base size=56 base align=8
+QXmlStreamNotationDeclaration (0x0x7f3bf5c51420) 0
+
+Class QXmlStreamEntityDeclaration
+ size=88 align=8
+ base size=88 base align=8
+QXmlStreamEntityDeclaration (0x0x7f3bf5cb0420) 0
+
+Vtable for QXmlStreamEntityResolver
+QXmlStreamEntityResolver::_ZTV24QXmlStreamEntityResolver: 6 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI24QXmlStreamEntityResolver)
+16 (int (*)(...))QXmlStreamEntityResolver::~QXmlStreamEntityResolver
+24 (int (*)(...))QXmlStreamEntityResolver::~QXmlStreamEntityResolver
+32 (int (*)(...))QXmlStreamEntityResolver::resolveEntity
+40 (int (*)(...))QXmlStreamEntityResolver::resolveUndeclaredEntity
+
+Class QXmlStreamEntityResolver
+ size=8 align=8
+ base size=8 base align=8
+QXmlStreamEntityResolver (0x0x7f3bf591a4e0) 0 nearly-empty
+ vptr=((& QXmlStreamEntityResolver::_ZTV24QXmlStreamEntityResolver) + 16)
+
+Class QXmlStreamReader
+ size=8 align=8
+ base size=8 base align=8
+QXmlStreamReader (0x0x7f3bf591a540) 0
+
+Class QXmlStreamWriter
+ size=8 align=8
+ base size=8 base align=8
+QXmlStreamWriter (0x0x7f3bf5977420) 0
+
+Class QNetworkRequest
+ size=8 align=8
+ base size=8 base align=8
+QNetworkRequest (0x0x7f3bf5977600) 0
+
+Class QNetworkCacheMetaData
+ size=8 align=8
+ base size=8 base align=8
+QNetworkCacheMetaData (0x0x7f3bf5a56a80) 0
+
+Class QAbstractNetworkCache::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QAbstractNetworkCache::QPrivateSignal (0x0x7f3bf5725f60) 0 empty
+
+Vtable for QAbstractNetworkCache
+QAbstractNetworkCache::_ZTV21QAbstractNetworkCache: 22 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI21QAbstractNetworkCache)
+16 (int (*)(...))QAbstractNetworkCache::metaObject
+24 (int (*)(...))QAbstractNetworkCache::qt_metacast
+32 (int (*)(...))QAbstractNetworkCache::qt_metacall
+40 0
+48 0
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))__cxa_pure_virtual
+120 (int (*)(...))__cxa_pure_virtual
+128 (int (*)(...))__cxa_pure_virtual
+136 (int (*)(...))__cxa_pure_virtual
+144 (int (*)(...))__cxa_pure_virtual
+152 (int (*)(...))__cxa_pure_virtual
+160 (int (*)(...))__cxa_pure_virtual
+168 (int (*)(...))__cxa_pure_virtual
+
+Class QAbstractNetworkCache
+ size=16 align=8
+ base size=16 base align=8
+QAbstractNetworkCache (0x0x7f3bf5728af8) 0
+ vptr=((& QAbstractNetworkCache::_ZTV21QAbstractNetworkCache) + 16)
+ QObject (0x0x7f3bf5725f00) 0
+ primary-for QAbstractNetworkCache (0x0x7f3bf5728af8)
+
+Class QAbstractSocket::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QAbstractSocket::QPrivateSignal (0x0x7f3bf57491e0) 0 empty
+
+Vtable for QAbstractSocket
+QAbstractSocket::_ZTV15QAbstractSocket: 41 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI15QAbstractSocket)
+16 (int (*)(...))QAbstractSocket::metaObject
+24 (int (*)(...))QAbstractSocket::qt_metacast
+32 (int (*)(...))QAbstractSocket::qt_metacall
+40 (int (*)(...))QAbstractSocket::~QAbstractSocket
+48 (int (*)(...))QAbstractSocket::~QAbstractSocket
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QAbstractSocket::isSequential
+120 (int (*)(...))QIODevice::open
+128 (int (*)(...))QAbstractSocket::close
+136 (int (*)(...))QIODevice::pos
+144 (int (*)(...))QIODevice::size
+152 (int (*)(...))QIODevice::seek
+160 (int (*)(...))QAbstractSocket::atEnd
+168 (int (*)(...))QIODevice::reset
+176 (int (*)(...))QAbstractSocket::bytesAvailable
+184 (int (*)(...))QAbstractSocket::bytesToWrite
+192 (int (*)(...))QAbstractSocket::canReadLine
+200 (int (*)(...))QAbstractSocket::waitForReadyRead
+208 (int (*)(...))QAbstractSocket::waitForBytesWritten
+216 (int (*)(...))QAbstractSocket::readData
+224 (int (*)(...))QAbstractSocket::readLineData
+232 (int (*)(...))QAbstractSocket::writeData
+240 (int (*)(...))QAbstractSocket::resume
+248 (int (*)(...))QAbstractSocket::connectToHost
+256 (int (*)(...))QAbstractSocket::connectToHost
+264 (int (*)(...))QAbstractSocket::disconnectFromHost
+272 (int (*)(...))QAbstractSocket::setReadBufferSize
+280 (int (*)(...))QAbstractSocket::socketDescriptor
+288 (int (*)(...))QAbstractSocket::setSocketDescriptor
+296 (int (*)(...))QAbstractSocket::setSocketOption
+304 (int (*)(...))QAbstractSocket::socketOption
+312 (int (*)(...))QAbstractSocket::waitForConnected
+320 (int (*)(...))QAbstractSocket::waitForDisconnected
+
+Class QAbstractSocket
+ size=16 align=8
+ base size=16 base align=8
+QAbstractSocket (0x0x7f3bf5728b60) 0
+ vptr=((& QAbstractSocket::_ZTV15QAbstractSocket) + 16)
+ QIODevice (0x0x7f3bf5728bc8) 0
+ primary-for QAbstractSocket (0x0x7f3bf5728b60)
+ QObject (0x0x7f3bf5749180) 0
+ primary-for QIODevice (0x0x7f3bf5728bc8)
+
+Class QAuthenticator
+ size=8 align=8
+ base size=8 base align=8
+QAuthenticator (0x0x7f3bf57c5900) 0
+
+Class QDnsDomainNameRecord
+ size=8 align=8
+ base size=8 base align=8
+QDnsDomainNameRecord (0x0x7f3bf57c59c0) 0
+
+Class QDnsHostAddressRecord
+ size=8 align=8
+ base size=8 base align=8
+QDnsHostAddressRecord (0x0x7f3bf589aa80) 0
+
+Class QDnsMailExchangeRecord
+ size=8 align=8
+ base size=8 base align=8
+QDnsMailExchangeRecord (0x0x7f3bf5555b40) 0
+
+Class QDnsServiceRecord
+ size=8 align=8
+ base size=8 base align=8
+QDnsServiceRecord (0x0x7f3bf5611b40) 0
+
+Class QDnsTextRecord
+ size=8 align=8
+ base size=8 base align=8
+QDnsTextRecord (0x0x7f3bf56cca20) 0
+
+Class QDnsLookup::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QDnsLookup::QPrivateSignal (0x0x7f3bf5389a80) 0 empty
+
+Vtable for QDnsLookup
+QDnsLookup::_ZTV10QDnsLookup: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI10QDnsLookup)
+16 (int (*)(...))QDnsLookup::metaObject
+24 (int (*)(...))QDnsLookup::qt_metacast
+32 (int (*)(...))QDnsLookup::qt_metacall
+40 (int (*)(...))QDnsLookup::~QDnsLookup
+48 (int (*)(...))QDnsLookup::~QDnsLookup
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QDnsLookup
+ size=16 align=8
+ base size=16 base align=8
+QDnsLookup (0x0x7f3bf538aa90) 0
+ vptr=((& QDnsLookup::_ZTV10QDnsLookup) + 16)
+ QObject (0x0x7f3bf5389a20) 0
+ primary-for QDnsLookup (0x0x7f3bf538aa90)
+
+Class QTcpSocket::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QTcpSocket::QPrivateSignal (0x0x7f3bf5389e40) 0 empty
+
+Vtable for QTcpSocket
+QTcpSocket::_ZTV10QTcpSocket: 41 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI10QTcpSocket)
+16 (int (*)(...))QTcpSocket::metaObject
+24 (int (*)(...))QTcpSocket::qt_metacast
+32 (int (*)(...))QTcpSocket::qt_metacall
+40 (int (*)(...))QTcpSocket::~QTcpSocket
+48 (int (*)(...))QTcpSocket::~QTcpSocket
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QAbstractSocket::isSequential
+120 (int (*)(...))QIODevice::open
+128 (int (*)(...))QAbstractSocket::close
+136 (int (*)(...))QIODevice::pos
+144 (int (*)(...))QIODevice::size
+152 (int (*)(...))QIODevice::seek
+160 (int (*)(...))QAbstractSocket::atEnd
+168 (int (*)(...))QIODevice::reset
+176 (int (*)(...))QAbstractSocket::bytesAvailable
+184 (int (*)(...))QAbstractSocket::bytesToWrite
+192 (int (*)(...))QAbstractSocket::canReadLine
+200 (int (*)(...))QAbstractSocket::waitForReadyRead
+208 (int (*)(...))QAbstractSocket::waitForBytesWritten
+216 (int (*)(...))QAbstractSocket::readData
+224 (int (*)(...))QAbstractSocket::readLineData
+232 (int (*)(...))QAbstractSocket::writeData
+240 (int (*)(...))QAbstractSocket::resume
+248 (int (*)(...))QAbstractSocket::connectToHost
+256 (int (*)(...))QAbstractSocket::connectToHost
+264 (int (*)(...))QAbstractSocket::disconnectFromHost
+272 (int (*)(...))QAbstractSocket::setReadBufferSize
+280 (int (*)(...))QAbstractSocket::socketDescriptor
+288 (int (*)(...))QAbstractSocket::setSocketDescriptor
+296 (int (*)(...))QAbstractSocket::setSocketOption
+304 (int (*)(...))QAbstractSocket::socketOption
+312 (int (*)(...))QAbstractSocket::waitForConnected
+320 (int (*)(...))QAbstractSocket::waitForDisconnected
+
+Class QTcpSocket
+ size=16 align=8
+ base size=16 base align=8
+QTcpSocket (0x0x7f3bf538aaf8) 0
+ vptr=((& QTcpSocket::_ZTV10QTcpSocket) + 16)
+ QAbstractSocket (0x0x7f3bf538ab60) 0
+ primary-for QTcpSocket (0x0x7f3bf538aaf8)
+ QIODevice (0x0x7f3bf538abc8) 0
+ primary-for QAbstractSocket (0x0x7f3bf538ab60)
+ QObject (0x0x7f3bf5389de0) 0
+ primary-for QIODevice (0x0x7f3bf538abc8)
+
+Class QSslCertificate
+ size=8 align=8
+ base size=8 base align=8
+QSslCertificate (0x0x7f3bf53bf720) 0
+
+Class QSslError
+ size=8 align=8
+ base size=8 base align=8
+QSslError (0x0x7f3bf54a7f60) 0
+
+Class QSslSocket::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QSslSocket::QPrivateSignal (0x0x7f3bf5191240) 0 empty
+
+Vtable for QSslSocket
+QSslSocket::_ZTV10QSslSocket: 41 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI10QSslSocket)
+16 (int (*)(...))QSslSocket::metaObject
+24 (int (*)(...))QSslSocket::qt_metacast
+32 (int (*)(...))QSslSocket::qt_metacall
+40 (int (*)(...))QSslSocket::~QSslSocket
+48 (int (*)(...))QSslSocket::~QSslSocket
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QAbstractSocket::isSequential
+120 (int (*)(...))QIODevice::open
+128 (int (*)(...))QSslSocket::close
+136 (int (*)(...))QIODevice::pos
+144 (int (*)(...))QIODevice::size
+152 (int (*)(...))QIODevice::seek
+160 (int (*)(...))QSslSocket::atEnd
+168 (int (*)(...))QIODevice::reset
+176 (int (*)(...))QSslSocket::bytesAvailable
+184 (int (*)(...))QSslSocket::bytesToWrite
+192 (int (*)(...))QSslSocket::canReadLine
+200 (int (*)(...))QSslSocket::waitForReadyRead
+208 (int (*)(...))QSslSocket::waitForBytesWritten
+216 (int (*)(...))QSslSocket::readData
+224 (int (*)(...))QAbstractSocket::readLineData
+232 (int (*)(...))QSslSocket::writeData
+240 (int (*)(...))QSslSocket::resume
+248 (int (*)(...))QSslSocket::connectToHost
+256 (int (*)(...))QAbstractSocket::connectToHost
+264 (int (*)(...))QSslSocket::disconnectFromHost
+272 (int (*)(...))QSslSocket::setReadBufferSize
+280 (int (*)(...))QAbstractSocket::socketDescriptor
+288 (int (*)(...))QSslSocket::setSocketDescriptor
+296 (int (*)(...))QSslSocket::setSocketOption
+304 (int (*)(...))QSslSocket::socketOption
+312 (int (*)(...))QSslSocket::waitForConnected
+320 (int (*)(...))QSslSocket::waitForDisconnected
+
+Class QSslSocket
+ size=16 align=8
+ base size=16 base align=8
+QSslSocket (0x0x7f3bf5180888) 0
+ vptr=((& QSslSocket::_ZTV10QSslSocket) + 16)
+ QTcpSocket (0x0x7f3bf51808f0) 0
+ primary-for QSslSocket (0x0x7f3bf5180888)
+ QAbstractSocket (0x0x7f3bf5180958) 0
+ primary-for QTcpSocket (0x0x7f3bf51808f0)
+ QIODevice (0x0x7f3bf51809c0) 0
+ primary-for QAbstractSocket (0x0x7f3bf5180958)
+ QObject (0x0x7f3bf51911e0) 0
+ primary-for QIODevice (0x0x7f3bf51809c0)
+
+Class QDtlsClientVerifier::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QDtlsClientVerifier::QPrivateSignal (0x0x7f3bf5191480) 0 empty
+
+Class QDtlsClientVerifier::GeneratorParameters
+ size=16 align=8
+ base size=16 base align=8
+QDtlsClientVerifier::GeneratorParameters (0x0x7f3bf51914e0) 0
+
+Vtable for QDtlsClientVerifier
+QDtlsClientVerifier::_ZTV19QDtlsClientVerifier: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI19QDtlsClientVerifier)
+16 (int (*)(...))QDtlsClientVerifier::metaObject
+24 (int (*)(...))QDtlsClientVerifier::qt_metacast
+32 (int (*)(...))QDtlsClientVerifier::qt_metacall
+40 (int (*)(...))QDtlsClientVerifier::~QDtlsClientVerifier
+48 (int (*)(...))QDtlsClientVerifier::~QDtlsClientVerifier
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QDtlsClientVerifier
+ size=16 align=8
+ base size=16 base align=8
+QDtlsClientVerifier (0x0x7f3bf5180a28) 0
+ vptr=((& QDtlsClientVerifier::_ZTV19QDtlsClientVerifier) + 16)
+ QObject (0x0x7f3bf5191420) 0
+ primary-for QDtlsClientVerifier (0x0x7f3bf5180a28)
+
+Class QDtls::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QDtls::QPrivateSignal (0x0x7f3bf5191720) 0 empty
+
+Vtable for QDtls
+QDtls::_ZTV5QDtls: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI5QDtls)
+16 (int (*)(...))QDtls::metaObject
+24 (int (*)(...))QDtls::qt_metacast
+32 (int (*)(...))QDtls::qt_metacall
+40 (int (*)(...))QDtls::~QDtls
+48 (int (*)(...))QDtls::~QDtls
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QDtls
+ size=16 align=8
+ base size=16 base align=8
+QDtls (0x0x7f3bf5180a90) 0
+ vptr=((& QDtls::_ZTV5QDtls) + 16)
+ QObject (0x0x7f3bf51916c0) 0
+ primary-for QDtls (0x0x7f3bf5180a90)
+
+Class QIPv6Address
+ size=16 align=1
+ base size=16 base align=1
+QIPv6Address (0x0x7f3bf5191960) 0
+
+Class QHostAddress
+ size=8 align=8
+ base size=8 base align=8
+QHostAddress (0x0x7f3bf5191a80) 0
+
+Class QHostInfo
+ size=8 align=8
+ base size=8 base align=8
+QHostInfo (0x0x7f3bf5293840) 0
+
+Class QHstsPolicy
+ size=8 align=8
+ base size=8 base align=8
+QHstsPolicy (0x0x7f3bf4f55f00) 0
+
+Class QHttp2Configuration
+ size=8 align=8
+ base size=8 base align=8
+QHttp2Configuration (0x0x7f3bf5058660) 0
+
+Class QHttpPart
+ size=8 align=8
+ base size=8 base align=8
+QHttpPart (0x0x7f3bf50b0ba0) 0
+
+Class QHttpMultiPart::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QHttpMultiPart::QPrivateSignal (0x0x7f3bf4d79840) 0 empty
+
+Vtable for QHttpMultiPart
+QHttpMultiPart::_ZTV14QHttpMultiPart: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI14QHttpMultiPart)
+16 (int (*)(...))QHttpMultiPart::metaObject
+24 (int (*)(...))QHttpMultiPart::qt_metacast
+32 (int (*)(...))QHttpMultiPart::qt_metacall
+40 (int (*)(...))QHttpMultiPart::~QHttpMultiPart
+48 (int (*)(...))QHttpMultiPart::~QHttpMultiPart
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QHttpMultiPart
+ size=16 align=8
+ base size=16 base align=8
+QHttpMultiPart (0x0x7f3bf4d82208) 0
+ vptr=((& QHttpMultiPart::_ZTV14QHttpMultiPart) + 16)
+ QObject (0x0x7f3bf4d797e0) 0
+ primary-for QHttpMultiPart (0x0x7f3bf4d82208)
+
+Class QLocalServer::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QLocalServer::QPrivateSignal (0x0x7f3bf4d79a80) 0 empty
+
+Vtable for QLocalServer
+QLocalServer::_ZTV12QLocalServer: 17 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI12QLocalServer)
+16 (int (*)(...))QLocalServer::metaObject
+24 (int (*)(...))QLocalServer::qt_metacast
+32 (int (*)(...))QLocalServer::qt_metacall
+40 (int (*)(...))QLocalServer::~QLocalServer
+48 (int (*)(...))QLocalServer::~QLocalServer
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QLocalServer::hasPendingConnections
+120 (int (*)(...))QLocalServer::nextPendingConnection
+128 (int (*)(...))QLocalServer::incomingConnection
+
+Class QLocalServer
+ size=16 align=8
+ base size=16 base align=8
+QLocalServer (0x0x7f3bf4d82270) 0
+ vptr=((& QLocalServer::_ZTV12QLocalServer) + 16)
+ QObject (0x0x7f3bf4d79a20) 0
+ primary-for QLocalServer (0x0x7f3bf4d82270)
+
+Class QLocalSocket::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QLocalSocket::QPrivateSignal (0x0x7f3bf4dc7540) 0 empty
+
+Vtable for QLocalSocket
+QLocalSocket::_ZTV12QLocalSocket: 30 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI12QLocalSocket)
+16 (int (*)(...))QLocalSocket::metaObject
+24 (int (*)(...))QLocalSocket::qt_metacast
+32 (int (*)(...))QLocalSocket::qt_metacall
+40 (int (*)(...))QLocalSocket::~QLocalSocket
+48 (int (*)(...))QLocalSocket::~QLocalSocket
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QLocalSocket::isSequential
+120 (int (*)(...))QLocalSocket::open
+128 (int (*)(...))QLocalSocket::close
+136 (int (*)(...))QIODevice::pos
+144 (int (*)(...))QIODevice::size
+152 (int (*)(...))QIODevice::seek
+160 (int (*)(...))QIODevice::atEnd
+168 (int (*)(...))QIODevice::reset
+176 (int (*)(...))QLocalSocket::bytesAvailable
+184 (int (*)(...))QLocalSocket::bytesToWrite
+192 (int (*)(...))QLocalSocket::canReadLine
+200 (int (*)(...))QLocalSocket::waitForReadyRead
+208 (int (*)(...))QLocalSocket::waitForBytesWritten
+216 (int (*)(...))QLocalSocket::readData
+224 (int (*)(...))QIODevice::readLineData
+232 (int (*)(...))QLocalSocket::writeData
+
+Class QLocalSocket
+ size=16 align=8
+ base size=16 base align=8
+QLocalSocket (0x0x7f3bf4d82410) 0
+ vptr=((& QLocalSocket::_ZTV12QLocalSocket) + 16)
+ QIODevice (0x0x7f3bf4d82478) 0
+ primary-for QLocalSocket (0x0x7f3bf4d82410)
+ QObject (0x0x7f3bf4dc74e0) 0
+ primary-for QIODevice (0x0x7f3bf4d82478)
+
+Class QSslConfiguration
+ size=8 align=8
+ base size=8 base align=8
+QSslConfiguration (0x0x7f3bf4dc7720) 0
+
+Class QSslPreSharedKeyAuthenticator
+ size=8 align=8
+ base size=8 base align=8
+QSslPreSharedKeyAuthenticator (0x0x7f3bf4ed2c00) 0
+
+Class QNetworkAccessManager::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QNetworkAccessManager::QPrivateSignal (0x0x7f3bf4bbf2a0) 0 empty
+
+Vtable for QNetworkAccessManager
+QNetworkAccessManager::_ZTV21QNetworkAccessManager: 15 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI21QNetworkAccessManager)
+16 (int (*)(...))QNetworkAccessManager::metaObject
+24 (int (*)(...))QNetworkAccessManager::qt_metacast
+32 (int (*)(...))QNetworkAccessManager::qt_metacall
+40 (int (*)(...))QNetworkAccessManager::~QNetworkAccessManager
+48 (int (*)(...))QNetworkAccessManager::~QNetworkAccessManager
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QNetworkAccessManager::createRequest
+
+Class QNetworkAccessManager
+ size=16 align=8
+ base size=16 base align=8
+QNetworkAccessManager (0x0x7f3bf4bab958) 0
+ vptr=((& QNetworkAccessManager::_ZTV21QNetworkAccessManager) + 16)
+ QObject (0x0x7f3bf4bbf240) 0
+ primary-for QNetworkAccessManager (0x0x7f3bf4bab958)
+
+Class QNetworkConfiguration
+ size=8 align=8
+ base size=8 base align=8
+QNetworkConfiguration (0x0x7f3bf4bbf540) 0
+
+Class QNetworkConfigurationManager::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QNetworkConfigurationManager::QPrivateSignal (0x0x7f3bf4ca6900) 0 empty
+
+Vtable for QNetworkConfigurationManager
+QNetworkConfigurationManager::_ZTV28QNetworkConfigurationManager: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI28QNetworkConfigurationManager)
+16 (int (*)(...))QNetworkConfigurationManager::metaObject
+24 (int (*)(...))QNetworkConfigurationManager::qt_metacast
+32 (int (*)(...))QNetworkConfigurationManager::qt_metacall
+40 (int (*)(...))QNetworkConfigurationManager::~QNetworkConfigurationManager
+48 (int (*)(...))QNetworkConfigurationManager::~QNetworkConfigurationManager
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QNetworkConfigurationManager
+ size=16 align=8
+ base size=16 base align=8
+QNetworkConfigurationManager (0x0x7f3bf4c96c30) 0
+ vptr=((& QNetworkConfigurationManager::_ZTV28QNetworkConfigurationManager) + 16)
+ QObject (0x0x7f3bf4ca68a0) 0
+ primary-for QNetworkConfigurationManager (0x0x7f3bf4c96c30)
+
+Class QNetworkCookie
+ size=8 align=8
+ base size=8 base align=8
+QNetworkCookie (0x0x7f3bf4cf9480) 0
+
+Class QNetworkCookieJar::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QNetworkCookieJar::QPrivateSignal (0x0x7f3bf49c8a80) 0 empty
+
+Vtable for QNetworkCookieJar
+QNetworkCookieJar::_ZTV17QNetworkCookieJar: 20 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI17QNetworkCookieJar)
+16 (int (*)(...))QNetworkCookieJar::metaObject
+24 (int (*)(...))QNetworkCookieJar::qt_metacast
+32 (int (*)(...))QNetworkCookieJar::qt_metacall
+40 (int (*)(...))QNetworkCookieJar::~QNetworkCookieJar
+48 (int (*)(...))QNetworkCookieJar::~QNetworkCookieJar
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QNetworkCookieJar::cookiesForUrl
+120 (int (*)(...))QNetworkCookieJar::setCookiesFromUrl
+128 (int (*)(...))QNetworkCookieJar::insertCookie
+136 (int (*)(...))QNetworkCookieJar::updateCookie
+144 (int (*)(...))QNetworkCookieJar::deleteCookie
+152 (int (*)(...))QNetworkCookieJar::validateCookie
+
+Class QNetworkCookieJar
+ size=16 align=8
+ base size=16 base align=8
+QNetworkCookieJar (0x0x7f3bf49bbea0) 0
+ vptr=((& QNetworkCookieJar::_ZTV17QNetworkCookieJar) + 16)
+ QObject (0x0x7f3bf49c8a20) 0
+ primary-for QNetworkCookieJar (0x0x7f3bf49bbea0)
+
+Class QNetworkDatagram
+ size=8 align=8
+ base size=8 base align=8
+QNetworkDatagram (0x0x7f3bf49c8c60) 0
+
+Class QNetworkDiskCache::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QNetworkDiskCache::QPrivateSignal (0x0x7f3bf4aa27e0) 0 empty
+
+Vtable for QNetworkDiskCache
+QNetworkDiskCache::_ZTV17QNetworkDiskCache: 23 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI17QNetworkDiskCache)
+16 (int (*)(...))QNetworkDiskCache::metaObject
+24 (int (*)(...))QNetworkDiskCache::qt_metacast
+32 (int (*)(...))QNetworkDiskCache::qt_metacall
+40 (int (*)(...))QNetworkDiskCache::~QNetworkDiskCache
+48 (int (*)(...))QNetworkDiskCache::~QNetworkDiskCache
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QNetworkDiskCache::metaData
+120 (int (*)(...))QNetworkDiskCache::updateMetaData
+128 (int (*)(...))QNetworkDiskCache::data
+136 (int (*)(...))QNetworkDiskCache::remove
+144 (int (*)(...))QNetworkDiskCache::cacheSize
+152 (int (*)(...))QNetworkDiskCache::prepare
+160 (int (*)(...))QNetworkDiskCache::insert
+168 (int (*)(...))QNetworkDiskCache::clear
+176 (int (*)(...))QNetworkDiskCache::expire
+
+Class QNetworkDiskCache
+ size=16 align=8
+ base size=16 base align=8
+QNetworkDiskCache (0x0x7f3bf4a95d68) 0
+ vptr=((& QNetworkDiskCache::_ZTV17QNetworkDiskCache) + 16)
+ QAbstractNetworkCache (0x0x7f3bf4a95dd0) 0
+ primary-for QNetworkDiskCache (0x0x7f3bf4a95d68)
+ QObject (0x0x7f3bf4aa2780) 0
+ primary-for QAbstractNetworkCache (0x0x7f3bf4a95dd0)
+
+Class QNetworkAddressEntry
+ size=8 align=8
+ base size=8 base align=8
+QNetworkAddressEntry (0x0x7f3bf4aa29c0) 0
+
+Class QNetworkInterface
+ size=8 align=8
+ base size=8 base align=8
+QNetworkInterface (0x0x7f3bf477b960) 0
+
+Class QNetworkProxyQuery
+ size=8 align=8
+ base size=8 base align=8
+QNetworkProxyQuery (0x0x7f3bf4878480) 0
+
+Class QNetworkProxy
+ size=8 align=8
+ base size=8 base align=8
+QNetworkProxy (0x0x7f3bf4555780) 0
+
+Vtable for QNetworkProxyFactory
+QNetworkProxyFactory::_ZTV20QNetworkProxyFactory: 5 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI20QNetworkProxyFactory)
+16 0
+24 0
+32 (int (*)(...))__cxa_pure_virtual
+
+Class QNetworkProxyFactory
+ size=8 align=8
+ base size=8 base align=8
+QNetworkProxyFactory (0x0x7f3bf4660000) 0 nearly-empty
+ vptr=((& QNetworkProxyFactory::_ZTV20QNetworkProxyFactory) + 16)
+
+Class QNetworkReply::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QNetworkReply::QPrivateSignal (0x0x7f3bf46602a0) 0 empty
+
+Vtable for QNetworkReply
+QNetworkReply::_ZTV13QNetworkReply: 36 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI13QNetworkReply)
+16 (int (*)(...))QNetworkReply::metaObject
+24 (int (*)(...))QNetworkReply::qt_metacast
+32 (int (*)(...))QNetworkReply::qt_metacall
+40 0
+48 0
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QNetworkReply::isSequential
+120 (int (*)(...))QIODevice::open
+128 (int (*)(...))QNetworkReply::close
+136 (int (*)(...))QIODevice::pos
+144 (int (*)(...))QIODevice::size
+152 (int (*)(...))QIODevice::seek
+160 (int (*)(...))QIODevice::atEnd
+168 (int (*)(...))QIODevice::reset
+176 (int (*)(...))QIODevice::bytesAvailable
+184 (int (*)(...))QIODevice::bytesToWrite
+192 (int (*)(...))QIODevice::canReadLine
+200 (int (*)(...))QIODevice::waitForReadyRead
+208 (int (*)(...))QIODevice::waitForBytesWritten
+216 (int (*)(...))__cxa_pure_virtual
+224 (int (*)(...))QIODevice::readLineData
+232 (int (*)(...))QNetworkReply::writeData
+240 (int (*)(...))QNetworkReply::setReadBufferSize
+248 (int (*)(...))__cxa_pure_virtual
+256 (int (*)(...))QNetworkReply::ignoreSslErrors
+264 (int (*)(...))QNetworkReply::sslConfigurationImplementation
+272 (int (*)(...))QNetworkReply::setSslConfigurationImplementation
+280 (int (*)(...))QNetworkReply::ignoreSslErrorsImplementation
+
+Class QNetworkReply
+ size=16 align=8
+ base size=16 base align=8
+QNetworkReply (0x0x7f3bf4630208) 0
+ vptr=((& QNetworkReply::_ZTV13QNetworkReply) + 16)
+ QIODevice (0x0x7f3bf4630270) 0
+ primary-for QNetworkReply (0x0x7f3bf4630208)
+ QObject (0x0x7f3bf4660240) 0
+ primary-for QIODevice (0x0x7f3bf4630270)
+
+Class QNetworkSession::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QNetworkSession::QPrivateSignal (0x0x7f3bf4660780) 0 empty
+
+Vtable for QNetworkSession
+QNetworkSession::_ZTV15QNetworkSession: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI15QNetworkSession)
+16 (int (*)(...))QNetworkSession::metaObject
+24 (int (*)(...))QNetworkSession::qt_metacast
+32 (int (*)(...))QNetworkSession::qt_metacall
+40 (int (*)(...))QNetworkSession::~QNetworkSession
+48 (int (*)(...))QNetworkSession::~QNetworkSession
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QNetworkSession::connectNotify
+104 (int (*)(...))QNetworkSession::disconnectNotify
+
+Class QNetworkSession
+ size=24 align=8
+ base size=24 base align=8
+QNetworkSession (0x0x7f3bf46302d8) 0
+ vptr=((& QNetworkSession::_ZTV15QNetworkSession) + 16)
+ QObject (0x0x7f3bf4660720) 0
+ primary-for QNetworkSession (0x0x7f3bf46302d8)
+
+Class QOcspResponse
+ size=8 align=8
+ base size=8 base align=8
+QOcspResponse (0x0x7f3bf46c9000) 0
+
+Class QTcpServer::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QTcpServer::QPrivateSignal (0x0x7f3bf4319840) 0 empty
+
+Vtable for QTcpServer
+QTcpServer::_ZTV10QTcpServer: 17 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI10QTcpServer)
+16 (int (*)(...))QTcpServer::metaObject
+24 (int (*)(...))QTcpServer::qt_metacast
+32 (int (*)(...))QTcpServer::qt_metacall
+40 (int (*)(...))QTcpServer::~QTcpServer
+48 (int (*)(...))QTcpServer::~QTcpServer
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QTcpServer::hasPendingConnections
+120 (int (*)(...))QTcpServer::nextPendingConnection
+128 (int (*)(...))QTcpServer::incomingConnection
+
+Class QTcpServer
+ size=16 align=8
+ base size=16 base align=8
+QTcpServer (0x0x7f3bf430cb60) 0
+ vptr=((& QTcpServer::_ZTV10QTcpServer) + 16)
+ QObject (0x0x7f3bf43197e0) 0
+ primary-for QTcpServer (0x0x7f3bf430cb60)
+
+Class QSslCertificateExtension
+ size=8 align=8
+ base size=8 base align=8
+QSslCertificateExtension (0x0x7f3bf4319a20) 0
+
+Class QSslCipher
+ size=8 align=8
+ base size=8 base align=8
+QSslCipher (0x0x7f3bf43ea9c0) 0
+
+Class QSslDiffieHellmanParameters
+ size=8 align=8
+ base size=8 base align=8
+QSslDiffieHellmanParameters (0x0x7f3bf44afa80) 0
+
+Class QSslEllipticCurve
+ size=4 align=4
+ base size=4 base align=4
+QSslEllipticCurve (0x0x7f3bf41737e0) 0
+
+Class QSslKey
+ size=8 align=8
+ base size=8 base align=8
+QSslKey (0x0x7f3bf41da180) 0
+
+Class QUdpSocket::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QUdpSocket::QPrivateSignal (0x0x7f3bf42a9060) 0 empty
+
+Vtable for QUdpSocket
+QUdpSocket::_ZTV10QUdpSocket: 41 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI10QUdpSocket)
+16 (int (*)(...))QUdpSocket::metaObject
+24 (int (*)(...))QUdpSocket::qt_metacast
+32 (int (*)(...))QUdpSocket::qt_metacall
+40 (int (*)(...))QUdpSocket::~QUdpSocket
+48 (int (*)(...))QUdpSocket::~QUdpSocket
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QAbstractSocket::isSequential
+120 (int (*)(...))QIODevice::open
+128 (int (*)(...))QAbstractSocket::close
+136 (int (*)(...))QIODevice::pos
+144 (int (*)(...))QIODevice::size
+152 (int (*)(...))QIODevice::seek
+160 (int (*)(...))QAbstractSocket::atEnd
+168 (int (*)(...))QIODevice::reset
+176 (int (*)(...))QAbstractSocket::bytesAvailable
+184 (int (*)(...))QAbstractSocket::bytesToWrite
+192 (int (*)(...))QAbstractSocket::canReadLine
+200 (int (*)(...))QAbstractSocket::waitForReadyRead
+208 (int (*)(...))QAbstractSocket::waitForBytesWritten
+216 (int (*)(...))QAbstractSocket::readData
+224 (int (*)(...))QAbstractSocket::readLineData
+232 (int (*)(...))QAbstractSocket::writeData
+240 (int (*)(...))QAbstractSocket::resume
+248 (int (*)(...))QAbstractSocket::connectToHost
+256 (int (*)(...))QAbstractSocket::connectToHost
+264 (int (*)(...))QAbstractSocket::disconnectFromHost
+272 (int (*)(...))QAbstractSocket::setReadBufferSize
+280 (int (*)(...))QAbstractSocket::socketDescriptor
+288 (int (*)(...))QAbstractSocket::setSocketDescriptor
+296 (int (*)(...))QAbstractSocket::setSocketOption
+304 (int (*)(...))QAbstractSocket::socketOption
+312 (int (*)(...))QAbstractSocket::waitForConnected
+320 (int (*)(...))QAbstractSocket::waitForDisconnected
+
+Class QUdpSocket
+ size=16 align=8
+ base size=16 base align=8
+QUdpSocket (0x0x7f3bf42a4138) 0
+ vptr=((& QUdpSocket::_ZTV10QUdpSocket) + 16)
+ QAbstractSocket (0x0x7f3bf42a41a0) 0
+ primary-for QUdpSocket (0x0x7f3bf42a4138)
+ QIODevice (0x0x7f3bf42a4208) 0
+ primary-for QAbstractSocket (0x0x7f3bf42a41a0)
+ QObject (0x0x7f3bf42a9000) 0
+ primary-for QIODevice (0x0x7f3bf42a4208)
+
+Class QRemoteObjectSourceLocationInfo
+ size=16 align=8
+ base size=16 base align=8
+QRemoteObjectSourceLocationInfo (0x0x7f3bf42a92a0) 0
+
+Class QAbstractItemModelReplica::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QAbstractItemModelReplica::QPrivateSignal (0x0x7f3bf3fa9180) 0 empty
+
+Vtable for QAbstractItemModelReplica
+QAbstractItemModelReplica::_ZTV25QAbstractItemModelReplica: 48 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI25QAbstractItemModelReplica)
+16 (int (*)(...))QAbstractItemModelReplica::metaObject
+24 (int (*)(...))QAbstractItemModelReplica::qt_metacast
+32 (int (*)(...))QAbstractItemModelReplica::qt_metacall
+40 (int (*)(...))QAbstractItemModelReplica::~QAbstractItemModelReplica
+48 (int (*)(...))QAbstractItemModelReplica::~QAbstractItemModelReplica
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QAbstractItemModelReplica::index
+120 (int (*)(...))QAbstractItemModelReplica::parent
+128 (int (*)(...))QAbstractItemModel::sibling
+136 (int (*)(...))QAbstractItemModelReplica::rowCount
+144 (int (*)(...))QAbstractItemModelReplica::columnCount
+152 (int (*)(...))QAbstractItemModelReplica::hasChildren
+160 (int (*)(...))QAbstractItemModelReplica::data
+168 (int (*)(...))QAbstractItemModelReplica::setData
+176 (int (*)(...))QAbstractItemModelReplica::headerData
+184 (int (*)(...))QAbstractItemModel::setHeaderData
+192 (int (*)(...))QAbstractItemModel::itemData
+200 (int (*)(...))QAbstractItemModel::setItemData
+208 (int (*)(...))QAbstractItemModel::mimeTypes
+216 (int (*)(...))QAbstractItemModel::mimeData
+224 (int (*)(...))QAbstractItemModel::canDropMimeData
+232 (int (*)(...))QAbstractItemModel::dropMimeData
+240 (int (*)(...))QAbstractItemModel::supportedDropActions
+248 (int (*)(...))QAbstractItemModel::supportedDragActions
+256 (int (*)(...))QAbstractItemModel::insertRows
+264 (int (*)(...))QAbstractItemModel::insertColumns
+272 (int (*)(...))QAbstractItemModel::removeRows
+280 (int (*)(...))QAbstractItemModel::removeColumns
+288 (int (*)(...))QAbstractItemModel::moveRows
+296 (int (*)(...))QAbstractItemModel::moveColumns
+304 (int (*)(...))QAbstractItemModel::fetchMore
+312 (int (*)(...))QAbstractItemModel::canFetchMore
+320 (int (*)(...))QAbstractItemModelReplica::flags
+328 (int (*)(...))QAbstractItemModel::sort
+336 (int (*)(...))QAbstractItemModel::buddy
+344 (int (*)(...))QAbstractItemModel::match
+352 (int (*)(...))QAbstractItemModel::span
+360 (int (*)(...))QAbstractItemModelReplica::roleNames
+368 (int (*)(...))QAbstractItemModel::submit
+376 (int (*)(...))QAbstractItemModel::revert
+
+Class QAbstractItemModelReplica
+ size=24 align=8
+ base size=24 base align=8
+QAbstractItemModelReplica (0x0x7f3bf42e5958) 0
+ vptr=((& QAbstractItemModelReplica::_ZTV25QAbstractItemModelReplica) + 16)
+ QAbstractItemModel (0x0x7f3bf42e59c0) 0
+ primary-for QAbstractItemModelReplica (0x0x7f3bf42e5958)
+ QObject (0x0x7f3bf3fa9120) 0
+ primary-for QAbstractItemModel (0x0x7f3bf42e59c0)
+
+Class ModelIndex
+ size=8 align=4
+ base size=8 base align=4
+ModelIndex (0x0x7f3bf3fa9300) 0
+
+Class IndexValuePair
+ size=40 align=8
+ base size=40 base align=8
+IndexValuePair (0x0x7f3bf3fa9ba0) 0
+
+Class DataEntries
+ size=8 align=8
+ base size=8 base align=8
+DataEntries (0x0x7f3bf4070420) 0
+
+Class MetaAndDataEntries
+ size=24 align=8
+ base size=24 base align=8
+MetaAndDataEntries (0x0x7f3bf4073dd0) 0
+ DataEntries (0x0x7f3bf4070e40) 0
+
+Class QRemoteObjectReplica::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QRemoteObjectReplica::QPrivateSignal (0x0x7f3bf3da21e0) 0 empty
+
+Vtable for QRemoteObjectReplica
+QRemoteObjectReplica::_ZTV20QRemoteObjectReplica: 16 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI20QRemoteObjectReplica)
+16 (int (*)(...))QRemoteObjectReplica::metaObject
+24 (int (*)(...))QRemoteObjectReplica::qt_metacast
+32 (int (*)(...))QRemoteObjectReplica::qt_metacall
+40 (int (*)(...))QRemoteObjectReplica::~QRemoteObjectReplica
+48 (int (*)(...))QRemoteObjectReplica::~QRemoteObjectReplica
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QRemoteObjectReplica::setNode
+120 (int (*)(...))QRemoteObjectReplica::initialize
+
+Class QRemoteObjectReplica
+ size=32 align=8
+ base size=32 base align=8
+QRemoteObjectReplica (0x0x7f3bf3d5f068) 0
+ vptr=((& QRemoteObjectReplica::_ZTV20QRemoteObjectReplica) + 16)
+ QObject (0x0x7f3bf3da2180) 0
+ primary-for QRemoteObjectReplica (0x0x7f3bf3d5f068)
+
+Vtable for QRemoteObjectDynamicReplica
+QRemoteObjectDynamicReplica::_ZTV27QRemoteObjectDynamicReplica: 16 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI27QRemoteObjectDynamicReplica)
+16 (int (*)(...))QRemoteObjectDynamicReplica::metaObject
+24 (int (*)(...))QRemoteObjectDynamicReplica::qt_metacast
+32 (int (*)(...))QRemoteObjectDynamicReplica::qt_metacall
+40 (int (*)(...))QRemoteObjectDynamicReplica::~QRemoteObjectDynamicReplica
+48 (int (*)(...))QRemoteObjectDynamicReplica::~QRemoteObjectDynamicReplica
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QRemoteObjectReplica::setNode
+120 (int (*)(...))QRemoteObjectReplica::initialize
+
+Class QRemoteObjectDynamicReplica
+ size=32 align=8
+ base size=32 base align=8
+QRemoteObjectDynamicReplica (0x0x7f3bf3d5f0d0) 0
+ vptr=((& QRemoteObjectDynamicReplica::_ZTV27QRemoteObjectDynamicReplica) + 16)
+ QRemoteObjectReplica (0x0x7f3bf3d5f138) 0
+ primary-for QRemoteObjectDynamicReplica (0x0x7f3bf3d5f0d0)
+ QObject (0x0x7f3bf3da2420) 0
+ primary-for QRemoteObjectReplica (0x0x7f3bf3d5f138)
+
+Class QRemoteObjectRegistry::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QRemoteObjectRegistry::QPrivateSignal (0x0x7f3bf3da24e0) 0 empty
+
+Vtable for QRemoteObjectRegistry
+QRemoteObjectRegistry::_ZTV21QRemoteObjectRegistry: 16 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI21QRemoteObjectRegistry)
+16 (int (*)(...))QRemoteObjectRegistry::metaObject
+24 (int (*)(...))QRemoteObjectRegistry::qt_metacast
+32 (int (*)(...))QRemoteObjectRegistry::qt_metacall
+40 (int (*)(...))QRemoteObjectRegistry::~QRemoteObjectRegistry
+48 (int (*)(...))QRemoteObjectRegistry::~QRemoteObjectRegistry
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QRemoteObjectReplica::setNode
+120 (int (*)(...))QRemoteObjectRegistry::initialize
+
+Class QRemoteObjectRegistry
+ size=32 align=8
+ base size=32 base align=8
+QRemoteObjectRegistry (0x0x7f3bf3d5f1a0) 0
+ vptr=((& QRemoteObjectRegistry::_ZTV21QRemoteObjectRegistry) + 16)
+ QRemoteObjectReplica (0x0x7f3bf3d5f208) 0
+ primary-for QRemoteObjectRegistry (0x0x7f3bf3d5f1a0)
+ QObject (0x0x7f3bf3da2480) 0
+ primary-for QRemoteObjectReplica (0x0x7f3bf3d5f208)
+
+Class QRemoteObjectAbstractPersistedStore::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QRemoteObjectAbstractPersistedStore::QPrivateSignal (0x0x7f3bf3da2720) 0 empty
+
+Vtable for QRemoteObjectAbstractPersistedStore
+QRemoteObjectAbstractPersistedStore::_ZTV35QRemoteObjectAbstractPersistedStore: 16 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI35QRemoteObjectAbstractPersistedStore)
+16 (int (*)(...))QRemoteObjectAbstractPersistedStore::metaObject
+24 (int (*)(...))QRemoteObjectAbstractPersistedStore::qt_metacast
+32 (int (*)(...))QRemoteObjectAbstractPersistedStore::qt_metacall
+40 0
+48 0
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))__cxa_pure_virtual
+120 (int (*)(...))__cxa_pure_virtual
+
+Class QRemoteObjectAbstractPersistedStore
+ size=16 align=8
+ base size=16 base align=8
+QRemoteObjectAbstractPersistedStore (0x0x7f3bf3d5f270) 0
+ vptr=((& QRemoteObjectAbstractPersistedStore::_ZTV35QRemoteObjectAbstractPersistedStore) + 16)
+ QObject (0x0x7f3bf3da26c0) 0
+ primary-for QRemoteObjectAbstractPersistedStore (0x0x7f3bf3d5f270)
+
+Class QRemoteObjectNode::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QRemoteObjectNode::QPrivateSignal (0x0x7f3bf3da29c0) 0 empty
+
+Vtable for QRemoteObjectNode
+QRemoteObjectNode::_ZTV17QRemoteObjectNode: 16 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI17QRemoteObjectNode)
+16 (int (*)(...))QRemoteObjectNode::metaObject
+24 (int (*)(...))QRemoteObjectNode::qt_metacast
+32 (int (*)(...))QRemoteObjectNode::qt_metacall
+40 (int (*)(...))QRemoteObjectNode::~QRemoteObjectNode
+48 (int (*)(...))QRemoteObjectNode::~QRemoteObjectNode
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QRemoteObjectNode::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QRemoteObjectNode::setName
+120 (int (*)(...))QRemoteObjectNode::setRegistryUrl
+
+Class QRemoteObjectNode
+ size=16 align=8
+ base size=16 base align=8
+QRemoteObjectNode (0x0x7f3bf3d5f2d8) 0
+ vptr=((& QRemoteObjectNode::_ZTV17QRemoteObjectNode) + 16)
+ QObject (0x0x7f3bf3da2960) 0
+ primary-for QRemoteObjectNode (0x0x7f3bf3d5f2d8)
+
+Class QRemoteObjectHostBase::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QRemoteObjectHostBase::QPrivateSignal (0x0x7f3bf3da2d80) 0 empty
+
+Vtable for QRemoteObjectHostBase
+QRemoteObjectHostBase::_ZTV21QRemoteObjectHostBase: 18 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI21QRemoteObjectHostBase)
+16 (int (*)(...))QRemoteObjectHostBase::metaObject
+24 (int (*)(...))QRemoteObjectHostBase::qt_metacast
+32 (int (*)(...))QRemoteObjectHostBase::qt_metacall
+40 (int (*)(...))QRemoteObjectHostBase::~QRemoteObjectHostBase
+48 (int (*)(...))QRemoteObjectHostBase::~QRemoteObjectHostBase
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QRemoteObjectNode::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QRemoteObjectHostBase::setName
+120 (int (*)(...))QRemoteObjectNode::setRegistryUrl
+128 (int (*)(...))QRemoteObjectHostBase::hostUrl
+136 (int (*)(...))QRemoteObjectHostBase::setHostUrl
+
+Class QRemoteObjectHostBase
+ size=16 align=8
+ base size=16 base align=8
+QRemoteObjectHostBase (0x0x7f3bf3d5f340) 0
+ vptr=((& QRemoteObjectHostBase::_ZTV21QRemoteObjectHostBase) + 16)
+ QRemoteObjectNode (0x0x7f3bf3d5f3a8) 0
+ primary-for QRemoteObjectHostBase (0x0x7f3bf3d5f340)
+ QObject (0x0x7f3bf3da2d20) 0
+ primary-for QRemoteObjectNode (0x0x7f3bf3d5f3a8)
+
+Class QRemoteObjectHost::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QRemoteObjectHost::QPrivateSignal (0x0x7f3bf3eba000) 0 empty
+
+Vtable for QRemoteObjectHost
+QRemoteObjectHost::_ZTV17QRemoteObjectHost: 18 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI17QRemoteObjectHost)
+16 (int (*)(...))QRemoteObjectHost::metaObject
+24 (int (*)(...))QRemoteObjectHost::qt_metacast
+32 (int (*)(...))QRemoteObjectHost::qt_metacall
+40 (int (*)(...))QRemoteObjectHost::~QRemoteObjectHost
+48 (int (*)(...))QRemoteObjectHost::~QRemoteObjectHost
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QRemoteObjectNode::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QRemoteObjectHostBase::setName
+120 (int (*)(...))QRemoteObjectNode::setRegistryUrl
+128 (int (*)(...))QRemoteObjectHost::hostUrl
+136 (int (*)(...))QRemoteObjectHost::setHostUrl
+
+Class QRemoteObjectHost
+ size=16 align=8
+ base size=16 base align=8
+QRemoteObjectHost (0x0x7f3bf3e95c30) 0
+ vptr=((& QRemoteObjectHost::_ZTV17QRemoteObjectHost) + 16)
+ QRemoteObjectHostBase (0x0x7f3bf3e95c98) 0
+ primary-for QRemoteObjectHost (0x0x7f3bf3e95c30)
+ QRemoteObjectNode (0x0x7f3bf3e95d00) 0
+ primary-for QRemoteObjectHostBase (0x0x7f3bf3e95c98)
+ QObject (0x0x7f3bf3e91f60) 0
+ primary-for QRemoteObjectNode (0x0x7f3bf3e95d00)
+
+Class QRemoteObjectRegistryHost::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QRemoteObjectRegistryHost::QPrivateSignal (0x0x7f3bf3eba240) 0 empty
+
+Vtable for QRemoteObjectRegistryHost
+QRemoteObjectRegistryHost::_ZTV25QRemoteObjectRegistryHost: 18 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI25QRemoteObjectRegistryHost)
+16 (int (*)(...))QRemoteObjectRegistryHost::metaObject
+24 (int (*)(...))QRemoteObjectRegistryHost::qt_metacast
+32 (int (*)(...))QRemoteObjectRegistryHost::qt_metacall
+40 (int (*)(...))QRemoteObjectRegistryHost::~QRemoteObjectRegistryHost
+48 (int (*)(...))QRemoteObjectRegistryHost::~QRemoteObjectRegistryHost
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QRemoteObjectNode::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QRemoteObjectHostBase::setName
+120 (int (*)(...))QRemoteObjectRegistryHost::setRegistryUrl
+128 (int (*)(...))QRemoteObjectHostBase::hostUrl
+136 (int (*)(...))QRemoteObjectHostBase::setHostUrl
+
+Class QRemoteObjectRegistryHost
+ size=16 align=8
+ base size=16 base align=8
+QRemoteObjectRegistryHost (0x0x7f3bf3e95d68) 0
+ vptr=((& QRemoteObjectRegistryHost::_ZTV25QRemoteObjectRegistryHost) + 16)
+ QRemoteObjectHostBase (0x0x7f3bf3e95dd0) 0
+ primary-for QRemoteObjectRegistryHost (0x0x7f3bf3e95d68)
+ QRemoteObjectNode (0x0x7f3bf3e95e38) 0
+ primary-for QRemoteObjectHostBase (0x0x7f3bf3e95dd0)
+ QObject (0x0x7f3bf3eba1e0) 0
+ primary-for QRemoteObjectNode (0x0x7f3bf3e95e38)
+
+Class QRemoteObjectPendingCall
+ size=8 align=8
+ base size=8 base align=8
+QRemoteObjectPendingCall (0x0x7f3bf3eba420) 0
+
+Class QRemoteObjectPendingCallWatcher::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QRemoteObjectPendingCallWatcher::QPrivateSignal (0x0x7f3bf3eba780) 0 empty
+
+Vtable for QRemoteObjectPendingCallWatcher
+QRemoteObjectPendingCallWatcher::_ZTV31QRemoteObjectPendingCallWatcher: 14 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI31QRemoteObjectPendingCallWatcher)
+16 (int (*)(...))QRemoteObjectPendingCallWatcher::metaObject
+24 (int (*)(...))QRemoteObjectPendingCallWatcher::qt_metacast
+32 (int (*)(...))QRemoteObjectPendingCallWatcher::qt_metacall
+40 (int (*)(...))QRemoteObjectPendingCallWatcher::~QRemoteObjectPendingCallWatcher
+48 (int (*)(...))QRemoteObjectPendingCallWatcher::~QRemoteObjectPendingCallWatcher
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+
+Class QRemoteObjectPendingCallWatcher
+ size=24 align=8
+ base size=24 base align=8
+QRemoteObjectPendingCallWatcher (0x0x7f3bf3ee53f0) 0
+ vptr=((& QRemoteObjectPendingCallWatcher::_ZTV31QRemoteObjectPendingCallWatcher) + 16)
+ QObject (0x0x7f3bf3eba6c0) 0
+ primary-for QRemoteObjectPendingCallWatcher (0x0x7f3bf3ee53f0)
+ QRemoteObjectPendingCall (0x0x7f3bf3eba720) 16
+
+Class QRemoteObjectSettingsStore::QPrivateSignal
+ size=1 align=1
+ base size=0 base align=1
+QRemoteObjectSettingsStore::QPrivateSignal (0x0x7f3bf3ebac60) 0 empty
+
+Vtable for QRemoteObjectSettingsStore
+QRemoteObjectSettingsStore::_ZTV26QRemoteObjectSettingsStore: 16 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI26QRemoteObjectSettingsStore)
+16 (int (*)(...))QRemoteObjectSettingsStore::metaObject
+24 (int (*)(...))QRemoteObjectSettingsStore::qt_metacast
+32 (int (*)(...))QRemoteObjectSettingsStore::qt_metacall
+40 (int (*)(...))QRemoteObjectSettingsStore::~QRemoteObjectSettingsStore
+48 (int (*)(...))QRemoteObjectSettingsStore::~QRemoteObjectSettingsStore
+56 (int (*)(...))QObject::event
+64 (int (*)(...))QObject::eventFilter
+72 (int (*)(...))QObject::timerEvent
+80 (int (*)(...))QObject::childEvent
+88 (int (*)(...))QObject::customEvent
+96 (int (*)(...))QObject::connectNotify
+104 (int (*)(...))QObject::disconnectNotify
+112 (int (*)(...))QRemoteObjectSettingsStore::saveProperties
+120 (int (*)(...))QRemoteObjectSettingsStore::restoreProperties
+
+Class QRemoteObjectSettingsStore
+ size=16 align=8
+ base size=16 base align=8
+QRemoteObjectSettingsStore (0x0x7f3bf3ef70d0) 0
+ vptr=((& QRemoteObjectSettingsStore::_ZTV26QRemoteObjectSettingsStore) + 16)
+ QRemoteObjectAbstractPersistedStore (0x0x7f3bf3ef7138) 0
+ primary-for QRemoteObjectSettingsStore (0x0x7f3bf3ef70d0)
+ QObject (0x0x7f3bf3ebac00) 0
+ primary-for QRemoteObjectAbstractPersistedStore (0x0x7f3bf3ef7138)
+
+Class QtPrivate::QMetaObjectPrivate
+ size=56 align=4
+ base size=56 base align=4
+QtPrivate::QMetaObjectPrivate (0x0x7f3bf3b311e0) 0
+
+Class ModelInfo
+ size=24 align=8
+ base size=24 base align=8
+ModelInfo (0x0x7f3bf3b31ea0) 0
+
+Vtable for SourceApiMap
+SourceApiMap::_ZTV12SourceApiMap: 32 entries
+0 (int (*)(...))0
+8 (int (*)(...))(& _ZTI12SourceApiMap)
+16 0
+24 0
+32 (int (*)(...))__cxa_pure_virtual
+40 (int (*)(...))__cxa_pure_virtual
+48 (int (*)(...))SourceApiMap::className
+56 (int (*)(...))__cxa_pure_virtual
+64 (int (*)(...))__cxa_pure_virtual
+72 (int (*)(...))__cxa_pure_virtual
+80 (int (*)(...))__cxa_pure_virtual
+88 (int (*)(...))__cxa_pure_virtual
+96 (int (*)(...))__cxa_pure_virtual
+104 (int (*)(...))__cxa_pure_virtual
+112 (int (*)(...))__cxa_pure_virtual
+120 (int (*)(...))__cxa_pure_virtual
+128 (int (*)(...))__cxa_pure_virtual
+136 (int (*)(...))__cxa_pure_virtual
+144 (int (*)(...))__cxa_pure_virtual
+152 (int (*)(...))__cxa_pure_virtual
+160 (int (*)(...))__cxa_pure_virtual
+168 (int (*)(...))__cxa_pure_virtual
+176 (int (*)(...))__cxa_pure_virtual
+184 (int (*)(...))__cxa_pure_virtual
+192 (int (*)(...))__cxa_pure_virtual
+200 (int (*)(...))__cxa_pure_virtual
+208 (int (*)(...))__cxa_pure_virtual
+216 (int (*)(...))__cxa_pure_virtual
+224 (int (*)(...))SourceApiMap::isDynamic
+232 (int (*)(...))SourceApiMap::isAdapterSignal
+240 (int (*)(...))SourceApiMap::isAdapterMethod
+248 (int (*)(...))SourceApiMap::isAdapterProperty
+
+Class SourceApiMap
+ size=24 align=8
+ base size=24 base align=8
+SourceApiMap (0x0x7f3bf3b31f00) 0
+ vptr=((& SourceApiMap::_ZTV12SourceApiMap) + 16)
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long int; _Ret = int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Save_errno
+ size=4 align=4
+ base size=4 base align=4
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long int; _Ret = int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Save_errno (0x0x7f3bf3bf3900) 0
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long int; _Ret = int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Range_chk
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long int; _Ret = int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Range_chk (0x0x7f3bf3bf3c60) 0 empty
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long int; _Ret = long int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Save_errno
+ size=4 align=4
+ base size=4 base align=4
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long int; _Ret = long int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Save_errno (0x0x7f3bf3bf3e40) 0
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long int; _Ret = long int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Range_chk
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long int; _Ret = long int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Range_chk (0x0x7f3bf3c241e0) 0 empty
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long unsigned int; _Ret = long unsigned int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Save_errno
+ size=4 align=4
+ base size=4 base align=4
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long unsigned int; _Ret = long unsigned int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Save_errno (0x0x7f3bf3c243c0) 0
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long unsigned int; _Ret = long unsigned int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Range_chk
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long unsigned int; _Ret = long unsigned int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Range_chk (0x0x7f3bf3c24720) 0 empty
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long long int; _Ret = long long int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Save_errno
+ size=4 align=4
+ base size=4 base align=4
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long long int; _Ret = long long int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Save_errno (0x0x7f3bf3c24900) 0
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long long int; _Ret = long long int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Range_chk
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long long int; _Ret = long long int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Range_chk (0x0x7f3bf3c24c60) 0 empty
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long long unsigned int; _Ret = long long unsigned int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Save_errno
+ size=4 align=4
+ base size=4 base align=4
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long long unsigned int; _Ret = long long unsigned int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Save_errno (0x0x7f3bf3c24e40) 0
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long long unsigned int; _Ret = long long unsigned int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Range_chk
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long long unsigned int; _Ret = long long unsigned int; _CharT = char; _Base = {int}; std::size_t = long unsigned int]::_Range_chk (0x0x7f3bf3c591e0) 0 empty
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = float; _Ret = float; _CharT = char; _Base = {}; std::size_t = long unsigned int]::_Save_errno
+ size=4 align=4
+ base size=4 base align=4
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = float; _Ret = float; _CharT = char; _Base = {}; std::size_t = long unsigned int]::_Save_errno (0x0x7f3bf3c593c0) 0
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = float; _Ret = float; _CharT = char; _Base = {}; std::size_t = long unsigned int]::_Range_chk
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = float; _Ret = float; _CharT = char; _Base = {}; std::size_t = long unsigned int]::_Range_chk (0x0x7f3bf3c59720) 0 empty
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = double; _Ret = double; _CharT = char; _Base = {}; std::size_t = long unsigned int]::_Save_errno
+ size=4 align=4
+ base size=4 base align=4
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = double; _Ret = double; _CharT = char; _Base = {}; std::size_t = long unsigned int]::_Save_errno (0x0x7f3bf3c59900) 0
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = double; _Ret = double; _CharT = char; _Base = {}; std::size_t = long unsigned int]::_Range_chk
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = double; _Ret = double; _CharT = char; _Base = {}; std::size_t = long unsigned int]::_Range_chk (0x0x7f3bf3c59c60) 0 empty
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long double; _Ret = long double; _CharT = char; _Base = {}; std::size_t = long unsigned int]::_Save_errno
+ size=4 align=4
+ base size=4 base align=4
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long double; _Ret = long double; _CharT = char; _Base = {}; std::size_t = long unsigned int]::_Save_errno (0x0x7f3bf3c59e40) 0
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long double; _Ret = long double; _CharT = char; _Base = {}; std::size_t = long unsigned int]::_Range_chk
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long double; _Ret = long double; _CharT = char; _Base = {}; std::size_t = long unsigned int]::_Range_chk (0x0x7f3bf3c911e0) 0 empty
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long int; _Ret = int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Save_errno
+ size=4 align=4
+ base size=4 base align=4
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long int; _Ret = int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Save_errno (0x0x7f3bf3cbb6c0) 0
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long int; _Ret = int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Range_chk
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long int; _Ret = int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Range_chk (0x0x7f3bf3cbba20) 0 empty
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long int; _Ret = long int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Save_errno
+ size=4 align=4
+ base size=4 base align=4
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long int; _Ret = long int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Save_errno (0x0x7f3bf3cbbba0) 0
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long int; _Ret = long int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Range_chk
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long int; _Ret = long int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Range_chk (0x0x7f3bf3cbbf00) 0 empty
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long unsigned int; _Ret = long unsigned int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Save_errno
+ size=4 align=4
+ base size=4 base align=4
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long unsigned int; _Ret = long unsigned int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Save_errno (0x0x7f3bf3ceb0c0) 0
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long unsigned int; _Ret = long unsigned int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Range_chk
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long unsigned int; _Ret = long unsigned int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Range_chk (0x0x7f3bf3ceb420) 0 empty
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long long int; _Ret = long long int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Save_errno
+ size=4 align=4
+ base size=4 base align=4
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long long int; _Ret = long long int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Save_errno (0x0x7f3bf3ceb5a0) 0
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long long int; _Ret = long long int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Range_chk
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long long int; _Ret = long long int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Range_chk (0x0x7f3bf3ceb900) 0 empty
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long long unsigned int; _Ret = long long unsigned int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Save_errno
+ size=4 align=4
+ base size=4 base align=4
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long long unsigned int; _Ret = long long unsigned int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Save_errno (0x0x7f3bf3ceba80) 0
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long long unsigned int; _Ret = long long unsigned int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Range_chk
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long long unsigned int; _Ret = long long unsigned int; _CharT = wchar_t; _Base = {int}; std::size_t = long unsigned int]::_Range_chk (0x0x7f3bf3cebde0) 0 empty
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = float; _Ret = float; _CharT = wchar_t; _Base = {}; std::size_t = long unsigned int]::_Save_errno
+ size=4 align=4
+ base size=4 base align=4
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = float; _Ret = float; _CharT = wchar_t; _Base = {}; std::size_t = long unsigned int]::_Save_errno (0x0x7f3bf3cebf60) 0
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = float; _Ret = float; _CharT = wchar_t; _Base = {}; std::size_t = long unsigned int]::_Range_chk
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = float; _Ret = float; _CharT = wchar_t; _Base = {}; std::size_t = long unsigned int]::_Range_chk (0x0x7f3bf389f300) 0 empty
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = double; _Ret = double; _CharT = wchar_t; _Base = {}; std::size_t = long unsigned int]::_Save_errno
+ size=4 align=4
+ base size=4 base align=4
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = double; _Ret = double; _CharT = wchar_t; _Base = {}; std::size_t = long unsigned int]::_Save_errno (0x0x7f3bf389f480) 0
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = double; _Ret = double; _CharT = wchar_t; _Base = {}; std::size_t = long unsigned int]::_Range_chk
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = double; _Ret = double; _CharT = wchar_t; _Base = {}; std::size_t = long unsigned int]::_Range_chk (0x0x7f3bf389f7e0) 0 empty
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long double; _Ret = long double; _CharT = wchar_t; _Base = {}; std::size_t = long unsigned int]::_Save_errno
+ size=4 align=4
+ base size=4 base align=4
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long double; _Ret = long double; _CharT = wchar_t; _Base = {}; std::size_t = long unsigned int]::_Save_errno (0x0x7f3bf389f960) 0
+
+Class __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long double; _Ret = long double; _CharT = wchar_t; _Base = {}; std::size_t = long unsigned int]::_Range_chk
+ size=1 align=1
+ base size=0 base align=1
+__gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long double; _Ret = long double; _CharT = wchar_t; _Base = {}; std::size_t = long unsigned int]::_Range_chk (0x0x7f3bf389fcc0) 0 empty
+
--- /dev/null
+# This is an automatic test for the CMake configuration files.
+# To run it manually,
+# 1) mkdir build # Create a build directory
+# 2) cd build
+# 3) # Run cmake on this directory
+# `$qt_prefix/bin/qt-cmake ..` or `cmake -DCMAKE_PREFIX_PATH=/path/to/qt ..`
+# 4) ctest # Run ctest
+
+cmake_minimum_required(VERSION 3.16)
+project(remoteobjects_cmake_tests)
+enable_testing()
+
+set(required_packages Core Network RemoteObjects)
+
+# Setup the test when called as a completely standalone project.
+if(TARGET Qt6::Core)
+ # Tests are built as part of the repository's build tree.
+ # Setup paths so that the Qt packages are found.
+ qt_internal_set_up_build_dir_package_paths()
+endif()
+
+find_package(Qt6 REQUIRED COMPONENTS ${required_packages})
+
+# Setup common test variables which were previously set by ctest_testcase_common.prf.
+set(CMAKE_MODULES_UNDER_TEST "${required_packages}")
+
+foreach(qt_package ${CMAKE_MODULES_UNDER_TEST})
+ set(package_name "${QT_CMAKE_EXPORT_NAMESPACE}${qt_package}")
+ if(${package_name}_FOUND)
+ set(CMAKE_${qt_package}_MODULE_MAJOR_VERSION "${${package_name}_VERSION_MAJOR}")
+ set(CMAKE_${qt_package}_MODULE_MINOR_VERSION "${${package_name}_VERSION_MINOR}")
+ set(CMAKE_${qt_package}_MODULE_PATCH_VERSION "${${package_name}_VERSION_PATCH}")
+ endif()
+endforeach()
+
+include("${_Qt6CTestMacros}")
+
+set(module_includes
+ RemoteObjects QRemoteObjectNode
+)
+
+_qt_internal_test_module_includes(
+ ${module_includes}
+)
+
+_qt_internal_test_expect_pass(test_cmake_macros)
--- /dev/null
+cmake_minimum_required(VERSION 3.16)
+
+project(test_qremoteobjects_module)
+
+find_package(Qt6RemoteObjects REQUIRED)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(MAIN_SRCS main.cpp)
+add_executable(mainapp ${MAIN_SRCS})
+qt6_add_repc_replicas(mainapp ../../integration/pod.rep)
+
+target_link_libraries(mainapp Qt::RemoteObjects)
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <QRemoteObjectNode>
+
+#include "rep_pod_replica.h"
+
+int main(int argc, char **argv)
+{
+ QRemoteObjectNode node;
+
+ MyClassReplica replica;
+
+ return 0;
+}
--- /dev/null
+
+add_subdirectory(sslTestServer)
+add_subdirectory(tst_client)
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIICrTCCAZUCFHOQggvUf1o8c5i3yNyiGLNcLC4pMA0GCSqGSIb3DQEBCwUAMBIx
+EDAOBgNVBAMMB1F0Uk8gQ0EwHhcNMjEwMjI0MTEzMzU1WhcNMjMwNTMwMTEzMzU1
+WjAUMRIwEAYDVQQDDAkxMjcuMC4wLjEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQDbl9iuedw0oSbtpC2m30YdzwRmemijasP9SQGQ6+piUOFUKCZsoGWc
+RcEnLGzC+KJ7FXh8jA1kTXSW6ghqvrUysN8VzjgmcCLFee4JAkCUY8yNrlq13ciR
+19BE09kJdOPZeI57pCSBNA6iy03Q4nc/GJpG63QTqJv/WUUgMek0UsmZIzDcWaqr
+MCMnLMaRi5oKFCnnl8E0XDuRm1nqPAzT+us/4upMv+7Q2xs4QFXbLUpSIToNc1wm
+tP6OAGaYClbJZgZbUNowj0wJeCUAwGGcDpliYj1JB8R015z8Kd8pDCvdD7XL35JR
+rT+eaBFNLUrl30aIl3lWf/buv3OoRmuVAgMBAAEwDQYJKoZIhvcNAQELBQADggEB
+AJjdfuy2pb3KgnpxYiXfKXCqGlN7E1RyoCIsMhldWcxAN5cwIJCrvde5MNI8kDvd
+0SfDpRpCP/hZqpR6DsR9iNYJprXlQNZ7Rs41Eswwlb66DqmBlb5ZQcYl8KsKV5fw
+7PhvLpjC5hEg1OBg1Ooz+aNvI9OJYIRFUJ1smtRzwXWuQd5QoqYVRpzvmrFawnGa
+2NHywiwgKyGvY/y82pPuj1rt0L+bae85cZm32f6gp1me9OuLIqA2G5UafSiigWBY
+YL249Rd4rrT87GAeaiBo8ZxZ8de8O7TOBjSNrfAMySepDWjfFfoNpyp+4foRKmpE
+aZmgGTIj5rfhYh4Gcj1nZBw=
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEA25fYrnncNKEm7aQtpt9GHc8EZnpoo2rD/UkBkOvqYlDhVCgm
+bKBlnEXBJyxswviiexV4fIwNZE10luoIar61MrDfFc44JnAixXnuCQJAlGPMja5a
+td3IkdfQRNPZCXTj2XiOe6QkgTQOostN0OJ3PxiaRut0E6ib/1lFIDHpNFLJmSMw
+3FmqqzAjJyzGkYuaChQp55fBNFw7kZtZ6jwM0/rrP+LqTL/u0NsbOEBV2y1KUiE6
+DXNcJrT+jgBmmApWyWYGW1DaMI9MCXglAMBhnA6ZYmI9SQfEdNec/CnfKQwr3Q+1
+y9+SUa0/nmgRTS1K5d9GiJd5Vn/27r9zqEZrlQIDAQABAoIBAEDLm4pQNuPosV3p
+1fapZz0gesHqWLnvpQk145ppom2ERBjbCAuBgLoN8yKl/ynAx+DdwwGtKb5xBHgL
+cpRc1YaxngIHKZZd/ESc59oMqhWfJRqhWe7UFHzEW5YTlLUvopPm+NQO6R6ex7rN
+lpaOXHVnww4uJ8AtPmqoYrdPQurG/txveRMLo84JJT+IH2YVWOzccp809zw4WZZD
+qBcgm/dV8ir+8nUHQlR+loMMrEoKeacNxtHUXWL6d6P93Q72L07t41/l0XmXXq7I
+cVJnGxcJtkeqj03FSHqDU3XM5fRg6f+XnnSnhnd4AUmHe8cvyeCnEf4bdh4UpzBG
+sCie+XkCgYEA93FU0X6ttWdb+rJNHRnHmb4DxOVo2LeXEk1A1ul+Yj+jFP+TwJH+
+bm8PbV7ALdyH2u66ElQG60gW9ztu86xl5ZLVdhijWJpjHKB45eXVhnRcb2Fy9tDc
+pUeRs8+IrrYbWDrNZZYWby83MqPHimCLTmAZl11NMB2ohyFDxr5voGMCgYEA4y/0
+2WN8r74H9I3L2Ghfe8e3i/W35BpjtElJxiL3L1vzGdU5Wo1hDnvjoHvdTxB7LtGU
+I+P0l77fwuAC8G8bh4SZ59jcxlqCmbXy7wDAyrYaCja5OWK9xWXvYuya5CCPrg6h
+wo7TcrxjdEvEVQ97PMZcq6HVBOtINZGfJeSieacCgYAHyQsQJFo20O+17ZI7jioX
+jkD0Gvu3hd889i1KFcKiOLpa2Me/UVieBOSJXmfRiZTEsKouFXK6SGRglwAgrpXu
+KTaKJrBNA16G8g2bviV/u32FC53gYiXvFVdiPu9f/97QYdlAjv5ZtTSZZUnL8smv
+R5rGhmr9TpGU3tkREcDVXQKBgBUfJ0dyvWvlYf31lOcYxQ/QAJuNi7w0S+K+EZLP
+O2X2yYI0VbG6hTSAhigse+XW5Wzz5S71CY92Gn2WsA9EdS3DQT/R5Ky4S34Y8W4R
+BtuR1JfwgIX6TSRmFrx+vOPKtzD6gUWCW9xF8YUlaipyVwXOd10pnZFogn0gfchb
+GlPvAoGAG2xikjlCTrnKv7KRF9sxO1eLixfzHwWKiAhrtFBoHSM4AwynrpAb0eMf
+ObSIjXeBy93LhTluVOsD5J9iXA/SKYoXqt/tDMCHRdwpTsJNBa56GMkpFHHLo6oC
+si20nmMXP949gpRIvrYsgYC8WObbi+RQEWDVutv7hVPCF0QvUHs=
+-----END RSA PRIVATE KEY-----
--- /dev/null
+#!/bin/sh
+# Copyright (C) 2021 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+# Generate the CA key
+openssl genrsa -out rootCA.key 2048
+# Generate the CA cert
+openssl req -x509 -key rootCA.key -out rootCA.pem -sha256 -nodes -subj "/CN=QtRO CA" -days 836
+
+# genFiles stem [extra args to signing]
+genFiles () {
+ stem=$1
+ shift
+ # Generate key
+ openssl genrsa -out $stem.key 2048
+ # Generate certificate-signing request
+ openssl req -new -key $stem.key -out $stem.csr -subj "/CN=127.0.0.1"
+ # Generate and sign the certificate
+ openssl x509 -req -in $stem.csr -out $stem.crt \
+ -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -days 825 -sha256 "$@"
+ # Delete the signing request, no longer needed
+ rm $stem.csr
+}
+genFiles server -extfile server-req.ext
+genFiles client
+
+dest1="../../../../examples/remoteobjects/ssl/sslserver/cert/"
+dest2="../../../../examples/remoteobjects/websockets/common/cert/"
+
+cp -f "client.crt" $dest1
+cp -f "client.crt" $dest2
+cp -f "client.key" $dest1
+cp -f "client.key" $dest2
+
+cp -f "server.crt" $dest1
+cp -f "server.crt" $dest2
+cp -f "server.key" $dest1
+cp -f "server.key" $dest2
+
+cp -f "rootCA.pem" $dest1
+cp -f "rootCA.key" $dest1
+cp -f "rootCA.srl" $dest1
+cp -f "rootCA.pem" $dest2
+cp -f "rootCA.key" $dest2
+cp -f "rootCA.srl" $dest2
--- /dev/null
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAulVnnHwRF6e2aAThSi1cZpUlO3ZdqzPIuf75NBbRY2y9Vm+F
+cyCqUquNxP/qDE02nfQFBd/PUKqUWQs0EXVEVZPEG6s/l7ionYHkMmWSjh+AAWp7
+Iwx3MlHFNi9c5Xrod7iS1igg0YhDQlnT0xGfTXZasUJ/s6NuNoZiN5L6sEKYDSAu
+kzyyqS23WrqE4RvsGAaoaaJqu1MT8DBqI9xoPpIvwb/4gdOZn4YClW2WWrVjCTT2
+zEzUAh1BjdH3dktXogiFfXHuOP4W8suOx46NXDcZ3f5LF8CT/2uq9l8vta+pV2ci
+BAIctGu5z+fEdTCojvCWOvCzYmjtep/yTukT+QIDAQABAoIBAQCRvRjkCRnpUZDW
+vPJk7SO3THIplwPeUwtthqLtfedaB4PzphhPmr39GRcyfSNXadENK/39wTbKlhCf
+sKaR/RFsib26EnATwCeJwj10uYMuTC73bIxRNn/ISLKiFdtn1YEbmq6meA5rNFO/
+Arkt/juF/8shl6yAGZSrauJJK1mOH8ItMaGR+6tVPWLIZOLk6TiOJhj9SXvMTCw/
+HzgNZMgFGfqnbctg1ki/CY0BkIkYNUTCjhoCwjcgBJl4ERCfTQS6UeEG9Ad+beIH
+g8WKzpfjF5+Jnjzqw87aWx1200OdlEdouTt677RXHJFN5naUc+GJZGpmA3RGZA17
+LqA4zBYNAoGBAOGwtg7JQkBOmUC0SiKdXnxG1hVnS4N3DMIDVX2tAe/wWIrP168e
+0UpCvswLD+JqO1IgWqw9+QUPnhJSQ9JbYB+678esOTLsT5Yd18VcsiRxSacvQfUw
+H4YJaHrFuuFlnxYMlMdPYS3knbIPsft9DVQLFBLL7qPVHbrJ3V6Sn4XrAoGBANNb
+mfhgVr5m0n3sQVTlYhWwbJq5K+Htzzl7Xl3JHpMLm2C/GoorP/2zLVhbH20lsE3A
+FyIfjcwRxGRu2TXCVnMc4GttlMX5leTxykEd2VrZuEVnTdrudm45Z6sZQpdf1QTg
+WebwKgN1eCg7Jkuk5YlRX/KwMtuq4MVzPtOvR+CrAoGAA8uC5DDCKm6n6QyfCoH2
+6sQOKYH5JRbFYiXINDrKg4xZEMx55fnwrvz8VFYDSF1c7f6ZR7grDci7cbdsaIcc
+0KvGCGd+9ro+hFmwHSN342D8ShFjXIoYnZpe5WGZyNx6llZT0h4lli338NyOs5ng
+tX8SMVa4hoy42UE3tbVldU0CgYA0l/K0b6SmNIfkdcm8Cmhh5UjhJ3rX+Yk7UIum
+4skM5jJ/3I4KG8EMrG14MxSa4GoCru4Su69ZPIKWS08ZpYZFlsXxdY8zxGucUN53
+XaochVjpTE9/Tx+BRh+Z3+tGJ76mO/2jDdgmjDCeMjnRUPMdPHaXuWiuaNMNzyOv
+IUrNiQKBgGvxEQ0Oe3d/om2Lp/cHbkhZkw/jO/FG5HtodxiO3+1YLhExsDOc5GVn
++x2eNv+dQSIrGagko9TJe1p9WqFnD19Ls+ezqfw2fR5Amg1KHKGUA7k1+Qe/QgoK
+D+T4/RkvdGRoBv/il+Rj1rfmMAhEzdD7Axek9a6rUj8geO22kp7I
+-----END RSA PRIVATE KEY-----
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIDBTCCAe2gAwIBAgIUV9eILCYaC+qwZHR7OO23uyd2UjwwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHUXRSTyBDQTAeFw0yMTAyMjQxMTMzNTVaFw0yMzA2MTAx
+MTMzNTVaMBIxEDAOBgNVBAMMB1F0Uk8gQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQC6VWecfBEXp7ZoBOFKLVxmlSU7dl2rM8i5/vk0FtFjbL1Wb4Vz
+IKpSq43E/+oMTTad9AUF389QqpRZCzQRdURVk8Qbqz+XuKidgeQyZZKOH4ABansj
+DHcyUcU2L1zleuh3uJLWKCDRiENCWdPTEZ9NdlqxQn+zo242hmI3kvqwQpgNIC6T
+PLKpLbdauoThG+wYBqhpomq7UxPwMGoj3Gg+ki/Bv/iB05mfhgKVbZZatWMJNPbM
+TNQCHUGN0fd2S1eiCIV9ce44/hbyy47Hjo1cNxnd/ksXwJP/a6r2Xy+1r6lXZyIE
+Ahy0a7nP58R1MKiO8JY68LNiaO16n/JO6RP5AgMBAAGjUzBRMB0GA1UdDgQWBBSu
+ehS/XLejTiDbCddGU2mMZ1t3CjAfBgNVHSMEGDAWgBSuehS/XLejTiDbCddGU2mM
+Z1t3CjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQB8JzSuhHPK
+cjhLqOHUGMKtKWOd5p9g2D45cAWh6jdzU/AhmslMPbsO5hZkqfE+3xARtcMmQfF2
+k1Qyp3hDTN1ZqHSM6Urq9uR33/wXZbNRUNCD8lAmqKyzF9NF7Q+tmC//IMRtVQhK
+aMN3LciyYGQjT0XhDKFWEz9/AvUQD97mLow2m0/izqE4SI6ekQDNL26IiCWFgFjh
+ScZjcJ1ogluD2a6sEUGywRXLNV/bdSjRgkAbpvJFrok7dDZ8xCNhOg4xJJQJRWm7
+ZusUydiVyfgrFan6MD+EdldRHjAs8S9BJfZ0RTOWnD9V8auKuVomzKDed54QlXXi
+zwowb3Objpqh
+-----END CERTIFICATE-----
--- /dev/null
+7390820BD47F5A3C7398B7C8DCA218B35C2C2E29
--- /dev/null
+authorityKeyIdentifier=keyid,issuer
+basicConstraints=CA:FALSE
+keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
+extendedKeyUsage = serverAuth
+subjectAltName = @alt_names
+
+[alt_names]
+IP.1 = 127.0.0.1
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIDFTCCAf2gAwIBAgIUc5CCC9R/WjxzmLfI3KIYs1wsLigwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHUXRSTyBDQTAeFw0yMTAyMjQxMTMzNTVaFw0yMzA1MzAx
+MTMzNTVaMBQxEjAQBgNVBAMMCTEyNy4wLjAuMTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBAOKHte9tB66OD+Um/WkqxHtW3sKrBs4IxKuWAef0UMRt3ld6
+5HvWk+xsCZdPxeL53nMOIy9FS6wKGvEWTwYRR4Id9iX2XQsI4cRJWl25qgCYohnm
+Eet9CUkXa3ywbyrSBWFD0r956sS+mwhHU9z05jphd6iZEonHu2b4BFFXMN7+prwj
+00EtGbte5wSWWE9ZfXzeGYd4cZBReNCRjaS5XJ3IgjZ4tfxsB3JzBjVafCfnth7r
+Is8a2SKCGnhYmV+A6Agth4xtSKDho+BSDYSuMux3dftM/eqtxF0wXzlnX5ApNwGB
+zWjcoUL63vjjy17oNEtbs5X2e1g8bGRaGRxGUHUCAwEAAaNhMF8wHwYDVR0jBBgw
+FoAUrnoUv1y3o04g2wnXRlNpjGdbdwowCQYDVR0TBAIwADALBgNVHQ8EBAMCBPAw
+EwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0RBAgwBocEfwAAATANBgkqhkiG9w0B
+AQsFAAOCAQEAqhBhxRgG9N1ZghwWC3ZhWSx4BFl3YrStWlQcjffcQ6p8NDxsrkFc
+gMG51TmJdaz8J4v2AZW8k9GJlEIaZdV/8czeyEwvjKD4vrUw88waeW7n6o8H+8k+
+ak9fRFvnerFrLEWNpyRqbjJWwm8bQ4T5UKsVNXkZnNLyG2Ha29L9gUHffgSMiyLO
+hWqcanPxsMJaDVhw/Gd8JwqaEC1nRPCGxhog2/D2sh4vCj1UykykjPwNz5fP/vfA
+VujNCA23eXAdgD3lALHu2WrmyPkQCM7Z61g4k8+v0KjhyJjdLSVTwkPePEo87Fv4
+sn4Jp5gPPBf7jDFKp8PDdbPmk0qN+Wm8gA==
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEA4oe1720Hro4P5Sb9aSrEe1bewqsGzgjEq5YB5/RQxG3eV3rk
+e9aT7GwJl0/F4vnecw4jL0VLrAoa8RZPBhFHgh32JfZdCwjhxElaXbmqAJiiGeYR
+630JSRdrfLBvKtIFYUPSv3nqxL6bCEdT3PTmOmF3qJkSice7ZvgEUVcw3v6mvCPT
+QS0Zu17nBJZYT1l9fN4Zh3hxkFF40JGNpLlcnciCNni1/GwHcnMGNVp8J+e2Husi
+zxrZIoIaeFiZX4DoCC2HjG1IoOGj4FINhK4y7Hd1+0z96q3EXTBfOWdfkCk3AYHN
+aNyhQvre+OPLXug0S1uzlfZ7WDxsZFoZHEZQdQIDAQABAoIBAGuKEYzALc1oE5Ot
+ls++RdhnvQidOHXHI9ZxOCZtjYoyvkK5TI6dp0utXkA+1qqSBFCKfZmLRAlAItog
+xRMUUOYsYxZShokehk8wo32rDlGKJCo3Vnp8uVPBkn13JM8nNPafxASyVAlikyay
+9dUHTeSZML0RLgPKleSkCSi0Q7cYOFG/HB9aNjp8F5rdut02KrmC3cxlHKF7QXXG
+VU+op1Z9o0V2/iUFJnF5CR40sW2THEbBJkkeYwbvUTnavz4XQtZst//DKsDQEe2r
+UrhsIHduvG4tWiBE77m1vyigTxUWCsLQ2KCnn9O+4KyTg9HWCiQ2QSU3istX/rpI
+zN2lOgECgYEA9PVVMnY+t59Q73IQ9LRg5KRqg6YyGQOrwJKbCUxDrA6ikh3MDgwV
+CkC6Jdl6e4DMog51l3CizrfR2+mtNSTUJDGFE1iGgI+Faem4aopRtFRiLWJ8n4m7
+U8pl3XTP0XFT68aBCAE6O/xVPXs0I/eKNvaF5vokB5zm4R79o37WP+UCgYEA7L26
+TiCFA73Fil/bPupqWJnvm896RlO1S+IBOKlPyCHVvxiGLvtv+YTucCFwXQ4FeNRh
+bQEWlURsgeNr7PHATtFUZ/zo/7l1WYNLXZDZwWD+JYllVPwskJOJMx5Rc77Q0aQ6
+7v60XMGwD5cxQ29RHuJs09Iwc9b1WqwOAEJAJVECgYBNsxQXMZKrRAm0KgZe2Ghz
+ngN7RthVPujX6KjsxhghF3NRzcnQGt0Bp45kOxuy2SQPs25xXvUFhSE4FGMwnEH+
+SQbhIA9p8BxtgAlTIhTQkoOhyb+mC1Y0Odsd59OTp9Lq0shS9bC3Hk8bdV0Qm5Bn
+5sKKhYWwNIC3n9Dsb2seUQKBgAS7biPtpnsCqhYwAFPrn6CRwyZcKVeKiM8xf1DA
+oaWgd4NQXC5IPF7Cd3mqUXKquxVFOYVSRj9JlNmr0BZ2Zp+ss4E4nvetn1jgtPrz
+0EZ7R9k8O9hNCh8Bs/ZfnsUvhUELhVoNoVFRVdGZ9hQg/4AcioxZYTqPi2v6kHUU
+3e9hAoGAec7anF5TiTx2jjcDFS9hrRw0w2PsNX24qjqPFqeuzDIorh6rq4Ip4aA0
+7rxeIXmxjmYA7pPCT9rPxtpEp4BQovF9kHMutd8lyB4rGbLpNpOY4m5v8Oo7cLQ3
+kLAwE+jrEwLNtuq+kUlGwK7YLeiGUm4Rsof5IXlSkXzL/99gHC4=
+-----END RSA PRIVATE KEY-----
--- /dev/null
+#include <QtCore>
+
+class PingPong
+{
+ SLOT(void ping(const QString &message));
+ SLOT(void quit());
+ SIGNAL(pong(const QString &message));
+};
--- /dev/null
+
+#####################################################################
+## sslTestServer Binary:
+#####################################################################
+
+qt_internal_add_executable(sslTestServer
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES
+ main.cpp
+ pingpong.cpp pingpong.h
+ sslserver.cpp sslserver.h
+ INCLUDE_DIRECTORIES
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+ Qt::Test
+)
+
+# Resources:
+set(cert_resource_files
+ "../cert/client.crt"
+ "../cert/client.key"
+ "../cert/rootCA.key"
+ "../cert/rootCA.pem"
+ "../cert/rootCA.srl"
+ "../cert/server.crt"
+ "../cert/server.key"
+)
+
+qt_internal_add_resource(sslTestServer "cert"
+ PREFIX
+ "/sslcert"
+ BASE
+ "../cert"
+ FILES
+ ${cert_resource_files}
+)
+
+qt6_add_repc_sources(sslTestServer
+ ../pingpong.rep
+)
+
+#### Keys ignored in scope 1:.:.:sslTestServer.pro:<TRUE>:
+# TEMPLATE = "app"
--- /dev/null
+// Copyright (C) 2018 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "pingpong.h"
+#include "sslserver.h"
+
+#include <QCoreApplication>
+#include <QDebug>
+#include <QSslConfiguration>
+
+#include <QRemoteObjectHost>
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication app(argc, argv);
+ QRemoteObjectHost host;
+ auto config = QSslConfiguration::defaultConfiguration();
+ config.setCaCertificates(QSslCertificate::fromPath(QStringLiteral(":/sslcert/rootCA.pem")));
+ QSslConfiguration::setDefaultConfiguration(config);
+ SslServer server;
+ server.listen(QHostAddress::Any, 65111);
+ host.setHostUrl(server.serverAddress().toString(), QRemoteObjectHost::AllowExternalRegistration);
+ QObject::connect(&server, &SslServer::encryptedSocketReady, &server, [&host](QSslSocket *socket){
+ QObject::connect(socket, &QSslSocket::errorOccurred,
+ socket, [](QAbstractSocket::SocketError error){
+ qDebug() << "QSslSocket::error" << error;
+ exit(1);
+ });
+ host.addHostSideConnection(socket);
+ });
+
+ PingPong pp;
+ host.enableRemoting(&pp);
+ return app.exec();
+}
--- /dev/null
+// Copyright (C) 2018 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <QCoreApplication>
+#include <QDebug>
+#include <QTimer>
+#include "pingpong.h"
+
+PingPong::PingPong(QObject *parent)
+ : PingPongSimpleSource(parent)
+{}
+
+void PingPong::ping(const QString &message)
+{
+ emit pong("Pong " + message);
+}
+
+void PingPong::quit()
+{
+ // Kill me softly
+ QMetaObject::invokeMethod(qApp, "quit", Qt::QueuedConnection);
+}
--- /dev/null
+// Copyright (C) 2018 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef PINGPONG_H
+#define PINGPONG_H
+
+#include "rep_pingpong_source.h"
+
+class PingPong : public PingPongSimpleSource
+{
+public:
+ PingPong(QObject *parent = nullptr);
+
+ // PingPongSource interface
+public slots:
+ void ping(const QString &message) override;
+ void quit() override;
+};
+
+#endif // PINGPONG_H
--- /dev/null
+// Copyright (C) 2018 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "sslserver.h"
+#include <QSslSocket>
+
+SslServer::SslServer(QObject *parent)
+ : QTcpServer(parent)
+{}
+
+
+void SslServer::incomingConnection(qintptr socketDescriptor)
+{
+ auto serverSocket = new QSslSocket;
+ if (serverSocket->setSocketDescriptor(socketDescriptor)) {
+ addPendingConnection(serverSocket);
+ connect(serverSocket, &QSslSocket::encrypted, this, [this, serverSocket] {
+ Q_EMIT encryptedSocketReady(serverSocket);
+ });
+ connect(serverSocket, static_cast<void (QSslSocket::*)(const QList<QSslError>&)>(&QSslSocket::sslErrors),
+ this, [serverSocket](const QList<QSslError>& errors){
+ qWarning() << "Error:" << serverSocket << errors;
+ delete serverSocket;
+ });
+ serverSocket->setPeerVerifyMode(QSslSocket::VerifyPeer);
+ serverSocket->setLocalCertificate(QStringLiteral(":/sslcert/server.crt"));
+ serverSocket->setPrivateKey(QStringLiteral(":/sslcert/server.key"));
+ serverSocket->startServerEncryption();
+ } else {
+ delete serverSocket;
+ }
+}
--- /dev/null
+// Copyright (C) 2018 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef SSLSERVER_H
+#define SSLSERVER_H
+
+#include <QTcpServer>
+
+QT_BEGIN_NAMESPACE
+class QSslSocket;
+QT_END_NAMESPACE
+
+class SslServer : public QTcpServer
+{
+ Q_OBJECT
+public:
+ SslServer(QObject *parent=nullptr);
+ void incomingConnection(qintptr socketDescriptor) override;
+
+signals:
+ void encryptedSocketReady(QSslSocket *socket);
+};
+
+
+#endif // SSLSERVER_H
--- /dev/null
+
+#####################################################################
+## tst_external_IODevice Test:
+#####################################################################
+
+qt_internal_add_test(tst_external_IODevice
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES
+ tst_client.cpp
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+)
+
+# Resources:
+set(cert_resource_files
+ "../cert/client.crt"
+ "../cert/client.key"
+ "../cert/rootCA.key"
+ "../cert/rootCA.pem"
+ "../cert/rootCA.srl"
+ "../cert/server.crt"
+ "../cert/server.key"
+)
+
+qt_internal_add_resource(tst_external_IODevice "cert"
+ PREFIX
+ "/sslcert"
+ BASE
+ "../cert"
+ FILES
+ ${cert_resource_files}
+)
+
+qt6_add_repc_replicas(tst_external_IODevice
+ ../pingpong.rep
+)
--- /dev/null
+// Copyright (C) 2018 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <QtTest/QtTest>
+#include <QRemoteObjectNode>
+#include <QScopedPointer>
+#include "rep_pingpong_replica.h"
+
+#include "../../../shared/testutils.h"
+
+class tst_clientSSL: public QObject
+{
+ Q_OBJECT
+public:
+ tst_clientSSL() = default;
+
+private slots:
+ void initTestCase()
+ {
+ QVERIFY(TestUtils::init("tst_client"));
+ }
+ void testRun()
+ {
+// TODO: This a limitation of QProcess on Android, QTBUG-88507 is relevant to this issue.
+#ifdef Q_OS_ANDROID
+ QSKIP("QProcess doesn't support running user bundled binaries on Android");
+#endif
+ QProcess serverProc;
+ serverProc.setProcessChannelMode(QProcess::ForwardedChannels);
+ serverProc.start(TestUtils::findExecutable("sslTestServer", "/sslTestServer"),
+ QStringList());
+ QVERIFY(serverProc.waitForStarted());
+
+ // wait for server start
+ QTest::qWait(200);
+ QRemoteObjectNode m_client;
+ auto config = QSslConfiguration::defaultConfiguration();
+ config.setCaCertificates(QSslCertificate::fromPath(QStringLiteral(":/sslcert/rootCA.pem")));
+ QSslConfiguration::setDefaultConfiguration(config);
+
+ QScopedPointer<QSslSocket> socketClient{new QSslSocket};
+ socketClient->setLocalCertificate(QStringLiteral(":/sslcert/client.crt"));
+ socketClient->setPrivateKey(QStringLiteral(":/sslcert/client.key"));
+ socketClient->setPeerVerifyMode(QSslSocket::VerifyPeer);
+ socketClient->connectToHostEncrypted(QStringLiteral("127.0.0.1"), 65111);
+ QVERIFY(socketClient->waitForEncrypted(-1));
+
+ connect(socketClient.data(), &QSslSocket::errorOccurred,
+ socketClient.data(), [](QAbstractSocket::SocketError error){
+ QCOMPARE(error, QAbstractSocket::RemoteHostClosedError);
+ });
+ m_client.addClientSideConnection(socketClient.data());
+
+ QScopedPointer<PingPongReplica> pp{m_client.acquire<PingPongReplica>()};
+ QVERIFY(pp->waitForSource());
+
+ QString pongStr;
+ connect(pp.data(), &PingPongReplica::pong, [&pongStr](const QString &str) {
+ pongStr = str;
+ });
+ pp->ping("yahoo");
+ QTRY_COMPARE(pongStr, "Pong yahoo");
+ pp->ping("one more");
+ QTRY_COMPARE(pongStr, "Pong one more");
+ pp->ping("last one");
+ QTRY_COMPARE(pongStr, "Pong last one");
+ pp->quit();
+ QTRY_VERIFY(serverProc.state() != QProcess::Running);
+ QCOMPARE(serverProc.exitCode(), 0);
+ }
+};
+
+QTEST_MAIN(tst_clientSSL)
+
+#include "tst_client.moc"
--- /dev/null
+
+#####################################################################
+## tst_integration Test:
+#####################################################################
+
+qt_internal_add_test(tst_integration
+ SOURCES
+ engine.cpp engine.h
+ speedometer.cpp speedometer.h
+ temperature.h
+ tst_integration.cpp
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+)
+qt6_add_repc_sources(tst_integration
+ engine.rep
+ ../repfiles/localdatacenter.rep
+ ../repfiles/tcpdatacenter.rep
+)
+qt6_add_repc_replicas(tst_integration
+ engine.rep
+ ../repfiles/localdatacenter.rep
+ ../repfiles/tcpdatacenter.rep
+)
+qt6_add_repc_merged(tst_integration
+ speedometer.rep
+ enum.rep
+ pod.rep
+)
+
+add_dependencies(tst_integration localsockettestserver)
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "engine.h"
+
+Engine::Engine(int cylinders, QObject *parent) :
+ EngineSimpleSource(cylinders, parent)
+{
+ setRpm(0);
+ setpurchasedPart(false);
+}
+
+Engine::~Engine()
+{
+}
+
+bool Engine::start()
+{
+ if (started())
+ return false; // already started
+
+ setStarted(true);
+ return true;
+}
+
+void Engine::increaseRpm(int deltaRpm)
+{
+ setRpm(rpm() + deltaRpm);
+}
+
+Temperature Engine::temperature()
+{
+ return _temperature;
+}
+
+void Engine::setTemperature(const Temperature &value)
+{
+ _temperature = value;
+}
+
+void Engine::setpurchasedPart(bool value)
+{
+ _purchasedPart = value;
+}
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTS_ENGINE_H
+#define TESTS_ENGINE_H
+
+#include "rep_engine_source.h"
+
+class Engine : public EngineSimpleSource
+{
+ Q_OBJECT
+ Q_PROPERTY(bool purchasedPart READ purchasedPart WRITE setpurchasedPart)
+
+public:
+ Engine(int cylinders = 4, QObject *parent = nullptr);
+ ~Engine() override;
+
+ bool start() override;
+ void increaseRpm(int deltaRpm) override;
+
+ void unnormalizedSignature(int, int) override {}
+
+ Temperature temperature() override;
+ void setTemperature(const Temperature &value);
+
+ void setSharedTemperature(const Temperature::Ptr &) override {}
+
+ bool purchasedPart() {return _purchasedPart;}
+
+public Q_SLOTS:
+ void setpurchasedPart(bool value);
+
+ QString myTestString() override { return _myTestString; }
+ void setMyTestString(QString value) override { _myTestString = value; }
+
+private:
+ bool _purchasedPart;
+ QString _myTestString;
+ Temperature _temperature;
+};
+
+#endif
--- /dev/null
+#include "temperature.h"
+
+class Engine
+{
+ ENUM EngineType { GAS=0, ELECTRIC=1, HYBRID=2 }
+ PROP(int cylinders = 4 CONSTANT);
+ PROP(bool started);
+ PROP(int rpm READWRITE);
+ PROP(EngineType engineType=GAS PERSISTED)
+
+ SLOT(bool start());
+ SLOT(void increaseRpm(int deltaRpm));
+
+ SLOT(void unnormalizedSignature(int a, int b));
+
+ SLOT(Temperature temperature())
+ SLOT(void setSharedTemperature(const Temperature::Ptr &sharedTemperature))
+
+ SLOT(QString myTestString())
+ SLOT(setMyTestString(QString value))
+};
--- /dev/null
+ENUM Test {TRUE, FALSE}
+
+class TestClass
+{
+ ENUM ClassEnum {Null, One, Two}
+ PROP(TestEnum::Test testEnum)
+ PROP(ClassEnum classEnum)
+ PROP(ClassEnum classEnumRW READWRITE)
+}
--- /dev/null
+POD MyPOD(int i, float f, QString s)
+
+class MyClass
+{
+ PROP(MyPOD myPOD)
+}
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "speedometer.h"
+
+Speedometer::Speedometer(QObject *parent) :
+ SpeedometerSimpleSource(parent)
+{
+}
+
+Speedometer::~Speedometer()
+{
+}
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTS_SPEEDOMETER_H
+#define TESTS_SPEEDOMETER_H
+
+#include "rep_speedometer_merged.h"
+
+class Speedometer : public SpeedometerSimpleSource
+{
+ Q_OBJECT
+public:
+ Speedometer(QObject *parent = nullptr);
+ ~Speedometer() override;
+
+private:
+ int speed;
+};
+
+#endif
--- /dev/null
+class Speedometer
+{
+ PROP(int mph);
+};
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TEMPERATURE_H
+#define TEMPERATURE_H
+
+#include <QSharedPointer>
+#include <QString>
+
+class Temperature
+{
+public:
+ typedef QSharedPointer<Temperature> Ptr;
+
+ Temperature() : _value(0) {}
+ Temperature(double value, const QString &unit) : _value(value), _unit(unit) {}
+
+ void setValue(double arg) { _value = arg; }
+ double value() const { return _value; }
+
+ void setUnit(const QString &arg) { _unit = arg; }
+ QString unit() const { return _unit; }
+
+private:
+ double _value;
+ QString _unit;
+};
+
+inline bool operator==(const Temperature &lhs, const Temperature &rhs)
+{
+ return lhs.unit() == rhs.unit() &&
+ lhs.value() == rhs.value();
+}
+
+inline bool operator!=(const Temperature &lhs, const Temperature &rhs)
+{
+ return !(lhs == rhs);
+}
+
+inline QDataStream &operator<<(QDataStream &out, const Temperature &temperature)
+{
+ out << temperature.value();
+ out << temperature.unit();
+ return out;
+}
+
+inline QDataStream &operator>>(QDataStream &in, Temperature &temperature)
+{
+ double value;
+ in >> value;
+ temperature.setValue(value);
+
+ QString unit;
+ in >> unit;
+ temperature.setUnit(unit);
+ return in;
+}
+
+inline QDataStream &operator>>(QDataStream &out, const Temperature::Ptr& temperaturePtr)
+{
+ out << *temperaturePtr;
+ return out;
+}
+
+inline QDataStream &operator>>(QDataStream &in, Temperature::Ptr& temperaturePtr)
+{
+ Temperature *temperature = new Temperature;
+ in >> *temperature;
+ temperaturePtr.reset(temperature);
+ return in;
+}
+
+Q_DECLARE_METATYPE(Temperature);
+Q_DECLARE_METATYPE(Temperature::Ptr);
+
+#endif
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "../../shared/testutils.h"
+
+#include <QtTest/QtTest>
+#include <QMetaType>
+#if QT_CONFIG(process)
+#include <QProcess>
+#endif
+#include <QFileInfo>
+#include <QTcpServer>
+#include <QTcpSocket>
+
+#include <QRemoteObjectReplica>
+#include <QRemoteObjectNode>
+#include <QRemoteObjectSettingsStore>
+#include "engine.h"
+#include "speedometer.h"
+#include "rep_engine_replica.h"
+#include "rep_speedometer_merged.h"
+#include "rep_enum_merged.h"
+#include "rep_pod_merged.h"
+#include "rep_localdatacenter_source.h"
+#include "rep_tcpdatacenter_source.h"
+#include "rep_localdatacenter_replica.h"
+#include "rep_tcpdatacenter_replica.h"
+
+#include "../../shared/testutils.h"
+
+#define SET_NODE_NAME(obj) (obj).setName(QLatin1String(#obj))
+
+//DUMMY impl for variant comparison
+bool operator<(const QList<int> &lhs, const QList<int> &rhs)
+{
+ return lhs.size() < rhs.size();
+}
+
+class TestLargeData: public QObject
+{
+ Q_OBJECT
+
+Q_SIGNALS:
+ void send(const QByteArray &data);
+};
+
+class TestDynamicBase : public QObject
+{
+ Q_OBJECT
+public:
+ TestDynamicBase(QObject *parent=nullptr) : QObject(parent) {}
+
+signals:
+ void otherValueChanged();
+};
+
+
+class TestDynamic : public TestDynamicBase
+{
+ Q_OBJECT
+ Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged)
+ Q_PROPERTY(int otherValue READ otherValue WRITE setOtherValue NOTIFY otherValueChanged)
+public:
+ TestDynamic(QObject *parent=nullptr) :
+ TestDynamicBase(parent),
+ m_value(0),
+ m_otherValue(0) {}
+
+ int value() const { return m_value; }
+ void setValue(int value)
+ {
+ if (m_value == value)
+ return;
+
+ m_value = value;
+ emit valueChanged();
+ }
+
+ int otherValue() const { return m_otherValue; }
+ void setOtherValue(int otherValue)
+ {
+ if (m_otherValue == otherValue)
+ return;
+
+ m_otherValue = otherValue;
+ emit otherValueChanged();
+ }
+
+signals:
+ void valueChanged();
+
+private:
+ int m_value;
+ int m_otherValue;
+};
+
+class TestPersistedStore : public QRemoteObjectAbstractPersistedStore
+{
+ Q_OBJECT
+
+public:
+ TestPersistedStore() : type(EngineReplica::HYBRID) {}
+ void saveProperties(const QString &, const QByteArray &, const QVariantList &values) override
+ {
+ type = values.at(0).value<EngineReplica::EngineType>();
+ }
+ QVariantList restoreProperties(const QString &, const QByteArray &) override
+ {
+ return QVariantList() << QVariant::fromValue(type);
+ }
+private:
+ EngineReplica::EngineType type;
+};
+
+class tst_Integration: public QObject
+{
+ Q_OBJECT
+
+ void setupTcp()
+ {
+ if (!tcpServer) {
+ tcpServer = new QTcpServer;
+ tcpServer->listen(QHostAddress::Any, 65511);
+ socketClient = new QTcpSocket;
+ socketClient->connectToHost(QHostAddress::LocalHost, tcpServer->serverPort());
+ QVERIFY(socketClient->waitForConnected(5000));
+
+ QVERIFY(tcpServer->waitForNewConnection(5000));
+ QVERIFY(tcpServer->hasPendingConnections());
+ socketServer = tcpServer->nextPendingConnection();
+ }
+ }
+
+ void setupHost(bool useRegistry=false)
+ {
+ QFETCH_GLOBAL(QUrl, hostUrl);
+ QFETCH_GLOBAL(QUrl, registryUrl);
+ host = new QRemoteObjectHost;
+ SET_NODE_NAME(*host);
+ if (!hostUrl.isEmpty()) {
+ host->setHostUrl(hostUrl);
+ if (useRegistry)
+ host->setRegistryUrl(registryUrl);
+ } else {
+ setupTcp();
+ host->addHostSideConnection(socketServer);
+ }
+ }
+
+ void setupClient(bool useRegistry=false)
+ {
+ QFETCH_GLOBAL(QUrl, hostUrl);
+ QFETCH_GLOBAL(QUrl, registryUrl);
+ client = new QRemoteObjectNode;
+ Q_SET_OBJECT_NAME(*client);
+ if (!hostUrl.isEmpty())
+ {
+ if (useRegistry)
+ client->setRegistryUrl(registryUrl);
+ else {
+ client->connectToNode(hostUrl);
+ }
+ } else {
+ setupTcp();
+ client->addClientSideConnection(socketClient);
+ }
+ }
+
+ void setupRegistry()
+ {
+ QFETCH_GLOBAL(QUrl, registryUrl);
+ registry = new QRemoteObjectRegistryHost(registryUrl);
+ SET_NODE_NAME(*registry);
+ }
+
+signals:
+ void forwardResult(int);
+
+private:
+ QRemoteObjectHost *host;
+ QRemoteObjectNode *client;
+ QRemoteObjectRegistryHost *registry;
+ QTcpServer *tcpServer;
+ QPointer<QTcpSocket> socketClient, socketServer;
+
+private slots:
+ void initTestCase_data()
+ {
+ QTest::addColumn<QUrl>("hostUrl");
+ QTest::addColumn<QUrl>("registryUrl");
+
+ QTest::newRow("tcp") << QUrl(QLatin1String("tcp://127.0.0.1:65511")) << QUrl(QLatin1String("tcp://127.0.0.1:65512"));
+#ifdef __QNXNTO__
+ QTest::newRow("qnx") << QUrl(QLatin1String("qnx:replica")) << QUrl(QLatin1String("qnx:registry"));
+#endif
+ QTest::newRow("local") << QUrl(QLatin1String(LOCAL_SOCKET ":replicaLocalIntegration")) << QUrl(QLatin1String(LOCAL_SOCKET ":registryLocalIntegration"));
+#ifdef Q_OS_LINUX
+ QTest::newRow("localabstract") << QUrl(QLatin1String("localabstract:replicaAbstractIntegration")) << QUrl(QLatin1String("localabstract:registryAbstractIntegration"));
+#endif
+ QTest::newRow("external") << QUrl() << QUrl();
+ }
+
+ void initTestCase()
+ {
+ QVERIFY(TestUtils::init("integration"));
+ QLoggingCategory::setFilterRules("qt.remoteobjects.warning=false");
+
+ // use different paths in QRemoteObjectSettingsStore
+ QCoreApplication::setOrganizationName(QLatin1String("QtProject"));
+ QStandardPaths::setTestModeEnabled(true);
+ }
+
+ void init()
+ {
+ registry = nullptr;
+ host = nullptr;
+ client = nullptr;
+ tcpServer = nullptr;
+ socketClient = nullptr;
+ socketServer = nullptr;
+ }
+
+ void cleanup()
+ {
+ delete registry;
+ delete host;
+ delete client;
+ delete tcpServer;
+ if (socketClient) {
+ socketClient->deleteLater();
+ }
+ if (socketServer) {
+ socketServer->deleteLater();
+ }
+ // wait for delivery of RemoveObject events to the source
+ QTest::qWait(200);
+ }
+
+ void basicTest()
+ {
+ setupHost();
+ Engine e;
+ e.setRpm(1234);
+ host->enableRemoting(&e);
+
+ setupClient();
+ const QScopedPointer<EngineReplica> engine_r(client->acquire<EngineReplica>());
+ engine_r->waitForSource();
+ QCOMPARE(engine_r->rpm(), e.rpm());
+ }
+
+ void persistRestoreTest()
+ {
+ QRemoteObjectNode _client;
+ Q_SET_OBJECT_NAME(_client);
+ TestPersistedStore store;
+ _client.setPersistedStore(&store);
+
+ const QScopedPointer<EngineReplica> engine_r(_client.acquire<EngineReplica>());
+ QCOMPARE(engine_r->engineType(), EngineReplica::HYBRID);
+ }
+
+ void persistTest()
+ {
+ QRemoteObjectSettingsStore store;
+
+ setupHost();
+ Engine e;
+ e.setEngineType(EngineSimpleSource::ELECTRIC);
+ host->enableRemoting(&e);
+
+ setupClient();
+ client->setPersistedStore(&store);
+
+ QScopedPointer<EngineReplica> engine_r(client->acquire<EngineReplica>());
+ engine_r->waitForSource();
+ QCOMPARE(engine_r->engineType(), EngineReplica::ELECTRIC);
+
+ // Delete to persist
+ engine_r.reset();
+ host->disableRemoting(&e);
+
+ engine_r.reset(client->acquire<EngineReplica>());
+ QCOMPARE(engine_r->waitForSource(1000), false);
+ QCOMPARE(engine_r->engineType(), EngineReplica::ELECTRIC);
+ }
+
+ // ensure we don't crash when ObjectList iterates over in process replicas
+ void inProcessObjectList()
+ {
+ setupRegistry();
+ setupHost(true);
+ setupClient(true);
+ Engine e;
+ host->enableRemoting(&e);
+ e.setStarted(false);
+
+ const QScopedPointer<EngineReplica> engine_r(host->acquire<EngineReplica>());
+ const QScopedPointer<EngineReplica> engine_r2(client->acquire<EngineReplica>());
+ engine_r->waitForSource(1000);
+ engine_r2->waitForSource(1000);
+ QCOMPARE(engine_r->started(), false);
+ QCOMPARE(engine_r2->started(), false);
+ engine_r->pushStarted(true);
+
+ QTRY_COMPARE(engine_r->started(), true);
+ QTRY_COMPARE(engine_r2->started(), true);
+ }
+
+ void enumTest()
+ {
+ setupHost();
+
+ setupClient();
+
+ TestClassSimpleSource tc;
+ tc.setTestEnum(TestEnum::FALSE);
+ tc.setClassEnum(TestClassSimpleSource::One);
+ tc.setClassEnumRW(TestClassSimpleSource::One);
+ host->enableRemoting(&tc);
+ const QScopedPointer<TestClassReplica> tc_rep(client->acquire<TestClassReplica>());
+ tc_rep->waitForSource();
+ QCOMPARE(tc.testEnum(), tc_rep->testEnum());
+ QCOMPARE(qint32(tc.classEnum()), qint32(TestClassSimpleSource::One));
+
+ // set property on the replica (test property change packet)
+ {
+ QSignalSpy spy(tc_rep.data(), &TestClassReplica::classEnumChanged);
+ QVERIFY(spy.isValid());
+ tc_rep->pushClassEnum(TestClassReplica::Two);
+ QVERIFY(spy.size() || spy.wait());
+
+ QCOMPARE(qint32(tc.classEnum()), qint32(tc_rep->classEnum()));
+ }
+
+ // set property on the source (test property change packet)
+ {
+ QSignalSpy spy(tc_rep.data(), &TestClassReplica::classEnumChanged);
+ tc.setClassEnum(TestClassSimpleSource::One);
+ QVERIFY(spy.wait());
+
+ QCOMPARE(qint32(tc.classEnum()), qint32(tc_rep->classEnum()));
+ }
+
+ QScopedPointer<QRemoteObjectDynamicReplica> tc_repDynamic(client->acquireDynamic(QStringLiteral("TestClass")));
+
+ tc_repDynamic->waitForSource(1000);
+ QVERIFY(tc_repDynamic->isInitialized());
+
+ const QMetaObject *metaObject = tc_repDynamic->metaObject();
+
+ int propertyIndex = metaObject->indexOfProperty("classEnumRW");
+ QVERIFY(propertyIndex >= 0);
+
+ QMetaProperty property = metaObject->property(propertyIndex);
+ QVERIFY(property.isValid());
+ QCOMPARE(property.typeName(), "TestClassReplica::ClassEnum");
+
+ // read enum on the dynamic replica
+ {
+ QCOMPARE(property.read(tc_repDynamic.data()).value<TestClassReplica::ClassEnum>(), TestClassReplica::One);
+ }
+
+ // write enum on the dynamic replica
+ {
+ QSignalSpy spy(tc_rep.data(), &TestClassReplica::classEnumRWChanged);
+ property.write(tc_repDynamic.data(), TestClassReplica::Two);
+ QVERIFY(spy.wait());
+
+ QCOMPARE(tc_rep->classEnumRW(), TestClassReplica::Two);
+ }
+
+ propertyIndex = metaObject->indexOfProperty("classEnum");
+ QVERIFY(propertyIndex >= 0);
+
+ property = metaObject->property(propertyIndex);
+ QVERIFY(property.isValid());
+ QCOMPARE(property.typeName(), "TestClassReplica::ClassEnum");
+
+ // read enum on the dynamic replica
+ {
+ QCOMPARE(property.read(tc_repDynamic.data()).value<TestClassReplica::ClassEnum>(), TestClassReplica::One);
+ }
+
+ // ensure write enum fails on ReadPush
+ {
+ QSignalSpy spy(tc_rep.data(), &TestClassReplica::classEnumChanged);
+ bool res = property.write(tc_repDynamic.data(), TestClassReplica::Two);
+ QVERIFY(!res);
+ int methodIndex = metaObject->indexOfMethod("pushClassEnum(TestClassReplica::ClassEnum)");
+ QVERIFY(methodIndex >= 0);
+ QMetaMethod method = metaObject->method(methodIndex);
+ QVERIFY(method.isValid());
+
+ QVERIFY(method.invoke(tc_repDynamic.data(), Q_ARG(TestClassReplica::ClassEnum, TestClassReplica::Two)));
+ QVERIFY(spy.wait());
+
+ QCOMPARE(tc_rep->classEnum(), TestClassReplica::Two);
+ }
+ }
+
+ void namedObjectTest()
+ {
+ setupHost();
+
+ setupClient();
+
+ Engine e;
+ e.setRpm(3333);
+ Engine *e2 = new Engine();
+ QScopedPointer<Engine> engineSave;
+ engineSave.reset(e2);
+ e2->setRpm(4444);
+ host->enableRemoting(&e);
+ host->enableRemoting(e2, QStringLiteral("MyTestEngine"));
+
+ const QScopedPointer<EngineReplica> engine_r(client->acquire<EngineReplica>());
+ const QScopedPointer<EngineReplica> namedEngine_r(client->acquire<EngineReplica>(QStringLiteral("MyTestEngine")));
+ engine_r->waitForSource();
+ QCOMPARE(engine_r->cylinders(), e.cylinders());
+ QCOMPARE(engine_r->rpm(), 3333);
+ namedEngine_r->waitForSource();
+ QCOMPARE(namedEngine_r->cylinders(), e2->cylinders());
+ QCOMPARE(namedEngine_r->rpm(), 4444);
+
+ engineSave.reset();
+ //Deleting the object before disable remoting will cause disable remoting to
+ //return false;
+ QVERIFY(!host->disableRemoting(e2));
+ }
+
+ void multipleInstancesTest()
+ {
+ setupHost();
+ Engine e;
+ host->enableRemoting(&e);
+
+ setupClient();
+
+ auto instances = client->instances<EngineReplica>();
+ QCOMPARE(instances, QStringList());
+
+ Engine e2;
+ host->enableRemoting(&e2, QStringLiteral("Engine2"));
+
+ const QScopedPointer<EngineReplica> engine_r(client->acquire<EngineReplica>());
+ const QScopedPointer<EngineReplica> engine2_r(client->acquire<EngineReplica>(QStringLiteral("Engine2")));
+ const QScopedPointer<EngineReplica> engine3_r(client->acquire<EngineReplica>(QStringLiteral("Engine_doesnotexist")));
+ QVERIFY(engine_r->waitForSource());
+ QVERIFY(engine2_r->waitForSource());
+ QVERIFY(!engine3_r->waitForSource(500));
+
+ instances = client->instances<EngineReplica>();
+ QCOMPARE(instances, QStringList({"Engine", "Engine2"}));
+
+ QSignalSpy spy(engine_r.data(), &QRemoteObjectReplica::stateChanged);
+ host->disableRemoting(&e);
+ spy.wait();
+ QCOMPARE(spy.size(), 1);
+
+ instances = client->instances<EngineReplica>();
+ QCOMPARE(instances, QStringList({"Engine2"}));
+ }
+
+ void registrySourceLocationBindings()
+ {
+ QFETCH_GLOBAL(QUrl, registryUrl);
+ QFETCH_GLOBAL(QUrl, hostUrl);
+ if (registryUrl.isEmpty())
+ QSKIP("Skipping registry tests for external QIODevice types.");
+
+ setupRegistry();
+ setupHost(true);
+ setupClient(true);
+
+ QVERIFY(host->registry()->sourceLocations().empty());
+ QVERIFY(client->registry()->sourceLocations().empty());
+
+ QVERIFY(host->registry()->bindableSourceLocations().isReadOnly());
+ QVERIFY(client->registry()->bindableSourceLocations().isReadOnly());
+
+ Engine e1;
+ const auto engine1 = QStringLiteral("Engine1");
+ Engine e2;
+ const auto engine2 = QStringLiteral("Engine2");
+
+ QRemoteObjectSourceLocations expectedSourceLocations;
+ expectedSourceLocations[engine1] = { QStringLiteral("Engine"), hostUrl };
+
+ int hostSrcLocationsChanged = 0;
+ auto hostHandler = host->registry()->bindableSourceLocations().onValueChanged([&] {
+ QCOMPARE(host->registry()->sourceLocations(), expectedSourceLocations);
+ ++hostSrcLocationsChanged;
+ });
+
+ int clientSrcLocationsChanged = 0;
+ auto clientHandler = client->registry()->bindableSourceLocations().onValueChanged([&] {
+ QCOMPARE(client->registry()->sourceLocations(), expectedSourceLocations);
+ ++clientSrcLocationsChanged;
+ });
+
+ QProperty<QRemoteObjectSourceLocations> hostObserver;
+ hostObserver.setBinding([&] { return host->registry()->sourceLocations(); });
+
+ QProperty<QRemoteObjectSourceLocations> clientObserver;
+ clientObserver.setBinding([&] { return client->registry()->sourceLocations(); });
+
+ QSignalSpy hostSpy(host->registry(), &QRemoteObjectRegistry::remoteObjectAdded);
+ QSignalSpy clientSpy(client->registry(), &QRemoteObjectRegistry::remoteObjectAdded);
+
+ host->enableRemoting(&e1, engine1);
+ QTRY_COMPARE(hostSpy.size(), 1);
+ QTRY_COMPARE(clientSpy.size(), 1);
+ QCOMPARE(hostObserver.value(), host->registry()->sourceLocations());
+ QCOMPARE(clientObserver.value(), client->registry()->sourceLocations());
+ QCOMPARE(hostObserver.value(), clientObserver.value());
+ QCOMPARE(hostObserver.value(), expectedSourceLocations);
+ QCOMPARE(hostSrcLocationsChanged, 1);
+ QCOMPARE(clientSrcLocationsChanged, 1);
+
+ expectedSourceLocations[engine2] = { QStringLiteral("Engine"), hostUrl };
+ host->enableRemoting(&e2, engine2);
+ QTRY_COMPARE(hostSpy.size(), 2);
+ QTRY_COMPARE(clientSpy.size(), 2);
+ QCOMPARE(hostObserver.value(), host->registry()->sourceLocations());
+ QCOMPARE(clientObserver.value(), client->registry()->sourceLocations());
+ QCOMPARE(hostObserver.value(), clientObserver.value());
+ QCOMPARE(hostObserver.value(), expectedSourceLocations);
+ QCOMPARE(hostSrcLocationsChanged, 2);
+ QCOMPARE(clientSrcLocationsChanged, 2);
+
+ // Test source removal
+ host->disableRemoting(&e1);
+ expectedSourceLocations.remove(engine1);
+ QSignalSpy srcRemovedHostSpy(host->registry(), &QRemoteObjectRegistry::remoteObjectRemoved);
+ QSignalSpy srcRemovedClientSpy(client->registry(),
+ &QRemoteObjectRegistry::remoteObjectRemoved);
+
+ QTRY_COMPARE(srcRemovedHostSpy.size(), 1);
+ QTRY_COMPARE(srcRemovedClientSpy.size(), 1);
+ QCOMPARE(hostObserver.value(), host->registry()->sourceLocations());
+ QCOMPARE(clientObserver.value(), client->registry()->sourceLocations());
+ QCOMPARE(hostObserver.value(), clientObserver.value());
+ QCOMPARE(hostObserver.value(), expectedSourceLocations);
+ QCOMPARE(hostSrcLocationsChanged, 3);
+ QCOMPARE(clientSrcLocationsChanged, 3);
+ }
+
+ void registryAddedTest()
+ {
+ QFETCH_GLOBAL(QUrl, registryUrl);
+ if (registryUrl.isEmpty())
+ QSKIP("Skipping registry tests for external QIODevice types.");
+ setupRegistry();
+
+ setupHost(true);
+
+ setupClient(true);
+
+ QScopedPointer<EngineReplica> regBase, regNamed;
+ QScopedPointer<QRemoteObjectDynamicReplica> regDynamic, regDynamicNamed;
+
+ int regAdded = 0;
+ connect(client->registry(), &QRemoteObjectRegistry::remoteObjectAdded, [&](QRemoteObjectSourceLocation entry)
+ {
+ if (entry.first == QLatin1String("Engine")) {
+ ++regAdded;
+ //Add regular replica first, then dynamic one
+ regBase.reset(client->acquire<EngineReplica>());
+ regDynamic.reset(client->acquireDynamic(QStringLiteral("Engine")));
+ }
+ if (entry.first == QLatin1String("MyTestEngine")) {
+ regAdded += 2;
+ //Now add dynamic replica first, then regular one
+ regDynamicNamed.reset(client->acquireDynamic(QStringLiteral("MyTestEngine")));
+ regNamed.reset(client->acquire<EngineReplica>(QStringLiteral("MyTestEngine")));
+ }
+ });
+
+ QSignalSpy addedSpy(client->registry(), &QRemoteObjectRegistry::remoteObjectAdded);
+
+ Engine e;
+ e.setRpm(1111);
+ host->enableRemoting(&e);
+ Engine e2;
+ e2.setRpm(2222);
+ host->enableRemoting(&e2, QStringLiteral("MyTestEngine"));
+ while (regAdded < 3) {
+ addedSpy.wait(100);
+ }
+ regBase->waitForSource(100);
+ regNamed->waitForSource(100);
+ regDynamic->waitForSource(100);
+ regDynamicNamed->waitForSource(100);
+ QVERIFY(regBase->isInitialized());
+ QCOMPARE(regBase->rpm(),e.rpm());
+ QVERIFY(regNamed->isInitialized());
+ QCOMPARE(regNamed->rpm(),e2.rpm());
+
+ QVERIFY(regDynamic->isInitialized());
+ const QMetaObject *metaObject = regDynamic->metaObject();
+
+ const int propertyIndex = metaObject->indexOfProperty("rpm");
+ QVERIFY(propertyIndex >= 0);
+ const QMetaProperty property = metaObject->property(propertyIndex);
+ QVERIFY(property.isValid());
+
+ QCOMPARE(property.read(regDynamic.data()).toInt(),e.rpm());
+
+ QVERIFY(regDynamicNamed->isInitialized());
+ QCOMPARE(property.read(regDynamicNamed.data()).toInt(),e2.rpm());
+
+ QVERIFY(host->disableRemoting(&e));
+ QVERIFY(host->disableRemoting(&e2));
+ }
+
+ void registryTest()
+ {
+ QFETCH_GLOBAL(QUrl, registryUrl);
+ if (registryUrl.isEmpty())
+ QSKIP("Skipping registry tests for external QIODevice types.");
+ setupRegistry();
+ TcpDataCenterSimpleSource source1;
+ source1.setData1(5);
+ source1.setData2(5.0);
+ source1.setData3(QStringLiteral("tcp"));
+ source1.setData4(QList<int> { 1, 2, 3, 4, 5 });
+ registry->enableRemoting(&source1);
+
+ setupHost(true);
+ LocalDataCenterSimpleSource source2;
+ source2.setData1(5);
+ source2.setData2(5.0);
+ source2.setData3(QStringLiteral("local"));
+ source2.setData4(QList<int> { 1, 2, 3, 4, 5 });
+ host->enableRemoting(&source2);
+ QVERIFY(host->waitForRegistry(1000));
+
+ setupClient(true);
+
+ const QScopedPointer<TcpDataCenterReplica> tcpCentre(client->acquire<TcpDataCenterReplica>());
+ const QScopedPointer<LocalDataCenterReplica> localCentre(client->acquire<LocalDataCenterReplica>());
+ QTRY_VERIFY(localCentre->waitForSource(100));
+ QTRY_VERIFY(tcpCentre->waitForSource(100));
+
+ QCOMPARE(client->registry()->sourceLocations(), host->registry()->sourceLocations());
+ QCOMPARE(client->registry()->sourceLocations(), registry->registry()->sourceLocations());
+ QTRY_VERIFY(localCentre->isInitialized());
+ QTRY_VERIFY(tcpCentre->isInitialized());
+
+ const QList<int> expected = { 1, 2, 3, 4, 5 };
+ QCOMPARE(tcpCentre->data1(), 5 );
+ QCOMPARE(tcpCentre->data2(), 5.0);
+ QCOMPARE(tcpCentre->data3(), QStringLiteral("tcp"));
+ QCOMPARE(tcpCentre->data4(), expected);
+
+ QCOMPARE(localCentre->data1(), 5);
+ QCOMPARE(localCentre->data2(), 5.0);
+ QCOMPARE(localCentre->data3(), QStringLiteral("local"));
+ QCOMPARE(localCentre->data4(), expected);
+ }
+
+ void invalidUrlsTest()
+ {
+ QFETCH_GLOBAL(QUrl, hostUrl);
+ QFETCH_GLOBAL(QUrl, registryUrl);
+ const QUrl invalidUrl;
+ {
+ QRemoteObjectHost _host(invalidUrl, registryUrl);
+ SET_NODE_NAME(_host);
+ const bool res = _host.waitForRegistry(3000);
+ QVERIFY(!res);
+ }
+
+ {
+ QRemoteObjectHost _host(hostUrl, invalidUrl);
+ SET_NODE_NAME(_host);
+ const bool res = _host.waitForRegistry(3000);
+ QVERIFY(!res);
+ }
+
+ {
+ QRemoteObjectHost _host(invalidUrl, invalidUrl);
+ SET_NODE_NAME(_host);
+ const bool res = _host.waitForRegistry(3000);
+ QVERIFY(!res);
+ }
+ }
+
+ void noRegistryTest()
+ {
+ QFETCH_GLOBAL(QUrl, registryUrl);
+ if (registryUrl.isEmpty())
+ QSKIP("Skipping registry tests for external QIODevice types.");
+ setupHost(true);
+ const bool res = host->waitForRegistry(3000);
+ QVERIFY(!res);
+ QCOMPARE(host->registry()->isInitialized(), false);
+ const QScopedPointer<Engine> localEngine(new Engine);
+ host->enableRemoting(localEngine.data());
+ QCOMPARE(host->registry()->sourceLocations().keys().isEmpty(), true);
+ }
+
+ void delayedRegistryTest()
+ {
+ QFETCH_GLOBAL(QUrl, hostUrl);
+ QFETCH_GLOBAL(QUrl, registryUrl);
+ if (registryUrl.isEmpty())
+ QSKIP("Skipping registry tests for external QIODevice types.");
+ setupClient(true);
+
+ // create a replica before the registry host started
+ // to check whether it gets valid later on
+ const QScopedPointer<EngineReplica> engine_r(client->acquire<EngineReplica>());
+ Q_SET_OBJECT_NAME(engine_r.data());
+ QTRY_VERIFY(!engine_r->waitForSource(100));
+
+ setupHost(true);
+ const bool res = host->waitForRegistry(3000);
+ QVERIFY(!res);
+ QCOMPARE(host->registry()->isInitialized(), false);
+
+ const QScopedPointer<Engine> localEngine(new Engine);
+ host->enableRemoting(localEngine.data());
+ QCOMPARE(host->registry()->sourceLocations().keys().isEmpty(), true);
+
+ QSignalSpy spy(host->registry(), &QRemoteObjectRegistry::initialized);
+ QSignalSpy addedSpy(host->registry(), &QRemoteObjectRegistry::remoteObjectAdded);
+ setupRegistry();
+ bool added = addedSpy.wait();
+ QVERIFY(spy.size() > 0);
+ QCOMPARE(added, true);
+ QCOMPARE(host->registry()->sourceLocations().keys().isEmpty(), false);
+ QCOMPARE(host->registry()->sourceLocations().keys().at(0), QStringLiteral("Engine"));
+ QCOMPARE(host->registry()->sourceLocations().value(QStringLiteral("Engine")).hostUrl, hostUrl);
+
+ // the replicate should be valid now
+ QTRY_VERIFY(engine_r->isInitialized());
+ QTRY_VERIFY(engine_r->isReplicaValid());
+
+ //This should produce a warning...
+ registry->enableRemoting(localEngine.data());
+ QVERIFY(host->registry()->sourceLocations().value(QStringLiteral("Engine")).hostUrl != registryUrl);
+ }
+
+ void defaultValueTest()
+ {
+ setupHost();
+ Engine e;
+ host->enableRemoting(&e);
+
+ setupClient();
+
+ const QScopedPointer<EngineReplica> engine_r(client->acquire<EngineReplica>());
+ engine_r->waitForSource();
+ QCOMPARE(engine_r->cylinders(), 4);
+ }
+
+ void notifyTest()
+ {
+ setupHost();
+ Engine e;
+ host->enableRemoting(&e);
+
+ setupClient();
+
+ const QScopedPointer<EngineReplica> engine_r(client->acquire<EngineReplica>());
+ QSignalSpy spy(engine_r.data(), &EngineReplica::rpmChanged);
+ e.setRpm(2345);
+
+ spy.wait();
+ QCOMPARE(spy.size(), 1);
+ const QVariantList &arguments = spy.first();
+ bool ok;
+ int res = arguments.at(0).toInt(&ok);
+ QVERIFY(ok);
+ QCOMPARE(res, e.rpm());
+ QCOMPARE(engine_r->rpm(), e.rpm());
+ }
+
+ void dynamicNotifyTest()
+ {
+ setupHost();
+ Engine e;
+ host->enableRemoting(&e);
+
+ setupClient();
+
+ QSignalSpy spy(this, &tst_Integration::forwardResult);
+ QScopedPointer<QRemoteObjectDynamicReplica> engine_dr(client->acquireDynamic(QStringLiteral("Engine")));
+ connect(engine_dr.data(), &QRemoteObjectDynamicReplica::initialized, [&]()
+ {
+ const QMetaObject *metaObject = engine_dr->metaObject();
+ const int propIndex = metaObject->indexOfProperty("rpm");
+ QVERIFY(propIndex >= 0);
+ const QMetaProperty mp = metaObject->property(propIndex);
+ QVERIFY(connect(engine_dr.data(), QByteArray(QByteArrayLiteral("2")+mp.notifySignal().methodSignature().constData()), this, SIGNAL(forwardResult(int))));
+ });
+ e.setRpm(3456);
+ spy.wait();
+ QCOMPARE(spy.size(), 1);
+ const QVariantList &arguments = spy.first();
+ bool ok;
+ int res = arguments.at(0).toInt(&ok);
+ QVERIFY(ok);
+ QCOMPARE(res, e.rpm());
+ }
+
+ void slotTest()
+ {
+ setupHost();
+ Engine e;
+ host->enableRemoting(&e);
+
+ setupClient();
+ e.setStarted(false);
+
+ const QScopedPointer<EngineReplica> engine_r(client->acquire<EngineReplica>());
+ QEventLoop loop;
+ QTimer::singleShot(100, &loop, &QEventLoop::quit);
+ connect(engine_r.data(), &EngineReplica::initialized, &loop, &QEventLoop::quit);
+ if (!engine_r->isInitialized())
+ loop.exec();
+ QCOMPARE(engine_r->started(), false);
+
+ QRemoteObjectPendingReply<bool> reply = engine_r->start();
+ QCOMPARE(reply.error(), QRemoteObjectPendingCall::InvalidMessage);
+ QVERIFY(reply.waitForFinished());
+ QVERIFY(reply.isFinished());
+ QCOMPARE(reply.returnValue(), true);
+ QCOMPARE(reply.error(), QRemoteObjectPendingCall::NoError);
+
+ QCOMPARE(engine_r->started(), true);
+ }
+
+ void slotTestWithWatcher()
+ {
+ setupHost();
+ Engine e;
+ host->enableRemoting(&e);
+
+ setupClient();
+ e.setStarted(false);
+
+ const QScopedPointer<EngineReplica> engine_r(client->acquire<EngineReplica>());
+ QEventLoop loop;
+ QTimer::singleShot(100, &loop, &QEventLoop::quit);
+ connect(engine_r.data(), &EngineReplica::initialized, &loop, &QEventLoop::quit);
+ if (!engine_r->isInitialized())
+ loop.exec();
+ QCOMPARE(engine_r->started(), false);
+
+ QRemoteObjectPendingReply<bool> reply = engine_r->start();
+ QCOMPARE(reply.error(), QRemoteObjectPendingCall::InvalidMessage);
+
+ QRemoteObjectPendingCallWatcher watcher(reply);
+ QSignalSpy spy(&watcher, &QRemoteObjectPendingCallWatcher::finished);
+ spy.wait();
+ QCOMPARE(spy.size(), 1);
+
+ QVERIFY(reply.isFinished());
+ QCOMPARE(reply.returnValue(), true);
+ QCOMPARE(engine_r->started(), true);
+ }
+
+ void slotTestDynamicReplica()
+ {
+ setupHost();
+ Engine e;
+ host->enableRemoting(&e);
+
+ setupClient();
+ e.setStarted(false);
+
+ const QScopedPointer<QRemoteObjectDynamicReplica> engine_r(client->acquireDynamic(QStringLiteral("Engine")));
+ Q_ASSERT(engine_r);
+ QEventLoop loop;
+ QTimer::singleShot(100, &loop, &QEventLoop::quit);
+ connect(engine_r.data(), &EngineReplica::initialized, &loop, &QEventLoop::quit);
+ if (!engine_r->isInitialized())
+ loop.exec();
+
+ const QMetaObject *metaObject = engine_r->metaObject();
+ const int propIndex = metaObject->indexOfProperty("started");
+ QVERIFY(propIndex >= 0);
+ const QMetaProperty property = metaObject->property(propIndex);
+ bool started = property.read(engine_r.data()).value<bool>();
+ QCOMPARE(started, false);
+
+ const int methodIndex = metaObject->indexOfMethod("start()");
+ QVERIFY(methodIndex >= 0);
+ const QMetaMethod method = metaObject->method(methodIndex);
+ QRemoteObjectPendingCall call;
+ QVERIFY(method.invoke(engine_r.data(), Q_RETURN_ARG(QRemoteObjectPendingCall, call)));
+ QCOMPARE(call.error(), QRemoteObjectPendingCall::InvalidMessage);
+ QVERIFY(call.waitForFinished());
+ QVERIFY(call.isFinished());
+ QCOMPARE(call.returnValue().metaType(), QMetaType::fromType<bool>());
+ QCOMPARE(call.returnValue().toBool(), true);
+ started = property.read(engine_r.data()).value<bool>();
+ QCOMPARE(started, true);
+ }
+
+ void slotTestDynamicReplicaWithArguments()
+ {
+ setupHost();
+ Engine e;
+ host->enableRemoting(&e);
+
+ setupClient();
+
+ const QScopedPointer<QRemoteObjectDynamicReplica> engine_r(client->acquireDynamic(QStringLiteral("Engine")));
+ Q_ASSERT(engine_r);
+ bool ok = engine_r->waitForSource();
+ QVERIFY(ok);
+ const QMetaObject *metaObject = engine_r->metaObject();
+
+ int methodIndex = metaObject->indexOfMethod("setMyTestString(QString)");
+ QVERIFY(methodIndex >= 0);
+ QMetaMethod method = metaObject->method(methodIndex);
+ QVERIFY(method.isValid());
+
+ // The slot has no return-value, calling it with a Q_RETURN_ARG should fail.
+ QRemoteObjectPendingCall setCall;
+ QString s = QLatin1String("Hello World 1");
+ QVERIFY(!method.invoke(engine_r.data(), Q_RETURN_ARG(QRemoteObjectPendingCall, setCall), Q_ARG(QString, s)));
+ QVERIFY(!setCall.waitForFinished());
+ QVERIFY(!setCall.isFinished());
+ QCOMPARE(setCall.error(), QRemoteObjectPendingCall::InvalidMessage);
+
+ // Now call the method without return-value, that should succeed.
+ s = QLatin1String("Hello World 2");
+ QVERIFY(method.invoke(engine_r.data(), Q_ARG(QString, s)));
+
+ // Verify that the passed argument was proper set.
+ methodIndex = metaObject->indexOfMethod("myTestString()");
+ QVERIFY(methodIndex >= 0);
+ method = metaObject->method(methodIndex);
+ QRemoteObjectPendingCall getCall;
+ QVERIFY(method.invoke(engine_r.data(), Q_RETURN_ARG(QRemoteObjectPendingCall, getCall)));
+ QVERIFY(getCall.waitForFinished());
+ QVERIFY(getCall.isFinished());
+ QCOMPARE(getCall.error(), QRemoteObjectPendingCall::NoError);
+ QCOMPARE(getCall.returnValue().metaType(), QMetaType::fromType<QString>());
+ QCOMPARE(getCall.returnValue().toString(), s);
+ }
+
+ void expapiTestDynamicReplica()
+ {
+ setupHost();
+ Engine e;
+ host->enableRemoting(&e);
+
+ setupClient();
+
+ const QScopedPointer<QRemoteObjectDynamicReplica> engine_r(client->acquireDynamic(QStringLiteral("Engine")));
+ const QMetaObject *metaObject = engine_r->metaObject();
+ const int propIndex = metaObject->indexOfProperty("purchasedPart");
+ QVERIFY(propIndex < 0);
+ const int methodIndex = metaObject->indexOfMethod("setpurchasedPart(bool)");
+ QVERIFY(methodIndex < 0);
+ }
+
+ void slotTestInProcess()
+ {
+ setupHost();
+ Engine e;
+ host->enableRemoting(&e);
+ e.setStarted(false);
+
+ const QScopedPointer<EngineReplica> engine_r(host->acquire<EngineReplica>());
+ engine_r->waitForSource();
+ QCOMPARE(engine_r->started(), false);
+
+ QRemoteObjectPendingReply<bool> reply = engine_r->start();
+ QVERIFY(reply.waitForFinished());
+ QVERIFY(reply.isFinished());
+ QCOMPARE(reply.returnValue(), true);
+ QCOMPARE(reply.error(), QRemoteObjectPendingCall::NoError);
+
+ QCOMPARE(engine_r->started(), true);
+ }
+
+ void slotTestWithUnnormalizedSignature()
+ {
+ setupHost();
+ Engine e;
+ host->enableRemoting(&e);
+
+ setupClient();
+
+ const QScopedPointer<EngineReplica> engine_r(client->acquire<EngineReplica>());
+ engine_r->waitForSource();
+
+ engine_r->unnormalizedSignature(0, 0);
+ }
+
+ void setterTest()
+ {
+ setupHost();
+ Engine e(6);
+ QCOMPARE(e.cylinders(), 6);
+ host->enableRemoting(&e);
+
+ setupClient();
+
+ const QScopedPointer<EngineReplica> engine_r(client->acquire<EngineReplica>());
+ QCOMPARE(engine_r->cylinders(), 4); // Default value
+ engine_r->waitForSource();
+ QCOMPARE(engine_r->cylinders(), 6);
+ QSignalSpy spy(engine_r.data(), &EngineReplica::rpmChanged);
+ engine_r->setRpm(42);
+ spy.wait();
+ QCOMPARE(spy.size(), 1);
+ QCOMPARE(engine_r->rpm(), 42);
+ }
+
+ void pushTest()
+ {
+ setupHost();
+ Engine e;
+ host->enableRemoting(&e);
+
+ setupClient();
+
+ const QScopedPointer<EngineReplica> engine_r(client->acquire<EngineReplica>());
+ engine_r->waitForSource();
+ QCOMPARE(engine_r->started(), false);
+ QSignalSpy spy(engine_r.data(), &EngineReplica::startedChanged);
+ engine_r->pushStarted(true);
+ spy.wait();
+ QCOMPARE(spy.size(), 1);
+ QCOMPARE(engine_r->started(), true);
+ }
+
+ void dynamicSetterTest()
+ {
+ setupHost();
+ Engine e(6);
+ QCOMPARE(e.cylinders(), 6);
+ host->enableRemoting(&e);
+
+ setupClient();
+
+ const QScopedPointer<QRemoteObjectDynamicReplica> engine_dr(client->acquireDynamic(QStringLiteral("Engine")));
+ engine_dr->waitForSource();
+ const QMetaObject *metaObject = engine_dr->metaObject();
+ const QMetaProperty const_mp = metaObject->property(metaObject->indexOfProperty("cylinders"));
+ QCOMPARE(const_mp.read(engine_dr.data()).toInt(), 6);
+ const int propIndex = metaObject->indexOfProperty("rpm");
+ const QMetaProperty mp = metaObject->property(propIndex);
+ QSignalSpy spy(engine_dr.data(), QByteArray(QByteArrayLiteral("2")+mp.notifySignal().methodSignature().constData()));
+ mp.write(engine_dr.data(), 44);
+ spy.wait();
+ QCOMPARE(spy.size(), 1);
+ QCOMPARE(mp.read(engine_dr.data()).toInt(), 44);
+ }
+
+ void slotWithParameterTest()
+ {
+ setupHost();
+ Engine e;
+ host->enableRemoting(&e);
+ e.setRpm(0);
+
+ setupClient();
+
+ const QScopedPointer<EngineReplica> engine_r(client->acquire<EngineReplica>());
+ engine_r->waitForSource();
+ QCOMPARE(engine_r->rpm(), 0);
+
+ QSignalSpy spy(engine_r.data(), &EngineReplica::rpmChanged);
+ engine_r->increaseRpm(1000);
+ spy.wait();
+ QCOMPARE(spy.size(), 1);
+ QCOMPARE(engine_r->rpm(), 1000);
+ }
+
+ void slotWithUserReturnTypeTest() {
+ setupHost();
+ Engine e;
+ host->enableRemoting(&e);
+
+ setupClient();
+
+ e.setTemperature(Temperature(400, QStringLiteral("Kelvin")));
+
+ const QScopedPointer<EngineReplica> engine_r(client->acquire<EngineReplica>());
+ engine_r->waitForSource();
+ QRemoteObjectPendingReply<Temperature> pendingReply = engine_r->temperature();
+ pendingReply.waitForFinished();
+ Temperature temperature = pendingReply.returnValue();
+ QCOMPARE(temperature, Temperature(400, QStringLiteral("Kelvin")));
+ }
+
+ void sequentialReplicaTest()
+ {
+ setupHost();
+ Engine e;
+ host->enableRemoting(&e);
+
+ setupClient();
+
+ e.setRpm(3456);
+
+ QScopedPointer<EngineReplica> engine_r(client->acquire<EngineReplica>());
+ engine_r->waitForSource();
+ QCOMPARE(engine_r->rpm(), e.rpm());
+
+ engine_r.reset(client->acquire<EngineReplica>());
+ engine_r->waitForSource();
+ QCOMPARE(engine_r->rpm(), e.rpm());
+ }
+
+ void doubleReplicaTest()
+ {
+ setupHost();
+ Engine e;
+ host->enableRemoting(&e);
+ e.setRpm(3412);
+
+ setupClient();
+
+ const QScopedPointer<EngineReplica> engine_r1(client->acquire< EngineReplica >());
+ const QScopedPointer<EngineReplica> engine_r2(client->acquire< EngineReplica >());
+
+ engine_r1->waitForSource();
+ engine_r2->waitForSource();
+
+ QCOMPARE(engine_r1->rpm(), e.rpm());
+ QCOMPARE(engine_r2->rpm(), e.rpm());
+ }
+
+ // verify that our second replica emits "Changed" signals when initialized
+ void doubleReplicaTest2()
+ {
+ setupHost();
+ Engine e;
+ host->enableRemoting(&e);
+ e.setRpm(3412);
+
+ setupClient();
+
+ const QScopedPointer<EngineReplica> engine_r1(client->acquire< EngineReplica >());
+ QSignalSpy spy_r1(engine_r1.data(), &EngineReplica::rpmChanged);
+ engine_r1->waitForSource();
+ QCOMPARE(engine_r1->rpm(), e.rpm());
+ QCOMPARE(spy_r1.size(), 1);
+
+ // NOTE: A second replica will have initialized and notify signals emitted as part of acquire,
+ // which leads to different semantics for first and second replicas. Specifically, there is no
+ // way to hook in to initialized and the initial notify signals. We should consider changing this.
+ const QScopedPointer<EngineReplica> engine_r2(client->acquire< EngineReplica >());
+// QSignalSpy spy_r2(engine_r2.data(), &EngineReplica::rpmChanged);
+// engine_r2->waitForSource();
+ QCOMPARE(engine_r2->rpm(), e.rpm());
+// QCOMPARE(spy_r2.count(), 1);
+ }
+
+ void twoReplicaTest() {
+ setupHost();
+ Engine e;
+ Speedometer s;
+ host->enableRemoting(&e);
+ host->enableRemoting(&s);
+
+ setupClient();
+
+ e.setRpm(1234);
+ s.setMph(70);
+
+ const QScopedPointer<EngineReplica> engine_r(client->acquire<EngineReplica>());
+ engine_r->waitForSource();
+ const QScopedPointer<SpeedometerReplica> speedometer_r(client->acquire<SpeedometerReplica>());
+ speedometer_r->waitForSource();
+
+ QCOMPARE(engine_r->rpm(), e.rpm());
+ QCOMPARE(speedometer_r->mph(), s.mph());
+ }
+
+ void rawDynamicReplicaTest()
+ {
+ setupHost();
+ TestDynamic source;
+ host->enableRemoting(&source, "TestDynamic");
+
+ setupClient();
+
+ const QScopedPointer<QRemoteObjectDynamicReplica> replica(client->acquireDynamic(QStringLiteral("TestDynamic")));
+ replica->waitForSource();
+ QVERIFY(replica->isInitialized());
+
+ QSignalSpy spy(replica.data(), SIGNAL(valueChanged()));
+
+ const QMetaObject *metaObject = replica->metaObject();
+ const int propIndex = metaObject->indexOfProperty("value");
+ QVERIFY(propIndex != -1);
+ const int signalIndex = metaObject->indexOfSignal("valueChanged()");
+ QVERIFY(signalIndex != -1);
+
+ // replica gets source change
+ source.setValue(1);
+ QTRY_COMPARE(spy.size(), 1);
+ QCOMPARE(replica->property("value"), QVariant(1));
+
+ // source gets replica change
+ replica->setProperty("value", 2);
+ QTRY_COMPARE(replica->property("value"), QVariant(2));
+ QCOMPARE(source.value(), 2);
+
+ // test parent NOTIFY
+ QSignalSpy otherSpy(replica.data(), SIGNAL(otherValueChanged()));
+
+ const int baseSignalIndex = metaObject->indexOfSignal("otherValueChanged()");
+ QVERIFY(baseSignalIndex != -1);
+
+ // replica gets source change
+ source.setOtherValue(1);
+ QTRY_COMPARE(otherSpy.size(), 1);
+ QCOMPARE(replica->property("otherValue"), QVariant(1));
+
+ // source gets replica change
+ replica->setProperty("otherValue", 2);
+ QTRY_COMPARE(replica->property("otherValue"), QVariant(2));
+ QCOMPARE(source.otherValue(), 2);
+ }
+
+ void dynamicReplicaTest()
+ {
+ setupHost();
+ TcpDataCenterSimpleSource t;
+ LocalDataCenterSimpleSource l;
+ host->enableRemoting(&t);
+ host->enableRemoting(&l);
+
+ setupClient();
+
+ const QScopedPointer<QRemoteObjectDynamicReplica> rep1(client->acquireDynamic(QStringLiteral("TcpDataCenter")));
+ const QScopedPointer<QRemoteObjectDynamicReplica> rep2(client->acquireDynamic(QStringLiteral("TcpDataCenter")));
+ const QScopedPointer<QRemoteObjectDynamicReplica> rep3(client->acquireDynamic(QStringLiteral("LocalDataCenter")));
+ rep1->waitForSource();
+ rep2->waitForSource();
+ rep3->waitForSource();
+ const QMetaObject *metaTcpRep1 = rep1->metaObject();
+ const QMetaObject *metaLocalRep1 = rep3->metaObject();
+ const QMetaObject *metaTcpSource = t.metaObject();
+ const QMetaObject *metaLocalSource = l.metaObject();
+ QVERIFY(rep1->isInitialized());
+ QVERIFY(rep2->isInitialized());
+ QVERIFY(rep3->isInitialized());
+
+ for (int i = 0; i < metaTcpRep1->propertyCount(); ++i)
+ {
+ const QMetaProperty propLhs = metaTcpRep1->property(i);
+ if (qstrcmp(propLhs.name(), "isReplicaValid") == 0 || qstrcmp(propLhs.name(), "state") == 0 || qstrcmp(propLhs.name(), "node") == 0) //Ignore properties only on the Replica side
+ continue;
+ const QMetaProperty propRhs = metaTcpSource->property(metaTcpSource->indexOfProperty(propLhs.name()));
+ if (propLhs.notifySignalIndex() == -1)
+ QCOMPARE(propRhs.hasNotifySignal(), false);
+ else {
+ QCOMPARE(propRhs.notifySignalIndex() != -1, true);
+ QCOMPARE(metaTcpRep1->method(propLhs.notifySignalIndex()).name(), metaTcpSource->method(propRhs.notifySignalIndex()).name());
+ }
+ QCOMPARE(propLhs.read(rep1.data()), propRhs.read(&t));
+ }
+ for (int i = 0; i < metaLocalRep1->propertyCount(); ++i )
+ {
+ const QMetaProperty propLhs = metaLocalRep1->property(i);
+ if (qstrcmp(propLhs.name(), "isReplicaValid") == 0 || qstrcmp(propLhs.name(), "state") == 0 || qstrcmp(propLhs.name(), "node") == 0) //Ignore properties only on the Replica side
+ continue;
+ const QMetaProperty propRhs = metaLocalSource->property(metaTcpSource->indexOfProperty(propLhs.name()));
+ if (propLhs.notifySignalIndex() == -1)
+ QCOMPARE(propRhs.hasNotifySignal(), false);
+ else {
+ QCOMPARE(propRhs.notifySignalIndex() != -1, true);
+ QCOMPARE(metaTcpRep1->method(propLhs.notifySignalIndex()).name(), metaTcpSource->method(propRhs.notifySignalIndex()).name());
+ }
+ QCOMPARE(propLhs.read(rep3.data()), propRhs.read(&l));
+ }
+
+ }
+
+ void apiTest()
+ {
+ setupHost();
+ Engine e;
+ host->enableRemoting<EngineSourceAPI>(&e);
+ e.setRpm(1234);
+
+ setupClient();
+
+ const QScopedPointer<EngineReplica> engine_r(client->acquire<EngineReplica>());
+ engine_r->waitForSource();
+
+ QCOMPARE(engine_r->rpm(), e.rpm());
+ }
+
+ void apiInProcTest()
+ {
+ setupHost();
+ Engine e;
+ host->enableRemoting<EngineSourceAPI>(&e);
+ e.setRpm(1234);
+
+ const QScopedPointer<EngineReplica> engine_r_inProc(host->acquire<EngineReplica>());
+ engine_r_inProc->waitForSource();
+
+ QCOMPARE(engine_r_inProc->rpm(), e.rpm());
+ }
+
+ void errorSignalTest()
+ {
+ QRemoteObjectNode _client;
+ Q_SET_OBJECT_NAME(_client);
+ QSignalSpy errorSpy(&_client, &QRemoteObjectNode::error);
+ QVERIFY(!_client.connectToNode(QUrl(QLatin1String("invalid:invalid"))));
+ QCOMPARE(errorSpy.size(), 1);
+ auto emittedErrorCode = errorSpy.first().at(0).value<QRemoteObjectNode::ErrorCode>();
+ QCOMPARE(emittedErrorCode, QRemoteObjectNode::RegistryNotAcquired);
+ QCOMPARE(_client.lastError(), QRemoteObjectNode::RegistryNotAcquired);
+ }
+
+ void clientBeforeServerTest() {
+ setupClient();
+ const QScopedPointer<EngineReplica> engine_d(client->acquire<EngineReplica>());
+
+ setupHost();
+ Engine e;
+ host->enableRemoting<EngineSourceAPI>(&e);
+ QSignalSpy spy(engine_d.data(), &EngineReplica::rpmChanged);
+ e.setRpm(50);
+
+ spy.wait();
+ QCOMPARE(spy.size(), 1);
+
+ QCOMPARE(engine_d->rpm(), e.rpm());
+ }
+
+ void largeDataTest()
+ {
+ TestLargeData t;
+ setupHost();
+ host->enableRemoting(&t, QStringLiteral("large"));
+
+ setupClient();
+ const QScopedPointer<QRemoteObjectDynamicReplica> rep(client->acquireDynamic(QStringLiteral("large")));
+ rep->waitForSource();
+ QVERIFY(rep->isInitialized());
+ const QMetaObject *metaObject = rep->metaObject();
+ const int sigIndex = metaObject->indexOfSignal("send(QByteArray)");
+ QVERIFY(sigIndex != -1);
+ const QMetaMethod mm = metaObject->method(sigIndex);
+ QSignalSpy spy(rep.data(), QByteArray(QByteArrayLiteral("2")+mm.methodSignature().constData()));
+ const QByteArray data(16384,'y');
+ emit t.send(data);
+ spy.wait();
+ QCOMPARE(spy.size(), 1);
+ const QVariantList &arguments = spy.first();
+ QVERIFY(arguments.at(0).toByteArray() == data);
+ QVERIFY(host->disableRemoting(&t));
+ }
+
+ void PODTest()
+ {
+ setupHost();
+
+ setupClient();
+
+ MyPOD shouldPass(1, 2.0, QStringLiteral("pass"));
+ MyPOD shouldFail(1, 2.0, QStringLiteral("fail"));
+ MyClassSimpleSource m;
+ m.setMyPOD(shouldPass);
+ host->enableRemoting(&m);
+ const QScopedPointer<MyClassReplica> myclass_r(client->acquire<MyClassReplica>());
+ myclass_r->waitForSource();
+
+ QVERIFY(myclass_r->myPOD() == m.myPOD());
+ QVERIFY(myclass_r->myPOD() != shouldFail);
+ }
+
+ void SchemeTest()
+ {
+ QFETCH_GLOBAL(QUrl, hostUrl);
+ QFETCH_GLOBAL(QUrl, registryUrl);
+ QRemoteObjectHost valid(hostUrl);
+ QVERIFY(valid.lastError() == QRemoteObjectNode::NoError);
+ QRemoteObjectHost invalid(QUrl(QLatin1String("invalid:invalid")));
+ QVERIFY(invalid.lastError() == QRemoteObjectNode::HostUrlInvalid);
+ QRemoteObjectHost validExternal(QUrl(QLatin1String("invalid:invalid")), registryUrl, QRemoteObjectHost::AllowExternalRegistration);
+ QVERIFY(validExternal.lastError() == QRemoteObjectNode::NoError);
+ QRemoteObjectNode invalidRegistry(QUrl(QLatin1String("invalid:invalid")));
+ QVERIFY(invalidRegistry.lastError() == QRemoteObjectNode::RegistryNotAcquired);
+ }
+
+#if QT_CONFIG(process) && (defined(Q_OS_LINUX) || defined(Q_OS_DARWIN))
+ void localServerConnectionTest()
+ {
+ QFETCH_GLOBAL(QUrl, hostUrl);
+ if (hostUrl.scheme() != QRemoteObjectStringLiterals::local())
+ QSKIP("Skipping 'local' specific backend for non-local test.");
+ const auto progName = TestUtils::findExecutable("localsockettestserver", "/localsockettestserver");
+
+ //create a fake socket as killing doesn't produce a necessarily unusable socket
+ QFile fake(QDir::temp().absoluteFilePath(QStringLiteral("crashMe")));
+ fake.remove();
+ QVERIFY(fake.open(QFile::Truncate | QFile::WriteOnly));
+ QFileInfo info(QDir::temp().absoluteFilePath(QStringLiteral("crashMe")));
+ QVERIFY(info.exists());
+
+ QRemoteObjectNode localSocketTestClient;
+ const QUrl connection = QUrl(QStringLiteral(LOCAL_SOCKET ":crashMe"));
+ const QString objectname = QStringLiteral("connectme");
+ localSocketTestClient.connectToNode(connection);
+ QVERIFY(localSocketTestClient.lastError() == QRemoteObjectNode::NoError);
+ QScopedPointer<QRemoteObjectDynamicReplica> replica;
+ replica.reset(localSocketTestClient.acquireDynamic(objectname));
+
+ QProcess testServer;
+ testServer.start(progName, QStringList());
+ QVERIFY(testServer.waitForStarted());
+ QVERIFY(localSocketTestClient.lastError() == QRemoteObjectNode::NoError);
+ replica->waitForSource(1000);
+ QVERIFY(replica->isInitialized());
+ testServer.terminate();
+ QVERIFY(testServer.waitForFinished());
+ }
+ // Tests to take over an existing socket if its still valid
+ void localServerConnectionTest2()
+ {
+ QFETCH_GLOBAL(QUrl, hostUrl);
+ if (hostUrl.scheme() != QRemoteObjectStringLiterals::local())
+ QSKIP("Skipping 'local' specific backend for non-local test.");
+ const auto progName = TestUtils::findExecutable("localsockettestserver", "/localsockettestserver");
+
+ QProcess testServer;
+ testServer.start(progName, QStringList());
+ QVERIFY(testServer.waitForStarted());
+ QFileInfo info(QDir::temp().absoluteFilePath(QStringLiteral("crashMe")));
+ QVERIFY(info.exists());
+ testServer.kill();
+ testServer.waitForFinished();
+ QVERIFY(info.exists());
+
+ QRemoteObjectNode localSocketTestClient;
+ const QUrl connection = QUrl(QStringLiteral(LOCAL_SOCKET ":crashMe"));
+ const QString objectname = QStringLiteral("connectme");
+ localSocketTestClient.connectToNode(connection);
+ QVERIFY(localSocketTestClient.lastError() == QRemoteObjectNode::NoError);
+ QScopedPointer<QRemoteObjectDynamicReplica> replica;
+ replica.reset(localSocketTestClient.acquireDynamic(objectname));
+
+ testServer.start(progName, QStringList());
+ QVERIFY(testServer.waitForStarted());
+ QVERIFY(localSocketTestClient.lastError() == QRemoteObjectNode::NoError);
+ replica->waitForSource(1000);
+ QVERIFY(replica->isInitialized());
+ testServer.terminate();
+ QVERIFY(testServer.waitForFinished());
+ }
+#endif
+
+ void tcpListenFailedTest()
+ {
+ QFETCH_GLOBAL(QUrl, registryUrl);
+
+ if (registryUrl.scheme() != QRemoteObjectStringLiterals::tcp())
+ QSKIP("Skipping test for local and external backends.");
+
+ // Need the Host or Registry running so that the port is in use.
+ setupRegistry();
+ QRemoteObjectHost badHost;
+ badHost.setHostUrl(registryUrl);
+ QCOMPARE(badHost.lastError(), QRemoteObjectNode::ListenFailed);
+
+ }
+
+ void invalidExternalTest()
+ {
+ QFETCH_GLOBAL(QUrl, hostUrl);
+ if (hostUrl.scheme() != QRemoteObjectStringLiterals::tcp())
+ QSKIP("Skipping test for tcp and external backends.");
+ QRemoteObjectHost srcNode;
+ QTest::ignoreMessage(QtWarningMsg, " Overriding a valid QtRO url ( QUrl(\"tcp://127.0.0.1:65511\") ) with AllowExternalRegistration is not allowed.");
+ srcNode.setHostUrl(hostUrl, QRemoteObjectHost::AllowExternalRegistration);
+ QCOMPARE(srcNode.lastError(), QRemoteObjectNode::HostUrlInvalid);
+ Engine e;
+ bool res = srcNode.enableRemoting(&e);
+ QVERIFY(res == false);
+ }
+
+ void startClientWithoutHost()
+ {
+ setupClient();
+ QScopedPointer<EngineReplica> replica(client->acquire<EngineReplica>());
+ client->setHeartbeatInterval(10);
+ // Wait, to make sure there's no crash (QTBUG-94513)
+ QTest::qWait(200);
+
+ // Make sure creating the host afterwards works
+ setupHost();
+ Engine e;
+ e.setRpm(42);
+ host->enableRemoting(&e);
+
+ QVERIFY(replica->waitForSource());
+ QCOMPARE(replica->rpm(), e.rpm());
+ }
+};
+
+QTEST_MAIN(tst_Integration)
+
+#include "tst_integration.moc"
--- /dev/null
+
+add_subdirectory(client)
+add_subdirectory(server)
+add_subdirectory(external)
--- /dev/null
+#include <QtCore>
+
+class MyInterface
+{
+ ENUM Enum1 { First, Second, Third }
+ PROP(Enum1 enum1 = First READWRITE)
+
+ PROP(bool started = false)
+
+ SLOT(bool start())
+ SLOT(bool stop())
+ SLOT(bool quit())
+ SLOT(bool next())
+ SLOT(void testEnumParamsInSlots(Enum1 enumSlotParam, bool slotParam2, int))
+
+ SIGNAL(advance())
+ SIGNAL(testEnumParamsInSignals(Enum1 enumSignalParam, bool signalParam2, QString))
+};
--- /dev/null
+
+#####################################################################
+## integration_external_client Binary:
+#####################################################################
+
+qt_internal_add_executable(integration_external_client
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES
+ main.cpp
+ INCLUDE_DIRECTORIES
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+ Qt::Test
+)
+qt6_add_repc_replicas(integration_external_client
+ ../MyInterface.rep
+)
+
+#### Keys ignored in scope 1:.:.:client.pro:<TRUE>:
+# TEMPLATE = "app"
--- /dev/null
+// Copyright (C) 2018 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "rep_MyInterface_replica.h"
+
+#include <QCoreApplication>
+#include <QtRemoteObjects/qremoteobjectnode.h>
+#include <QtTest/QtTest>
+#include <QTcpSocket>
+
+const QUrl registryUrl = QUrl(QStringLiteral("tcp://127.0.0.1:65212"));
+
+class tst_Client_Process : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void initTestCase()
+ {
+ m_repNode.setRegistryUrl(registryUrl);
+ QRemoteObjectNode::RemoteObjectSchemaHandler setupTcp = [this](QUrl url) {
+ QTcpSocket *socket = new QTcpSocket(&this->m_repNode);
+ connect(socket, &QTcpSocket::connected,
+ [socket, this]() {
+ this->m_repNode.addClientSideConnection(socket);
+ });
+ connect(socket, &QTcpSocket::errorOccurred,
+ [socket](QAbstractSocket::SocketError error) {
+ qDebug() << "SocketError" << error;
+ delete socket;
+ });
+ socket->connectToHost(url.host(), quint16(url.port()));
+ };
+ m_repNode.registerExternalSchema(QStringLiteral("exttcp"), setupTcp);
+ QVERIFY(m_repNode.waitForRegistry(3000));
+ m_rep.reset(m_repNode.acquire<MyInterfaceReplica>());
+ }
+
+ void testRun()
+ {
+
+ QVERIFY(m_rep->waitForSource());
+ auto reply = m_rep->start();
+ QVERIFY(reply.waitForFinished());
+
+ // BEGIN: Testing
+ QSignalSpy advanceSpy(m_rep.data(), &MyInterfaceReplica::advance);
+
+ QSignalSpy spy(m_rep.data(), &MyInterfaceReplica::enum1Changed);
+ QVERIFY(advanceSpy.wait());
+
+ QCOMPARE(spy.size(), 2);
+ // END: Testing
+
+ reply = m_rep->stop();
+ QVERIFY(reply.waitForFinished());
+ }
+
+ void testEnumDetails()
+ {
+ QHash<QByteArray, int> kvs = {{"First", 0}, {"Second", 1}, {"Third", 2}};
+ QScopedPointer<QRemoteObjectDynamicReplica> rep(m_repNode.acquireDynamic("MyInterface"));
+ QVERIFY(rep->waitForSource());
+
+ auto mo = rep->metaObject();
+ int enumIdx = mo->indexOfEnumerator("Enum1");
+ QVERIFY(enumIdx != -1);
+ auto enumerator = mo->enumerator(enumIdx);
+ QCOMPARE(enumerator.name(), "Enum1");
+ QCOMPARE(enumerator.keyCount(), 3);
+ for (int i = 0; i < 3; ++i) {
+ auto key = enumerator.key(i);
+ auto val = enumerator.value(i);
+ auto it = kvs.find(key);
+ QVERIFY(it != kvs.end());
+ QCOMPARE(*it, val);
+ kvs.erase(it);
+ }
+
+ int propIdx = mo->indexOfProperty("enum1");
+ QVERIFY(propIdx != -1);
+ auto property = mo->property(propIdx);
+ property.write(rep.data(), 1);
+ QTRY_COMPARE(property.read(rep.data()).toInt(), 1);
+ }
+
+ void testMethodSignalParamDetails()
+ {
+ QScopedPointer<QRemoteObjectDynamicReplica> rep(m_repNode.acquireDynamic("MyInterface"));
+ QVERIFY(rep->waitForSource());
+
+ auto mo = rep->metaObject();
+ int signalIdx = mo->indexOfSignal("testEnumParamsInSignals(MyInterfaceReplica::Enum1,bool,QString)");
+ QVERIFY(signalIdx != -1);
+ auto simm = mo->method(signalIdx);
+ {
+ QCOMPARE(simm.parameterCount(), 3);
+ auto paramNames = simm.parameterNames();
+ QCOMPARE(paramNames.size(), 3);
+ QCOMPARE(paramNames.at(0), QByteArrayLiteral("enumSignalParam"));
+ QCOMPARE(paramNames.at(1), QByteArrayLiteral("signalParam2"));
+ QCOMPARE(paramNames.at(2), QByteArrayLiteral("__repc_variable_1"));
+ QCOMPARE(simm.parameterMetaType(0), QMetaType::fromType<MyInterfaceReplica::Enum1>());
+ QCOMPARE(simm.parameterMetaType(1), QMetaType::fromType<bool>());
+ QCOMPARE(simm.parameterMetaType(2), QMetaType::fromType<QString>());
+ }
+
+ int slotIdx = mo->indexOfSlot("testEnumParamsInSlots(MyInterfaceReplica::Enum1,bool,int)");
+ QVERIFY(slotIdx != -1);
+ auto slmm = mo->method(slotIdx);
+ {
+ QCOMPARE(slmm .parameterCount(), 3);
+ auto paramNames = slmm .parameterNames();
+ QCOMPARE(paramNames.size(), 3);
+ QCOMPARE(paramNames.at(0), QByteArrayLiteral("enumSlotParam"));
+ QCOMPARE(paramNames.at(1), QByteArrayLiteral("slotParam2"));
+ QCOMPARE(paramNames.at(2), QByteArrayLiteral("__repc_variable_1"));
+ }
+
+ int enumVal = 0;
+ mo->invokeMethod(rep.data(), "testEnumParamsInSlots",
+ QGenericArgument("MyInterfaceReplica::Enum1", &enumVal),
+ Q_ARG(bool, true), Q_ARG(int, 1234));
+
+ int enumIdx = mo->indexOfProperty("enum1");
+ QVERIFY(enumIdx != -1);
+ QTRY_COMPARE(mo->property(enumIdx).read(rep.data()).toInt(), 0);
+
+ int startedIdx = mo->indexOfProperty("started");
+ QVERIFY(startedIdx != -1);
+ QTRY_COMPARE(mo->property(startedIdx).read(rep.data()).toBool(), true);
+ }
+
+ void testMethodSignal()
+ {
+ QScopedPointer<MyInterfaceReplica> rep(new MyInterfaceReplica());
+ rep->setNode(&m_repNode);
+ QVERIFY(rep->waitForSource());
+
+ rep->testEnumParamsInSlots(MyInterfaceReplica::Second, false, 74);
+
+ connect(rep.data(), &MyInterfaceReplica::testEnumParamsInSignals,
+ [](MyInterfaceReplica::Enum1 enumSignalParam) { QCOMPARE(enumSignalParam, MyInterfaceReplica::Second); });
+
+ QTRY_COMPARE(rep->enum1(), MyInterfaceReplica::Second);
+ QTRY_COMPARE(rep->started(), false);
+ }
+
+ void testDisconnect()
+ {
+ auto reply = m_rep->next();
+ QSignalSpy stateSpy(m_rep.data(), &MyInterfaceReplica::stateChanged);
+ QVERIFY(reply.waitForFinished());
+
+ QTRY_COMPARE(stateSpy.size(), 1);
+ QCOMPARE(m_rep->state(), QRemoteObjectReplica::Suspect);
+
+ QTRY_COMPARE(stateSpy.size(), 2);
+ QCOMPARE(m_rep->state(), QRemoteObjectReplica::Valid);
+ // Make sure we updated to the correct enum1 value
+ QCOMPARE(m_rep->enum1(), MyInterfaceReplica::First);
+ }
+
+ void cleanupTestCase()
+ {
+ auto reply = m_rep->quit();
+ QVERIFY(reply.waitForFinished());
+ }
+
+private:
+ QRemoteObjectNode m_repNode;
+ QScopedPointer<MyInterfaceReplica> m_rep;
+};
+
+QTEST_MAIN(tst_Client_Process)
+
+#include "main.moc"
--- /dev/null
+
+#####################################################################
+## tst_integration_external Test:
+#####################################################################
+
+qt_internal_add_test(tst_integration_external
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES
+ tst_integration_external.cpp
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+)
--- /dev/null
+// Copyright (C) 2018 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <QtTest/QtTest>
+#include <QMetaType>
+#include <QProcess>
+
+#include "../../../shared/testutils.h"
+
+class tst_Integration_External: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase()
+ {
+ QVERIFY(TestUtils::init("external"));
+ QLoggingCategory::setFilterRules("qt.remoteobjects.warning=false");
+ }
+
+ void cleanup()
+ {
+ // wait for delivery of RemoveObject events to the source
+ QTest::qWait(200);
+ }
+
+ void testRun_data()
+ {
+ QTest::addColumn<bool>("templated");
+ QTest::newRow("non-templated enableRemoting") << false;
+ QTest::newRow("templated enableRemoting") << true;
+ }
+
+ void testRun()
+ {
+#ifdef Q_OS_ANDROID
+ QSKIP("QProcess doesn't support running user bundled binaries on Android");
+#endif
+ QFETCH(bool, templated);
+
+ qDebug() << "Starting server process";
+ QProcess serverProc;
+ serverProc.setProcessChannelMode(QProcess::ForwardedChannels);
+ if (templated) {
+ QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
+ env.insert("TEMPLATED_REMOTING", "true");
+ serverProc.setProcessEnvironment(env);
+ }
+ serverProc.start(TestUtils::findExecutable("integration_external_server", "/server"),
+ QStringList());
+ QVERIFY(serverProc.waitForStarted());
+
+ // wait for server start
+ QTest::qWait(200);
+
+ qDebug() << "Starting client process";
+ QProcess clientProc;
+ clientProc.setProcessChannelMode(QProcess::ForwardedChannels);
+ clientProc.start(TestUtils::findExecutable("integration_external_client", "/client"),
+ QStringList());
+ QVERIFY(clientProc.waitForStarted());
+
+ QVERIFY(clientProc.waitForFinished());
+ QVERIFY(serverProc.waitForFinished());
+
+ QCOMPARE(serverProc.exitCode(), 0);
+ QCOMPARE(clientProc.exitCode(), 0);
+ }
+};
+
+QTEST_MAIN(tst_Integration_External)
+
+#include "tst_integration_external.moc"
--- /dev/null
+
+#####################################################################
+## integration_external_server Binary:
+#####################################################################
+
+qt_internal_add_executable(integration_external_server
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES
+ main.cpp
+ mytestserver.cpp mytestserver.h
+ INCLUDE_DIRECTORIES
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+ Qt::Test
+)
+qt6_add_repc_sources(integration_external_server
+ ../MyInterface.rep
+)
+
+#### Keys ignored in scope 1:.:.:server.pro:<TRUE>:
+# TEMPLATE = "app"
--- /dev/null
+// Copyright (C) 2018 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "mytestserver.h"
+
+#include <QCoreApplication>
+#include <QTcpServer>
+#include <QtTest/QtTest>
+#include <QTcpSocket>
+
+const QUrl registryUrl = QUrl(QStringLiteral("tcp://127.0.0.1:65212"));
+const QUrl extUrl = QUrl(QStringLiteral("exttcp://127.0.0.1:65213"));
+const QUrl extUrl2 = QUrl(QStringLiteral("exttcp://127.0.0.1:65214"));
+
+class tst_Server_Process : public QObject
+{
+ Q_OBJECT
+
+ struct Device
+ {
+ Device(QUrl url) : srcNode(url, registryUrl, QRemoteObjectHost::AllowExternalRegistration)
+ {
+ tcpServer.listen(QHostAddress(url.host()), quint16(url.port()));
+ QVERIFY(srcNode.waitForRegistry(3000));
+ QObject::connect(&tcpServer, &QTcpServer::newConnection, [this]() {
+ auto conn = this->tcpServer.nextPendingConnection();
+ this->srcNode.addHostSideConnection(conn);
+ });
+ }
+ QTcpServer tcpServer;
+ QRemoteObjectHost srcNode;
+ };
+
+private Q_SLOTS:
+ void testRun()
+ {
+ QRemoteObjectRegistryHost registry(registryUrl);
+
+ Device dev1(extUrl);
+ MyTestServer myTestServer;
+ bool templated = qEnvironmentVariableIsSet("TEMPLATED_REMOTING");
+ if (templated)
+ QVERIFY(dev1.srcNode.enableRemoting<MyInterfaceSourceAPI>(&myTestServer));
+ else
+ QVERIFY(dev1.srcNode.enableRemoting(&myTestServer));
+
+ qDebug() << "Waiting for incoming connections";
+
+ QSignalSpy waitForStartedSpy(&myTestServer, &MyTestServer::startedChanged);
+ QVERIFY(waitForStartedSpy.isValid());
+ QVERIFY(waitForStartedSpy.wait());
+ QCOMPARE(waitForStartedSpy.value(0).value(0).toBool(), true);
+
+ // wait for delivery of events
+ QTest::qWait(200);
+
+ qDebug() << "Client connected";
+
+ // BEGIN: Testing
+
+ // make sure continuous changes to enums don't mess up the protocol
+ myTestServer.setEnum1(MyTestServer::Second);
+ myTestServer.setEnum1(MyTestServer::Third);
+
+ emit myTestServer.advance();
+
+ waitForStartedSpy.clear();
+ QVERIFY(waitForStartedSpy.wait());
+ QCOMPARE(waitForStartedSpy.value(0).value(0).toBool(), false);
+
+ bool next = false;
+ connect(&myTestServer, &MyTestServer::nextStep, [&next]{ next = true; });
+ QTRY_VERIFY_WITH_TIMEOUT(next, 5000);
+
+ qDebug() << "Disable remoting";
+ QVERIFY(dev1.srcNode.disableRemoting(&myTestServer));
+
+ // Wait before changing the state
+ QTest::qWait(200);
+
+ // Change a value while replica is suspect
+ myTestServer.setEnum1(MyTestServer::First);
+
+ // Share the object on a different "device", make sure registry updates and connects
+ qDebug() << "Enable remoting";
+ Device dev2(extUrl2);
+ QVERIFY(dev2.srcNode.enableRemoting(&myTestServer));
+
+ // wait for quit
+ bool quit = false;
+ connect(&myTestServer, &MyTestServer::quitApp, [&quit]{quit = true;});
+ QTRY_VERIFY_WITH_TIMEOUT(quit, 5000);
+
+ // wait for delivery of events
+ QTest::qWait(200);
+
+ qDebug() << "Done. Shutting down.";
+ }
+};
+
+QTEST_MAIN(tst_Server_Process)
+
+#include "main.moc"
--- /dev/null
+// Copyright (C) 2018 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <qdebug.h>
+
+#include "mytestserver.h"
+#include "rep_MyInterface_source.h"
+
+MyTestServer::MyTestServer(QObject *parent)
+ : MyInterfaceSimpleSource(parent)
+{
+ qDebug() << "Server started";
+}
+
+MyTestServer::~MyTestServer()
+{
+ qDebug() << "Server stopped";
+}
+
+bool MyTestServer::start()
+{
+ setStarted(true);
+ return true;
+}
+
+bool MyTestServer::stop()
+{
+ setStarted(false);
+ return true;
+}
+
+bool MyTestServer::quit()
+{
+ emit quitApp();
+ return true;
+}
+
+bool MyTestServer::next()
+{
+ emit nextStep();
+ return true;
+}
+
+void MyTestServer::testEnumParamsInSlots(Enum1 enumSlotParam, bool slotParam2, int number)
+{
+ setEnum1(enumSlotParam);
+ setStarted(slotParam2);
+ emit testEnumParamsInSignals(enum1(), started(), QString::number(number));
+}
--- /dev/null
+// Copyright (C) 2018 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef MYTESTSERVER_H
+#define MYTESTSERVER_H
+
+#include <QTimer>
+
+#include <QtRemoteObjects/qremoteobjectnode.h>
+#include <QtRemoteObjects/qremoteobjectsource.h>
+
+#include "rep_MyInterface_source.h"
+
+class MyTestServer : public MyInterfaceSimpleSource
+{
+ Q_OBJECT
+
+public:
+ MyTestServer(QObject *parent = nullptr);
+ ~MyTestServer() override;
+
+public Q_SLOTS:
+ bool start() override;
+ bool stop() override;
+ bool quit() override;
+ bool next() override;
+ void testEnumParamsInSlots(Enum1 enumSlotParam, bool slotParam2, int __repc_variable_1) override;
+
+Q_SIGNALS:
+ void quitApp();
+ void nextStep();
+};
+
+#endif // MYTESTSERVER_H
--- /dev/null
+
+add_subdirectory(client)
+add_subdirectory(server)
+add_subdirectory(tst)
--- /dev/null
+POD ExtPOD(int i, float f, QString s)
--- /dev/null
+#include <QtCore>
+#include "rep_ExtPodInterface_merged.h"
+
+class MyInterface
+{
+ ENUM Enum1 { First, Second, Third }
+ PROP(Enum1 enum1 = First READWRITE)
+
+ PROP(bool started = false)
+ PROP(int initialValue)
+
+ SLOT(bool start())
+ SLOT(bool stop())
+ SLOT(bool quit())
+ SLOT(void testEnumParamsInSlots(Enum1 enumSlotParam, bool slotParam2, int))
+
+ SIGNAL(advance())
+ SIGNAL(testEnumParamsInSignals(Enum1 enumSignalParam, bool signalParam2, QString))
+
+ SLOT(void testExtPODListSlot(const QList<ExtPOD> &))
+ SIGNAL(testExtPODListSignal(const QList<ExtPOD> &))
+};
--- /dev/null
+#include <QtCore>
+
+POD MyPOD(int i, float f, QString s)
+
+class PodInterface
+{
+ PROP(MyPOD myPod)
+};
--- /dev/null
+
+#####################################################################
+## integration_multiprocess_client Binary:
+#####################################################################
+
+qt_internal_add_executable(integration_multiprocess_client
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES
+ main.cpp
+ INCLUDE_DIRECTORIES
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+ Qt::Test
+)
+
+qt6_add_repc_merged(integration_multiprocess_client
+ ../ExtPodInterface.rep
+)
+
+qt6_add_repc_replicas(integration_multiprocess_client
+ ../MyInterface.rep
+)
+
+#### Keys ignored in scope 1:.:.:client.pro:<TRUE>:
+# TEMPLATE = "app"
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "rep_MyInterface_replica.h"
+#include "rep_ExtPodInterface_merged.h"
+
+#include <QCoreApplication>
+#include <QtRemoteObjects/qremoteobjectnode.h>
+#include <QtTest/QtTest>
+
+class tst_Client_Process : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void initTestCase()
+ {
+ m_repNode.reset(new QRemoteObjectNode);
+ m_repNode->connectToNode(QUrl(QStringLiteral("tcp://127.0.0.1:65213")));
+ m_rep.reset(m_repNode->acquire<MyInterfaceReplica>());
+ connect(m_rep.data(), &MyInterfaceReplica::notified, [&]() { m_notified = true; });
+ connect(m_rep.data(), &MyInterfaceReplica::initialValueChanged, [&]() {
+ // this value is only set when the replica first connects to the source
+ QCOMPARE(m_notified, false);
+ QCOMPARE(m_rep->initialValue(), 18);
+ });
+ QVERIFY(m_rep->waitForSource());
+ }
+
+ void testRun()
+ {
+ auto reply = m_rep->start();
+ QVERIFY(reply.waitForFinished());
+
+ // BEGIN: Testing
+ QSignalSpy advanceSpy(m_rep.data(), &MyInterfaceReplica::advance);
+
+ QSignalSpy spy(m_rep.data(), &MyInterfaceReplica::enum1Changed);
+ QVERIFY(advanceSpy.wait());
+
+ QCOMPARE(spy.size(), 2);
+ // END: Testing
+
+ reply = m_rep->stop();
+ QVERIFY(reply.waitForFinished());
+ }
+
+ void testEnumDetails()
+ {
+ QHash<QByteArray, int> kvs = {{"First", 0}, {"Second", 1}, {"Third", 2}};
+ QScopedPointer<QRemoteObjectDynamicReplica> rep(m_repNode->acquireDynamic("MyInterface"));
+ QVERIFY(rep->waitForSource());
+
+ auto mo = rep->metaObject();
+ int enumIdx = mo->indexOfEnumerator("Enum1");
+ QVERIFY(enumIdx != -1);
+ auto enumerator = mo->enumerator(enumIdx);
+ QCOMPARE(enumerator.name(), "Enum1");
+ QCOMPARE(enumerator.keyCount(), 3);
+ for (int i = 0; i < 3; ++i) {
+ auto key = enumerator.key(i);
+ auto val = enumerator.value(i);
+ auto it = kvs.find(key);
+ QVERIFY(it != kvs.end());
+ QCOMPARE(*it, val);
+ kvs.erase(it);
+ }
+
+ int propIdx = mo->indexOfProperty("enum1");
+ QVERIFY(propIdx != -1);
+ auto property = mo->property(propIdx);
+ property.write(rep.data(), 1);
+ QTRY_COMPARE(property.read(rep.data()).toInt(), 1);
+ }
+
+ void testMethodSignalParamDetails()
+ {
+ QScopedPointer<QRemoteObjectDynamicReplica> rep(m_repNode->acquireDynamic("MyInterface"));
+ QVERIFY(rep->waitForSource());
+
+ auto mo = rep->metaObject();
+ int signalIdx = mo->indexOfSignal("testEnumParamsInSignals(MyInterfaceReplica::Enum1,bool,QString)");
+ QVERIFY(signalIdx != -1);
+ auto simm = mo->method(signalIdx);
+ {
+ QCOMPARE(simm.parameterCount(), 3);
+ auto paramNames = simm.parameterNames();
+ QCOMPARE(paramNames.size(), 3);
+ QCOMPARE(paramNames.at(0), QByteArrayLiteral("enumSignalParam"));
+ QCOMPARE(paramNames.at(1), QByteArrayLiteral("signalParam2"));
+ QCOMPARE(paramNames.at(2), QByteArrayLiteral("__repc_variable_1"));
+ QCOMPARE(simm.parameterMetaType(0), QMetaType::fromType<MyInterfaceReplica::Enum1>());
+ QCOMPARE(simm.parameterMetaType(1), QMetaType::fromType<bool>());
+ QCOMPARE(simm.parameterMetaType(2), QMetaType::fromType<QString>());
+ }
+
+ int slotIdx = mo->indexOfSlot("testEnumParamsInSlots(MyInterfaceReplica::Enum1,bool,int)");
+ QVERIFY(slotIdx != -1);
+ auto slmm = mo->method(slotIdx);
+ {
+ QCOMPARE(slmm .parameterCount(), 3);
+ auto paramNames = slmm .parameterNames();
+ QCOMPARE(paramNames.size(), 3);
+ QCOMPARE(paramNames.at(0), QByteArrayLiteral("enumSlotParam"));
+ QCOMPARE(paramNames.at(1), QByteArrayLiteral("slotParam2"));
+ QCOMPARE(paramNames.at(2), QByteArrayLiteral("__repc_variable_1"));
+ }
+
+ int enumVal = 0;
+ mo->invokeMethod(rep.data(), "testEnumParamsInSlots",
+ QGenericArgument("MyInterfaceReplica::Enum1", &enumVal),
+ Q_ARG(bool, true), Q_ARG(int, 1234));
+
+ int enumIdx = mo->indexOfProperty("enum1");
+ QVERIFY(enumIdx != -1);
+ QTRY_COMPARE(mo->property(enumIdx).read(rep.data()).toInt(), 0);
+
+ int startedIdx = mo->indexOfProperty("started");
+ QVERIFY(startedIdx != -1);
+ QTRY_COMPARE(mo->property(startedIdx).read(rep.data()).toBool(), true);
+ }
+
+ void testMethodSignal()
+ {
+ QScopedPointer<MyInterfaceReplica> rep(new MyInterfaceReplica());
+ rep->setNode(m_repNode.get());
+ QVERIFY(rep->waitForSource());
+
+ rep->testEnumParamsInSlots(MyInterfaceReplica::Second, false, 74);
+
+ connect(rep.data(), &MyInterfaceReplica::testEnumParamsInSignals,
+ [](MyInterfaceReplica::Enum1 enumSignalParam) { QCOMPARE(enumSignalParam, MyInterfaceReplica::Second); });
+
+ QTRY_COMPARE(rep->enum1(), MyInterfaceReplica::Second);
+ QTRY_COMPARE(rep->started(), false);
+ }
+
+ void testExtPodListSignals()
+ {
+ QScopedPointer<MyInterfaceReplica> rep(new MyInterfaceReplica());
+ rep->setNode(m_repNode.get());
+ QVERIFY(rep->waitForSource());
+
+ auto list = QList { ExtPOD(1, 1.1f, QStringLiteral("v1")),
+ ExtPOD(2, 2.2f, QStringLiteral("v2")) };
+ rep->testExtPODListSlot(list);
+ QSignalSpy spy(rep.data(), &MyInterfaceReplica::testExtPODListSignal);
+ connect(rep.data(), &MyInterfaceReplica::testExtPODListSignal,
+ [list](const QList<ExtPOD> &l) { QCOMPARE(l, list); });
+ QTRY_COMPARE(spy.size(), 1);
+ }
+
+ void testPod()
+ {
+ QScopedPointer<QRemoteObjectDynamicReplica> podRep(m_repNode->acquireDynamic("PodInterface"));
+ QVERIFY(podRep->waitForSource());
+ QVariant value = podRep->property("myPod");
+ const QMetaObject *mo = value.metaType().metaObject();
+ const void *gadget = value.constData();
+
+ QMetaProperty iProp = mo->property(mo->indexOfProperty("i"));
+ QVariant iValue = iProp.readOnGadget(gadget);
+ QCOMPARE(iValue.toInt(), 1);
+
+ QMetaProperty fProp = mo->property(mo->indexOfProperty("f"));
+ QVariant fValue = fProp.readOnGadget(gadget);
+ QCOMPARE(fValue.toFloat(), 5.0f);
+
+ QMetaProperty sProp = mo->property(mo->indexOfProperty("s"));
+ QVariant sValue = sProp.readOnGadget(gadget);
+ QCOMPARE(sValue.toString(), QString(QLatin1String("test")));
+ }
+
+ void cleanupTestCase()
+ {
+ auto reply = m_rep->quit();
+ QVERIFY(reply.waitForFinished());
+ m_rep.reset();
+ QVERIFY(QMetaType::fromName("MyPOD").isValid());
+ m_repNode.reset();
+ QVERIFY(!QMetaType::fromName("MyPOD").isValid());
+ }
+
+private:
+ QScopedPointer<QRemoteObjectNode> m_repNode;
+ QScopedPointer<MyInterfaceReplica> m_rep;
+ bool m_notified = false;
+};
+
+QTEST_MAIN(tst_Client_Process)
+
+#include "main.moc"
--- /dev/null
+
+#####################################################################
+## integration_multiprocess_server Binary:
+#####################################################################
+
+qt_internal_add_executable(integration_multiprocess_server
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES
+ main.cpp
+ mytestserver.cpp mytestserver.h
+ INCLUDE_DIRECTORIES
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+ Qt::Test
+)
+qt6_add_repc_merged(integration_multiprocess_server
+ ../ExtPodInterface.rep
+)
+
+qt6_add_repc_sources(integration_multiprocess_server
+ ../MyInterface.rep
+ ../PodInterface.rep
+)
+
+#### Keys ignored in scope 1:.:.:server.pro:<TRUE>:
+# TEMPLATE = "app"
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "mytestserver.h"
+#include "rep_PodInterface_source.h"
+
+#include <QCoreApplication>
+#include <QtTest/QtTest>
+
+class tst_Server_Process : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void testRun()
+ {
+ QRemoteObjectHost srcNode(QUrl(QStringLiteral("tcp://127.0.0.1:65213")));
+ MyTestServer myTestServer;
+ bool templated = qEnvironmentVariableIsSet("TEMPLATED_REMOTING");
+ if (templated)
+ srcNode.enableRemoting<MyInterfaceSourceAPI>(&myTestServer);
+ else
+ srcNode.enableRemoting(&myTestServer);
+
+ PodInterfaceSimpleSource myPodSource;
+ myPodSource.setMyPod(MyPOD(1, 5.0, "test"));
+ if (templated)
+ srcNode.enableRemoting<PodInterfaceSourceAPI>(&myPodSource);
+ else
+ srcNode.enableRemoting(&myPodSource);
+
+ qDebug() << "Waiting for incoming connections";
+
+ QSignalSpy waitForStartedSpy(&myTestServer, &MyTestServer::startedChanged);
+ QVERIFY(waitForStartedSpy.isValid());
+ QVERIFY(waitForStartedSpy.wait());
+ QCOMPARE(waitForStartedSpy.value(0).value(0).toBool(), true);
+
+ // wait for delivery of events
+ QTest::qWait(200);
+
+ qDebug() << "Client connected";
+
+ // BEGIN: Testing
+
+ // make sure continuous changes to enums don't mess up the protocol
+ myTestServer.setEnum1(MyTestServer::Second);
+ myTestServer.setEnum1(MyTestServer::Third);
+
+ emit myTestServer.advance();
+
+ // END: Testing
+
+ waitForStartedSpy.clear();
+ QVERIFY(waitForStartedSpy.wait());
+ QCOMPARE(waitForStartedSpy.value(0).value(0).toBool(), false);
+
+ // wait for quit
+ bool quit = false;
+ connect(&myTestServer, &MyTestServer::quitApp, [&quit]{quit = true;});
+ QTRY_VERIFY_WITH_TIMEOUT(quit, 5000);
+
+ // wait for delivery of events
+ QTest::qWait(200);
+
+ qDebug() << "Done. Shutting down.";
+ }
+};
+
+QTEST_MAIN(tst_Server_Process)
+
+#include "main.moc"
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <qdebug.h>
+
+#include "mytestserver.h"
+#include "rep_MyInterface_source.h"
+
+MyTestServer::MyTestServer(QObject *parent)
+ : MyInterfaceSimpleSource(parent)
+{
+ qDebug() << "Server started";
+ setInitialValue(18);
+}
+
+MyTestServer::~MyTestServer()
+{
+ qDebug() << "Server stopped";
+}
+
+bool MyTestServer::start()
+{
+ setStarted(true);
+ return true;
+}
+
+bool MyTestServer::stop()
+{
+ setStarted(false);
+ return true;
+}
+
+bool MyTestServer::quit()
+{
+ emit quitApp();
+ return true;
+}
+
+void MyTestServer::testEnumParamsInSlots(Enum1 enumSlotParam, bool slotParam2, int number)
+{
+ setEnum1(enumSlotParam);
+ setStarted(slotParam2);
+ emit testEnumParamsInSignals(enum1(), started(), QString::number(number));
+}
+
+void MyTestServer::testExtPODListSlot(const QList<ExtPOD> &l)
+{
+ emit testExtPODListSignal(l);
+}
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef MYTESTSERVER_H
+#define MYTESTSERVER_H
+
+#include <QTimer>
+
+#include <QtRemoteObjects/qremoteobjectnode.h>
+#include <QtRemoteObjects/qremoteobjectsource.h>
+
+#include "rep_MyInterface_source.h"
+
+class MyTestServer : public MyInterfaceSimpleSource
+{
+ Q_OBJECT
+
+public:
+ MyTestServer(QObject *parent = nullptr);
+ ~MyTestServer() override;
+
+public Q_SLOTS:
+ bool start() override;
+ bool stop() override;
+ bool quit() override;
+ void testEnumParamsInSlots(Enum1 enumSlotParam, bool slotParam2, int __repc_variable_1) override;
+ void testExtPODListSlot(const QList<ExtPOD> &l) override;
+
+Q_SIGNALS:
+ void quitApp();
+};
+
+#endif // MYTESTSERVER_H
--- /dev/null
+
+#####################################################################
+## tst_integration_multiprocess Test:
+#####################################################################
+
+qt_internal_add_test(tst_integration_multiprocess
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES
+ tst_integration_multiprocess.cpp
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+)
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <QtTest/QtTest>
+#include <QMetaType>
+#include <QProcess>
+
+#include "../../../shared/testutils.h"
+
+class tst_Integration_MultiProcess: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase()
+ {
+ QVERIFY(TestUtils::init("tst"));
+ QLoggingCategory::setFilterRules("qt.remoteobjects.warning=false");
+ }
+
+ void cleanup()
+ {
+ // wait for delivery of RemoveObject events to the source
+ QTest::qWait(200);
+ }
+
+ void testRun_data()
+ {
+ QTest::addColumn<bool>("templated");
+ QTest::newRow("non-templated enableRemoting") << false;
+ QTest::newRow("templated enableRemoting") << true;
+ }
+
+ void testRun()
+ {
+#ifdef Q_OS_ANDROID
+ QSKIP("QProcess doesn't support running user bundled binaries on Android");
+#endif
+ QFETCH(bool, templated);
+
+ qDebug() << "Starting server process";
+ QProcess serverProc;
+ serverProc.setProcessChannelMode(QProcess::ForwardedChannels);
+ if (templated) {
+ QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
+ env.insert("TEMPLATED_REMOTING", "true");
+ serverProc.setProcessEnvironment(env);
+ }
+ serverProc.start(TestUtils::findExecutable("integration_multiprocess_server", "/server"),
+ QStringList());
+ QVERIFY(serverProc.waitForStarted());
+
+ // wait for server start
+ QTest::qWait(200);
+
+ qDebug() << "Starting client process";
+ QProcess clientProc;
+ clientProc.setProcessChannelMode(QProcess::ForwardedChannels);
+ clientProc.start(TestUtils::findExecutable("integration_multiprocess_client", "/client"),
+ QStringList());
+ QVERIFY(clientProc.waitForStarted());
+
+ QVERIFY(clientProc.waitForFinished());
+ QVERIFY(serverProc.waitForFinished());
+
+ QCOMPARE(serverProc.exitCode(), 0);
+ QCOMPARE(clientProc.exitCode(), 0);
+ }
+};
+
+QTEST_MAIN(tst_Integration_MultiProcess)
+
+#include "tst_integration_multiprocess.moc"
--- /dev/null
+
+#####################################################################
+## localsockettestserver Binary:
+#####################################################################
+
+qt_internal_add_executable(localsockettestserver
+ SOURCES
+ main.cpp
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+ OUTPUT_DIRECTORY
+ ${CMAKE_CURRENT_BINARY_DIR}
+)
+
+#### Keys ignored in scope 1:.:.:localsockettestserver.pro:<TRUE>:
+# TEMPLATE = "app"
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <QCoreApplication>
+#include <QRemoteObjectNode>
+
+#include "../../shared/testutils.h"
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication a(argc, argv);
+ QObject remotedObject;
+ remotedObject.setObjectName(QStringLiteral("connectme"));
+ QRemoteObjectHost node(QUrl(QStringLiteral(LOCAL_SOCKET ":crashMe")));
+ node.enableRemoting(&remotedObject);
+
+ return a.exec();
+}
--- /dev/null
+
+#####################################################################
+## tst_modelreplicatest Test:
+#####################################################################
+
+qt_internal_add_test(tst_modelreplicatest
+ SOURCES
+ tst_modelreplicatest.cpp
+ DEFINES
+ SRCDIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/\\\"
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+)
+qt6_add_repc_merged(tst_modelreplicatest
+ model.rep
+)
+
+#### Keys ignored in scope 1:.:.:modelreplica.pro:<TRUE>:
+# TEMPLATE = "app"
--- /dev/null
+class Media
+{
+ ENUM state{Stopped, Paused, Playing}
+ PROP(QString currentTrack)
+ PROP(state playState)
+ MODEL tracks(display)
+}
+
+class OtherMedia
+{
+ MODEL tracks(display,decoration)
+}
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <QString>
+#include <QtTest>
+#include "rep_model_merged.h"
+
+class ModelreplicaTest : public QObject
+{
+ Q_OBJECT
+
+public:
+ ModelreplicaTest() = default;
+
+private Q_SLOTS:
+ void basicFunctions();
+ void basicFunctions_data();
+ void nullModel();
+ void nestedSortFilterProxyModel_data() { basicFunctions_data(); }
+ void nestedSortFilterProxyModel();
+ void sortFilterProxyModel_data();
+ void sortFilterProxyModel();
+};
+
+void ModelreplicaTest::basicFunctions_data()
+{
+ QTest::addColumn<bool>("templated");
+ QTest::newRow("non-templated enableRemoting") << false;
+ QTest::newRow("templated enableRemoting") << true;
+}
+
+void ModelreplicaTest::basicFunctions()
+{
+ QFETCH(bool, templated);
+
+ QRemoteObjectRegistryHost host(QUrl("tcp://localhost:5550"));
+ auto model = new QStringListModel();
+ model->setStringList(QStringList() << "Track1" << "Track2" << "Track3");
+ MediaSimpleSource source;
+ source.setTracks(model);
+ if (templated)
+ host.enableRemoting<MediaSourceAPI>(&source);
+ else
+ host.enableRemoting(&source);
+
+ auto model2 = new QStringListModel();
+ model2->setStringList(QStringList() << "Track4" << "Track5");
+ OtherMediaSimpleSource source2;
+ source2.setTracks(model2);
+ host.enableRemoting(&source2);
+
+ QRemoteObjectNode client(QUrl("tcp://localhost:5550"));
+ const QScopedPointer<MediaReplica> replica(client.acquire<MediaReplica>());
+ QSignalSpy tracksSpy(replica->tracks(), &QAbstractItemModelReplica::initialized);
+ QVERIFY(replica->waitForSource(300));
+ QVERIFY(tracksSpy.wait());
+ // Rep file only uses display role
+ QCOMPARE(QList<int>{Qt::DisplayRole}, replica->tracks()->availableRoles());
+
+ QCOMPARE(model->rowCount(), replica->tracks()->rowCount());
+ for (int i = 0; i < replica->tracks()->rowCount(); i++)
+ {
+ // We haven't received any data yet
+ QCOMPARE(QVariant(), replica->tracks()->data(replica->tracks()->index(i, 0)));
+ }
+
+ // Wait for data to be fetch and confirm
+ QTest::qWait(100);
+ QCOMPARE(model->rowCount(), replica->tracks()->rowCount());
+ for (int i = 0; i < replica->tracks()->rowCount(); i++)
+ {
+ QTRY_COMPARE(model->data(model->index(i), Qt::DisplayRole), replica->tracks()->data(replica->tracks()->index(i, 0)));
+ }
+
+ // ensure the tracks objects are distinct
+ const QScopedPointer<OtherMediaReplica> otherReplica(client.acquire<OtherMediaReplica>());
+ QSignalSpy otherTracksSpy(otherReplica->tracks(), &QAbstractItemModelReplica::initialized);
+ QVERIFY(otherReplica->waitForSource(300));
+ QVERIFY(otherTracksSpy.wait());
+ QCOMPARE(otherReplica->tracks()->availableRoles().size(), 2);
+
+}
+
+void ModelreplicaTest::nullModel()
+{
+ QRemoteObjectRegistryHost host(QUrl("tcp://localhost:5550"));
+ MediaSimpleSource source;
+ host.enableRemoting(&source);
+
+ QRemoteObjectNode client(QUrl("tcp://localhost:5550"));
+ const QScopedPointer<MediaReplica> replica(client.acquire<MediaReplica>());
+ QVERIFY(replica->waitForSource(300));
+
+ auto model = new QStringListModel(this);
+ model->setStringList(QStringList() << "Track1" << "Track2" << "Track3");
+ source.setTracks(model);
+
+ QTRY_VERIFY(replica->tracks());
+ QTRY_COMPARE(replica->tracks()->rowCount(), 3);
+ QTRY_COMPARE(replica->tracks()->data(replica->tracks()->index(0, 0)), "Track1");
+
+ model = new QStringListModel(this);
+ model->setStringList(QStringList() << "New Track1" << "New Track2" << "New Track3" << "New Track4");
+ source.setTracks(model);
+ QTRY_COMPARE(replica->tracks()->rowCount(), 4);
+ QTRY_COMPARE(replica->tracks()->data(replica->tracks()->index(3, 0)), "New Track4");
+}
+
+void ModelreplicaTest::nestedSortFilterProxyModel()
+{
+ QFETCH(bool, templated);
+
+ QRemoteObjectRegistryHost host(QUrl("tcp://localhost:5550"));
+ auto model = new QStringListModel(this);
+ model->setStringList(QStringList() << "CCC" << "AAA" << "BBB");
+ auto proxyModel = new QSortFilterProxyModel(this);
+ proxyModel->setSourceModel(model);
+
+ MediaSimpleSource source;
+ source.setTracks(proxyModel);
+ if (templated)
+ host.enableRemoting<MediaSourceAPI>(&source);
+ else
+ host.enableRemoting(&source);
+
+ QRemoteObjectNode client(QUrl("tcp://localhost:5550"));
+ const QScopedPointer<MediaReplica> replica(client.acquire<MediaReplica>());
+ QSignalSpy tracksSpy(replica->tracks(), &QAbstractItemModelReplica::initialized);
+ QVERIFY(replica->waitForSource(300));
+ QVERIFY(tracksSpy.wait());
+ // Rep file only uses display role
+ QCOMPARE(QVector<int>{Qt::DisplayRole}, replica->tracks()->availableRoles());
+
+ QCOMPARE(proxyModel->rowCount(), replica->tracks()->rowCount());
+ for (int i = 0; i < replica->tracks()->rowCount(); i++) {
+ // We haven't received any data yet
+ QCOMPARE(QVariant(), replica->tracks()->data(replica->tracks()->index(i, 0)));
+ }
+
+ // Wait for data to be fetch and confirm
+ QCOMPARE(proxyModel->rowCount(), replica->tracks()->rowCount());
+ for (int i = 0; i < replica->tracks()->rowCount(); i++)
+ QTRY_COMPARE(proxyModel->data(proxyModel->index(i, 0), Qt::DisplayRole), replica->tracks()->data(replica->tracks()->index(i, 0)));
+
+ proxyModel->sort(0, Qt::AscendingOrder);
+ QCOMPARE(proxyModel->rowCount(), replica->tracks()->rowCount());
+ for (int i = 0; i < replica->tracks()->rowCount(); i++)
+ QTRY_COMPARE(proxyModel->data(proxyModel->index(i, 0), Qt::DisplayRole), replica->tracks()->data(replica->tracks()->index(i, 0)));
+}
+
+void ModelreplicaTest::sortFilterProxyModel_data()
+{
+ QTest::addColumn<bool>("prefetch");
+
+ QTest::newRow("size only") << false;
+ QTest::newRow("prefetch") << true;
+}
+
+void ModelreplicaTest::sortFilterProxyModel()
+{
+ QFETCH(bool, prefetch);
+
+ QRemoteObjectRegistryHost host(QUrl("tcp://localhost:5550"));
+ auto model = new QStringListModel(this);
+ model->setStringList(QStringList() << "CCC" << "AAA" << "BBB");
+ auto proxyModel = new QSortFilterProxyModel(this);
+ proxyModel->setSourceModel(model);
+
+ QList<int> roles = { Qt::DisplayRole };
+ host.enableRemoting(proxyModel, "test", roles);
+
+ auto fetchMode = prefetch ? QtRemoteObjects::PrefetchData : QtRemoteObjects::FetchRootSize;
+ QRemoteObjectNode client(QUrl("tcp://localhost:5550"));
+ QScopedPointer<QAbstractItemModelReplica> replica(client.acquireModel("test", fetchMode));
+ QSignalSpy initSpy(replica.get(), &QAbstractItemModelReplica::initialized);
+ QVERIFY(initSpy.wait());
+
+ QCOMPARE(roles, replica->availableRoles());
+
+ // Wait for data to be fetch and confirm
+ QCOMPARE(proxyModel->rowCount(), replica->rowCount());
+ for (int i = 0; i < replica->rowCount(); i++)
+ QTRY_COMPARE(proxyModel->data(proxyModel->index(i, 0), Qt::DisplayRole),
+ replica->data(replica->index(i, 0)));
+
+ proxyModel->sort(0, Qt::AscendingOrder);
+ QCOMPARE(proxyModel->rowCount(), replica->rowCount());
+
+ for (int i = 0; i < replica->rowCount(); i++)
+ QTRY_COMPARE(proxyModel->data(proxyModel->index(i, 0), Qt::DisplayRole),
+ replica->data(replica->index(i, 0)));
+}
+
+QTEST_MAIN(ModelreplicaTest)
+
+#include "tst_modelreplicatest.moc"
--- /dev/null
+
+#####################################################################
+## tst_modelview Test:
+#####################################################################
+
+qt_internal_add_test(tst_modelview
+ SOURCES
+ tst_modelview.cpp
+ ../shared/model_utilities.h
+ PUBLIC_LIBRARIES
+ Qt::Gui
+ Qt::RemoteObjects
+)
+
+## Scopes:
+#####################################################################
+
+qt_internal_extend_target(tst_modelview CONDITION boot2qt
+ DEFINES
+ SLOW_MODELTEST
+)
+
+qt_internal_extend_target(tst_modelview CONDITION MINGW
+ DEFINES
+ SLOW_MODELTEST
+)
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "../shared/model_utilities.h"
+
+#include <QtTest/QtTest>
+#include <QAbstractItemModelTester>
+#include <QMetaType>
+#include <QRemoteObjectReplica>
+#include <QRemoteObjectNode>
+#include <QAbstractItemModelReplica>
+#include <QStandardItemModel>
+#include <QSortFilterProxyModel>
+#include <QEventLoop>
+#include <QRandomGenerator>
+
+namespace {
+
+QList<QStandardItem*> createInsertionChildren(int num, const QString& name, const QColor &background)
+{
+ QList<QStandardItem*> children;
+ for (int i = 0; i < num; ++i) {
+ QStandardItem *item = new QStandardItem(QStringLiteral("%1 %2").arg(name).arg(i+1));
+ item->setBackground(background);
+ children.append(item);
+ }
+ return children;
+}
+
+class RowsWatcher : public WaitHelper
+{
+public:
+ RowsWatcher(const QAbstractItemModel *model, int expectedRowsCount)
+ : WaitHelper(), m_model(model), m_expectedRowsCount(expectedRowsCount)
+ {
+ connect(m_model, &QAbstractItemModel::rowsInserted, this,
+ [this](const QModelIndex &parent, int first, int last) {
+ const auto columnCount = m_model->columnCount(parent);
+ for (int row = first; row <= last; ++row) {
+ for (int column = 0; column < columnCount; ++column)
+ m_changedData.append(m_model->index(row, column, parent));
+ }
+ onNumRowsChanged(parent, first, last);
+ });
+
+ connect(m_model, &QAbstractItemModel::rowsRemoved, this, &RowsWatcher::onNumRowsChanged);
+ }
+
+ void onNumRowsChanged(const QModelIndex &parent, int first, int last)
+ {
+ const auto compare = [=](const RowData &row) {
+ return (row.m_start == first && row.m_end == last
+ && compareIndices(row.m_index, parent));
+ };
+ QVERIFY(std::find_if(m_pendingRows.begin(), m_pendingRows.end(), compare)
+ != m_pendingRows.end());
+
+ m_currentRowsCount += last - first + 1;
+ if (m_currentRowsCount == m_expectedRowsCount)
+ finish();
+ }
+
+ void scheduleRowsToWatch(const QModelIndex &index, int start, int end)
+ {
+ m_pendingRows.push_back(RowData { index, start, end });
+ }
+
+ QList<QModelIndex> changedData() const { return m_changedData; }
+
+private:
+ struct RowData
+ {
+ QModelIndex m_index;
+ int m_start = -1;
+ int m_end = -1;
+ };
+
+ const QAbstractItemModel *m_model;
+ QList<RowData> m_pendingRows;
+ QList<QModelIndex> m_changedData;
+ int m_currentRowsCount = 0;
+ const int m_expectedRowsCount;
+};
+
+QTextStream cout(stdout, QIODevice::WriteOnly);
+
+//Keep in case we need detailed debugging in the future...
+#if 0
+inline QDebug operator <<(QDebug dbg, const InsertedRow &row)
+{
+ return dbg.nospace() << "Index(" << row.m_index << ", " << row.m_start << ", " << row.m_end << ")";
+}
+
+inline void dumpModel(const QAbstractItemModel *model, const QModelIndex &parent = QModelIndex())
+{
+ int level = 0;
+ for (QModelIndex idx = parent; idx.isValid(); idx = model->parent(idx), ++level);
+ const QHash<int,QByteArray> roles = model->roleNames();
+ for (int i = 0; i < model->rowCount(parent); ++i) {
+ for (int j = 0; j < model->columnCount(parent); ++j) {
+ const QModelIndex index = model->index(i, j);
+ Q_ASSERT(index.isValid());
+ QString s;
+ s.fill(QChar(' '), level*2);
+ cout << qPrintable(s);
+ cout << QString(QLatin1String("%1:%2")).arg(i).arg(j);
+ for (auto it = roles.cbegin(), end = roles.cend(); it != end; ++it) {
+ const QVariant v = model->data(index, it.key());
+ if (!v.isValid()) continue;
+ QString t;
+ QDebug(&t) << v;
+ cout << " " << QString::fromUtf8(it.value()) << "=" << t.trimmed();
+ }
+
+ {
+ QString t;
+ QDebug(&t) << model->flags(index);
+ cout << " flags=" << t;
+ }
+
+ cout << "\n";
+ cout.flush();
+ dumpModel(model, index); // recursive
+ }
+ }
+}
+#endif
+
+void compareData(const QAbstractItemModel *sourceModel, const QAbstractItemModelReplica *replica)
+{
+ QVERIFY(sourceModel);
+ QVERIFY(replica);
+
+ QCOMPARE(replica->rowCount(), sourceModel->rowCount());
+ QCOMPARE(replica->columnCount(), sourceModel->columnCount());
+ QCOMPARE(replica->roleNames(), sourceModel->roleNames());
+
+ for (int i = 0; i < sourceModel->rowCount(); ++i) {
+ for (int j = 0; j < sourceModel->columnCount(); ++j) {
+ const auto roles = replica->availableRoles();
+ for (int role : roles) {
+ QCOMPARE(replica->index(i, j).data(role), sourceModel->index(i, j).data(role));
+ }
+ }
+ }
+}
+
+void compareIndex(const QModelIndex &sourceIndex, const QModelIndex &replicaIndex,
+ const QList<int> &roles)
+{
+ QVERIFY(sourceIndex.isValid());
+ QVERIFY(replicaIndex.isValid());
+ for (int role : roles) {
+ QCOMPARE(replicaIndex.data(role), sourceIndex.data(role));
+ }
+ const QAbstractItemModel *sourceModel = sourceIndex.model();
+ const QAbstractItemModel *replicaModel = replicaIndex.model();
+ const int sourceRowCount = sourceModel->rowCount(sourceIndex);
+ const int replicaRowCount = replicaModel->rowCount(replicaIndex);
+ QCOMPARE(replicaRowCount, sourceRowCount);
+ const int sourceColumnCount = sourceModel->columnCount(sourceIndex);
+ const int replicaColumnCount = replicaModel->columnCount(replicaIndex);
+ // only test the column count if the row count is larger than zero, because we
+ // assume the column count is constant over a tree model and it doesn't make a
+ // difference in the view.
+ if (sourceRowCount)
+ QCOMPARE(replicaColumnCount, sourceColumnCount);
+ for (int i = 0; i < sourceRowCount; ++i) {
+ for (int j = 0; j < sourceColumnCount; ++j) {
+ auto sourceChild = sourceModel->index(i, j, sourceIndex);
+ auto replicaChild = replicaModel->index(i, j, replicaIndex);
+ compareIndex(sourceChild, replicaChild, roles);
+
+ }
+ }
+}
+
+void compareTreeData(const QAbstractItemModel *sourceModel, const QAbstractItemModelReplica *replica)
+{
+ QVERIFY(sourceModel);
+ QVERIFY(replica);
+
+ QCOMPARE(replica->rowCount(), sourceModel->rowCount());
+ QCOMPARE(replica->columnCount(), sourceModel->columnCount());
+
+ for (int i = 0; i < sourceModel->rowCount(); ++i) {
+ for (int j = 0; j < sourceModel->columnCount(); ++j) {
+ const QModelIndex replicaIndex = replica->index(i, j);
+ const QModelIndex sourceIndex = sourceModel->index(i, j);
+ compareIndex(sourceIndex, replicaIndex, replica->availableRoles());
+ }
+ }
+}
+
+void compareTreeData(const QAbstractItemModel *sourceModel, const QAbstractItemModel *replica, const QList<int> &roles)
+{
+ QVERIFY(sourceModel);
+ QVERIFY(replica);
+
+ QCOMPARE(replica->rowCount(), sourceModel->rowCount());
+ QCOMPARE(replica->columnCount(), sourceModel->columnCount());
+
+ for (int i = 0; i < sourceModel->rowCount(); ++i) {
+ for (int j = 0; j < sourceModel->columnCount(); ++j) {
+ const QModelIndex replicaIndex = replica->index(i, j);
+ const QModelIndex sourceIndex = sourceModel->index(i, j);
+ compareIndex(sourceIndex, replicaIndex, roles);
+ }
+ }
+}
+
+void compareFlags(const QAbstractItemModel *sourceModel, const QAbstractItemModelReplica *replica)
+{
+ QVERIFY(sourceModel);
+ QVERIFY(replica);
+
+ QCOMPARE(replica->rowCount(), sourceModel->rowCount());
+ QCOMPARE(replica->columnCount(), sourceModel->columnCount());
+
+ for (int i = 0; i < sourceModel->rowCount(); ++i) {
+ for (int j = 0; j < sourceModel->columnCount(); ++j) {
+ if (replica->index(i, j).flags() != sourceModel->index(i, j).flags())
+ qWarning() << sourceModel->index(i, j).flags() << replica->index(i, j).flags() << i << j;
+ QCOMPARE(replica->index(i, j).flags(), sourceModel->index(i, j).flags());
+ }
+ }
+}
+
+// class to test cutom role names
+class RolenamesListModel : public QAbstractListModel
+{
+public:
+ explicit RolenamesListModel(QObject *parent = nullptr) : QAbstractListModel(parent) { }
+ int rowCount(const QModelIndex &) const override { return int(m_list.size()); }
+ QVariant data(const QModelIndex &index, int role) const override
+ {
+ if (role == Qt::UserRole)
+ return m_list.at(index.row()).second;
+ else if (role == Qt::UserRole+1)
+ return m_list.at(index.row()).first;
+ else
+ return QVariant();
+ }
+ QHash<int, QByteArray> roleNames() const override
+ {
+ QHash<int, QByteArray> roles;
+ roles[Qt::UserRole] = "name";
+ roles[Qt::UserRole+1] = "pid";
+ return roles;
+ }
+ void addPair(const QVariant pid,const QVariant name) {
+ m_list.append(qMakePair(pid, name));
+ }
+ void clearList() {
+ m_list.clear();
+ }
+private:
+ QList<QPair<QVariant, QVariant>> m_list;
+};
+
+QList<QStandardItem*> addChild(int numChilds, int nestingLevel)
+{
+ QList<QStandardItem*> result;
+ if (nestingLevel == 0)
+ return result;
+ for (int i = 0; i < numChilds; ++i) {
+ QStandardItem *child = new QStandardItem(QStringLiteral("Child num %1, nesting Level %2").arg(i+1).arg(nestingLevel));
+ if (i == 0) {
+ QList<QStandardItem*> res = addChild(numChilds, nestingLevel -1);
+ if (res.size() > 0)
+ child->appendRow(res);
+ }
+ result.push_back(child);
+ }
+ return result;
+}
+
+int getRandomNumber(int min, int max)
+{
+ int res = QRandomGenerator::global()->generate() & INT_MAX;
+ const int diff = (max - min);
+ res = res % diff;
+ res += min;
+ return res;
+}
+
+class FetchData : public WaitHelper
+{
+public:
+ FetchData(const QAbstractItemModelReplica *replica) : WaitHelper(), m_replica(replica)
+ {
+ if (!m_replica->isInitialized()) {
+ QEventLoop l;
+ connect(m_replica, &QAbstractItemModelReplica::initialized, &l, &QEventLoop::quit);
+ l.exec();
+ }
+
+ connect(m_replica, &QAbstractItemModelReplica::dataChanged, this, &FetchData::dataChanged);
+ connect(m_replica, &QAbstractItemModelReplica::rowsInserted, this, &FetchData::rowsInserted);
+ }
+
+ bool fetchAndWait(int timeout = 15000)
+ {
+ addAll();
+ fetch();
+ return wait(timeout);
+ }
+
+private:
+ const QAbstractItemModelReplica *m_replica;
+ QHash<QPersistentModelIndex, QList<int>> m_pending;
+ QSet<QPersistentModelIndex> m_waitForInsertion;
+
+ void addData(const QModelIndex &index, const QList<int> &roles)
+ {
+ for (int role : roles) {
+ const bool cached = m_replica->hasData(index, role);
+ if (cached)
+ continue;
+ if (!m_pending.contains(index))
+ m_pending[index] = QList<int>() << role;
+ else {
+ if (!m_pending[index].contains(role))
+ m_pending[index].append(role);
+ }
+ }
+ }
+
+ void addIndex(const QModelIndex &parent, const QList<int> &roles)
+ {
+ if (parent.isValid())
+ addData(parent, roles);
+ for (int i = 0; i < m_replica->rowCount(parent); ++i) {
+ for (int j = 0; j < m_replica->columnCount(parent); ++j) {
+ const QModelIndex index = m_replica->index(i, j, parent);
+ Q_ASSERT(index.isValid());
+ addIndex(index, roles);
+ }
+ }
+ }
+
+ void addAll()
+ {
+ addIndex(QModelIndex(), m_replica->availableRoles());
+ }
+
+ void fetch()
+ {
+ if (m_pending.isEmpty() && m_waitForInsertion.isEmpty()) {
+ finish();
+ return;
+ }
+ QHash<QPersistentModelIndex, QList<int>> pending(m_pending);
+ pending.detach();
+ auto it(pending.constBegin()), end(pending.constEnd());
+ for (; it != end; ++it) {
+ for (int role : it.value()) {
+ QVariant v = m_replica->data(it.key(), role);
+ Q_UNUSED(v)
+ }
+ }
+ }
+
+ void rowsInserted(const QModelIndex &parent, int first, int last)
+ {
+ static QList<int> rolesV;
+ if (rolesV.isEmpty())
+ rolesV << Qt::DisplayRole << Qt::BackgroundRole;
+ m_waitForInsertion.remove(parent);
+ const int columnCount = m_replica->columnCount(parent);
+ if (!(m_replica->hasChildren(parent) && columnCount > 0 && m_replica->rowCount(parent) > 0))
+ qWarning() << m_replica->hasChildren(parent) << columnCount << m_replica->rowCount(parent) << parent.data();
+ QVERIFY(m_replica->hasChildren(parent) && columnCount > 0 && m_replica->rowCount(parent) > 0);
+ for (int i = first; i <= last; ++i) {
+ for (int j = 0; j < columnCount; ++j) {
+ const QModelIndex index = m_replica->index(i, j, parent);
+ const int childRowCount = m_replica->rowCount(index);
+
+ QVERIFY(index.isValid());
+ if (m_replica->hasChildren(index) && childRowCount == 0) {
+ if (index.column() == 0)
+ m_waitForInsertion.insert(index);
+ }
+ addIndex(index, rolesV);
+ }
+ }
+ if (m_replica->hasChildren(parent))
+ fetch();
+ }
+
+ void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList<int> &roles = {})
+ {
+ Q_ASSERT(topLeft.isValid());
+ Q_ASSERT(bottomRight.isValid());
+ Q_ASSERT(topLeft.parent() == bottomRight.parent());
+
+ const QModelIndex parent = topLeft.parent();
+ for (int i = topLeft.row(); i <= bottomRight.row(); ++i) {
+ for (int j = topLeft.column(); j <= bottomRight.column(); ++j) {
+ const QModelIndex index = m_replica->index(i, j, parent);
+ Q_ASSERT(index.isValid());
+ auto it = m_pending.find(index);
+ if (it == m_pending.end())
+ continue;
+
+#if 0
+ QList<int> itroles = it.value();
+ itroles.detach();
+ if (roles.isEmpty()) {
+ itroles.clear();
+ } else {
+ for (int r : roles) {
+ itroles.removeAll(r);
+ }
+ }
+ if (itroles.isEmpty()) {
+ m_pending.erase(it);
+ } else {
+ m_pending[index] = itroles;
+ }
+#else
+ Q_UNUSED(roles)
+ m_pending.erase(it);
+ if (m_replica->hasChildren(index)) {
+ // ask for the row count to get an update
+ const int rowCount = m_replica->rowCount(index);
+ for (int i = 0; i < rowCount; ++i) {
+ const QModelIndex cIndex = m_replica->index(i, 0, index);
+ Q_ASSERT(cIndex.isValid());
+ addIndex(cIndex, m_replica->availableRoles());
+ }
+ if (rowCount)
+ fetch();
+ else if (index.column() == 0)
+ m_waitForInsertion.insert(index);
+ }
+#endif
+ }
+ }
+
+ if (m_pending.isEmpty() && m_waitForInsertion.isEmpty())
+ finish();
+ }
+};
+
+} // namespace
+
+#define _SETUP_TEST_ \
+ QRemoteObjectHost basicServer; \
+ QRemoteObjectNode client; \
+ QRemoteObjectRegistryHost registryServer; \
+ setup_models(basicServer, client, registryServer);
+
+class TestModelView: public QObject
+{
+ Q_OBJECT
+
+ QStandardItemModel m_sourceModel;
+ RolenamesListModel m_listModel;
+
+public:
+ void setup_models(QRemoteObjectHost &basicServer,
+ QRemoteObjectNode &client,
+ QRemoteObjectRegistryHost ®istryServer);
+
+private slots:
+ // NB: The tests have side effects on the models used, and need to be run
+ // in order (they may depend on previous side effects).
+ void initTestCase();
+
+ void testEmptyModel();
+ void testInitialData();
+ void testInitialDataTree();
+ void testHeaderData();
+ void testHeaderDataChange();
+ void testFlags();
+ void testDataChanged();
+ void testDataChangedTree();
+ void testDataInsertion();
+ void testDataInsertionTree();
+ void testSetData();
+ void testSetDataTree();
+ void testDataRemoval();
+ void testDataRemovalTree();
+ void testServerInsertDataTree();
+
+ void testRoleNames();
+
+ void testModelTest_data();
+ void testModelTest();
+ void testSortFilterModel();
+
+ void testSelectionFromReplica();
+ void testSelectionFromSource();
+ void testChildSelection();
+
+ void testCacheData_data();
+ void testCacheData();
+
+ void cleanup();
+};
+
+void TestModelView::initTestCase()
+{
+ QLoggingCategory::setFilterRules("qt.remoteobjects.warning=false");
+
+ static const int modelSize = 20;
+
+ QHash<int,QByteArray> roleNames;
+ roleNames[Qt::DisplayRole] = "text";
+ roleNames[Qt::BackgroundRole] = "background";
+ m_sourceModel.setItemRoleNames(roleNames);
+
+ QStringList list;
+ list.reserve(modelSize);
+
+ QStringList hHeaderList;
+ hHeaderList << QStringLiteral("First Column with spacing") << QStringLiteral("Second Column with spacing");
+ m_sourceModel.setHorizontalHeaderLabels(hHeaderList);
+
+ for (int i = 0; i < modelSize; ++i) {
+ QStandardItem *firstItem = new QStandardItem(QStringLiteral("FancyTextNumber %1").arg(i));
+ QStandardItem *secondItem = new QStandardItem(QStringLiteral("FancyRow2TextNumber %1").arg(i));
+ if (i % 2 == 0)
+ firstItem->setBackground(Qt::red);
+ firstItem->appendRow(addChild(2,getRandomNumber(1, 4)));
+ QList<QStandardItem*> row;
+ row << firstItem << secondItem;
+ m_sourceModel.appendRow(row);
+ list << QStringLiteral("FancyTextNumber %1").arg(i);
+ }
+
+ const int numElements = 1000;
+ for (int i = 0; i < numElements; ++i) {
+ QString name = QString("Data %1").arg(i);
+ QString pid = QString("%1").arg(i);
+ m_listModel.addPair(name, pid);
+ }
+}
+
+void TestModelView::setup_models(QRemoteObjectHost &basicServer, QRemoteObjectNode &client, QRemoteObjectRegistryHost ®istryServer)
+{
+ static int port = 65211;
+ static const QString url = QStringLiteral("tcp://127.0.0.1:%1");
+
+ // QStandardItem::flags are stored as data with Qt::UserRole - 1
+ static const QList<int> sourceModelRoles( {Qt::DisplayRole, Qt::BackgroundRole, (Qt::UserRole - 1)} );
+
+ static const QList<int> listModelRoles( {Qt::UserRole, Qt::UserRole+1} );
+
+ //Setup registry
+ //Registry needs to be created first until we get the retry mechanism implemented
+ basicServer.setHostUrl(QUrl(url.arg(port)));
+ registryServer.setRegistryUrl(QUrl(url.arg(port+1)));
+ basicServer.setRegistryUrl(QUrl(url.arg(port+1)));
+ basicServer.enableRemoting(&m_sourceModel, "test", sourceModelRoles);
+ basicServer.enableRemoting(&m_listModel, "testRoleNames", listModelRoles);
+ client.setRegistryUrl(QUrl(url.arg(port+1)));
+ port += 2;
+}
+
+#ifdef SLOW_MODELTEST
+#define MODELTEST_WAIT_TIME 25000
+#else
+#define MODELTEST_WAIT_TIME
+#endif
+
+void TestModelView::testEmptyModel()
+{
+ _SETUP_TEST_
+ QList<int> roles = QList<int>() << Qt::DisplayRole << Qt::BackgroundRole;
+ QStandardItemModel emptyModel;
+ basicServer.enableRemoting(&emptyModel, "emptyModel", roles);
+
+ QScopedPointer<QAbstractItemModelReplica> model(client.acquireModel("emptyModel"));
+ model->setRootCacheSize(1000);
+
+ FetchData f(model.data());
+ QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
+
+ compareData(&emptyModel, model.data());
+}
+
+void TestModelView::testInitialData()
+{
+ _SETUP_TEST_
+ QScopedPointer<QAbstractItemModelReplica> model(client.acquireModel("test"));
+
+ FetchData f(model.data());
+ QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
+
+ compareData(&m_sourceModel, model.data());
+}
+
+void TestModelView::testInitialDataTree()
+{
+ _SETUP_TEST_
+ QScopedPointer<QAbstractItemModelReplica> model(client.acquireModel("test"));
+
+ FetchData f(model.data());
+ QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
+
+ compareTreeData(&m_sourceModel, model.data());
+}
+
+void TestModelView::testHeaderData()
+{
+ _SETUP_TEST_
+ QScopedPointer<QAbstractItemModelReplica> model(client.acquireModel("test"));
+
+ FetchData f(model.data());
+ QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
+
+ // ask for all Data members first, so we don't have to wait for update signals
+ QSignalSpy spyHeader(model.data(), &QAbstractItemModelReplica::headerDataChanged);
+ for (int i = 0; i < m_sourceModel.rowCount(); ++i)
+ model->headerData(i, Qt::Vertical, Qt::DisplayRole);
+ for (int i = 0; i < m_sourceModel.columnCount(); ++i)
+ model->headerData(i, Qt::Horizontal, Qt::DisplayRole);
+ spyHeader.wait();
+
+ for (int i = 0; i < m_sourceModel.rowCount(); ++i)
+ QCOMPARE(model->headerData(i, Qt::Vertical, Qt::DisplayRole), m_sourceModel.headerData(i, Qt::Vertical, Qt::DisplayRole));
+ for (int i = 0; i < m_sourceModel.columnCount(); ++i)
+ QCOMPARE(model->headerData(i, Qt::Horizontal, Qt::DisplayRole), m_sourceModel.headerData(i, Qt::Horizontal, Qt::DisplayRole));
+}
+
+void TestModelView::testHeaderDataChange()
+{
+ _SETUP_TEST_
+ QString newHeader = QStringLiteral("New header name");
+ QScopedPointer<QAbstractItemModelReplica> model(client.acquireModel("test"));
+
+ FetchData f(model.data());
+ QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
+ QVERIFY(model->headerData(0, Qt::Horizontal, Qt::DisplayRole).toString() != newHeader);
+
+ QSignalSpy spyHeader(model.data(), &QAbstractItemModelReplica::headerDataChanged);
+ m_sourceModel.setHeaderData(0, Qt::Horizontal, newHeader, Qt::DisplayRole);
+ spyHeader.wait();
+ QTRY_COMPARE(model->headerData(0, Qt::Horizontal, Qt::DisplayRole).toString(), newHeader);
+
+ spyHeader.clear();
+ m_sourceModel.setHeaderData(1, Qt::Horizontal, newHeader, Qt::DisplayRole);
+ spyHeader.wait();
+ QTRY_COMPARE(model->headerData(1, Qt::Horizontal, Qt::DisplayRole).toString(), newHeader);
+
+ QString anotherHeader = QStringLiteral("Modified header name");
+ m_sourceModel.setHeaderData(0, Qt::Horizontal, anotherHeader, Qt::DisplayRole);
+ spyHeader.wait();
+
+ QTRY_COMPARE(model->headerData(0, Qt::Horizontal, Qt::DisplayRole).toString(), anotherHeader);
+ QCOMPARE(model->headerData(1, Qt::Horizontal, Qt::DisplayRole).toString(), newHeader);
+}
+
+void TestModelView::testDataChangedTree()
+{
+ _SETUP_TEST_
+ QScopedPointer<QAbstractItemModelReplica> model(client.acquireModel("test"));
+
+ FetchData f(model.data());
+ QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
+
+ compareTreeData(&m_sourceModel, model.data());
+ QSignalSpy dataChangedSpy(model.data(), &QAbstractItemModelReplica::dataChanged);
+ QSet<int> expected;
+ for (int i = 10; i < 20; ++i) {
+ const QModelIndex parent = m_sourceModel.index(i,0);
+ const int rowCount = m_sourceModel.rowCount(parent);
+ const int colCount = m_sourceModel.columnCount(parent);
+ for (int row = 0; row < rowCount; ++row) {
+ for (int col = 0; col < colCount; ++col) {
+ if (col % 2 == 0)
+ m_sourceModel.setData(m_sourceModel.index(row, col, parent), QColor(Qt::gray), Qt::BackgroundRole);
+ else
+ m_sourceModel.setData(m_sourceModel.index(row, col, parent), QColor(Qt::cyan), Qt::BackgroundRole);
+ }
+ }
+ m_sourceModel.setData(m_sourceModel.index(i, 1), QColor(Qt::magenta), Qt::BackgroundRole);
+ expected << i;
+ }
+
+ bool signalsReceived = false;
+ int runs = 0;
+ const int maxRuns = 10;
+ while (runs < maxRuns) {
+ if (dataChangedSpy.wait() &&!dataChangedSpy.isEmpty()) {
+ signalsReceived = true;
+ for (auto args : dataChangedSpy) {
+ int row = args.at(1).value<QModelIndex>().row();
+ if (row && expected.contains(row))
+ expected.remove(row);
+ }
+ if (expected.size() == 0)
+ break;
+ }
+ ++runs;
+ }
+ QVERIFY(signalsReceived);
+ compareTreeData(&m_sourceModel, model.data());
+}
+
+void TestModelView::testFlags()
+{
+ _SETUP_TEST_
+ QScopedPointer<QAbstractItemModelReplica> model(client.acquireModel("test"));
+
+ FetchData f(model.data());
+ QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
+
+ QSignalSpy dataChangedSpy(model.data(), &QAbstractItemModelReplica::dataChanged);
+ for (int i = 10; i < 20; ++i) {
+ QStandardItem* firstItem = m_sourceModel.item(i, 0);
+ QStandardItem* secondItem = m_sourceModel.item(i, 1);
+ firstItem->setFlags(firstItem->flags() | Qt::ItemIsEnabled | Qt::ItemIsAutoTristate);
+ secondItem->setFlags(firstItem->flags() | Qt::ItemIsEnabled);
+ }
+ bool signalsReceived = false;
+ while (dataChangedSpy.wait()) {
+ signalsReceived = true;
+ if (dataChangedSpy.takeLast().at(1).value<QModelIndex>().row() == 19)
+ break;
+ }
+ QVERIFY(signalsReceived);
+ compareFlags(&m_sourceModel, model.data());
+}
+
+void TestModelView::testDataChanged()
+{
+ _SETUP_TEST_
+ QScopedPointer<QAbstractItemModelReplica> model(client.acquireModel("test"));
+
+ FetchData f(model.data());
+ QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
+
+ QSignalSpy dataChangedSpy(model.data(), &QAbstractItemModelReplica::dataChanged);
+ for (int i = 10; i < 20; ++i)
+ m_sourceModel.setData(m_sourceModel.index(i, 1), QColor(Qt::blue), Qt::BackgroundRole);
+
+ bool signalsReceived = false;
+ while (dataChangedSpy.wait()) {
+ signalsReceived = true;
+ if (dataChangedSpy.takeLast().at(1).value<QModelIndex>().row() == 19)
+ break;
+ }
+ QVERIFY(signalsReceived);
+ compareData(&m_sourceModel, model.data());
+}
+
+void TestModelView::testDataInsertion()
+{
+ _SETUP_TEST_
+ QScopedPointer<QAbstractItemModelReplica> model(client.acquireModel("test"));
+
+ FetchData f(model.data());
+ QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
+
+ const int insertedRowsCount = 9;
+ RowsWatcher watcher(model.data(), insertedRowsCount);
+ QList<QModelIndex> pending;
+
+ m_sourceModel.insertRows(2, insertedRowsCount);
+
+ watcher.scheduleRowsToWatch(QModelIndex(), 2, 2 + insertedRowsCount - 1);
+ QVERIFY(watcher.wait());
+ QCOMPARE(m_sourceModel.rowCount(), model->rowCount());
+
+ pending.append(watcher.changedData());
+
+ // change one row to check for inconsistencies
+ m_sourceModel.setData(m_sourceModel.index(0, 1), QColor(Qt::green), Qt::BackgroundRole);
+ m_sourceModel.setData(m_sourceModel.index(0, 1), QLatin1String("foo"), Qt::DisplayRole);
+ pending.append(model->index(0, 1));
+ WaitForDataChanged w(model.data(), pending);
+
+ QVERIFY(w.wait());
+ compareData(&m_sourceModel, model.data());
+}
+
+void TestModelView::testDataInsertionTree()
+{
+ _SETUP_TEST_
+ QScopedPointer<QAbstractItemModelReplica> model(client.acquireModel("test"));
+
+ FetchData f(model.data());
+ QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
+
+ const int insertedRowsCount = 9;
+ const int insertedChildRowsCount = 4;
+ RowsWatcher watcher(model.data(), insertedRowsCount + insertedChildRowsCount);
+
+ QList<QModelIndex> pending;
+
+ for (int i = 0; i < insertedRowsCount; ++i) {
+ watcher.scheduleRowsToWatch(QModelIndex(), 2 + i, 2 + i);
+ m_sourceModel.insertRow(2 + i, createInsertionChildren(2, QStringLiteral("insertedintree"), Qt::darkRed));
+ const QModelIndex childIndex = m_sourceModel.index(2 + i, 0);
+ const QModelIndex childIndex2 = m_sourceModel.index(2 + i, 1);
+ pending.append(childIndex);
+ pending.append(childIndex2);
+ }
+ const QModelIndex parent = m_sourceModel.index(10, 0);
+ QStandardItem* parentItem = m_sourceModel.item(10, 0);
+ for (int i = 0; i < insertedChildRowsCount; ++i) {
+ watcher.scheduleRowsToWatch(parent, i, i);
+ parentItem->insertRow(i, createInsertionChildren(2, QStringLiteral("insertedintreedeep"), Qt::darkCyan));
+ const QModelIndex childIndex = m_sourceModel.index(0, 0, parent);
+ const QModelIndex childIndex2 = m_sourceModel.index(0, 1, parent);
+ Q_ASSERT(childIndex.isValid());
+ Q_ASSERT(childIndex2.isValid());
+ pending.append(childIndex);
+ pending.append(childIndex2);
+ }
+
+ QVERIFY(watcher.wait());
+ QCOMPARE(m_sourceModel.rowCount(), model->rowCount());
+
+ pending.append(watcher.changedData());
+
+ // change one row to check for inconsistencies
+
+ pending << m_sourceModel.index(0, 0, parent);
+ WaitForDataChanged w(model.data(), pending);
+ m_sourceModel.setData(m_sourceModel.index(0, 0, parent), QColor(Qt::green), Qt::BackgroundRole);
+ m_sourceModel.setData(m_sourceModel.index(0, 0, parent), QLatin1String("foo"), Qt::DisplayRole);
+
+ QVERIFY(w.wait());
+
+ compareTreeData(&m_sourceModel, model.data());
+}
+
+void TestModelView::testDataRemoval()
+{
+ _SETUP_TEST_
+ QScopedPointer<QAbstractItemModelReplica> model(client.acquireModel("test"));
+ qputenv("QTRO_NODES_CACHE_SIZE", "1000");
+ model->setRootCacheSize(1000);
+ FetchData f(model.data());
+ QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
+
+ const QPersistentModelIndex parent = m_sourceModel.index(10, 0);
+ {
+ const int removedRowsCount = 3;
+ RowsWatcher watcher(model.data(), removedRowsCount);
+ m_sourceModel.removeRows(0, removedRowsCount, parent);
+ watcher.scheduleRowsToWatch(parent, 0, removedRowsCount - 1);
+ QVERIFY(watcher.wait());
+ QCOMPARE(m_sourceModel.rowCount(parent), model->rowCount(model->index(10, 0)));
+ }
+ {
+ const int removedRowsCount = 8;
+ RowsWatcher watcher(model.data(), removedRowsCount);
+ m_sourceModel.removeRows(2, removedRowsCount);
+ watcher.scheduleRowsToWatch(QModelIndex(), 2, 2 + removedRowsCount - 1);
+ QVERIFY(watcher.wait());
+ QCOMPARE(m_sourceModel.rowCount(), model->rowCount());
+ }
+
+ // change one row to check for inconsistencies
+
+ QList<QModelIndex> pending;
+ pending << m_sourceModel.index(0, 0, parent);
+ WaitForDataChanged w(model.data(), pending);
+ m_sourceModel.setData(m_sourceModel.index(0, 0, parent), QColor(Qt::green), Qt::BackgroundRole);
+ m_sourceModel.setData(m_sourceModel.index(0, 0, parent), QLatin1String("foo"), Qt::DisplayRole);
+
+ QVERIFY(w.wait());
+
+ compareTreeData(&m_sourceModel, model.data());
+}
+
+void TestModelView::testRoleNames()
+{
+ _SETUP_TEST_
+ QScopedPointer<QAbstractItemModelReplica> repModel( client.acquireModel(QStringLiteral("testRoleNames")));
+ // Set a bigger cache enough to keep all the data otherwise the last test will fail
+ repModel->setRootCacheSize(1500);
+ FetchData f(repModel.data());
+ QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
+
+ // test custom role names
+ QCOMPARE(repModel.data()->roleNames(), m_listModel.roleNames());
+
+ // test data associated with custom roles
+ compareData(&m_listModel,repModel.data());
+}
+
+void TestModelView::testDataRemovalTree()
+{
+ _SETUP_TEST_
+ m_sourceModel.removeRows(2, 4);
+ //Maybe some checks here?
+}
+
+void TestModelView::testServerInsertDataTree()
+{
+ _SETUP_TEST_
+ QList<int> roles = QList<int> { Qt::DisplayRole, Qt::BackgroundRole };
+ QStandardItemModel testTreeModel;
+ basicServer.enableRemoting(&testTreeModel, "testTreeModel", roles);
+
+ QScopedPointer<QAbstractItemModelReplica> model(client.acquireModel("testTreeModel"));
+
+ QTRY_COMPARE(testTreeModel.rowCount(), model->rowCount());
+
+ QVERIFY(testTreeModel.insertRow(0));
+ QVERIFY(testTreeModel.insertColumn(0));
+ auto root = testTreeModel.index(0, 0);
+ QVERIFY(testTreeModel.setData(root, QLatin1String("Root"), Qt::DisplayRole));
+ QVERIFY(testTreeModel.setData(root, QColor(Qt::green), Qt::BackgroundRole));
+ QVERIFY(testTreeModel.insertRow(0, root));
+ QVERIFY(testTreeModel.insertColumn(0, root));
+ auto child1 = testTreeModel.index(0, 0, root);
+ QVERIFY(testTreeModel.setData(child1, QLatin1String("Child1"), Qt::DisplayRole));
+ QVERIFY(testTreeModel.setData(child1, QColor(Qt::red), Qt::BackgroundRole));
+
+ QTRY_COMPARE(testTreeModel.rowCount(), model->rowCount());
+
+ FetchData f(model.data());
+ QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
+
+ compareData(&testTreeModel, model.data());
+}
+
+void TestModelView::testModelTest_data()
+{
+ // QtRemoteObjects::InitialAction is a namespace enum. QTest/QFETCH fail to compile with it as
+ // the column type, so use bool instead.
+ QTest::addColumn<bool>("prefetch");
+
+ QTest::newRow("size only") << false;
+ QTest::newRow("prefetch") << true;
+}
+
+void TestModelView::testModelTest()
+{
+ QFETCH(bool, prefetch);
+ _SETUP_TEST_
+ QtRemoteObjects::InitialAction action = QtRemoteObjects::InitialAction::FetchRootSize;
+ if (prefetch)
+ action = QtRemoteObjects::InitialAction::PrefetchData;
+ QScopedPointer<QAbstractItemModelReplica> repModel( client.acquireModel(QStringLiteral("test"), action));
+ QAbstractItemModelTester test(repModel.data(), QAbstractItemModelTester::FailureReportingMode::Fatal);
+
+ FetchData f(repModel.data());
+ QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
+ Q_UNUSED(test)
+}
+
+void TestModelView::testSortFilterModel()
+{
+ _SETUP_TEST_
+ QScopedPointer<QAbstractItemModelReplica> repModel( client.acquireModel(QStringLiteral("test")));
+
+ FetchData f(repModel.data());
+ QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
+
+ QSortFilterProxyModel clientSort;
+ clientSort.setSourceModel(repModel.data());
+ clientSort.setSortRole(Qt::DisplayRole);
+ QSortFilterProxyModel sourceSort;
+ sourceSort.setSourceModel(&m_sourceModel);
+ sourceSort.setSortRole(Qt::DisplayRole);
+
+ compareTreeData(&sourceSort, &clientSort, repModel->availableRoles());
+}
+
+void TestModelView::testSetData()
+{
+ _SETUP_TEST_
+ QScopedPointer<QAbstractItemModelReplica> model(client.acquireModel("test"));
+
+ FetchData f(model.data());
+ QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
+ compareTreeData(&m_sourceModel, model.data(), model->availableRoles());
+
+ //fetched and verified initial state, now setData on the client
+ QList<QModelIndex> pending;
+ QList<QModelIndex> pendingReplica;
+ for (int row = 0, numRows = model->rowCount(); row < numRows; ++row) {
+ for (int column = 0, numColumns = model->columnCount(); column != numColumns; ++column) {
+ const QModelIndex index = model->index(row, column);
+ const QString newData = QStringLiteral("This entry was changed with setData");
+ QVERIFY(model->setData(index, newData, Qt::DisplayRole));
+ pending.append(m_sourceModel.index(row, column));
+ pendingReplica.append(model->index(row, column));
+ }
+ }
+ WaitForDataChanged waiter(&m_sourceModel, pending);
+ QVERIFY(waiter.wait());
+ WaitForDataChanged waiterReplica(model.data(), pendingReplica);
+ QVERIFY(waiterReplica.wait());
+ compareData(&m_sourceModel, model.data());
+}
+
+void TestModelView::testSetDataTree()
+{
+ _SETUP_TEST_
+ QScopedPointer<QAbstractItemModelReplica> model(client.acquireModel("test"));
+
+ FetchData f(model.data());
+ QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
+ compareTreeData(&m_sourceModel, model.data(), model->availableRoles());
+
+ //fetched and verified initial state, now setData on the client
+ QList<QModelIndex> pending;
+ QList<QModelIndex> pendingReplica;
+
+ QList<QModelIndex> stack;
+ stack.push_back(QModelIndex());
+ QList<QModelIndex> sourceStack;
+ sourceStack.push_back(QModelIndex());
+
+
+ const QString newData = QStringLiteral("This entry was changed with setData in a tree %1 %2 %3");
+ while (!stack.isEmpty()) {
+ const QModelIndex parent = stack.takeLast();
+ const QModelIndex parentSource = sourceStack.takeLast();
+ for (int row = 0; row < model->rowCount(parent); ++row) {
+ for (int column = 0; column < model->columnCount(parent); ++column) {
+ const QModelIndex index = model->index(row, column, parent);
+ const QModelIndex indexSource = m_sourceModel.index(row, column, parentSource);
+ QVERIFY(model->setData(index, newData.arg(parent.isValid()).arg(row).arg(column), Qt::DisplayRole));
+ pending.append(indexSource);
+ pendingReplica.append(index);
+ if (column == 0) {
+ stack.push_back(index);
+ sourceStack.push_back(indexSource);
+ }
+ }
+ }
+ }
+ WaitForDataChanged waiter(&m_sourceModel, pending);
+ QVERIFY(waiter.wait());
+ WaitForDataChanged waiterReplica(model.data(), pendingReplica);
+ QVERIFY(waiterReplica.wait());
+ compareData(&m_sourceModel, model.data());
+}
+
+void TestModelView::testSelectionFromReplica()
+{
+ _SETUP_TEST_
+ QList<int> roles = QList<int> { Qt::DisplayRole, Qt::BackgroundRole };
+ QStandardItemModel simpleModel;
+ for (int i = 0; i < 4; ++i)
+ simpleModel.appendRow(new QStandardItem(QString("item %0").arg(i)));
+ QItemSelectionModel selectionModel(&simpleModel);
+ basicServer.enableRemoting(&simpleModel, "simpleModelFromReplica", roles, &selectionModel);
+
+ QScopedPointer<QAbstractItemModelReplica> model(client.acquireModel("simpleModelFromReplica"));
+ QItemSelectionModel *replicaSelectionModel = model->selectionModel();
+
+ FetchData f(model.data());
+ QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
+
+ replicaSelectionModel->setCurrentIndex(model->index(1,0), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Current);
+ QTRY_COMPARE(selectionModel.currentIndex().row(), 1);
+}
+
+void TestModelView::testSelectionFromSource()
+{
+ _SETUP_TEST_
+ QList<int> roles = QList<int> { Qt::DisplayRole, Qt::BackgroundRole };
+ QStandardItemModel simpleModel;
+ for (int i = 0; i < 4; ++i)
+ simpleModel.appendRow(new QStandardItem(QString("item %0").arg(i)));
+ QItemSelectionModel selectionModel(&simpleModel);
+ basicServer.enableRemoting(&simpleModel, "simpleModelFromSource", roles, &selectionModel);
+
+ QScopedPointer<QAbstractItemModelReplica> model(client.acquireModel("simpleModelFromSource"));
+ QItemSelectionModel *replicaSelectionModel = model->selectionModel();
+
+ FetchData f(model.data());
+ QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
+
+ selectionModel.setCurrentIndex(simpleModel.index(1,0), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Current);
+ QTRY_COMPARE(replicaSelectionModel->currentIndex().row(), 1);
+}
+
+void TestModelView::testCacheData_data()
+{
+ QTest::addColumn<QList<int>>("roles");
+
+ QTest::newRow("empty") << QList<int> {};
+ QTest::newRow("all") << QList<int> { Qt::UserRole, Qt::UserRole + 1 };
+}
+
+void TestModelView::testCacheData()
+{
+ QFETCH(QList<int>, roles);
+
+ _SETUP_TEST_
+ QScopedPointer<QAbstractItemModelReplica> model(client.acquireModel("testRoleNames", QtRemoteObjects::PrefetchData, roles));
+ model->setRootCacheSize(1000);
+
+ QEventLoop l;
+ connect(model.data(), &QAbstractItemModelReplica::initialized, &l, &QEventLoop::quit);
+ l.exec();
+
+ compareData(&m_listModel, model.data());
+}
+
+void TestModelView::testChildSelection()
+{
+ _SETUP_TEST_
+ QList<int> roles = { Qt::DisplayRole, Qt::BackgroundRole };
+ QStandardItemModel simpleModel;
+ QStandardItem *parentItem = simpleModel.invisibleRootItem();
+ for (int i = 0; i < 4; ++i) {
+ QStandardItem *item = new QStandardItem(QString("item %0").arg(i));
+ parentItem->appendRow(item);
+ parentItem = item;
+ }
+ QItemSelectionModel selectionModel(&simpleModel);
+ basicServer.enableRemoting(&simpleModel, "treeModelFromSource", roles, &selectionModel);
+
+ QScopedPointer<QAbstractItemModelReplica> model(client.acquireModel("treeModelFromSource", QtRemoteObjects::PrefetchData, roles));
+ QItemSelectionModel *replicaSelectionModel = model->selectionModel();
+
+ QTRY_COMPARE(simpleModel.rowCount(), model->rowCount());
+ QTRY_COMPARE(model->data(model->index(0, 0)), QVariant(QString("item 0")));
+
+ // select an item not yet "seen" by the replica
+ selectionModel.setCurrentIndex(simpleModel.index(0, 0, simpleModel.index(0,0)), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Current);
+ QTRY_COMPARE(replicaSelectionModel->currentIndex().row(), 0);
+ QVERIFY(replicaSelectionModel->currentIndex().parent().isValid());
+}
+
+void TestModelView::cleanup()
+{
+ // wait for delivery of RemoveObject events to the source
+ QTest::qWait(20);
+}
+
+QTEST_MAIN(TestModelView)
+
+#include "tst_modelview.moc"
--- /dev/null
+
+#####################################################################
+## tst_pods Test:
+#####################################################################
+
+qt_internal_add_test(tst_pods
+ SOURCES
+ tst_pods.cpp
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+)
+qt6_add_repc_replicas(tst_pods
+ pods.h
+)
+
+## Scopes:
+#####################################################################
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+// This is an autogenerated file.
+// Do not edit this file, any changes made will be lost the next time it is generated.
+
+#include <QObject>
+
+
+#include <QString>
+class PodI
+{
+ Q_GADGET
+ Q_PROPERTY(int i READ i WRITE setI)
+public:
+ explicit PodI() : _i() {}
+ explicit PodI(int i) : _i(i) {}
+ PodI(const PodI& other)
+ {
+ QtRemoteObjects::copyStoredProperties(&other, this);
+ }
+
+ PodI &operator=(const PodI &other)
+ {
+ if (this != &other)
+ QtRemoteObjects::copyStoredProperties(&other, this);
+ return *this;
+ }
+
+ int i() const { return _i; }
+ void setI(int i) { if (i != _i) { _i = i; } }
+private:
+ int _i;
+};
+
+inline QDataStream &operator<<(QDataStream &ds, const PodI &obj) {
+ QtRemoteObjects::copyStoredProperties(&obj, ds);
+ return ds;
+}
+
+inline QDataStream &operator>>(QDataStream &ds, PodI &obj) {
+ QtRemoteObjects::copyStoredProperties(ds, &obj);
+ return ds;
+}
+
+class PodF
+{
+ Q_GADGET
+ Q_PROPERTY(float f READ f WRITE setF)
+public:
+ explicit PodF() : _f() {}
+ explicit PodF(float f) : _f(f) {}
+ PodF(const PodF& other)
+ {
+ QtRemoteObjects::copyStoredProperties(&other, this);
+ }
+
+ PodF &operator=(const PodF &other)
+ {
+ if (this != &other)
+ QtRemoteObjects::copyStoredProperties(&other, this);
+ return *this;
+ }
+
+ float f() const { return _f; }
+ void setF(float f) { if (f != _f) { _f = f; } }
+private:
+ float _f;
+};
+
+inline QDataStream &operator<<(QDataStream &ds, const PodF &obj) {
+ QtRemoteObjects::copyStoredProperties(&obj, ds);
+ return ds;
+}
+
+inline QDataStream &operator>>(QDataStream &ds, PodF &obj) {
+ QtRemoteObjects::copyStoredProperties(ds, &obj);
+ return ds;
+}
+
+class PodS
+{
+ Q_GADGET
+ Q_PROPERTY(QString s READ s WRITE setS)
+public:
+ explicit PodS() : _s() {}
+ explicit PodS(QString s) : _s(s) {}
+ PodS(const PodS& other)
+ {
+ QtRemoteObjects::copyStoredProperties(&other, this);
+ }
+
+ PodS &operator=(const PodS &other)
+ {
+ if (this != &other)
+ QtRemoteObjects::copyStoredProperties(&other, this);
+ return *this;
+ }
+
+ QString s() const { return _s; }
+ void setS(QString s) { if (s != _s) { _s = s; } }
+private:
+ QString _s;
+};
+
+inline QDataStream &operator<<(QDataStream &ds, const PodS &obj) {
+ QtRemoteObjects::copyStoredProperties(&obj, ds);
+ return ds;
+}
+
+inline QDataStream &operator>>(QDataStream &ds, PodS &obj) {
+ QtRemoteObjects::copyStoredProperties(ds, &obj);
+ return ds;
+}
+
+class PodIFS
+{
+ Q_GADGET
+ Q_PROPERTY(int i READ i WRITE setI)
+ Q_PROPERTY(float f READ f WRITE setF)
+ Q_PROPERTY(QString s READ s WRITE setS)
+public:
+ explicit PodIFS() : _i(), _f(), _s() {}
+ explicit PodIFS(int i, float f, QString s) : _i(i), _f(f), _s(s) {}
+ PodIFS(const PodIFS& other)
+ {
+ QtRemoteObjects::copyStoredProperties(&other, this);
+ }
+
+ PodIFS &operator=(const PodIFS &other)
+ {
+ if (this != &other)
+ QtRemoteObjects::copyStoredProperties(&other, this);
+ return *this;
+ }
+
+ int i() const { return _i; }
+ void setI(int i) { if (i != _i) { _i = i; } }
+ float f() const { return _f; }
+ void setF(float f) { if (f != _f) { _f = f; } }
+ QString s() const { return _s; }
+ void setS(QString s) { if (s != _s) { _s = s; } }
+private:
+ int _i;
+ float _f;
+ QString _s;
+};
+
+inline QDataStream &operator<<(QDataStream &ds, const PodIFS &obj) {
+ QtRemoteObjects::copyStoredProperties(&obj, ds);
+ return ds;
+}
+
+inline QDataStream &operator>>(QDataStream &ds, PodIFS &obj) {
+ QtRemoteObjects::copyStoredProperties(ds, &obj);
+ return ds;
+}
+
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "rep_pods_replica.h"
+
+#include <QTest>
+
+#include <QByteArray>
+#include <QDataStream>
+
+class tst_pods : public QObject {
+ Q_OBJECT
+
+private Q_SLOTS:
+ void testConstructors();
+ void testMarshalling();
+};
+
+
+void tst_pods::testConstructors()
+{
+ PodI pi1;
+ QCOMPARE(pi1.i(), 0);
+
+ PodI pi2(1);
+ QCOMPARE(pi2.i(), 1);
+
+ PodI pi3(pi2);
+ QCOMPARE(pi3.i(), pi2.i());
+}
+
+void tst_pods::testMarshalling()
+{
+ QByteArray ba;
+ QDataStream ds(&ba, QIODevice::ReadWrite);
+
+ {
+ PodI i1(1), i2(2), i3(3), iDeadBeef(0xdeadbeef);
+ ds << i1 << i2 << i3 << iDeadBeef;
+ }
+
+ ds.device()->seek(0);
+
+ {
+ PodI i1, i2, i3, iDeadBeef;
+ ds >> i1 >> i2 >> i3 >> iDeadBeef;
+
+ QCOMPARE(i1.i(), 1);
+ QCOMPARE(i2.i(), 2);
+ QCOMPARE(i3.i(), 3);
+ QCOMPARE(iDeadBeef.i(), int(0xdeadbeef));
+ }
+}
+
+QTEST_APPLESS_MAIN(tst_pods)
+
+#include "tst_pods.moc"
+
--- /dev/null
+
+#####################################################################
+## tst_proxy Test:
+#####################################################################
+
+qt_internal_add_test(tst_proxy
+ SOURCES
+ tst_proxy.cpp
+ ../shared/model_utilities.h
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+)
+qt6_add_repc_merged(tst_proxy
+ engine.rep
+ subclass.rep
+)
+
+## Scopes:
+#####################################################################
--- /dev/null
+class Engine
+{
+ ENUM EngineType {Null, Gas, Electric, Hybrid};
+ PROP(int cylinders = 4)
+ PROP(bool started)
+ PROP(int rpm)
+ PROP(EngineType type)
+}
--- /dev/null
+POD MyPOD(int i, float f, QString s)
+
+class SubClass
+{
+ PROP(MyPOD myPOD)
+}
+
+class ParentClass
+{
+ CLASS subClass(SubClass)
+ MODEL tracks(display)
+}
+
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "rep_engine_merged.h"
+#include "rep_subclass_merged.h"
+#include "../shared/model_utilities.h"
+
+#include <QtTest/QtTest>
+#include <QRemoteObjectReplica>
+#include <QRemoteObjectNode>
+
+#include "../../shared/testutils.h"
+
+const QUrl localHostUrl = QUrl(QLatin1String(LOCAL_SOCKET ":testHost"));
+const QUrl tcpHostUrl = QUrl(QLatin1String("tcp://127.0.0.1:9989"));
+const QUrl proxyNodeUrl = QUrl(QLatin1String("tcp://127.0.0.1:12123"));
+const QUrl remoteNodeUrl = QUrl(QLatin1String("tcp://127.0.0.1:23234"));
+const QUrl registryUrl = QUrl(QLatin1String(LOCAL_SOCKET ":testRegistry"));
+const QUrl proxyHostUrl = QUrl(QLatin1String(LOCAL_SOCKET ":fromProxy"));
+
+#define SET_NODE_NAME(obj) (obj).setName(QLatin1String(#obj))
+
+class ProxyTest : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+ void cleanup()
+ {
+ // wait for delivery of RemoveObject events to the source
+ QTest::qWait(200);
+ }
+
+ void testProxy_data();
+ void testProxy();
+ void testForwardProxy();
+ void testReverseProxy();
+ // The following should fail to compile, verifying the SourceAPI templates work
+ // for subclasses
+ /*
+ void testSubclass()
+ {
+ QRemoteObjectHost host(localHostUrl);
+
+ struct invalidchild {
+ MyPOD myPOD() { return MyPOD(12, 13.f, QStringLiteral("Yay!")); }
+ };
+ struct badparent {
+ invalidchild *subClass() { return new invalidchild; }
+ } parent;
+ host.enableRemoting<ParentClassSourceAPI>(&parent);
+ }
+ */
+
+ void testTopLevelModel();
+};
+
+void ProxyTest::testProxy_data()
+{
+ QTest::addColumn<bool>("sourceApi");
+ QTest::addColumn<bool>("useProxy");
+ QTest::addColumn<bool>("dynamic");
+
+ QTest::newRow("dynamicApi, no proxy") << false << false << false;
+ QTest::newRow("sourceApi, no proxy") << true << false << false;
+ QTest::newRow("dynamicApi, with proxy") << false << true << false;
+ QTest::newRow("sourceApi, with proxy") << true << true << false;
+ QTest::newRow("dynamicApi, no proxy, dynamicRep") << false << false << true;
+ QTest::newRow("sourceApi, no proxy, dynamicRep") << true << false << true;
+ QTest::newRow("dynamicApi, with proxy, dynamicRep") << false << true << true;
+ QTest::newRow("sourceApi, with proxy, dynamicRep") << true << true << true;
+}
+
+void ProxyTest::testProxy()
+{
+ QFETCH(bool, sourceApi);
+ QFETCH(bool, useProxy);
+ QFETCH(bool, dynamic);
+
+ //Setup Local Registry
+ QRemoteObjectRegistryHost registry(registryUrl);
+ SET_NODE_NAME(registry);
+ //Setup Local Host
+ QRemoteObjectHost host(localHostUrl);
+ SET_NODE_NAME(host);
+ host.setRegistryUrl(registryUrl);
+ EngineSimpleSource engine;
+ engine.setRpm(1234);
+ engine.setType(EngineSimpleSource::Gas);
+ if (sourceApi)
+ host.enableRemoting<EngineSourceAPI>(&engine);
+ else
+ host.enableRemoting(&engine);
+
+ QRemoteObjectHost proxyNode;
+ SET_NODE_NAME(proxyNode);
+ if (useProxy) {
+ proxyNode.setHostUrl(tcpHostUrl);
+ proxyNode.proxy(registryUrl);
+ }
+
+ //Setup Local Replica
+ QRemoteObjectNode client;
+ SET_NODE_NAME(client);
+ if (useProxy)
+ client.connectToNode(tcpHostUrl);
+ else
+ client.setRegistryUrl(registryUrl);
+
+ QScopedPointer<QRemoteObjectReplica> replica;
+ if (!dynamic) {
+ //QLoggingCategory::setFilterRules("qt.remoteobjects*=true");
+ replica.reset(client.acquire<EngineReplica>());
+ QVERIFY(replica->waitForSource(1000));
+
+ EngineReplica *rep = qobject_cast<EngineReplica *>(replica.data());
+
+ //Compare Replica to Source
+ QCOMPARE(rep->rpm(), engine.rpm());
+ QCOMPARE(EngineReplica::EngineType(rep->type()), EngineReplica::Gas);
+
+ //Change Replica and make sure change propagates to source
+ QSignalSpy sourceSpy(&engine, &EngineSimpleSource::rpmChanged);
+ QSignalSpy replicaSpy(rep, &EngineReplica::rpmChanged);
+ rep->pushRpm(42);
+ sourceSpy.wait();
+ QCOMPARE(sourceSpy.size(), 1);
+ QCOMPARE(engine.rpm(), 42);
+
+ // ... and the change makes it back to the replica
+ replicaSpy.wait();
+ QCOMPARE(replicaSpy.size(), 1);
+ QCOMPARE(rep->rpm(), 42);
+ } else {
+ replica.reset(client.acquireDynamic(QStringLiteral("Engine")));
+ QVERIFY(replica->waitForSource(1000));
+
+ //Compare Replica to Source
+ const QMetaObject *metaObject = replica->metaObject();
+ const int rpmIndex = metaObject->indexOfProperty("rpm");
+ Q_ASSERT(rpmIndex != -1);
+ const QMetaProperty rpmMeta = metaObject->property(rpmIndex);
+ QCOMPARE(rpmMeta.read(replica.data()).value<int>(), engine.rpm());
+ const int typeIndex = metaObject->indexOfProperty("type");
+ Q_ASSERT(typeIndex != -1);
+ const QMetaProperty typeMeta = metaObject->property(typeIndex);
+ QCOMPARE(typeMeta.read(replica.data()).value<EngineReplica::EngineType>(), EngineReplica::Gas);
+
+ //Change Replica and make sure change propagates to source
+ QSignalSpy sourceSpy(&engine, &EngineSimpleSource::rpmChanged);
+ QSignalSpy replicaSpy(replica.data(), QByteArray(QByteArrayLiteral("2")+rpmMeta.notifySignal().methodSignature().constData()));
+
+ const int rpmPushIndex = metaObject->indexOfMethod("pushRpm(int)");
+ Q_ASSERT(rpmPushIndex != -1);
+ QMetaMethod pushMethod = metaObject->method(rpmPushIndex);
+ Q_ASSERT(pushMethod.isValid());
+ QVERIFY(pushMethod.invoke(replica.data(), Q_ARG(int, 42)));
+
+ sourceSpy.wait();
+ QCOMPARE(sourceSpy.size(), 1);
+ QCOMPARE(engine.rpm(), 42);
+
+ // ... and the change makes it back to the replica
+ replicaSpy.wait();
+ QCOMPARE(replicaSpy.size(), 1);
+ QCOMPARE(rpmMeta.read(replica.data()).value<int>(), engine.rpm());
+ }
+
+ // Make sure disabling the Source cascades the state change
+ bool res = host.disableRemoting(&engine);
+ Q_ASSERT(res);
+ QSignalSpy stateSpy(replica.data(), &QRemoteObjectReplica::stateChanged);
+ stateSpy.wait();
+ QCOMPARE(stateSpy.size(), 1);
+ QCOMPARE(replica->state(), QRemoteObjectReplica::Suspect);
+
+ // Now test subclass Source
+ ParentClassSimpleSource parent;
+ SubClassSimpleSource subclass;
+ const MyPOD initialValue(42, 3.14f, QStringLiteral("SubClass"));
+ subclass.setMyPOD(initialValue);
+ QStringListModel model;
+ model.setStringList(QStringList() << "Track1" << "Track2" << "Track3");
+ parent.setSubClass(&subclass);
+ parent.setTracks(&model);
+ QCOMPARE(subclass.myPOD(), initialValue);
+ if (sourceApi)
+ host.enableRemoting<ParentClassSourceAPI>(&parent);
+ else
+ host.enableRemoting(&parent);
+ if (!dynamic) {
+ replica.reset(client.acquire<ParentClassReplica>());
+ ParentClassReplica *rep = qobject_cast<ParentClassReplica *>(replica.data());
+ QSignalSpy tracksSpy(rep->tracks(), &QAbstractItemModelReplica::initialized);
+ QVERIFY(replica->waitForSource(1000));
+ //QLoggingCategory::setFilterRules("qt.remoteobjects*=false");
+
+ //Compare Replica to Source
+ QVERIFY(rep->subClass() != nullptr);
+ QCOMPARE(rep->subClass()->myPOD(), parent.subClass()->myPOD());
+ QVERIFY(rep->tracks() != nullptr);
+ QVERIFY(tracksSpy.size() || tracksSpy.wait());
+ // Rep file only uses display role, but proxy doesn't forward that yet
+ if (!useProxy)
+ QCOMPARE(rep->tracks()->availableRoles(), QList<int> { Qt::DisplayRole });
+ else {
+ const auto &availableRolesVec = rep->tracks()->availableRoles();
+ QSet<int> availableRoles;
+ for (int r : availableRolesVec)
+ availableRoles.insert(r);
+ const auto &rolesHash = model.roleNames();
+ QSet<int> roles;
+ for (auto it = rolesHash.cbegin(), end = rolesHash.cend(); it != end; ++it)
+ roles.insert(it.key());
+ QCOMPARE(availableRoles, roles);
+ }
+ QList<QModelIndex> pending;
+ QTRY_COMPARE(rep->tracks()->rowCount(), model.rowCount());
+ for (int i = 0; i < rep->tracks()->rowCount(); i++)
+ {
+ // We haven't received any data yet
+ const auto index = rep->tracks()->index(i, 0);
+ QCOMPARE(rep->tracks()->data(index), QVariant());
+ pending.append(index);
+ }
+ if (useProxy) { // A first batch of updates will be the empty proxy values
+ WaitForDataChanged w(rep->tracks(), pending);
+ QVERIFY(w.wait());
+ }
+ WaitForDataChanged w(rep->tracks(), pending);
+ QVERIFY(w.wait());
+ for (int i = 0; i < rep->tracks()->rowCount(); i++)
+ {
+ QTRY_COMPARE(rep->tracks()->data(rep->tracks()->index(i, 0)), model.data(model.index(i), Qt::DisplayRole));
+ }
+
+ //Change SubClass and make sure change propagates
+ SubClassSimpleSource updatedSubclass;
+ const MyPOD updatedValue(-1, 123.456f, QStringLiteral("Updated"));
+ updatedSubclass.setMyPOD(updatedValue);
+ QSignalSpy replicaSpy(rep, &ParentClassReplica::subClassChanged);
+ parent.setSubClass(&updatedSubclass);
+ replicaSpy.wait();
+ QCOMPARE(replicaSpy.size(), 1);
+ QCOMPARE(rep->subClass()->myPOD(), parent.subClass()->myPOD());
+ QCOMPARE(rep->subClass()->myPOD(), updatedValue);
+ } else {
+ replica.reset(client.acquireDynamic(QStringLiteral("ParentClass")));
+ QVERIFY(replica->waitForSource(1000));
+
+ const QMetaObject *metaObject = replica->metaObject();
+ // Verify subClass pointer
+ const int subclassIndex = metaObject->indexOfProperty("subClass");
+ QVERIFY(subclassIndex != -1);
+ const QMetaProperty subclassMeta = metaObject->property(subclassIndex);
+ QObject *subclassQObjectPtr = subclassMeta.read(replica.data()).value<QObject *>();
+ QVERIFY(subclassQObjectPtr != nullptr);
+ QRemoteObjectDynamicReplica *subclassReplica = qobject_cast<QRemoteObjectDynamicReplica *>(subclassQObjectPtr);
+ QVERIFY(subclassReplica != nullptr);
+ // Verify tracks pointer
+ const int tracksIndex = metaObject->indexOfProperty("tracks");
+ QVERIFY(tracksIndex != -1);
+ const QMetaProperty tracksMeta = metaObject->property(tracksIndex);
+ QObject *tracksQObjectPtr = tracksMeta.read(replica.data()).value<QObject *>();
+ QVERIFY(tracksQObjectPtr != nullptr);
+ QAbstractItemModelReplica *tracksReplica = qobject_cast<QAbstractItemModelReplica *>(tracksQObjectPtr);
+ QVERIFY(tracksReplica != nullptr);
+
+ // Verify subClass data
+ const int podIndex = subclassReplica->metaObject()->indexOfProperty("myPOD");
+ QVERIFY(podIndex != -1);
+ const QMetaProperty podMeta = subclassReplica->metaObject()->property(podIndex);
+ MyPOD pod = podMeta.read(subclassReplica).value<MyPOD>();
+ QCOMPARE(pod, parent.subClass()->myPOD());
+
+ // Verify tracks data
+ // Rep file only uses display role, but proxy doesn't forward that yet
+ if (!useProxy)
+ QCOMPARE(tracksReplica->availableRoles(), QList<int> { Qt::DisplayRole });
+ else {
+ const auto &availableRolesVec = tracksReplica->availableRoles();
+ QSet<int> availableRoles;
+ for (int r : availableRolesVec)
+ availableRoles.insert(r);
+ const auto &rolesHash = model.roleNames();
+ QSet<int> roles;
+ for (auto it = rolesHash.cbegin(), end = rolesHash.cend(); it != end; ++it)
+ roles.insert(it.key());
+ QCOMPARE(availableRoles, roles);
+ }
+ QTRY_COMPARE(tracksReplica->isInitialized(), true);
+ QSignalSpy dataSpy(tracksReplica, &QAbstractItemModelReplica::dataChanged);
+ QList<QModelIndex> pending;
+ QTRY_COMPARE(tracksReplica->rowCount(), model.rowCount());
+ for (int i = 0; i < tracksReplica->rowCount(); i++)
+ {
+ // We haven't received any data yet
+ const auto index = tracksReplica->index(i, 0);
+ QCOMPARE(tracksReplica->data(index), QVariant());
+ pending.append(index);
+ }
+ if (useProxy) { // A first batch of updates will be the empty proxy values
+ WaitForDataChanged w(tracksReplica, pending);
+ QVERIFY(w.wait());
+ }
+ WaitForDataChanged w(tracksReplica, pending);
+ QVERIFY(w.wait());
+ for (int i = 0; i < tracksReplica->rowCount(); i++)
+ {
+ QCOMPARE(tracksReplica->data(tracksReplica->index(i, 0)), model.data(model.index(i), Qt::DisplayRole));
+ }
+
+ //Change SubClass and make sure change propagates
+ SubClassSimpleSource updatedSubclass;
+ const MyPOD updatedValue(-1, 123.456f, QStringLiteral("Updated"));
+ updatedSubclass.setMyPOD(updatedValue);
+ QSignalSpy replicaSpy(replica.data(), QByteArray(QByteArrayLiteral("2")+subclassMeta.notifySignal().methodSignature().constData()));
+ parent.setSubClass(&updatedSubclass);
+ replicaSpy.wait();
+ QCOMPARE(replicaSpy.size(), 1);
+ subclassQObjectPtr = subclassMeta.read(replica.data()).value<QObject *>();
+ QVERIFY(subclassQObjectPtr != nullptr);
+ subclassReplica = qobject_cast<QRemoteObjectDynamicReplica *>(subclassQObjectPtr);
+ QVERIFY(subclassReplica != nullptr);
+
+ pod = podMeta.read(subclassReplica).value<MyPOD>();
+ QCOMPARE(pod, parent.subClass()->myPOD());
+ }
+ replica.reset();
+}
+
+void ProxyTest::testForwardProxy()
+{
+ // Setup Local Registry
+ QRemoteObjectRegistryHost registry(registryUrl);
+ SET_NODE_NAME(registry);
+
+ // Setup Local Host
+ QRemoteObjectHost host(localHostUrl, registryUrl);
+ SET_NODE_NAME(host);
+
+ // Setup Proxy
+ QRemoteObjectRegistryHost proxyNode(proxyNodeUrl);
+ SET_NODE_NAME(proxyNode);
+ proxyNode.proxy(registryUrl, proxyHostUrl);
+ // Include the reverseProxy to make sure we don't try to send back
+ // proxied objects.
+ proxyNode.reverseProxy();
+
+ // Setup Source
+ EngineSimpleSource engine;
+ engine.setRpm(1234);
+
+ // Setup Remote Node
+ QRemoteObjectHost remoteNode(remoteNodeUrl);
+ SET_NODE_NAME(remoteNode);
+ remoteNode.connectToNode(proxyNodeUrl);
+
+ // Add source
+ host.enableRemoting(&engine);
+
+ // Setup Replica
+ const QScopedPointer<EngineReplica> replica(remoteNode.acquire<EngineReplica>());
+ QTRY_VERIFY(replica->waitForSource(300));
+
+ // Compare Replica to Source
+ QCOMPARE(replica->rpm(), engine.rpm());
+}
+
+void ProxyTest::testReverseProxy()
+{
+ // Setup Local Registry
+ QRemoteObjectRegistryHost registry(registryUrl);
+ SET_NODE_NAME(registry);
+
+ // Setup Local Host
+ QRemoteObjectHost host(localHostUrl, registryUrl);
+ SET_NODE_NAME(host);
+
+ // Setup Proxy
+ QRemoteObjectRegistryHost proxyNode(proxyNodeUrl);
+ SET_NODE_NAME(proxyNode);
+ proxyNode.proxy(registryUrl, proxyHostUrl);
+ proxyNode.reverseProxy();
+
+ // Setup Source
+ EngineSimpleSource engine;
+ engine.setRpm(1234);
+
+ // Setup Remote Node
+ QRemoteObjectHost remoteNode(remoteNodeUrl, proxyNodeUrl);
+ SET_NODE_NAME(remoteNode);
+
+ // Add source
+ remoteNode.enableRemoting(&engine);
+
+ // Setup Replica
+ const QScopedPointer<EngineReplica> replica(host.acquire<EngineReplica>());
+ QTRY_VERIFY(replica->waitForSource(300));
+
+ //Compare Replica to Source
+ QCOMPARE(replica->rpm(), engine.rpm());
+}
+
+void ProxyTest::testTopLevelModel()
+{
+ QRemoteObjectRegistryHost registry(registryUrl);
+
+ //Setup Local Host
+ QRemoteObjectHost host(localHostUrl);
+ SET_NODE_NAME(host);
+ host.setRegistryUrl(registryUrl);
+
+ QStringListModel model;
+ model.setStringList(QStringList() << "Track1" << "Track2" << "Track3");
+ host.enableRemoting(&model, "trackList", QList<int> { Qt::DisplayRole });
+
+ QRemoteObjectHost proxyNode;
+ SET_NODE_NAME(proxyNode);
+ proxyNode.setHostUrl(tcpHostUrl);
+ proxyNode.proxy(registryUrl);
+
+ //Setup Local Replica
+ QRemoteObjectNode client;
+ SET_NODE_NAME(client);
+ client.connectToNode(tcpHostUrl);
+ QAbstractItemModelReplica *replica = client.acquireModel("trackList");
+ QSignalSpy tracksSpy(replica, &QAbstractItemModelReplica::initialized);
+ QVERIFY(tracksSpy.wait());
+ QTRY_COMPARE(replica->rowCount(), model.rowCount());
+}
+
+QTEST_MAIN(ProxyTest)
+
+#include "tst_proxy.moc"
--- /dev/null
+
+add_subdirectory(client)
+add_subdirectory(server)
+add_subdirectory(proxy)
+add_subdirectory(tst)
--- /dev/null
+
+#####################################################################
+## proxy_multiprocess_client Binary:
+#####################################################################
+
+qt_internal_add_executable(proxy_multiprocess_client
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES
+ ../namespace.h
+ ../shared.h
+ main.cpp
+ INCLUDE_DIRECTORIES
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+ Qt::Test
+)
+qt6_add_repc_replicas(proxy_multiprocess_client
+ ../subclass.rep
+)
+
+#### Keys ignored in scope 1:.:.:client.pro:<TRUE>:
+# TEMPLATE = "app"
--- /dev/null
+// Copyright (C) 2019 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "rep_subclass_replica.h"
+#include "../shared.h"
+
+#include <QCoreApplication>
+#include <QtRemoteObjects/qremoteobjectnode.h>
+#include <QtTest/QtTest>
+
+static QMap<int, MyPOD> int_map{{1, initialValue},
+ {16, initialValue}};
+static ParentClassReplica::ActivePositions flags1 = ParentClassReplica::Position::position1;
+static ParentClassReplica::ActivePositions flags2 = ParentClassReplica::Position::position2
+ | ParentClassReplica::Position::position3;
+static QMap<ParentClassReplica::ActivePositions, MyPOD> my_map{{flags1, initialValue},
+ {flags2, initialValue}};
+static QHash<NS2::NamespaceEnum, MyPOD> my_hash{{NS2::NamespaceEnum::Alpha, initialValue},
+ {NS2::NamespaceEnum::Charlie, initialValue}};
+
+class tst_Client_Process : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void initTestCase()
+ {
+ m_repNode.reset(new QRemoteObjectNode);
+ m_repNode->connectToNode(QUrl(QStringLiteral("tcp://127.0.0.1:65213")));
+ m_rep.reset(m_repNode->acquire<ParentClassReplica>());
+ const auto objectMode = qEnvironmentVariable("ObjectMode");
+ qDebug() << "Waiting to connect, mode =" << objectMode;
+ QVERIFY(m_rep->waitForSource());
+ }
+
+ void testSubClass()
+ {
+ const auto objectMode = qEnvironmentVariable("ObjectMode");
+
+ qDebug() << "Starting test" << objectMode;
+ if (objectMode == QLatin1String("ObjectPointer")) {
+ QSignalSpy tracksSpy(m_rep->tracks(), &QAbstractItemModelReplica::initialized);
+ QVERIFY(m_rep->subClass() != nullptr);
+ QCOMPARE(m_rep->subClass()->myPOD(), initialValue);
+ QCOMPARE(m_rep->subClass()->i(), initialI);
+ QVERIFY(m_rep->tracks() != nullptr);
+ QVERIFY(tracksSpy.size() || tracksSpy.wait());
+ QCOMPARE(m_rep->myEnum(), ParentClassReplica::bar);
+ QCOMPARE(m_rep->date(), Qt::RFC2822Date);
+ QCOMPARE(m_rep->nsEnum(), NS::Bravo);
+ QCOMPARE(m_rep->ns2Enum(), NS2::NamespaceEnum::Bravo);
+ QCOMPARE(m_rep->variant(), QVariant::fromValue(42.0f));
+ QCOMPARE(m_rep->simpleList(), QList<QString>() << "one" << "two");
+ QCOMPARE(m_rep->podList(), QList<MyPOD>() << initialValue << initialValue);
+ QCOMPARE(m_rep->intMap(), int_map);
+ QCOMPARE(m_rep->enumMap(), my_map);
+ QCOMPARE(m_rep->podHash(), my_hash);
+ } else {
+ QVERIFY(m_rep->subClass() == nullptr);
+ QVERIFY(m_rep->tracks() == nullptr);
+ QCOMPARE(m_rep->myEnum(), ParentClassReplica::foo);
+ QCOMPARE(m_rep->date(), Qt::ISODate);
+ QCOMPARE(m_rep->nsEnum(), NS::Alpha);
+ QCOMPARE(m_rep->ns2Enum(), NS2::NamespaceEnum::Alpha);
+ QCOMPARE(m_rep->variant(), QVariant());
+ }
+
+ QPoint p(1, 2);
+ auto enumReply = m_rep->enumSlot(p, ParentClassReplica::bar);
+ QVERIFY(enumReply.waitForFinished());
+ QCOMPARE(enumReply.error(), QRemoteObjectPendingCall::NoError);
+ QCOMPARE(enumReply.returnValue(), ParentClassReplica::foobar);
+
+ qDebug() << "Verified expected initial states, sending start.";
+ QSignalSpy enumSpy(m_rep.data(), &ParentClassReplica::enum2);
+ QSignalSpy advanceSpy(m_rep.data(), &ParentClassReplica::advance);
+ auto reply = m_rep->start();
+ QVERIFY(reply.waitForFinished());
+ QVERIFY(reply.error() == QRemoteObjectPendingCall::NoError);
+ QCOMPARE(reply.returnValue(), QVariant::fromValue(true));
+ QVERIFY(enumSpy.wait());
+ QCOMPARE(enumSpy.size(), 1);
+ const auto arguments = enumSpy.takeFirst();
+ QCOMPARE(arguments.at(0), QVariant::fromValue(ParentClassReplica::foo));
+ QCOMPARE(arguments.at(1), QVariant::fromValue(ParentClassReplica::bar));
+
+ QVERIFY(advanceSpy.size() || advanceSpy.wait());
+ QVERIFY(m_rep->subClass() != nullptr);
+ QCOMPARE(m_rep->subClass()->myPOD(), updatedValue);
+ QCOMPARE(m_rep->subClass()->i(), updatedI);
+ QVERIFY(m_rep->tracks() != nullptr);
+ QCOMPARE(m_rep->myEnum(), ParentClassReplica::foobar);
+ QCOMPARE(m_rep->date(), Qt::ISODateWithMs);
+ QCOMPARE(m_rep->nsEnum(), NS::Charlie);
+ QCOMPARE(m_rep->ns2Enum(), NS2::NamespaceEnum::Charlie);
+ QCOMPARE(m_rep->variant(), QVariant::fromValue(podValue));
+ qDebug() << "Verified expected final states, cleaning up.";
+ }
+
+ void cleanupTestCase()
+ {
+ auto reply = m_rep->quit();
+ // Don't verify the wait result, depending on the timing of the server and proxy
+ // closing it may return false. We just need this process to stay alive long
+ // enough for the packets to be sent.
+ reply.waitForFinished(5000);
+ }
+
+private:
+ QScopedPointer<QRemoteObjectNode> m_repNode;
+ QScopedPointer<ParentClassReplica> m_rep;
+};
+
+QTEST_MAIN(tst_Client_Process)
+
+#include "main.moc"
--- /dev/null
+#ifndef __PROXY_MULTIPROCESS_NAMESPACE_H__
+#define __PROXY_MULTIPROCESS_NAMESPACE_H__
+
+#include <QMetaType>
+
+namespace NS
+{
+ Q_NAMESPACE
+ enum NamespaceEnum { Alpha=1, Bravo, Charlie };
+ Q_ENUM_NS(NamespaceEnum)
+}
+
+namespace NS2
+{
+ Q_NAMESPACE
+ enum class NamespaceEnum : quint8 { Alpha=1, Bravo, Charlie };
+ Q_ENUM_NS(NamespaceEnum)
+}
+
+#endif // include guard
--- /dev/null
+
+#####################################################################
+## proxy Binary:
+#####################################################################
+
+qt_internal_add_executable(proxy
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES
+ main.cpp
+ INCLUDE_DIRECTORIES
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+ Qt::Test
+)
+
+#### Keys ignored in scope 1:.:.:proxy.pro:<TRUE>:
+# TEMPLATE = "app"
--- /dev/null
+// Copyright (C) 2019 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <QCoreApplication>
+#include <QtRemoteObjects/qremoteobjectnode.h>
+#include <QtTest/QtTest>
+
+#include "../../../shared/testutils.h"
+
+class tst_Proxy_Process : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void testRun()
+ {
+ m_hostNode.reset(new QRemoteObjectHost);
+ m_hostNode->setHostUrl(QUrl(QStringLiteral("tcp://127.0.0.1:65213")));
+ m_hostNode->proxy(QUrl(LOCAL_SOCKET ":testRegistry"));
+
+ // our proxied object should be added, and then later removed when the server shuts down
+ QSignalSpy addSpy(m_hostNode.data(), &QRemoteObjectNode::remoteObjectAdded);
+ QSignalSpy removeSpy(m_hostNode.data(), &QRemoteObjectNode::remoteObjectRemoved);
+ QVERIFY(addSpy.wait());
+ QVERIFY(removeSpy.wait());
+ }
+
+private:
+ QScopedPointer<QRemoteObjectHost> m_hostNode;
+};
+
+QTEST_MAIN(tst_Proxy_Process)
+
+#include "main.moc"
--- /dev/null
+
+#####################################################################
+## proxy_multiprocess_server Binary:
+#####################################################################
+
+qt_internal_add_executable(proxy_multiprocess_server
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES
+ ${CMAKE_CURRENT_BINARY_DIR}/rep_subclass_source.h
+ ../namespace.h
+ ../shared.h
+ main.cpp
+ mytestserver.cpp mytestserver.h
+ INCLUDE_DIRECTORIES
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+ Qt::Test
+)
+qt6_add_repc_sources(proxy_multiprocess_server
+ ../subclass.rep
+)
+
+#### Keys ignored in scope 1:.:.:server.pro:<TRUE>:
+# TEMPLATE = "app"
--- /dev/null
+// Copyright (C) 2019 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "mytestserver.h"
+#include "rep_subclass_source.h"
+#include "../shared.h"
+
+#include <QCoreApplication>
+#include <QtTest/QtTest>
+
+#include "../../../shared/testutils.h"
+
+static QMap<int, MyPOD> int_map{{1, initialValue},
+ {16, initialValue}};
+static MyTestServer::ActivePositions flags1 = MyTestServer::Position::position1;
+static MyTestServer::ActivePositions flags2 = MyTestServer::Position::position2
+ | MyTestServer::Position::position3;
+static QMap<MyTestServer::ActivePositions, MyPOD> my_map{{flags1, initialValue},
+ {flags2, initialValue}};
+static QHash<NS2::NamespaceEnum, MyPOD> my_hash{{NS2::NamespaceEnum::Alpha, initialValue},
+ {NS2::NamespaceEnum::Charlie, initialValue}};
+
+class tst_Server_Process : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void testRun()
+ {
+ const auto objectMode = qEnvironmentVariable("ObjectMode");
+ bool templated = qEnvironmentVariableIsSet("TEMPLATED_REMOTING");
+
+ qDebug() << "Starting tests:" << objectMode << "templated =" << templated;
+ QRemoteObjectRegistryHost srcNode(QUrl(QStringLiteral(LOCAL_SOCKET ":testRegistry")));
+
+ MyTestServer parent;
+ SubClassSimpleSource subclass;
+ subclass.setMyPOD(initialValue);
+ subclass.setI(initialI);
+ QStringListModel model;
+ model.setStringList(QStringList() << "Track1" << "Track2" << "Track3");
+ if (objectMode == QLatin1String("ObjectPointer")) {
+ parent.setSubClass(&subclass);
+ parent.setTracks(&model);
+ parent.setMyEnum(ParentClassSource::bar);
+ parent.setDate(Qt::RFC2822Date);
+ parent.setNsEnum(NS::Bravo);
+ parent.setNs2Enum(NS2::NamespaceEnum::Bravo);
+ parent.setVariant(QVariant::fromValue(42.0f));
+ parent.setSimpleList(QList<QString>() << "one" << "two");
+ parent.setPodList(QList<MyPOD>() << initialValue << initialValue);
+ parent.setIntMap(int_map);
+ parent.setEnumMap(my_map);
+ parent.setPodHash(my_hash);
+ }
+
+ if (templated)
+ srcNode.enableRemoting<ParentClassSourceAPI>(&parent);
+ else
+ srcNode.enableRemoting(&parent);
+
+ qDebug() << "Waiting for incoming connections";
+
+ QSignalSpy waitForStartedSpy(&parent, &MyTestServer::startedChanged);
+ QVERIFY(waitForStartedSpy.isValid());
+ QVERIFY(waitForStartedSpy.wait());
+ QCOMPARE(waitForStartedSpy.value(0).value(0).toBool(), true);
+
+ // wait for delivery of events
+ QTest::qWait(200);
+
+ //Change SubClass and make sure change propagates
+ SubClassSimpleSource updatedSubclass;
+ updatedSubclass.setMyPOD(updatedValue);
+ updatedSubclass.setI(updatedI);
+ parent.setSubClass(&updatedSubclass);
+ if (objectMode == QLatin1String("NullPointer"))
+ parent.setTracks(&model);
+ parent.setMyEnum(ParentClassSource::foobar);
+ parent.setDate(Qt::ISODateWithMs);
+ parent.setNsEnum(NS::Charlie);
+ parent.setNs2Enum(NS2::NamespaceEnum::Charlie);
+ parent.setVariant(QVariant::fromValue(podValue));
+ emit parent.enum2(ParentClassSource::foo, ParentClassSource::bar);
+
+ emit parent.advance();
+
+ // wait for quit
+ bool quit = false;
+ connect(&parent, &MyTestServer::quitApp, [&quit]{quit = true;});
+ QTRY_VERIFY_WITH_TIMEOUT(quit, 5000);
+
+ // wait for delivery of events
+ QTest::qWait(200);
+
+ qDebug() << "Done. Shutting down.";
+ }
+};
+
+QTEST_MAIN(tst_Server_Process)
+
+#include "main.moc"
--- /dev/null
+// Copyright (C) 2019 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <qdebug.h>
+
+#include "mytestserver.h"
+
+MyTestServer::MyTestServer(QObject *parent)
+ : ParentClassSimpleSource(parent)
+{
+ qDebug() << "Server started";
+}
+
+MyTestServer::~MyTestServer()
+{
+ qDebug() << "Server stopped";
+}
+
+bool MyTestServer::start()
+{
+ setStarted(true);
+ return true;
+}
+
+bool MyTestServer::quit()
+{
+ emit quitApp();
+ return true;
+}
+
+ParentClassSource::MyEnum MyTestServer::enumSlot(QPoint p, MyEnum myEnum)
+{
+ Q_UNUSED(p)
+ Q_UNUSED(myEnum)
+ return ParentClassSource::foobar;
+}
+
+Qt::DateFormat MyTestServer::dateSlot(Qt::DateFormat date)
+{
+ Q_UNUSED(date)
+ return Qt::RFC2822Date;
+}
--- /dev/null
+// Copyright (C) 2019 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef MYTESTSERVER_H
+#define MYTESTSERVER_H
+
+#include <QTimer>
+
+#include <QtRemoteObjects/qremoteobjectnode.h>
+#include <QtRemoteObjects/qremoteobjectsource.h>
+
+#include "rep_subclass_source.h"
+
+class MyTestServer : public ParentClassSimpleSource
+{
+ Q_OBJECT
+
+public:
+ MyTestServer(QObject *parent = nullptr);
+ ~MyTestServer() override;
+
+public Q_SLOTS:
+ bool start() override;
+ bool quit() override;
+ MyEnum enumSlot(QPoint p, MyEnum myEnum) override;
+ Qt::DateFormat dateSlot(Qt::DateFormat date) override;
+
+Q_SIGNALS:
+ void quitApp();
+};
+
+#endif // MYTESTSERVER_H
--- /dev/null
+const MyPOD initialValue(MyPOD::Position::position1, 3.14, QStringLiteral("SubClass"));
+const MyPOD updatedValue(MyPOD::Position::position2 | MyPOD::Position::position3, 123.456, QStringLiteral("Updated"));
+const VariantPOD podValue(10, 11);
+const int initialI = 100;
+const int updatedI = 200;
+
--- /dev/null
+#include <QPoint>
+#include "../namespace.h"
+
+USE_ENUM(Qt::DateFormat)
+USE_ENUM(NS::NamespaceEnum)
+USE_ENUM(NS2::NamespaceEnum)
+
+POD MyPOD{
+ ENUM class Position : unsigned short {position1=1, position2=2, position3=4}
+ FLAG (ActivePositions Position)
+ ActivePositions positions, float f, QString s
+}
+POD VariantPOD(int i, int j)
+
+class SubClass
+{
+ PROP(MyPOD myPOD)
+ PROP(int i)
+}
+
+class ParentClass
+{
+ ENUM MyEnum {foo=1, bar=3, foobar=6}
+ ENUM class Position : unsigned short {position1=1, position2=2, position3=4}
+ FLAG (ActivePositions Position)
+ PROP(bool started = false)
+ PROP(MyEnum myEnum=foo)
+ PROP(QVariant variant)
+ PROP(Qt::DateFormat date = Qt::ISODate)
+ PROP(NS::NamespaceEnum nsEnum = NS::Alpha)
+ PROP(NS2::NamespaceEnum ns2Enum = NS2::NamespaceEnum::Alpha)
+ PROP(QList<QString> simpleList)
+ PROP(QList<MyPOD> podList)
+ PROP(QMap<int, MyPOD> intMap)
+ PROP(QMap<ActivePositions, MyPOD> enumMap)
+ PROP(QHash<NS2::NamespaceEnum, MyPOD> podHash)
+
+ SLOT(bool start())
+ SLOT(bool quit())
+ SIGNAL(advance())
+ SIGNAL(enum2(MyEnum myEnum1, MyEnum myEnum2))
+ SLOT(MyEnum enumSlot(QPoint point, MyEnum myEnum))
+
+ SIGNAL(updateDate(Qt::DateFormat date1, Qt::DateFormat date2))
+ SLOT(Qt::DateFormat dateSlot(Qt::DateFormat date))
+
+ CLASS subClass(SubClass)
+ MODEL tracks(display)
+}
+
--- /dev/null
+
+#####################################################################
+## tst_proxy_multiprocess Test:
+#####################################################################
+
+qt_internal_add_test(tst_proxy_multiprocess
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES
+ tst_proxy_multiprocess.cpp
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+)
--- /dev/null
+// Copyright (C) 2019 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <QtTest/QtTest>
+#include <QMetaType>
+#include <QProcess>
+
+#include "../../../shared/testutils.h"
+
+class tst_Proxy_MultiProcess: public QObject
+{
+ Q_OBJECT
+
+public:
+ enum ObjectMode { NullPointer, ObjectPointer };
+ Q_ENUM(ObjectMode)
+
+private slots:
+ void initTestCase()
+ {
+ QVERIFY(TestUtils::init("tst"));
+ }
+
+ void cleanup()
+ {
+ // wait for delivery of RemoveObject events to the source
+ QTest::qWait(200);
+ }
+
+ void testRun_data()
+ {
+ QTest::addColumn<bool>("templated");
+ QTest::addColumn<ObjectMode>("objectMode");
+ QTest::newRow("non-templated, subobject") << false << ObjectPointer;
+ QTest::newRow("templated, subobject") << true << ObjectPointer;
+ QTest::newRow("non-templated, nullptr") << false << NullPointer;
+ QTest::newRow("templated, nullptr") << true << NullPointer;
+ }
+
+ void testRun()
+ {
+#ifdef Q_OS_ANDROID
+ QSKIP("QProcess doesn't support running user bundled binaries on Android");
+#endif
+ QFETCH(bool, templated);
+ QFETCH(ObjectMode, objectMode);
+
+ qDebug() << "Starting server process";
+ QProcess serverProc;
+ serverProc.setProcessChannelMode(QProcess::ForwardedChannels);
+ QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
+ env.insert("ObjectMode", QVariant::fromValue(objectMode).toString());
+ if (templated) {
+ env.insert("TEMPLATED_REMOTING", "true");
+ }
+ serverProc.setProcessEnvironment(env);
+ serverProc.start(TestUtils::findExecutable("proxy_multiprocess_server", "/server"),
+ QStringList());
+ QVERIFY(serverProc.waitForStarted());
+
+ // wait for server start
+ QTest::qWait(200);
+
+
+ qDebug() << "Starting client process";
+ QProcess clientProc;
+ clientProc.setProcessChannelMode(QProcess::ForwardedChannels);
+ clientProc.setProcessEnvironment(env);
+ clientProc.start(TestUtils::findExecutable("proxy_multiprocess_client", "/client"),
+ QStringList());
+ QVERIFY(clientProc.waitForStarted());
+
+ // wait for client start
+ QTest::qWait(200);
+
+
+ qDebug() << "Starting proxy process";
+ QProcess proxyProc;
+ proxyProc.setProcessChannelMode(QProcess::ForwardedChannels);
+ proxyProc.start(TestUtils::findExecutable("proxy", "/proxy"),
+ QStringList());
+ QVERIFY(proxyProc.waitForStarted());
+
+ // wait for proxy start
+ QTest::qWait(200);
+
+
+ QVERIFY(clientProc.waitForFinished());
+ QVERIFY(proxyProc.waitForFinished());
+ QVERIFY(serverProc.waitForFinished());
+
+ QCOMPARE(serverProc.exitCode(), 0);
+ QCOMPARE(proxyProc.exitCode(), 0);
+ QCOMPARE(clientProc.exitCode(), 0);
+ }
+};
+
+QTEST_MAIN(tst_Proxy_MultiProcess)
+
+#include "tst_proxy_multiprocess.moc"
--- /dev/null
+
+add_subdirectory(integration)
+add_subdirectory(usertypes)
--- /dev/null
+
+#####################################################################
+## tst_qmlintegration Test:
+#####################################################################
+
+# Collect test data
+file(GLOB_RECURSE test_data_glob
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}/data/tst_*)
+list(APPEND test_data ${test_data_glob})
+
+qt_internal_add_test(tst_qmlintegration
+ GUI
+ QMLTEST
+ SOURCES
+ tst_integration.cpp
+ PUBLIC_LIBRARIES
+ Qt::Gui
+ TESTDATA ${test_data}
+)
+
+#### Keys ignored in scope 1:.:.:integration.pro:<TRUE>:
+# OTHER_FILES = "$$PWD/data/*.qml"
+# TEMPLATE = "app"
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import QtTest 1.1
+import QtRemoteObjects 5.12
+
+TestCase {
+ id: testCase
+
+ Node {
+ id: defaultNode
+ }
+
+ Node {
+ id: nodeWithPersistedStore
+
+ persistedStore: SettingsStore {}
+ }
+
+ name: "testIntegration"
+ function test_integration()
+ {
+ // test defaultNode
+ compare(defaultNode.registryUrl, "")
+ compare(defaultNode.persistedStore, null)
+
+ // test nodeWithPersistedStore
+ compare(nodeWithPersistedStore.registryUrl, "")
+ verify(nodeWithPersistedStore.persistedStore)
+ }
+}
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <QtQuickTest/quicktest.h>
+QUICK_TEST_MAIN(tst_integration)
--- /dev/null
+
+#####################################################################
+## tst_usertypes Test:
+#####################################################################
+file(GLOB_RECURSE test_data_glob
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}/data/*)
+list(APPEND test_data ${test_data_glob})
+
+qt_internal_add_test(tst_usertypes
+ SOURCES
+ tst_usertypes.cpp
+ PUBLIC_LIBRARIES
+ Qt::Gui
+ Qt::Qml
+ Qt::RemoteObjects
+ Qt::QuickTestUtilsPrivate
+ TESTDATA ${test_data}
+)
+qt6_add_repc_merged(tst_usertypes
+ usertypes.rep
+)
+
+qt_internal_extend_target(tst_usertypes CONDITION ANDROID OR IOS
+ DEFINES
+ QT_QMLTEST_DATADIR=\\\":/data\\\"
+)
+
+qt_internal_extend_target(tst_usertypes CONDITION NOT ANDROID AND NOT IOS
+ DEFINES
+ QT_QMLTEST_DATADIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/data\\\"
+)
+
+if(QT_BUILD_STANDALONE_TESTS)
+ # For fully static builds, let qmlimportscanner take care of plugin importing
+ qt_import_qml_plugins(tst_usertypes)
+endif()
+
+#### Keys ignored in scope 1:.:.:usertypes.pro:<TRUE>:
+# TEMPLATE = "app"
--- /dev/null
+import QtQuick 2.0
+
+QtObject {
+ property int value: 39
+}
--- /dev/null
+import QtQuick 2.0
+import QtRemoteObjects 5.12
+import usertypes 1.0
+
+ComplexTypeReplica {
+ node: Node {
+ property string local_socket: Qt.platform.os == "android" ? "localabstract" : "local"
+ registryUrl: local_socket + ":testModel"
+ }
+}
--- /dev/null
+import QtQuick 2.0
+import "../data" as MyComponents //for staric build resource path should be set
+
+QtObject {
+ property QtObject myTypeOk: MyComponents.MyType {} // this works
+ property MyComponents.MyType myType: MyComponents.MyType {} // this crashes
+ property MyComponents.MyType myType2 // this crashes (ensure solution works with null object)
+}
--- /dev/null
+import QtQuick 2.0
+import QtRemoteObjects 5.12
+import usertypes 1.0
+
+ComplexTypeReplica {
+ // test that the existence of this property doesn't cause issues
+ property QtObject object: QtObject {}
+
+ node: Node {
+ property string local_socket: Qt.platform.os == "android" ? "localabstract" : "local"
+ registryUrl: local_socket + ":testExtraComplex"
+ }
+}
+
--- /dev/null
+import QtQuick 2.0
+import QtRemoteObjects 5.12
+import usertypes 1.0
+
+SimpleClockReplica {
+ property string result: hour // test that the existence of this property doesn't cause issues
+ node: Node {
+ property string local_socket: Qt.platform.os == "android" ? "localabstract" : "local"
+ registryUrl: local_socket + ":test"
+ }
+ onStateChanged: if (state == SimpleClockReplica.Valid) pushHour(10)
+}
--- /dev/null
+import QtQuick 2.0
+import QtRemoteObjects 5.12
+import usertypes 1.0
+
+SimpleClockReplica {
+ property string result: hour // test that the existence of this property doesn't cause issues
+
+ node: Node {
+ property string local_socket: Qt.platform.os == "android" ? "localabstract" : "local"
+ registryUrl: local_socket + ":test2"
+ }
+}
--- /dev/null
+import QtQuick 2.0
+import QtRemoteObjects 5.15
+import usertypes 1.0
+
+Item {
+ SimpleClockSimpleSource {
+ id: clock
+ }
+
+ Host {
+ property string local_socket: Qt.platform.os == "android" ? "localabstract" : "local"
+ hostUrl: local_socket + ":testHost"
+ Component.onCompleted: enableRemoting(clock)
+ }
+
+ Timer {
+ interval: 500
+ running: true
+ onTriggered: clock.timeUpdated(10, 30)
+ }
+}
--- /dev/null
+import QtQuick 2.0
+import QtRemoteObjects 5.12
+import usertypes 1.0
+
+TypeWithModelReplica {
+ node: Node {
+ property string local_socket: Qt.platform.os == "android" ? "localabstract" : "local"
+ registryUrl: local_socket + ":testModel"
+ }
+}
--- /dev/null
+import QtQuick 2.0
+import QtRemoteObjects 5.12
+import usertypes 1.0
+
+Item {
+ property int result: replica.clock.hour
+
+ property Node sharedNode: Node {
+ property string local_socket: Qt.platform.os == "android" ? "localabstract" : "local"
+ registryUrl: local_socket + ":testSubObject"
+ }
+
+ property TypeWithSubObjectReplica replica: TypeWithSubObjectReplica {
+ node: sharedNode
+ onStateChanged: if (state == TypeWithSubObjectReplica.Valid) clock.pushHour(7)
+ }
+}
+
--- /dev/null
+import QtQuick 2.0
+import QtRemoteObjects 5.12
+import usertypes 1.0
+
+Item {
+ property int result: replica.hour
+ property int result2: replica2.hour
+
+ Node {
+ id: sharedNode
+ property string local_socket: Qt.platform.os == "android" ? "localabstract" : "local"
+ registryUrl: local_socket + ":testTwoReplicas"
+ }
+
+ SimpleClockReplica {
+ id: replica
+ node: sharedNode
+ }
+
+ SimpleClockReplica {
+ id: replica2
+ }
+
+ Timer {
+ running: true
+ interval: 200
+ onTriggered: replica2.node = sharedNode
+ }
+}
+
--- /dev/null
+import QtQuick 2.0
+import QtRemoteObjects 5.14
+import usertypes 1.0
+
+TypeWithReplyReplica {
+ property variant result
+ property bool hasError
+
+ node: Node {
+ property string local_socket: Qt.platform.os == "android" ? "localabstract" : "local"
+ registryUrl: local_socket + ":testWatcher"
+ }
+ onStateChanged: function(state) {
+ if (state != TypeWithReplyReplica.Valid)
+ return;
+ QtRemoteObjects.watch(uppercase("hello"), 1000)
+ .then(function(value) { hasError = false; result = value },
+ function(error) { hasError = true })
+ }
+
+ function callSlowFunction() {
+ result = 0
+ hasError = false
+
+ QtRemoteObjects.watch(slowFunction(), 300)
+ .then(function(value) { hasError = false; result = value },
+ function(error) { hasError = true })
+ }
+
+ function callComplexFunction() {
+ result = null
+ hasError = false
+
+ QtRemoteObjects.watch(complexReturnType(), 300)
+ .then(function(value) { hasError = false; result = value },
+ function(error) { hasError = true })
+ }
+}
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <QString>
+#include <QtTest>
+#include <qqmlengine.h>
+#include <qqmlcomponent.h>
+#include <QtQuickTestUtils/private/qmlutils_p.h>
+
+#include "rep_usertypes_merged.h"
+
+#include "../../../shared/testutils.h"
+
+class TypeWithReply : public TypeWithReplySimpleSource
+{
+public:
+ QString uppercase(const QString & input) override {
+ return input.toUpper();
+ }
+ QMap<QString, QString> complexReturnType() override {
+ return QMap<QString, QString>{{"one","1"}};
+ }
+ int slowFunction() override {
+ QTest::qWait(1000);
+ return 15;
+ }
+};
+
+class tst_usertypes : public QQmlDataTest
+{
+ Q_OBJECT
+
+public:
+ tst_usertypes();
+
+private Q_SLOTS:
+ void extraPropertyInQml();
+ void extraPropertyInQml2();
+ void extraPropertyInQmlComplex();
+ void modelInQml();
+ void subObjectInQml();
+ void complexInQml_data();
+ void complexInQml();
+ void watcherInQml();
+ void hostInQml();
+ void twoReplicas();
+ void remoteCompositeType();
+};
+
+tst_usertypes::tst_usertypes() : QQmlDataTest(QT_QMLTEST_DATADIR)
+{
+ qmlRegisterType<ComplexTypeReplica>("usertypes", 1, 0, "ComplexTypeReplica");
+}
+
+void tst_usertypes::extraPropertyInQml()
+{
+ qmlRegisterType<SimpleClockReplica>("usertypes", 1, 0, "SimpleClockReplica");
+
+ QRemoteObjectRegistryHost host(QUrl(LOCAL_SOCKET ":test"));
+ SimpleClockSimpleSource clock;
+ host.enableRemoting(&clock);
+
+ QQmlEngine e;
+ QQmlComponent c(&e, testFileUrl("extraprop.qml"));
+ QObject *obj = c.create();
+ QVERIFY(obj);
+
+ QTRY_COMPARE_WITH_TIMEOUT(obj->property("result").value<int>(), 10, 1000);
+}
+
+void tst_usertypes::extraPropertyInQml2()
+{
+ qmlRegisterType<SimpleClockReplica>("usertypes", 1, 0, "SimpleClockReplica");
+
+ QRemoteObjectRegistryHost host(QUrl(LOCAL_SOCKET ":test2"));
+ SimpleClockSimpleSource clock;
+ clock.setHour(10);
+ host.enableRemoting(&clock);
+
+ QQmlEngine e;
+ QQmlComponent c(&e, testFileUrl("extraprop2.qml"));
+ QObject *obj = c.create();
+ QVERIFY(obj);
+
+ QTRY_COMPARE_WITH_TIMEOUT(obj->property("hour").value<int>(), 10, 1000);
+ QCOMPARE(obj->property("result").value<int>(), 10);
+}
+
+void tst_usertypes::extraPropertyInQmlComplex()
+{
+ QRemoteObjectRegistryHost host(QUrl(LOCAL_SOCKET ":testExtraComplex"));
+
+ SimpleClockSimpleSource clock;
+ QStringListModel *model = new QStringListModel();
+ model->setStringList(QStringList() << "Track1" << "Track2" << "Track3");
+ ComplexTypeSimpleSource source;
+ source.setClock(&clock);
+ source.setTracks(model);
+ host.enableRemoting(&source);
+
+ QQmlEngine e;
+ QQmlComponent c(&e, testFileUrl("extraPropComplex.qml"));
+ QObject *obj = c.create();
+ QVERIFY(obj);
+
+ ComplexTypeReplica *rep = qobject_cast<ComplexTypeReplica*>(obj);
+ QVERIFY(rep);
+
+ // don't crash
+ QTRY_VERIFY_WITH_TIMEOUT(rep->isInitialized(), 1000);
+}
+
+void tst_usertypes::modelInQml()
+{
+ qmlRegisterType<TypeWithModelReplica>("usertypes", 1, 0, "TypeWithModelReplica");
+
+ QRemoteObjectRegistryHost host(QUrl(LOCAL_SOCKET ":testModel"));
+
+ QStringListModel *model = new QStringListModel();
+ model->setStringList(QStringList() << "Track1" << "Track2" << "Track3");
+ TypeWithModelSimpleSource source;
+ source.setTracks(model);
+ host.enableRemoting<TypeWithModelSourceAPI>(&source);
+
+ QQmlEngine e;
+ QQmlComponent c(&e, testFileUrl("model.qml"));
+ QObject *obj = c.create();
+ QVERIFY(obj);
+
+ QTRY_VERIFY_WITH_TIMEOUT(obj->property("tracks").value<QAbstractItemModelReplica*>() != nullptr, 1000);
+ auto tracks = obj->property("tracks").value<QAbstractItemModelReplica*>();
+ QTRY_VERIFY_WITH_TIMEOUT(tracks->isInitialized(), 1000);
+
+ TypeWithModelReplica *rep = qobject_cast<TypeWithModelReplica *>(obj);
+ QVERIFY(rep->isInitialized());
+}
+
+void tst_usertypes::subObjectInQml()
+{
+ qmlRegisterType<TypeWithSubObjectReplica>("usertypes", 1, 0, "TypeWithSubObjectReplica");
+
+ QRemoteObjectRegistryHost host(QUrl(LOCAL_SOCKET ":testSubObject"));
+
+ SimpleClockSimpleSource clock;
+ TypeWithSubObjectSimpleSource source;
+ source.setClock(&clock);
+ host.enableRemoting(&source);
+
+ QQmlEngine e;
+ QQmlComponent c(&e, testFileUrl("subObject.qml"));
+ QObject *obj = c.create();
+ QVERIFY(obj);
+
+ TypeWithSubObjectReplica *replica = obj->property("replica").value<TypeWithSubObjectReplica*>();
+ QVERIFY(replica);
+
+ QTRY_VERIFY_WITH_TIMEOUT(replica->property("clock").value<SimpleClockReplica*>() != nullptr, 1000);
+ QTRY_COMPARE_WITH_TIMEOUT(obj->property("result").toInt(), 7, 1000);
+}
+
+void tst_usertypes::complexInQml_data()
+{
+ QTest::addColumn<bool>("templated");
+ QTest::addColumn<bool>("nullobject");
+ QTest::newRow("non-templated pointer") << false << false;
+ QTest::newRow("templated pointer") << true << false;
+ QTest::newRow("non-templated nullptr") << false << true;
+ QTest::newRow("templated nullptr") << true << true;
+}
+
+void tst_usertypes::complexInQml()
+{
+ QFETCH(bool, templated);
+ QFETCH(bool, nullobject);
+
+ QRemoteObjectRegistryHost host(QUrl(LOCAL_SOCKET ":testModel"));
+
+ QStringListModel *model = new QStringListModel();
+ model->setStringList(QStringList() << "Track1" << "Track2" << "Track3");
+ QScopedPointer<SimpleClockSimpleSource> clock;
+ if (!nullobject)
+ clock.reset(new SimpleClockSimpleSource());
+ ComplexTypeSimpleSource source;
+ source.setTracks(model);
+ source.setClock(clock.data());
+ if (templated)
+ host.enableRemoting<ComplexTypeSourceAPI>(&source);
+ else
+ host.enableRemoting(&source);
+
+ QQmlEngine e;
+ QQmlComponent c(&e, testFileUrl("complex.qml"));
+ QObject *obj = c.create();
+ QVERIFY(obj);
+
+ QTRY_VERIFY_WITH_TIMEOUT(obj->property("tracks").value<QAbstractItemModelReplica*>() != nullptr, 1000);
+ auto tracks = obj->property("tracks").value<QAbstractItemModelReplica*>();
+ QTRY_VERIFY_WITH_TIMEOUT(tracks->isInitialized(), 1000);
+ ComplexTypeReplica *rep = qobject_cast<ComplexTypeReplica *>(obj);
+ QVERIFY(rep->waitForSource(300));
+ QCOMPARE(rep->property("before").value<int>(), 0);
+ QCOMPARE(rep->property("after").value<int>(), 42);
+ if (nullobject) {
+ QCOMPARE(rep->clock(), nullptr);
+ QCOMPARE(source.clock(), nullptr);
+ } else {
+ QCOMPARE(source.clock()->hour(), 6);
+ QCOMPARE(rep->clock()->hour(), source.clock()->hour());
+ }
+}
+
+void tst_usertypes::watcherInQml()
+{
+ qmlRegisterType<TypeWithReplyReplica>("usertypes", 1, 0, "TypeWithReplyReplica");
+
+ QRemoteObjectRegistryHost host(QUrl(LOCAL_SOCKET ":testWatcher"));
+ TypeWithReply source;
+ host.enableRemoting(&source);
+
+ QQmlEngine e;
+ QQmlComponent c(&e, testFileUrl("watcher.qml"));
+ QObject *obj = c.create();
+ QVERIFY(obj);
+
+ QTRY_COMPARE_WITH_TIMEOUT(obj->property("result").value<QString>(), QString::fromLatin1("HELLO"), 1000);
+ QCOMPARE(obj->property("hasError").value<bool>(), false);
+
+ QMetaObject::invokeMethod(obj, "callSlowFunction");
+ QTRY_COMPARE_WITH_TIMEOUT(obj->property("hasError").value<bool>(), true, 1000);
+ QVERIFY(obj->property("result").value<int>() != 10);
+
+ QMetaObject::invokeMethod(obj, "callComplexFunction");
+ QTRY_VERIFY_WITH_TIMEOUT(!obj->property("result").isNull(), 1000);
+ auto map = obj->property("result").value<QMap<QString,QString>>();
+ QCOMPARE(map.value("one"), QString::fromLatin1("1"));
+ QCOMPARE(obj->property("hasError").value<bool>(), false);
+}
+
+void tst_usertypes::hostInQml()
+{
+ qmlRegisterType<SimpleClockSimpleSource>("usertypes", 1, 0, "SimpleClockSimpleSource");
+
+ QQmlEngine e;
+ QQmlComponent c(&e, testFileUrl("hosted.qml"));
+ QObject *obj = c.create();
+ QVERIFY(obj);
+
+ QRemoteObjectNode node;
+ node.connectToNode(QUrl(LOCAL_SOCKET ":testHost"));
+ SimpleClockReplica *replica = node.acquire<SimpleClockReplica>();
+ QTRY_COMPARE_WITH_TIMEOUT(replica->state(), QRemoteObjectReplica::Valid, 1000);
+
+ QSignalSpy spy(replica, &SimpleClockReplica::timeUpdated);
+ spy.wait();
+ QCOMPARE(spy.size(), 1);
+}
+
+void tst_usertypes::twoReplicas()
+{
+ qmlRegisterType<SimpleClockReplica>("usertypes", 1, 0, "SimpleClockReplica");
+
+ QRemoteObjectRegistryHost host(QUrl(LOCAL_SOCKET ":testTwoReplicas"));
+ SimpleClockSimpleSource clock;
+ clock.setHour(7);
+ host.enableRemoting(&clock);
+
+ QQmlEngine e;
+ QQmlComponent c(&e, testFileUrl("twoReplicas.qml"));
+ QObject *obj = c.create();
+ QVERIFY(obj);
+
+ QTRY_COMPARE_WITH_TIMEOUT(obj->property("result").value<int>(), 7, 1000);
+ QTRY_COMPARE_WITH_TIMEOUT(obj->property("result2").value<int>(), 7, 1000);
+}
+
+void tst_usertypes::remoteCompositeType()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e, testFileUrl("composite.qml"));
+ QScopedPointer<QObject> obj(c.create());
+ QVERIFY(obj);
+
+ QRemoteObjectRegistryHost host(QUrl(LOCAL_SOCKET ":remoteCompositeType"));
+ host.enableRemoting(obj.data(), "composite");
+}
+
+QTEST_MAIN(tst_usertypes)
+
+#include "tst_usertypes.moc"
--- /dev/null
+#include <QtCore>
+
+class SimpleClock
+{
+ PROP(int hour=6);
+ PROP(int minute=30);
+ SIGNAL(timeUpdated(int hour, int minute, int second=0, int millisecond=0));
+};
+
+class TypeWithModel
+{
+ MODEL tracks(display);
+};
+
+class TypeWithSubObject
+{
+ CLASS clock(SimpleClock);
+};
+
+class ComplexType
+{
+ PROP(int before = 0)
+ MODEL tracks(display)
+ CLASS clock(SimpleClock)
+ PROP(int after = 42)
+}
+
+class TypeWithReply
+{
+ SLOT(QString uppercase(const QString &input))
+ SLOT(QMap<QString, QString> complexReturnType())
+ SLOT(int slowFunction())
+};
--- /dev/null
+
+add_subdirectory(client)
+add_subdirectory(server)
+add_subdirectory(tst)
--- /dev/null
+qt_internal_add_executable(qtro_reconnect_client
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES main.cpp
+ INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}
+ PUBLIC_LIBRARIES Qt::RemoteObjects Qt::Test
+)
--- /dev/null
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <QCoreApplication>
+#include <QtTest/QtTest>
+#include <QtRemoteObjects/qremoteobjectnode.h>
+
+class tst_Client_Process : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void testRun()
+ {
+ const QString url = qEnvironmentVariable("RO_URL");
+ QRemoteObjectNode node;
+ node.connectToNode(QUrl(url));
+ QRemoteObjectDynamicReplica *ro = node.acquireDynamic("SourceObj");
+
+ QSignalSpy initSpy(ro, &QRemoteObjectDynamicReplica::initialized);
+ QVERIFY(initSpy.wait());
+ QSignalSpy pongSpy(ro, SIGNAL(pong()));
+ QMetaObject::invokeMethod(ro, "ping");
+ QVERIFY(pongSpy.wait());
+ QMetaObject::invokeMethod(ro, "ping");
+
+ QVERIFY(initSpy.wait());
+ QMetaObject::invokeMethod(ro, "ping");
+ QVERIFY(pongSpy.wait());
+ QMetaObject::invokeMethod(ro, "ping");
+ QTest::qWait(100);
+ delete ro;
+ }
+};
+
+QTEST_MAIN(tst_Client_Process)
+
+#include "main.moc"
--- /dev/null
+qt_internal_add_executable(qtro_reconnect_server
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES main.cpp
+ INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}
+ PUBLIC_LIBRARIES Qt::RemoteObjects Qt::Test
+)
--- /dev/null
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <QCoreApplication>
+#include <QtTest/QtTest>
+#include <QtRemoteObjects/qremoteobjectnode.h>
+#include <QtRemoteObjects/qremoteobjectsource.h>
+
+
+class SourceObj : public QObject
+{
+ Q_OBJECT
+
+public Q_SLOTS:
+ void ping() { ++m_pingCount; };
+
+Q_SIGNALS:
+ void pong();
+
+public:
+ int m_pingCount = 0;
+};
+
+class tst_Server_Process : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void testRun()
+ {
+ const QString url = qEnvironmentVariable("RO_URL");
+ QRemoteObjectHost *srcNode = new QRemoteObjectHost(QUrl(url));
+ SourceObj so;
+ so.setObjectName(QStringLiteral("SourceObj"));
+ srcNode->enableRemoting(&so);
+
+ QTRY_VERIFY(so.m_pingCount == 1);
+ emit so.pong();
+ QTRY_VERIFY(so.m_pingCount == 2);
+ delete srcNode;
+
+ srcNode = new QRemoteObjectHost(QUrl(url));
+ srcNode->enableRemoting(&so);
+
+ QTRY_VERIFY(so.m_pingCount == 3);
+ emit so.pong();
+ QTRY_VERIFY(so.m_pingCount == 4);
+ delete srcNode;
+ }
+};
+
+QTEST_MAIN(tst_Server_Process)
+
+#include "main.moc"
--- /dev/null
+qt_internal_add_test(tst_reconnect
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES tst_reconnect.cpp
+ PUBLIC_LIBRARIES Qt::RemoteObjects
+)
--- /dev/null
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <QtTest/QtTest>
+#include <QStandardPaths>
+#include <QProcess>
+#include "../../../shared/testutils.h"
+
+
+class tst_Reconnect: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void testRun_data()
+ {
+ QTest::addColumn<QString>("url");
+ QTest::addRow("local") << QStringLiteral(LOCAL_SOCKET ":replica");
+ QTest::addRow("tcp") << QStringLiteral("tcp://127.0.0.1:65217");
+ }
+
+ void testRun()
+ {
+#ifdef Q_OS_ANDROID
+ QSKIP("QProcess doesn't support running user bundled binaries on Android");
+#endif
+ QFETCH(QString, url);
+
+ QVERIFY(TestUtils::init("tst"));
+
+ QProcess serverProc;
+ serverProc.setProcessChannelMode(QProcess::ForwardedChannels);
+ QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
+ env.insert("RO_URL", url);
+ serverProc.setProcessEnvironment(env);
+ serverProc.start(TestUtils::findExecutable("qtro_reconnect_server", "/server"),
+ QStringList());
+ QVERIFY(serverProc.waitForStarted());
+
+ QProcess clientProc;
+ clientProc.setProcessChannelMode(QProcess::ForwardedChannels);
+ clientProc.setProcessEnvironment(env);
+ clientProc.start(TestUtils::findExecutable("qtro_reconnect_client", "/client"),
+ QStringList());
+ qDebug() << "Started server and client process on:" << url;
+ QVERIFY(clientProc.waitForStarted());
+
+ QVERIFY(clientProc.waitForFinished());
+ QVERIFY(serverProc.waitForFinished());
+
+ QCOMPARE(serverProc.exitCode(), 0);
+ QCOMPARE(clientProc.exitCode(), 0);
+ }
+};
+
+QTEST_MAIN(tst_Reconnect)
+
+#include "tst_reconnect.moc"
--- /dev/null
+
+#####################################################################
+## tst_rep_from_header Test:
+#####################################################################
+
+qt_internal_add_test(tst_rep_from_header
+ SOURCES
+ tst_rep_from_header.cpp
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+)
+qt6_reps_from_headers(tst_rep_from_header pods.h)
+
+#### Keys ignored in scope 1:.:.:rep_from_header.pro:<TRUE>:
+# QOBJECT_REP = "$$REP_FILES"
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+// This is an autogenerated file.
+// Do not edit this file, any changes made will be lost the next time it is generated.
+
+#include <QObject>
+
+
+#include <QString>
+class PodI
+{
+ Q_GADGET
+ Q_PROPERTY(int i READ i WRITE setI)
+public:
+ explicit PodI() : _i() {}
+ explicit PodI(int i) : _i(i) {}
+ PodI(const PodI& other)
+ {
+ QtRemoteObjects::copyStoredProperties(&other, this);
+ }
+
+ PodI &operator=(const PodI &other)
+ {
+ if (this != &other)
+ QtRemoteObjects::copyStoredProperties(&other, this);
+ return *this;
+ }
+
+ int i() const { return _i; }
+ void setI(int i) { if (i != _i) { _i = i; } }
+private:
+ int _i;
+};
+
+inline QDataStream &operator<<(QDataStream &ds, const PodI &obj) {
+ QtRemoteObjects::copyStoredProperties(&obj, ds);
+ return ds;
+}
+
+inline QDataStream &operator>>(QDataStream &ds, PodI &obj) {
+ QtRemoteObjects::copyStoredProperties(ds, &obj);
+ return ds;
+}
+
+class PodF
+{
+ Q_GADGET
+ Q_PROPERTY(float f READ f WRITE setF)
+public:
+ explicit PodF() : _f() {}
+ explicit PodF(float f) : _f(f) {}
+ PodF(const PodF& other)
+ {
+ QtRemoteObjects::copyStoredProperties(&other, this);
+ }
+
+ PodF &operator=(const PodF &other)
+ {
+ if (this != &other)
+ QtRemoteObjects::copyStoredProperties(&other, this);
+ return *this;
+ }
+
+ float f() const { return _f; }
+ void setF(float f) { if (f != _f) { _f = f; } }
+private:
+ float _f;
+};
+
+inline QDataStream &operator<<(QDataStream &ds, const PodF &obj) {
+ QtRemoteObjects::copyStoredProperties(&obj, ds);
+ return ds;
+}
+
+inline QDataStream &operator>>(QDataStream &ds, PodF &obj) {
+ QtRemoteObjects::copyStoredProperties(ds, &obj);
+ return ds;
+}
+
+class PodS
+{
+ Q_GADGET
+ Q_PROPERTY(QString s READ s WRITE setS)
+public:
+ explicit PodS() : _s() {}
+ explicit PodS(QString s) : _s(s) {}
+ PodS(const PodS& other)
+ {
+ QtRemoteObjects::copyStoredProperties(&other, this);
+ }
+
+ PodS &operator=(const PodS &other)
+ {
+ if (this != &other)
+ QtRemoteObjects::copyStoredProperties(&other, this);
+ return *this;
+ }
+
+ QString s() const { return _s; }
+ void setS(QString s) { if (s != _s) { _s = s; } }
+private:
+ QString _s;
+};
+
+inline QDataStream &operator<<(QDataStream &ds, const PodS &obj) {
+ QtRemoteObjects::copyStoredProperties(&obj, ds);
+ return ds;
+}
+
+inline QDataStream &operator>>(QDataStream &ds, PodS &obj) {
+ QtRemoteObjects::copyStoredProperties(ds, &obj);
+ return ds;
+}
+
+class PodIFS
+{
+ Q_GADGET
+ Q_PROPERTY(int i READ i WRITE setI)
+ Q_PROPERTY(float f READ f WRITE setF)
+ Q_PROPERTY(QString s READ s WRITE setS)
+public:
+ explicit PodIFS() : _i(), _f(), _s() {}
+ explicit PodIFS(int i, float f, QString s) : _i(i), _f(f), _s(s) {}
+ PodIFS(const PodIFS& other)
+ {
+ QtRemoteObjects::copyStoredProperties(&other, this);
+ }
+
+ PodIFS &operator=(const PodIFS &other)
+ {
+ if (this != &other)
+ QtRemoteObjects::copyStoredProperties(&other, this);
+ return *this;
+ }
+
+ int i() const { return _i; }
+ void setI(int i) { if (i != _i) { _i = i; } }
+ float f() const { return _f; }
+ void setF(float f) { if (f != _f) { _f = f; } }
+ QString s() const { return _s; }
+ void setS(QString s) { if (s != _s) { _s = s; } }
+private:
+ int _i;
+ float _f;
+ QString _s;
+};
+
+inline QDataStream &operator<<(QDataStream &ds, const PodIFS &obj) {
+ QtRemoteObjects::copyStoredProperties(&obj, ds);
+ return ds;
+}
+
+inline QDataStream &operator>>(QDataStream &ds, PodIFS &obj) {
+ QtRemoteObjects::copyStoredProperties(ds, &obj);
+ return ds;
+}
+
--- /dev/null
+// Copyright (C) 2020 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+//#include "rep_pods_replica.h"
+
+#include <QTest>
+
+#include <QByteArray>
+
+class tst_rep_from_header : public QObject {
+ Q_OBJECT
+
+private Q_SLOTS:
+ void testRep_data();
+ void testRep();
+};
+
+void tst_rep_from_header::testRep_data()
+{
+ QTest::addColumn<QString>("actualFile");
+ QTest::addColumn<QByteArrayList>("expectedLines");
+
+ QTest::newRow("pods") << QStringLiteral("pods.rep")
+ << QByteArrayList{"POD PodI(int i)",
+ "POD PodF(float f)",
+ "POD PodS(QString s)",
+ "POD PodIFS(int i, float f, QString s)",
+ ""};
+}
+
+void tst_rep_from_header::testRep()
+{
+ QFETCH(QString, actualFile);
+ QFETCH(QByteArrayList, expectedLines);
+ const auto readFile = [&](const QString &fileName) {
+ QFile f(fileName);
+ f.open(QIODevice::ReadOnly);
+ QByteArrayList lines;
+ while (!f.atEnd())
+ lines.append(f.readLine().trimmed());
+ return lines;
+ };
+
+ QVERIFY2(QFile::exists(actualFile), qPrintable(actualFile));
+
+ QByteArrayList actualLines = readFile(actualFile);
+
+ QVERIFY(actualLines == expectedLines);
+}
+
+QTEST_APPLESS_MAIN(tst_rep_from_header)
+
+#include "tst_rep_from_header.moc"
+
--- /dev/null
+
+add_subdirectory(enums)
+add_subdirectory(pods)
+if(NOT windows)
+ add_subdirectory(signature)
+endif()
--- /dev/null
+
+#####################################################################
+## tst_enums Test:
+#####################################################################
+
+qt_internal_add_test(tst_enums
+ SOURCES
+ tst_enums.cpp
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+)
+qt6_add_repc_replicas(tst_enums
+ enums.rep
+)
+
+## Scopes:
+#####################################################################
--- /dev/null
+
+USE_ENUM(Qt::DayOfWeek)
+
+class TestInterface
+{
+};
--- /dev/null
+// Copyright (C) 2017-2020 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "rep_enums_replica.h"
+
+#include <QTest>
+
+#include <QByteArray>
+#include <QDataStream>
+
+class tst_Enums : public QObject {
+ Q_OBJECT
+
+private Q_SLOTS:
+ void testMarshalling();
+};
+
+void tst_Enums::testMarshalling()
+{
+ QByteArray ba;
+ QDataStream ds(&ba, QIODevice::ReadWrite);
+
+ {
+ const Qt::DayOfWeek format1 = Qt::Monday;
+ const Qt::DayOfWeek format2 = Qt::Tuesday;
+ const Qt::DayOfWeek format3 = Qt::Wednesday;
+ const Qt::DayOfWeek format4 = Qt::Thursday;
+ const Qt::DayOfWeek format5 = Qt::Friday;
+ const Qt::DayOfWeek format6 = Qt::Saturday;
+ const Qt::DayOfWeek format7 = Qt::Sunday;
+
+ ds << format1 << format2 << format3 << format4 << format5 << format6 << format7;
+ }
+
+ ds.device()->seek(0);
+
+ {
+ Qt::DayOfWeek format1, format2, format3, format4, format5, format6, format7;
+
+ ds >> format1 >> format2 >> format3 >> format4 >> format5 >> format6 >> format7;
+
+ QCOMPARE(format1, Qt::Monday);
+ QCOMPARE(format2, Qt::Tuesday);
+ QCOMPARE(format3, Qt::Wednesday);
+ QCOMPARE(format4, Qt::Thursday);
+ QCOMPARE(format5, Qt::Friday);
+ QCOMPARE(format6, Qt::Saturday);
+ QCOMPARE(format7, Qt::Sunday);
+ }
+}
+
+QTEST_APPLESS_MAIN(tst_Enums)
+
+#include "tst_enums.moc"
--- /dev/null
+
+#####################################################################
+## tst_repc_pods Test:
+#####################################################################
+
+qt_internal_add_test(tst_repc_pods
+ SOURCES
+ tst_pods.cpp
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+)
+qt6_add_repc_replicas(tst_repc_pods
+ pods.rep
+)
+
+## Scopes:
+#####################################################################
--- /dev/null
+#include <QString>
+
+ENUM Test {TRUE, FALSE}
+
+POD PodI(int i)
+POD PodF(float f)
+POD PodS(QString s)
+POD PodIFS(int i, float f, QString s)
+POD PodT(QList<TestEnum::Test> t)
+
+class Container {
+};
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "rep_pods_replica.h"
+
+#include <QTest>
+
+#include <QByteArray>
+#include <QDataStream>
+
+class tst_Pods : public QObject {
+ Q_OBJECT
+
+private Q_SLOTS:
+ void testConstructors();
+ void testMarshalling();
+ void testProperty();
+};
+
+
+void tst_Pods::testConstructors()
+{
+ PodI pi1;
+ QCOMPARE(pi1.i(), 0);
+
+ PodI pi2(1);
+ QCOMPARE(pi2.i(), 1);
+
+ PodI pi3(pi2);
+ QCOMPARE(pi3.i(), pi2.i());
+}
+
+void tst_Pods::testMarshalling()
+{
+ QByteArray ba;
+ QDataStream ds(&ba, QIODevice::ReadWrite);
+
+ {
+ PodI i1(1), i2(2), i3(3), iDeadBeef(0xdeadbeef);
+ ds << i1 << i2 << i3 << iDeadBeef;
+ }
+
+ ds.device()->seek(0);
+
+ {
+ PodI i1, i2, i3, iDeadBeef;
+ ds >> i1 >> i2 >> i3 >> iDeadBeef;
+
+ QCOMPARE(i1.i(), 1);
+ QCOMPARE(i2.i(), 2);
+ QCOMPARE(i3.i(), 3);
+ QCOMPARE(iDeadBeef.i(), int(0xdeadbeef));
+ }
+}
+
+void tst_Pods::testProperty()
+{
+ ContainerReplica::registerMetatypes();
+
+ PodT pt;
+ QMetaProperty prop = pt.staticMetaObject.property(0);
+ QVERIFY(prop.userType() != 0);
+}
+
+QTEST_APPLESS_MAIN(tst_Pods)
+
+#include "tst_pods.moc"
--- /dev/null
+
+add_subdirectory(signatureServer)
+add_subdirectory(matchAndQuit)
+add_subdirectory(differentGlobalEnum)
+add_subdirectory(differentClassEnum)
+add_subdirectory(differentPropertyCount)
+add_subdirectory(differentPropertyCountChild)
+add_subdirectory(differentPropertyType)
+add_subdirectory(scrambledProperties)
+add_subdirectory(differentSlotCount)
+add_subdirectory(differentSlotType)
+add_subdirectory(differentSlotParamCount)
+add_subdirectory(differentSlotParamType)
+add_subdirectory(scrambledSlots)
+add_subdirectory(differentSignalCount)
+add_subdirectory(differentSignalParamCount)
+add_subdirectory(differentSignalParamType)
+add_subdirectory(scrambledSignals)
+add_subdirectory(state)
+if(QT_FEATURE_process)
+ add_subdirectory(signatureTests)
+endif()
--- /dev/null
+
+#####################################################################
+## differentClassEnum Binary:
+#####################################################################
+
+qt_internal_add_executable(differentClassEnum
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES
+ ../mismatch.cpp
+ INCLUDE_DIRECTORIES
+ ..
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+ Qt::Test
+)
+qt6_add_repc_replicas(differentClassEnum
+ mismatch.rep
+)
--- /dev/null
+ENUM Test {TRUE, FALSE}
+
+class TestClass
+{
+ ENUM ClassEnum {Null = 2, One, Two}
+
+ PROP(TestEnum::Test testEnum)
+ PROP(ClassEnum classEnum)
+
+ PROP(int prop1);
+ PROP(double prop2);
+
+ SLOT(bool slot1());
+ SLOT(QString slot2());
+
+ SIGNAL(signal1());
+ SIGNAL(signal2());
+
+ SLOT(void ping(const QString &message));
+ SIGNAL(pong(const QString &message));
+
+ SLOT(bool quit());
+};
--- /dev/null
+
+#####################################################################
+## differentGlobalEnum Binary:
+#####################################################################
+
+qt_internal_add_executable(differentGlobalEnum
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES
+ ../mismatch.cpp
+ INCLUDE_DIRECTORIES
+ ..
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+ Qt::Test
+)
+qt6_add_repc_replicas(differentGlobalEnum
+ mismatch.rep
+)
--- /dev/null
+ENUM Test {FALSE, TRUE}
+
+class TestChildClass
+{
+ PROP(int prop1);
+ PROP(double prop2);
+};
+
+class TestClass
+{
+ ENUM ClassEnum {Null, One, Two}
+
+ PROP(TestEnum::Test testEnum)
+ PROP(ClassEnum classEnum)
+
+ PROP(int prop1);
+ PROP(double prop2);
+
+ SLOT(bool slot1());
+ SLOT(QString slot2());
+
+ CLASS childProp(TestChildClass);
+
+ SIGNAL(signal1());
+ SIGNAL(signal2());
+
+ SLOT(void ping(const QString &message));
+ SIGNAL(pong(const QString &message));
+
+ SLOT(bool quit());
+};
--- /dev/null
+
+#####################################################################
+## differentPropertyCount Binary:
+#####################################################################
+
+qt_internal_add_executable(differentPropertyCount
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES
+ ../mismatch.cpp
+ INCLUDE_DIRECTORIES
+ ..
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+ Qt::Test
+)
+qt6_add_repc_replicas(differentPropertyCount
+ mismatch.rep
+)
--- /dev/null
+ENUM Test {TRUE, FALSE}
+
+class TestChildClass
+{
+ PROP(int prop1);
+ PROP(double prop2);
+};
+
+class TestClass
+{
+ ENUM ClassEnum {Null, One, Two}
+
+ PROP(TestEnum::Test testEnum)
+ PROP(ClassEnum classEnum)
+
+ PROP(int prop1);
+ PROP(double prop2);
+ PROP(double prop3);
+
+ CLASS childProp(TestChildClass);
+
+ SLOT(bool slot1());
+ SLOT(QString slot2());
+
+ SIGNAL(signal1());
+ SIGNAL(signal2());
+
+ SLOT(void ping(const QString &message));
+ SIGNAL(pong(const QString &message));
+
+ SLOT(bool quit());
+};
--- /dev/null
+
+#####################################################################
+## differentPropertyCount Binary:
+#####################################################################
+
+qt_internal_add_executable(differentPropertyCountChild
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES
+ ../mismatch.cpp
+ INCLUDE_DIRECTORIES
+ ..
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+ Qt::Test
+)
+qt6_add_repc_replicas(differentPropertyCountChild
+ mismatch.rep
+)
--- /dev/null
+include(../mismatch.pri)
+
+TARGET = differentPropertyCountChild
+
+REPC_REPLICA = $$PWD/mismatch.rep
--- /dev/null
+ENUM Test {TRUE, FALSE}
+
+class TestChildClass
+{
+ PROP(double prop2);
+};
+
+class TestClass
+{
+ ENUM ClassEnum {Null, One, Two}
+
+ PROP(TestEnum::Test testEnum)
+ PROP(ClassEnum classEnum)
+
+ PROP(int prop1);
+ PROP(double prop2);
+
+ CLASS childProp(TestChildClass);
+
+ SLOT(bool slot1());
+ SLOT(QString slot2());
+
+ SIGNAL(signal1());
+ SIGNAL(signal2());
+
+ SLOT(void ping(const QString &message));
+ SIGNAL(pong(const QString &message));
+
+ SLOT(bool quit());
+};
--- /dev/null
+
+#####################################################################
+## differentPropertyType Binary:
+#####################################################################
+
+qt_internal_add_executable(differentPropertyType
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES
+ ../mismatch.cpp
+ INCLUDE_DIRECTORIES
+ ..
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+ Qt::Test
+)
+qt6_add_repc_replicas(differentPropertyType
+ mismatch.rep
+)
--- /dev/null
+ENUM Test {TRUE, FALSE}
+
+class TestChildClass
+{
+ PROP(int prop1);
+ PROP(double prop2);
+};
+
+class TestClass
+{
+ ENUM ClassEnum {Null, One, Two}
+
+ PROP(TestEnum::Test testEnum)
+ PROP(ClassEnum classEnum)
+
+ PROP(int prop1);
+ PROP(QString prop2);
+
+ SLOT(bool slot1());
+ SLOT(QString slot2());
+
+ CLASS childProp(TestChildClass);
+
+ SIGNAL(signal1());
+ SIGNAL(signal2());
+
+ SLOT(void ping(const QString &message));
+ SIGNAL(pong(const QString &message));
+
+ SLOT(bool quit());
+};
--- /dev/null
+
+#####################################################################
+## differentSignalCount Binary:
+#####################################################################
+
+qt_internal_add_executable(differentSignalCount
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES
+ ../mismatch.cpp
+ INCLUDE_DIRECTORIES
+ ..
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+ Qt::Test
+)
+qt6_add_repc_replicas(differentSignalCount
+ mismatch.rep
+)
--- /dev/null
+ENUM Test {TRUE, FALSE}
+
+class TestChildClass
+{
+ PROP(int prop1);
+ PROP(double prop2);
+};
+
+class TestClass
+{
+ ENUM ClassEnum {Null, One, Two}
+
+ PROP(TestEnum::Test testEnum)
+ PROP(ClassEnum classEnum)
+
+ PROP(int prop1);
+ PROP(double prop2);
+
+ CLASS childProp(TestChildClass);
+
+ SLOT(bool slot1());
+ SLOT(QString slot2());
+
+ SIGNAL(signal1());
+ SIGNAL(signal2());
+ SIGNAL(anotherSignal());
+
+ SLOT(void ping(const QString &message));
+ SIGNAL(pong(const QString &message));
+
+ SLOT(bool quit());
+};
--- /dev/null
+
+#####################################################################
+## differentSignalParamCount Binary:
+#####################################################################
+
+qt_internal_add_executable(differentSignalParamCount
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES
+ ../mismatch.cpp
+ INCLUDE_DIRECTORIES
+ ..
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+ Qt::Test
+)
+qt6_add_repc_replicas(differentSignalParamCount
+ mismatch.rep
+)
--- /dev/null
+ENUM Test {TRUE, FALSE}
+
+class TestChildClass
+{
+ PROP(int prop1);
+ PROP(double prop2);
+};
+
+class TestClass
+{
+ ENUM ClassEnum {Null, One, Two}
+
+ PROP(TestEnum::Test testEnum)
+ PROP(ClassEnum classEnum)
+
+ PROP(int prop1);
+ PROP(double prop2);
+
+ SLOT(bool slot1());
+ SLOT(QString slot2());
+
+ CLASS childProp(TestChildClass);
+
+ SIGNAL(signal1());
+ SIGNAL(signal2());
+
+ SLOT(void ping(const QString &message));
+ SIGNAL(pong(const QString &message, int anotherParam));
+
+ SLOT(bool quit());
+};
--- /dev/null
+
+#####################################################################
+## differentSignalParamType Binary:
+#####################################################################
+
+qt_internal_add_executable(differentSignalParamType
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES
+ ../mismatch.cpp
+ INCLUDE_DIRECTORIES
+ ..
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+ Qt::Test
+)
+qt6_add_repc_replicas(differentSignalParamType
+ mismatch.rep
+)
--- /dev/null
+ENUM Test {TRUE, FALSE}
+
+class TestChildClass
+{
+ PROP(int prop1);
+ PROP(double prop2);
+};
+
+class TestClass
+{
+ ENUM ClassEnum {Null, One, Two}
+
+ PROP(TestEnum::Test testEnum)
+ PROP(ClassEnum classEnum)
+
+ PROP(int prop1);
+ PROP(double prop2);
+
+ CLASS childProp(TestChildClass);
+
+ SLOT(bool slot1());
+ SLOT(QString slot2());
+
+ SIGNAL(signal1());
+ SIGNAL(signal2());
+
+ SLOT(void ping(const QString &message));
+ SIGNAL(pong(const QByteArray &message));
+
+ SLOT(bool quit());
+};
--- /dev/null
+
+#####################################################################
+## differentSlotCount Binary:
+#####################################################################
+
+qt_internal_add_executable(differentSlotCount
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES
+ ../mismatch.cpp
+ INCLUDE_DIRECTORIES
+ ..
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+ Qt::Test
+)
+qt6_add_repc_replicas(differentSlotCount
+ mismatch.rep
+)
--- /dev/null
+ENUM Test {TRUE, FALSE}
+
+class TestChildClass
+{
+ PROP(int prop1);
+ PROP(double prop2);
+};
+
+class TestClass
+{
+ ENUM ClassEnum {Null, One, Two}
+
+ PROP(TestEnum::Test testEnum)
+ PROP(ClassEnum classEnum)
+
+ PROP(int prop1);
+ PROP(double prop2);
+
+ CLASS childProp(TestChildClass);
+
+ SLOT(bool slot1());
+ SLOT(QString slot2());
+ SLOT(void anotherSlot());
+
+ SIGNAL(signal1());
+ SIGNAL(signal2());
+
+ SLOT(void ping(const QString &message));
+ SIGNAL(pong(const QString &message));
+
+ SLOT(bool quit());
+};
--- /dev/null
+
+#####################################################################
+## differentSlotParamCount Binary:
+#####################################################################
+
+qt_internal_add_executable(differentSlotParamCount
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES
+ ../mismatch.cpp
+ INCLUDE_DIRECTORIES
+ ..
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+ Qt::Test
+)
+qt6_add_repc_replicas(differentSlotParamCount
+ mismatch.rep
+)
--- /dev/null
+ENUM Test {TRUE, FALSE}
+
+class TestChildClass
+{
+ PROP(int prop1);
+ PROP(double prop2);
+};
+
+class TestClass
+{
+ ENUM ClassEnum {Null, One, Two}
+
+ PROP(TestEnum::Test testEnum)
+ PROP(ClassEnum classEnum)
+
+ PROP(int prop1);
+ PROP(double prop2);
+
+ CLASS childProp(TestChildClass);
+
+ SLOT(bool slot1());
+ SLOT(QString slot2());
+
+ SIGNAL(signal1());
+ SIGNAL(signal2());
+
+ SLOT(void ping(const QString &message, int anotherParam));
+ SIGNAL(pong(const QString &message));
+
+ SLOT(bool quit());
+};
--- /dev/null
+
+#####################################################################
+## differentSlotParamType Binary:
+#####################################################################
+
+qt_internal_add_executable(differentSlotParamType
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES
+ ../mismatch.cpp
+ INCLUDE_DIRECTORIES
+ ..
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+ Qt::Test
+)
+qt6_add_repc_replicas(differentSlotParamType
+ mismatch.rep
+)
--- /dev/null
+ENUM Test {TRUE, FALSE}
+
+class TestChildClass
+{
+ PROP(int prop1);
+ PROP(double prop2);
+};
+
+class TestClass
+{
+ ENUM ClassEnum {Null, One, Two}
+
+ PROP(TestEnum::Test testEnum)
+ PROP(ClassEnum classEnum)
+
+ PROP(int prop1);
+ PROP(double prop2);
+
+ CLASS childProp(TestChildClass);
+
+ SLOT(bool slot1());
+ SLOT(QString slot2());
+
+ SIGNAL(signal1());
+ SIGNAL(signal2());
+
+ SLOT(void ping(const QByteArray &message));
+ SIGNAL(pong(const QString &message));
+
+ SLOT(bool quit());
+};
--- /dev/null
+
+#####################################################################
+## differentSlotType Binary:
+#####################################################################
+
+qt_internal_add_executable(differentSlotType
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES
+ ../mismatch.cpp
+ INCLUDE_DIRECTORIES
+ ..
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+ Qt::Test
+)
+qt6_add_repc_replicas(differentSlotType
+ mismatch.rep
+)
--- /dev/null
+ENUM Test {TRUE, FALSE}
+
+class TestChildClass
+{
+ PROP(int prop1);
+ PROP(double prop2);
+};
+
+class TestClass
+{
+ ENUM ClassEnum {Null, One, Two}
+
+ PROP(TestEnum::Test testEnum)
+ PROP(ClassEnum classEnum)
+
+ PROP(int prop1);
+ PROP(double prop2);
+
+ CLASS childProp(TestChildClass);
+
+ SLOT(bool slot1());
+ SLOT(int slot2());
+
+ SIGNAL(signal1());
+ SIGNAL(signal2());
+
+ SLOT(void ping(const QString &message));
+ SIGNAL(pong(const QString &message));
+
+ SLOT(bool quit());
+};
--- /dev/null
+
+#####################################################################
+## matchAndQuit Binary:
+#####################################################################
+
+qt_internal_add_executable(matchAndQuit
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES
+ main.cpp
+ INCLUDE_DIRECTORIES
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+ Qt::Test
+)
+qt6_add_repc_replicas(matchAndQuit
+ ../server.rep
+)
+
+#### Keys ignored in scope 1:.:.:matchAndQuit.pro:<TRUE>:
+# TEMPLATE = "app"
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "rep_server_replica.h"
+
+#include <QCoreApplication>
+#include <QtRemoteObjects/qremoteobjectnode.h>
+#include <QtTest/QtTest>
+
+class tst_Match_Process : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void testRun()
+ {
+ QRemoteObjectNode repNode;
+ repNode.connectToNode(QUrl(QStringLiteral("tcp://127.0.0.1:65214")));
+ QSharedPointer<TestClassReplica> rep(repNode.acquire<TestClassReplica>());
+ QSignalSpy stateChangedSpy(rep.data(), &QRemoteObjectReplica::stateChanged);
+ QVERIFY(rep->waitForSource());
+ QCOMPARE(rep->state(), QRemoteObjectReplica::Valid);
+
+ QVERIFY(rep->quit().waitForFinished());
+ QTRY_COMPARE(rep->state(), QRemoteObjectReplica::Suspect);
+
+ QCOMPARE(stateChangedSpy.size(), 2);
+
+ // Test Default to Valid transition
+ auto args = stateChangedSpy.takeFirst();
+ QCOMPARE(args.size(), 2);
+ QCOMPARE(args.at(0).toInt(), int(QRemoteObjectReplica::Valid));
+ QCOMPARE(args.at(1).toInt(), int(QRemoteObjectReplica::Default));
+
+ // Test Valid to Suspect transition
+ args = stateChangedSpy.takeFirst();
+ QCOMPARE(args.size(), 2);
+ QCOMPARE(args.at(0).toInt(), int(QRemoteObjectReplica::Suspect));
+ QCOMPARE(args.at(1).toInt(), int(QRemoteObjectReplica::Valid));
+ }
+};
+
+QTEST_MAIN(tst_Match_Process)
+
+#include "main.moc"
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "rep_mismatch_replica.h"
+
+#include <QCoreApplication>
+#include <QtRemoteObjects/qremoteobjectnode.h>
+#include <QtTest/QtTest>
+
+class tst_Mismatch_Process : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void testRun()
+ {
+ QRemoteObjectNode repNode;
+ repNode.connectToNode(QUrl(QStringLiteral("tcp://127.0.0.1:65214")));
+ QTest::ignoreMessage(QtWarningMsg, " Signature mismatch for TestClassReplica \"TestClass\"");
+ QSharedPointer<TestClassReplica> rep(repNode.acquire<TestClassReplica>());
+ QTRY_COMPARE(rep->state(), QRemoteObjectReplica::SignatureMismatch);
+ }
+};
+
+QTEST_MAIN(tst_Mismatch_Process)
+
+#include "mismatch.moc"
--- /dev/null
+TEMPLATE = app
+QT += remoteobjects core testlib
+QT -= gui
+
+DESTDIR = ./
+CONFIG += c++11
+CONFIG -= app_bundle
+
+INCLUDEPATH += $$PWD
+SOURCES += $$PWD/mismatch.cpp
--- /dev/null
+
+#####################################################################
+## scrambledProperties Binary:
+#####################################################################
+
+qt_internal_add_executable(scrambledProperties
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES
+ ../mismatch.cpp
+ INCLUDE_DIRECTORIES
+ ..
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+ Qt::Test
+)
+qt6_add_repc_replicas(scrambledProperties
+ mismatch.rep
+)
--- /dev/null
+ENUM Test {TRUE, FALSE}
+
+class TestChildClass
+{
+ PROP(int prop1);
+ PROP(double prop2);
+};
+
+class TestClass
+{
+ ENUM ClassEnum {Null, One, Two}
+
+ PROP(TestEnum::Test testEnum)
+ PROP(ClassEnum classEnum)
+
+ PROP(double prop2);
+ PROP(int prop1);
+
+ CLASS childProp(TestChildClass);
+
+ SLOT(bool slot1());
+ SLOT(QString slot2());
+
+ SIGNAL(signal1());
+ SIGNAL(signal2());
+
+ SLOT(void ping(const QString &message));
+ SIGNAL(pong(const QString &message));
+
+ SLOT(bool quit());
+};
--- /dev/null
+
+#####################################################################
+## scrambledSignals Binary:
+#####################################################################
+
+qt_internal_add_executable(scrambledSignals
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES
+ ../mismatch.cpp
+ INCLUDE_DIRECTORIES
+ ..
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+ Qt::Test
+)
+qt6_add_repc_replicas(scrambledSignals
+ mismatch.rep
+)
--- /dev/null
+ENUM Test {TRUE, FALSE}
+
+class TestChildClass
+{
+ PROP(int prop1);
+ PROP(double prop2);
+};
+
+class TestClass
+{
+ ENUM ClassEnum {Null, One, Two}
+
+ PROP(TestEnum::Test testEnum)
+ PROP(ClassEnum classEnum)
+
+ PROP(int prop1);
+ PROP(double prop2);
+
+ CLASS childProp(TestChildClass);
+
+ SLOT(bool slot1());
+ SLOT(QString slot2());
+
+ SIGNAL(signal2());
+ SIGNAL(signal1());
+
+ SLOT(void ping(const QString &message));
+ SIGNAL(pong(const QString &message));
+
+ SLOT(bool quit());
+};
--- /dev/null
+
+#####################################################################
+## scrambledSlots Binary:
+#####################################################################
+
+qt_internal_add_executable(scrambledSlots
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES
+ ../mismatch.cpp
+ INCLUDE_DIRECTORIES
+ ..
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+ Qt::Test
+)
+qt6_add_repc_replicas(scrambledSlots
+ mismatch.rep
+)
--- /dev/null
+ENUM Test {TRUE, FALSE}
+
+class TestChildClass
+{
+ PROP(int prop1);
+ PROP(double prop2);
+};
+
+class TestClass
+{
+ ENUM ClassEnum {Null, One, Two}
+
+ PROP(TestEnum::Test testEnum)
+ PROP(ClassEnum classEnum)
+
+ PROP(int prop1);
+ PROP(double prop2);
+
+ CLASS childProp(TestChildClass);
+
+ SLOT(QString slot2());
+ SLOT(bool slot1());
+
+ SIGNAL(signal1());
+ SIGNAL(signal2());
+
+ SLOT(void ping(const QString &message));
+ SIGNAL(pong(const QString &message));
+
+ SLOT(bool quit());
+};
--- /dev/null
+ENUM Test {TRUE, FALSE}
+
+class TestChildClass
+{
+ PROP(int prop1);
+ PROP(double prop2);
+};
+
+class TestClass
+{
+ ENUM ClassEnum {Null, One, Two}
+
+ PROP(TestEnum::Test testEnum)
+ PROP(ClassEnum classEnum)
+
+ PROP(int prop1);
+ PROP(double prop2);
+
+ CLASS childProp(TestChildClass);
+
+ SLOT(bool slot1());
+ SLOT(QString slot2());
+
+ SIGNAL(signal1());
+ SIGNAL(signal2());
+
+ SLOT(void ping(const QString &message));
+ SIGNAL(pong(const QString &message));
+
+ SLOT(bool quit());
+};
--- /dev/null
+
+#####################################################################
+## signatureServer Binary:
+#####################################################################
+
+qt_internal_add_executable(signatureServer
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES
+ ${CMAKE_CURRENT_BINARY_DIR}/rep_server_source.h
+ main.cpp
+ INCLUDE_DIRECTORIES
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+ Qt::Test
+)
+qt6_add_repc_sources(signatureServer
+ ../server.rep
+)
+
+#### Keys ignored in scope 1:.:.:signatureServer.pro:<TRUE>:
+# TEMPLATE = "app"
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "rep_server_source.h"
+
+#include <QCoreApplication>
+#include <QtTest/QtTest>
+
+class MyTestServer : public TestClassSimpleSource
+{
+ Q_OBJECT
+public:
+ bool shouldQuit = false;
+ // TestClassSimpleSource interface
+public slots:
+ bool slot1() override {return true;}
+ QString slot2() override {return QLatin1String("Hello there");}
+ void ping(const QString &message) override
+ {
+ emit pong(message);
+ }
+
+ bool quit() override
+ {
+ qDebug() << "quit() called";
+
+ return shouldQuit = true;
+ }
+};
+
+class tst_Server_Process : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void testRun()
+ {
+ QRemoteObjectHost srcNode(QUrl(QStringLiteral("tcp://127.0.0.1:65214")));
+ MyTestServer myTestServer{};
+ TestChildClassSimpleSource child;
+ myTestServer.setChildProp(&child);
+
+ srcNode.enableRemoting(&myTestServer);
+
+ qDebug() << "Waiting for incoming connections";
+
+ QTRY_VERIFY_WITH_TIMEOUT(myTestServer.shouldQuit, 20000); // wait up to 20s
+
+ qDebug() << "Stopping server";
+ QTest::qWait(200); // wait for server to send reply to client invoking quit() function
+ }
+};
+
+QTEST_MAIN(tst_Server_Process)
+
+#include "main.moc"
--- /dev/null
+
+#####################################################################
+## tst_signature Test:
+#####################################################################
+
+qt_internal_add_test(tst_ro_signature
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES
+ tst_signature.cpp
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+)
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "../../../../shared/testutils.h"
+
+#include <QtTest/QtTest>
+#include <QMetaType>
+#include <QProcess>
+
+typedef QLatin1String _;
+class tst_Signature: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase()
+ {
+ QVERIFY(TestUtils::init("signatureTests"));
+ QLoggingCategory::setFilterRules("qt.remoteobjects.warning=false");
+ }
+
+ void cleanup()
+ {
+ // wait for delivery of RemoveObject events to the source
+ QTest::qWait(200);
+ }
+
+ void testRun()
+ {
+#ifdef Q_OS_ANDROID
+ QSKIP("QProcess doesn't support running user bundled binaries on Android");
+#endif
+ qDebug() << "Starting signatureServer process";
+ QProcess serverProc;
+ serverProc.setProcessChannelMode(QProcess::ForwardedChannels);
+ serverProc.start(TestUtils::findExecutable("signatureServer", "/signatureServer"),
+ QStringList());
+ QVERIFY(serverProc.waitForStarted());
+
+ // wait for server start
+ QTest::qWait(200);
+
+ const QLatin1String tests[] = {
+ _("differentGlobalEnum"),
+ _("differentClassEnum"),
+ _("differentPropertyCount"),
+ _("differentPropertyCountChild"),
+ _("differentPropertyType"),
+ _("scrambledProperties"),
+ _("differentSlotCount"),
+ _("differentSlotType"),
+ _("differentSlotParamCount"),
+ _("differentSlotParamType"),
+ _("scrambledSlots"),
+ _("differentSignalCount"),
+ _("differentSignalParamCount"),
+ _("differentSignalParamType"),
+ _("scrambledSignals"),
+ _("state"),
+ _("matchAndQuit"), // matchAndQuit should be the last one
+ };
+
+ for (const auto &test : tests) {
+ qDebug() << "Starting" << test << "process";
+ QProcess testProc;
+ testProc.setProcessChannelMode(QProcess::ForwardedChannels);
+ testProc.start(TestUtils::findExecutable(test, "/" + test ),
+ QStringList());
+ QVERIFY(testProc.waitForStarted());
+ QVERIFY(testProc.waitForFinished());
+ QCOMPARE(testProc.exitCode(), 0);
+ }
+
+ QVERIFY(serverProc.waitForFinished());
+ QCOMPARE(serverProc.exitCode(), 0);
+ }
+};
+
+QTEST_MAIN(tst_Signature)
+
+#include "tst_signature.moc"
--- /dev/null
+
+#####################################################################
+## state Binary:
+#####################################################################
+
+qt_internal_add_executable(state
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES
+ main.cpp
+ INCLUDE_DIRECTORIES
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+ Qt::Test
+)
+qt6_add_repc_replicas(state
+ mismatch.rep
+)
+
+#### Keys ignored in scope 1:.:.:state.pro:<TRUE>:
+# TEMPLATE = "app"
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "rep_mismatch_replica.h"
+
+#include <QCoreApplication>
+#include <QtRemoteObjects/qremoteobjectnode.h>
+#include <QtTest/QtTest>
+
+class tst_State_Process : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void testMismatch()
+ {
+ QRemoteObjectNode repNode;
+ repNode.connectToNode(QUrl{QStringLiteral("tcp://127.0.0.1:65214")});
+ QSharedPointer<TestClassReplica> rep{repNode.acquire<TestClassReplica>()};
+ QSignalSpy stateChangedSpy(rep.data(), &QRemoteObjectReplica::stateChanged);
+ QTRY_COMPARE(rep->state(), QRemoteObjectReplica::SignatureMismatch);
+ QCOMPARE(stateChangedSpy.size(), 1);
+ auto args = stateChangedSpy.takeFirst();
+ QCOMPARE(args.size(), 2);
+ QCOMPARE(args.at(0).toInt(), int(QRemoteObjectReplica::SignatureMismatch));
+ QCOMPARE(args.at(1).toInt(), int(QRemoteObjectReplica::Default));
+ }
+
+ void testDynamic()
+ {
+ QRemoteObjectNode repNode;
+ repNode.connectToNode(QUrl{QStringLiteral("tcp://127.0.0.1:65214")});
+ QSharedPointer<QRemoteObjectDynamicReplica> rep{repNode.acquireDynamic("TestClass")};
+ QSignalSpy stateChangedSpy(rep.data(), &QRemoteObjectReplica::stateChanged);
+ QTRY_COMPARE(rep->state(), QRemoteObjectReplica::Valid);
+ QCOMPARE(stateChangedSpy.size(), 1);
+ auto args = stateChangedSpy.takeFirst();
+ QCOMPARE(args.size(), 2);
+ QCOMPARE(args.at(0).toInt(), int(QRemoteObjectReplica::Valid));
+ QCOMPARE(args.at(1).toInt(), int(QRemoteObjectReplica::Uninitialized));
+ }
+};
+
+QTEST_MAIN(tst_State_Process)
+
+#include "main.moc"
--- /dev/null
+ENUM Test {TRUE, FALSE}
+
+class TestChildClass
+{
+ PROP(int prop1);
+ PROP(double prop2);
+};
+
+class TestClass
+{
+ ENUM ClassEnum {Null, One, Two}
+
+ PROP(TestEnum::Test testEnum)
+ PROP(ClassEnum classEnum)
+
+ PROP(int prop1);
+ PROP(double prop2);
+
+ CLASS childProp(TestChildClass);
+
+ SLOT(QString slot2());
+ SLOT(bool slot1());
+
+ SIGNAL(signal1());
+ SIGNAL(signal2());
+
+ SLOT(void ping(const QString &message));
+ SIGNAL(pong(const QString &message));
+
+ SLOT(bool quit());
+};
--- /dev/null
+
+#####################################################################
+## tst_repcodegenerator Test:
+#####################################################################
+
+qt_internal_add_test(tst_repcodegenerator
+ SOURCES
+ tst_repcodegenerator.cpp
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+)
+qt6_add_repc_merged(tst_repcodegenerator
+ classwithsignalonlytest.rep
+ preprocessortest.rep
+ classwithreadonlypropertytest.rep
+)
+
+## Scopes:
+#####################################################################
--- /dev/null
+class MyReadOnlyPropClass
+{
+ PROP(bool myProp READONLY)
+}
--- /dev/null
+class MyClass
+{
+ SIGNAL(mySignal(int))
+}
--- /dev/null
+#include <QString>
+
+#define PREPROCESSORTEST_MACRO 1
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "rep_preprocessortest_merged.h"
+
+#include <QTest>
+
+class tst_RepCodeGenerator : public QObject {
+ Q_OBJECT
+
+private Q_SLOTS:
+ void testPreprocessorTestFile();
+};
+
+void tst_RepCodeGenerator::testPreprocessorTestFile()
+{
+ // could be a compile-time test, but let's just do something here
+ QVERIFY(PREPROCESSORTEST_MACRO);
+}
+
+QTEST_APPLESS_MAIN(tst_RepCodeGenerator)
+
+#include "tst_repcodegenerator.moc"
--- /dev/null
+class LocalDataCenter
+{
+ PROP(int data1);
+ PROP(float data2);
+ PROP(QString data3);
+ PROP(QList<int> data4);
+ SIGNAL(callMe(QList<int> fun));
+};
--- /dev/null
+class TcpDataCenter
+{
+ PROP(int data1);
+ PROP(float data2);
+ PROP(QString data3);
+ PROP(QList<int> data4);
+};
--- /dev/null
+#####################################################################
+## tst_parser Test:
+#####################################################################
+
+set(REPPARSER_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../src/repparser")
+
+qt_internal_add_test(tst_parser
+ SOURCES
+ tst_parser.cpp
+ INCLUDE_DIRECTORIES
+ ${REPPARSER_DIR}
+ PUBLIC_LIBRARIES
+ Qt::CorePrivate
+)
+
+# QLALR Grammars:
+qt_process_qlalr(
+ tst_parser
+ ${REPPARSER_DIR}/parser.g
+ ""
+)
+
+## Scopes:
+#####################################################################
+
+qt_internal_extend_target(tst_parser CONDITION MSVC
+ COMPILE_OPTIONS
+ /wd4129
+)
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "repparser.h"
+
+#include <QTemporaryFile>
+#include <QTest>
+#include <QTextStream>
+
+Q_DECLARE_METATYPE(ASTProperty::Modifier)
+Q_DECLARE_METATYPE(ASTModelRole)
+
+class tst_Parser : public QObject {
+ Q_OBJECT
+
+private Q_SLOTS:
+ void testBasic_data();
+ void testBasic();
+ void testProperties_data();
+ void testProperties();
+ void testSlots_data();
+ void testSlots();
+ void testSignals_data();
+ void testSignals();
+ void testPods_data();
+ void testPods();
+ void testPods2_data();
+ void testPods2();
+ void testEnums_data();
+ void testEnums();
+ void testTypedEnums_data();
+ void testTypedEnums();
+ void testModels_data();
+ void testModels();
+ void testClasses_data();
+ void testClasses();
+ void testInvalid_data();
+ void testInvalid();
+};
+
+void tst_Parser::testBasic_data()
+{
+ QTest::addColumn<QString>("content");
+
+ //Comment out "empty" tests that fail QLALR parser...
+ //QTest::newRow("empty") << ""; // empty lines are fine...
+ QTest::newRow("preprocessor_line_include") << "#include \"foo\"";
+ QTest::newRow("preprocessor_line_include_spaces") << "# include \"foo\"";
+ QTest::newRow("preprocessor_line_ifgroup") << "#if 1\n#include \"foo\n#endif";
+ //QTest::newRow("comment") << "//This is a comment";
+ QTest::newRow("enum") << "ENUM MyEnum {test}";
+ QTest::newRow("empty class with comment") << "class MyClass {\n//comment\n}";
+ QTest::newRow("comment, class") << "//comment\nclass MyClass {}";
+ QTest::newRow("include, comment, class") << "#include \"foo\"\n//comment\nclass MyClass {}";
+}
+
+void tst_Parser::testBasic()
+{
+ QFETCH(QString, content);
+
+ QTemporaryFile file;
+ file.open();
+ QTextStream stream(&file);
+ stream << content << Qt::endl;
+ file.seek(0);
+
+ RepParser parser(file);
+ QVERIFY(parser.parse());
+}
+
+void tst_Parser::testProperties_data()
+{
+ QTest::addColumn<QString>("propertyDeclaration");
+ QTest::addColumn<QString>("expectedType");
+ QTest::addColumn<QString>("expectedName");
+ QTest::addColumn<QString>("expectedDefaultValue");
+ QTest::addColumn<ASTProperty::Modifier>("expectedModifier");
+ QTest::addColumn<bool>("expectedPersistence");
+
+ QTest::newRow("default") << "PROP(QString foo)" << "QString" << "foo" << QString() << ASTProperty::ReadPush << false;
+ QTest::newRow("default with comment") << "PROP(QString foo) // my property" << "QString" << "foo" << QString() << ASTProperty::ReadPush << false;
+ QTest::newRow("default with comment above") << "// my property\nPROP(QString foo)" << "QString" << "foo" << QString() << ASTProperty::ReadPush << false;
+ QTest::newRow("default with indented comment above") << " // my property\nPROP(QString foo)" << "QString" << "foo" << QString() << ASTProperty::ReadPush << false;
+ QTest::newRow("readonly") << "PROP(QString foo READONLY)" << "QString" << "foo" << QString() << ASTProperty::ReadOnly << false;
+ QTest::newRow("constant") << "PROP(QString foo CONSTANT)" << "QString" << "foo" << QString() << ASTProperty::Constant << false;
+ QTest::newRow("readwrite") << "PROP(QString foo READWRITE)" << "QString" << "foo" << QString() << ASTProperty::ReadWrite << false;
+ QTest::newRow("readpush") << "PROP(QString foo READPUSH)" << "QString" << "foo" << QString() << ASTProperty::ReadPush << false;
+ QTest::newRow("persisted") << "PROP(QString foo PERSISTED)" << "QString" << "foo" << QString() << ASTProperty::ReadPush << true;
+ QTest::newRow("readonly, persisted") << "PROP(QString foo READONLY, PERSISTED)" << "QString" << "foo" << QString() << ASTProperty::ReadOnly << true;
+ QTest::newRow("readwrite, persisted") << "PROP(QString foo READWRITE, PERSISTED)" << "QString" << "foo" << QString() << ASTProperty::ReadWrite << true;
+ QTest::newRow("readpush, persisted") << "PROP(QString foo READPUSH, PERSISTED)" << "QString" << "foo" << QString() << ASTProperty::ReadPush << true;
+ QTest::newRow("constant,persisted") << "PROP(QString foo CONSTANT, PERSISTED)" << "QString" << "foo" << QString() << ASTProperty::Constant << true;
+ QTest::newRow("constant,readonly,persisted") << "PROP(QString foo CONSTANT, READONLY, PERSISTED)" << "QString" << "foo" << QString() << ASTProperty::Constant << true;
+ QTest::newRow("readonly,constant,persisted") << "PROP(QString foo READONLY,CONSTANT, PERSISTED)" << "QString" << "foo" << QString() << ASTProperty::Constant << true;
+ QTest::newRow("defaultWithValue") << "PROP(int foo=1)" << "int" << "foo" << "1" << ASTProperty::ReadPush << false;
+ QTest::newRow("readonlyWithValue") << "PROP(int foo=1 READONLY)" << "int" << "foo" << "1" << ASTProperty::ReadOnly << false;
+ QTest::newRow("constantWithValue") << "PROP(int foo=1 CONSTANT)" << "int" << "foo" << "1" << ASTProperty::Constant << false;
+ QTest::newRow("defaultWhitespaces") << "PROP( QString foo )" << "QString" << "foo" << QString() << ASTProperty::ReadPush << false;
+ QTest::newRow("defaultWhitespacesBeforeParentheses") << "PROP ( QString foo )" << "QString" << "foo" << QString() << ASTProperty::ReadPush << false;
+ QTest::newRow("readonlyWhitespaces") << "PROP( QString foo READONLY )" << "QString" << "foo" << QString() << ASTProperty::ReadOnly << false;
+ QTest::newRow("constantWhitespaces") << "PROP( QString foo CONSTANT )" << "QString" << "foo" << QString() << ASTProperty::Constant << false;
+ QTest::newRow("defaultWithValueWhitespaces") << "PROP( int foo = 1 )" << "int" << "foo" << "1" << ASTProperty::ReadPush << false;
+ QTest::newRow("readonlyWithValueWhitespaces") << "PROP( int foo = 1 READONLY )" << "int" << "foo" << "1" << ASTProperty::ReadOnly << false;
+ QTest::newRow("constantWithValueWhitespaces") << "PROP( int foo = 1 CONSTANT )" << "int" << "foo" << "1" << ASTProperty::Constant << false;
+ QTest::newRow("templatetype") << "PROP(QList<int> bar)" << "QList<int>" << "bar" << QString() << ASTProperty::ReadPush << false;
+ QTest::newRow("nested templatetype") << "PROP(QMap<int, QList<int>> bar)" << "QMap<int, QList<int>>" << "bar" << QString() << ASTProperty::ReadPush << false;
+ QTest::newRow("non-int default value") << "PROP(double foo=1.1 CONSTANT)" << "double" << "foo" << "1.1" << ASTProperty::Constant << false;
+ QTest::newRow("tab") << "PROP(double\tfoo)" << "double" << "foo" << "" << ASTProperty::ReadPush << false;
+ QTest::newRow("two tabs") << "PROP(double\t\tfoo)" << "double" << "foo" << "" << ASTProperty::ReadPush << false;
+ QTest::newRow("stringWithValue") << "PROP(QString foo=\"Hello World\")" << "QString" << "foo"
+ << QString("\"Hello World\"") << ASTProperty::ReadPush << false;
+ QTest::newRow("stringWithValueWhitespaces") << "PROP(QString foo = \"Hello World\")"
+ << "QString" << "foo" << QString("\"Hello World\"")
+ << ASTProperty::ReadPush << false;
+ QTest::newRow("readonlyStringWithValueWhitespaces") << "PROP(QString foo = \"Hello World\" READONLY )"
+ << "QString" << "foo" << "\"Hello World\""
+ << ASTProperty::ReadOnly << false;
+ QTest::newRow("readonlyStringWithValue") << "PROP(QString foo=\"Hello World\" READONLY)"
+ << "QString" << "foo" << "\"Hello World\""
+ << ASTProperty::ReadOnly << false;
+}
+
+void tst_Parser::testProperties()
+{
+ QFETCH(QString, propertyDeclaration);
+ QFETCH(QString, expectedType);
+ QFETCH(QString, expectedName);
+ QFETCH(QString, expectedDefaultValue);
+ QFETCH(ASTProperty::Modifier, expectedModifier);
+ QFETCH(bool, expectedPersistence);
+
+ QTemporaryFile file;
+ file.open();
+ QTextStream stream(&file);
+ stream << "class TestClass" << Qt::endl;
+ stream << "{" << Qt::endl;
+ stream << propertyDeclaration << Qt::endl;
+ stream << "};" << Qt::endl;
+ file.seek(0);
+
+ RepParser parser(file);
+ QVERIFY(parser.parse());
+
+ const AST ast = parser.ast();
+ QCOMPARE(ast.classes.size(), 1);
+
+ const ASTClass astClass = ast.classes.first();
+ const QList<ASTProperty> properties = astClass.properties;
+ QCOMPARE(properties.size(), 1);
+
+ const ASTProperty property = properties.first();
+ QCOMPARE(property.type, expectedType);
+ QCOMPARE(property.name, expectedName);
+ QCOMPARE(property.defaultValue, expectedDefaultValue);
+ QCOMPARE(property.modifier, expectedModifier);
+ QCOMPARE(property.persisted, expectedPersistence);
+}
+
+void tst_Parser::testSlots_data()
+{
+ QTest::addColumn<QString>("slotDeclaration");
+ QTest::addColumn<QString>("expectedSlot");
+ QTest::addColumn<bool>("voidWarning");
+ QTest::newRow("slotwithoutspacebeforeparentheses") << "SLOT(test())" << "void test()" << true;
+ QTest::newRow("slotwithspacebeforeparentheses") << "SLOT (test())" << "void test()" << true;
+ QTest::newRow("slotwitharguments") << "SLOT(void test(QString value, int number))" << "void test(QString value, int number)" << false;
+ QTest::newRow("slotwithunnamedarguments") << "SLOT(void test(QString, int))" << "void test(QString __repc_variable_1, int __repc_variable_2)" << false;
+ QTest::newRow("slotwithspaces") << "SLOT( void test (QString value, int number) )" << "void test(QString value, int number)" << false;
+ QTest::newRow("slotwithtemplates") << "SLOT(test(QMap<QString,int> foo))" << "void test(QMap<QString,int> foo)" << true;
+ QTest::newRow("slotwithmultitemplates") << "SLOT(test(QMap<QString,int> foo, QMap<QString,int> bla))" << "void test(QMap<QString,int> foo, QMap<QString,int> bla)" << true;
+ QTest::newRow("slotwithtemplatetemplates") << "SLOT(test(QMap<QList<QString>,int> foo))" << "void test(QMap<QList<QString>,int> foo)" << true;
+ QTest::newRow("slotwithtemplateswithspace") << "SLOT ( test (QMap<QString , int> foo ) )" << "void test(QMap<QString , int> foo)" << true;
+ QTest::newRow("slotWithConstRefArgument") << "SLOT (test(const QString &val))" << "void test(const QString & val)" << true;
+ QTest::newRow("slotWithRefArgument") << "SLOT (test(QString &val))" << "void test(QString & val)" << true;
+ QTest::newRow("slotwithtemplatetemplatesAndConstRef") << "SLOT(test(const QMap<QList<QString>,int> &foo))" << "void test(const QMap<QList<QString>,int> & foo)" << true;
+ QTest::newRow("slotWithConstRefArgumentAndWithout") << "SLOT (test(const QString &val, int value))" << "void test(const QString & val, int value)" << true;
+}
+
+void tst_Parser::testSlots()
+{
+ QFETCH(QString, slotDeclaration);
+ QFETCH(QString, expectedSlot);
+ QFETCH(bool, voidWarning);
+
+ QTemporaryFile file;
+ file.open();
+ QTextStream stream(&file);
+ stream << "class TestClass" << Qt::endl;
+ stream << "{" << Qt::endl;
+ stream << slotDeclaration << Qt::endl;
+ stream << "};" << Qt::endl;
+ file.seek(0);
+
+ if (voidWarning)
+ QTest::ignoreMessage(QtWarningMsg, "[repc] - Adding 'void' for unspecified return type on test");
+ RepParser parser(file);
+ QVERIFY(parser.parse());
+
+ const AST ast = parser.ast();
+ QCOMPARE(ast.classes.size(), 1);
+
+ const ASTClass astClass = ast.classes.first();
+ const QList<ASTFunction> slotsList = astClass.slotsList;
+ QCOMPARE(slotsList.size(), 1);
+ ASTFunction slot = slotsList.first();
+ QCOMPARE(QString("%1 %2(%3)").arg(slot.returnType).arg(slot.name).arg(slot.paramsAsString()), expectedSlot);
+}
+
+void tst_Parser::testSignals_data()
+{
+ QTest::addColumn<QString>("signalDeclaration");
+ QTest::addColumn<QString>("expectedSignal");
+ QTest::newRow("signalwithoutspacebeforeparentheses") << "SIGNAL(test())" << "test()";
+ QTest::newRow("signalwithspacebeforeparentheses") << "SIGNAL (test())" << "test()";
+ QTest::newRow("signalwitharguments") << "SIGNAL(test(QString value, int value))" << "test(QString value, int value)";
+ QTest::newRow("signalwithtemplates") << "SIGNAL(test(QMap<QString,int> foo))" << "test(QMap<QString,int> foo)";
+ QTest::newRow("signalwithtemplateswithspace") << "SIGNAL ( test (QMap<QString , int> foo ) )" << "test(QMap<QString , int> foo)";
+ QTest::newRow("signalWithConstRefArgument") << "SIGNAL (test(const QString &val))" << "test(const QString & val)";
+ QTest::newRow("signalWithRefArgument") << "SIGNAL (test(QString &val))" << "test(QString & val)";
+}
+
+void tst_Parser::testSignals()
+{
+ QFETCH(QString, signalDeclaration);
+ QFETCH(QString, expectedSignal);
+
+ QTemporaryFile file;
+ file.open();
+ QTextStream stream(&file);
+ stream << "class TestClass" << Qt::endl;
+ stream << "{" << Qt::endl;
+ stream << signalDeclaration << Qt::endl;
+ stream << "};" << Qt::endl;
+ file.seek(0);
+
+ RepParser parser(file);
+ QVERIFY(parser.parse());
+
+ const AST ast = parser.ast();
+ QCOMPARE(ast.classes.size(), 1);
+
+ const ASTClass astClass = ast.classes.first();
+ const QList<ASTFunction> signalsList = astClass.signalsList;
+ ASTFunction signal = signalsList.first();
+ QCOMPARE(QString("%1(%2)").arg(signal.name).arg(signal.paramsAsString()), expectedSignal);
+}
+
+void tst_Parser::testPods_data()
+{
+ QTest::addColumn<QString>("podsdeclaration");
+ QTest::addColumn<QString>("expectedtypes");
+ QTest::addColumn<QString>("expectedvariables");
+
+ //Variable/Type separate by ";"
+ QTest::newRow("one pod") << "POD preset(int presetNumber)" << "int" << "presetNumber";
+ QTest::newRow("two pod") << "POD preset(int presetNumber, double foo)" << "int;double" << "presetNumber;foo";
+ QTest::newRow("two pod with space") << "POD preset ( int presetNumber , double foo ) " << "int;double" << "presetNumber;foo";
+ QTest::newRow("two pod multiline") << "POD preset(\nint presetNumber,\ndouble foo\n)" << "int;double" << "presetNumber;foo";
+ //Template
+ QTest::newRow("pod template") << "POD preset(QMap<QString,int> foo) " << "QMap<QString,int>" << "foo";
+ QTest::newRow("pod template (QList)") << "POD preset(QList<QString> foo) " << "QList<QString>" << "foo";
+ QTest::newRow("two pod template") << "POD preset(QMap<QString,int> foo, QMap<double,int> bla) " << "QMap<QString,int>;QMap<double,int>" << "foo;bla";
+ QTest::newRow("two pod template with space") << "POD preset( QMap<QString , int > foo , QMap< double , int > bla ) " << "QMap<QString , int >;QMap< double , int >" << "foo;bla";
+
+}
+
+void tst_Parser::testPods()
+{
+ QFETCH(QString, podsdeclaration);
+ QFETCH(QString, expectedtypes);
+ QFETCH(QString, expectedvariables);
+
+ QTemporaryFile file;
+ file.open();
+ QTextStream stream(&file);
+ stream << podsdeclaration << Qt::endl;
+ stream << "class TestClass" << Qt::endl;
+ stream << "{" << Qt::endl;
+ stream << "};" << Qt::endl;
+ file.seek(0);
+
+ RepParser parser(file);
+ QVERIFY(parser.parse());
+
+ const AST ast = parser.ast();
+ QCOMPARE(ast.classes.size(), 1);
+
+ QCOMPARE(ast.pods.size(), 1);
+ const POD pods = ast.pods.first();
+ const QList<PODAttribute> podsList = pods.attributes;
+ const QStringList typeList = expectedtypes.split(QLatin1Char(';'));
+ const QStringList variableList = expectedvariables.split(QLatin1Char(';'));
+ QVERIFY(typeList.size() == variableList.size());
+ QVERIFY(podsList.size() == variableList.size());
+ for (int i=0; i < podsList.size(); ++i) {
+ QCOMPARE(podsList.at(i).name, variableList.at(i));
+ QCOMPARE(podsList.at(i).type, typeList.at(i));
+ }
+}
+
+void tst_Parser::testPods2_data()
+{
+ QTest::addColumn<QString>("podsdeclaration");
+ QTest::addColumn<QString>("expectedtypes");
+ QTest::addColumn<QString>("expectedvariables");
+
+ //Variable/Type separate by ";"
+ QTest::newRow("one pod") << "POD preset{int presetNumber}" << "int" << "presetNumber";
+ QTest::newRow("two pod") << "POD preset{int presetNumber, double foo}" << "int;double" << "presetNumber;foo";
+ QTest::newRow("two pod with space") << "POD preset { int presetNumber , double foo } " << "int;double" << "presetNumber;foo";
+ QTest::newRow("two pod multiline") << "POD preset{\nint presetNumber,\ndouble foo\n}" << "int;double" << "presetNumber;foo";
+ //Template
+ QTest::newRow("pod template") << "POD preset{QMap<QString,int> foo} " << "QMap<QString,int>" << "foo";
+ QTest::newRow("pod template (QList)") << "POD preset{QList<QString> foo} " << "QList<QString>" << "foo";
+ QTest::newRow("two pod template") << "POD preset{QMap<QString,int> foo, QMap<double,int> bla} " << "QMap<QString,int>;QMap<double,int>" << "foo;bla";
+ QTest::newRow("two pod template with space") << "POD preset{ QMap<QString , int > foo , QMap< double , int > bla } " << "QMap<QString,int>;QMap<double,int>" << "foo;bla";
+ //Enum
+ QTest::newRow("enum multiline") << "POD preset{ENUM val {val1 = 1,\nval2,\nval3=12}\nval value,\ndouble foo\n}" << "val;double" << "value;foo";
+
+}
+
+void tst_Parser::testPods2()
+{
+ QFETCH(QString, podsdeclaration);
+ QFETCH(QString, expectedtypes);
+ QFETCH(QString, expectedvariables);
+
+ QTemporaryFile file;
+ file.open();
+ QTextStream stream(&file);
+ stream << podsdeclaration << Qt::endl;
+ stream << "class TestClass" << Qt::endl;
+ stream << "{" << Qt::endl;
+ stream << "};" << Qt::endl;
+ file.seek(0);
+
+ RepParser parser(file);
+ QVERIFY(parser.parse());
+
+ const AST ast = parser.ast();
+ QCOMPARE(ast.classes.size(), 1);
+
+ QCOMPARE(ast.pods.size(), 1);
+ const POD pods = ast.pods.first();
+ const QVector<PODAttribute> podsList = pods.attributes;
+ const QStringList typeList = expectedtypes.split(QLatin1Char(';'));
+ const QStringList variableList = expectedvariables.split(QLatin1Char(';'));
+ QVERIFY(typeList.size() == variableList.size());
+ QVERIFY(podsList.size() == variableList.size());
+ for (int i=0; i < podsList.size(); ++i) {
+ QCOMPARE(podsList.at(i).name, variableList.at(i));
+ QCOMPARE(podsList.at(i).type, typeList.at(i));
+ }
+}
+
+void tst_Parser::testEnums_data()
+{
+ QTest::addColumn<QString>("enumdeclaration");
+ QTest::addColumn<QString>("expectednames");
+ QTest::addColumn<QList<int> >("expectedvalues");
+ QTest::addColumn<int>("expectedmax");
+ QTest::addColumn<bool>("expectedsigned");
+ QTest::addColumn<bool>("inclass");
+
+ for (int i = 0; i <= 1; ++i) {
+ bool inclass = i == 1;
+ QString identifier = inclass ? QLatin1String("%1 in class") : QLatin1String("%1 outside class");
+ //Separate by ";"
+ QTest::newRow(identifier.arg("one enum val").toLatin1()) << "ENUM preset {presetNumber}" << "presetNumber" << (QList<int>() << 0) << 0 << false << inclass;
+ QTest::newRow(identifier.arg("two enum val").toLatin1()) << "ENUM preset {presetNumber, foo}" << "presetNumber;foo" << (QList<int>() << 0 << 1) << 1 << false << inclass;
+ QTest::newRow(identifier.arg("two enum val -1 2nd").toLatin1()) << "ENUM preset {presetNumber, foo = -1}" << "presetNumber;foo" << (QList<int>() << 0 << -1) << 1 << true << inclass;
+ QTest::newRow(identifier.arg("two enum val -1 1st").toLatin1()) << "ENUM preset {presetNumber=-1, foo}" << "presetNumber;foo" << (QList<int>() << -1 << 0) << 1 << true << inclass;
+ QTest::newRow(identifier.arg("two enum val hex").toLatin1()) << "ENUM preset {presetNumber=0xf, foo}" << "presetNumber;foo" << (QList<int>() << 15 << 16) << 16 << false << inclass;
+ QTest::newRow(identifier.arg("two enum val hex").toLatin1()) << "ENUM preset {presetNumber=0xff, foo}" << "presetNumber;foo" << (QList<int>() << 255 << 256) << 256 << false << inclass;
+ QTest::newRow(identifier.arg("two enum val with space").toLatin1()) << "ENUM preset { presetNumber , foo } " << "presetNumber;foo" << (QList<int>() << 0 << 1) << 1 << false << inclass;
+ QTest::newRow(identifier.arg("set values").toLatin1()) << "ENUM preset { val1=1 , val3=3, val5=5 } " << "val1;val3;val5" << (QList<int>() << 1 << 3 << 5) << 5 << false << inclass;
+ QTest::newRow(identifier.arg("multiline").toLatin1()) << "ENUM preset {\nval1,\nval2,\nval3\n} " << "val1;val2;val3" << (QList<int>() << 0 << 1 << 2) << 2 << false << inclass;
+ QTest::newRow(identifier.arg("multiline indented").toLatin1()) << " ENUM preset {\n val1,\n val2,\n val3\n } " << "val1;val2;val3" << (QList<int>() << 0 << 1 << 2) << 2 << false << inclass;
+ }
+}
+
+void tst_Parser::testEnums()
+{
+ QFETCH(QString, enumdeclaration);
+ QFETCH(QString, expectednames);
+ QFETCH(QList<int>, expectedvalues);
+ QFETCH(int, expectedmax);
+ QFETCH(bool, expectedsigned);
+ QFETCH(bool, inclass);
+
+ QTemporaryFile file;
+ file.open();
+ QTextStream stream(&file);
+ if (!inclass)
+ stream << enumdeclaration << Qt::endl;
+ stream << "class TestClass" << Qt::endl;
+ stream << "{" << Qt::endl;
+ if (inclass)
+ stream << enumdeclaration << Qt::endl;
+ stream << "};" << Qt::endl;
+ file.seek(0);
+
+ RepParser parser(file);
+ QVERIFY(parser.parse());
+
+ const AST ast = parser.ast();
+ QCOMPARE(ast.classes.size(), 1);
+ ASTEnum enums;
+ if (inclass) {
+ const ASTClass astClass = ast.classes.first();
+ QCOMPARE(astClass.enums.size(), 1);
+ enums = astClass.enums.first();
+ } else {
+ QCOMPARE(ast.enums.size(), 1);
+ enums = ast.enums.first();
+ }
+ QVERIFY(enums.isScoped == false);
+ QVERIFY(enums.type.isEmpty());
+ const QList<ASTEnumParam> paramList = enums.params;
+ const QStringList nameList = expectednames.split(QLatin1Char(';'));
+ QVERIFY(nameList.size() == expectedvalues.size());
+ QVERIFY(paramList.size() == expectedvalues.size());
+ for (int i=0; i < paramList.size(); ++i) {
+ QCOMPARE(paramList.at(i).name, nameList.at(i));
+ QCOMPARE(paramList.at(i).value, expectedvalues.at(i));
+ }
+ QCOMPARE(enums.max, expectedmax);
+ QCOMPARE(enums.isSigned, expectedsigned);
+}
+
+void tst_Parser::testTypedEnums_data()
+{
+ QTest::addColumn<QString>("enumdeclaration");
+ QTest::addColumn<QString>("expectedtype");
+ QTest::addColumn<bool>("inclass");
+ QTest::addColumn<bool>("isscoped");
+ QTest::addColumn<bool>("isflag");
+
+ for (int i = 0; i <= 7; ++i) {
+ bool inclass = i % 2 == 1;
+ bool isscoped = i % 4 > 1;
+ bool isflag = i > 3;
+ QString identifier = inclass ? QLatin1String("%1 %2 %3 in class") : QLatin1String("%1 %2 %3 outside class");
+ QString scopeString = isscoped ? QLatin1String("Scoped") : QLatin1String("Non-scoped");
+ QString flagString = isflag ? QLatin1String("Flag") : QLatin1String("Enum");
+ QTest::newRow(identifier.arg(scopeString, flagString, "no type").toLatin1()) << "preset {presetNumber}" << QString() << inclass << isscoped << isflag;
+ QTest::newRow(identifier.arg(scopeString, flagString, "quint16").toLatin1()) << "preset : quint16 {presetNumber}" << "quint16" << inclass << isscoped << isflag;
+ QTest::newRow(identifier.arg(scopeString, flagString, "qint64").toLatin1()) << "preset : qint64 {presetNumber}" << "qint64" << inclass << isscoped << isflag;
+ QTest::newRow(identifier.arg(scopeString, flagString, "unsigned char").toLatin1()) << "preset: unsigned char {presetNumber}" << "unsigned char" << inclass << isscoped << isflag;
+ }
+}
+
+void tst_Parser::testTypedEnums()
+{
+ QFETCH(QString, enumdeclaration);
+ QFETCH(QString, expectedtype);
+ QFETCH(bool, inclass);
+ QFETCH(bool, isscoped);
+ QFETCH(bool, isflag);
+
+ QTemporaryFile file;
+ file.open();
+ QTextStream stream(&file);
+ if (!inclass) {
+ stream << "ENUM " << (isscoped ? "class " : "") << enumdeclaration << Qt::endl;
+ if (isflag)
+ stream << "FLAG(MyFlags preset)" << Qt::endl;
+ }
+ stream << "class TestClass" << Qt::endl;
+ stream << "{" << Qt::endl;
+ if (inclass) {
+ stream << "ENUM " << (isscoped ? "class " : "") << enumdeclaration << Qt::endl;
+ if (isflag)
+ stream << "FLAG(MyFlags preset)" << Qt::endl;
+ }
+ stream << "};" << Qt::endl;
+ file.seek(0);
+
+ RepParser parser(file);
+ QVERIFY(parser.parse());
+
+ const AST ast = parser.ast();
+ QCOMPARE(ast.classes.size(), 1);
+ ASTEnum enums;
+ ASTFlag flags;
+ if (inclass) {
+ const ASTClass astClass = ast.classes.first();
+ QCOMPARE(astClass.enums.size(), 1);
+ enums = astClass.enums.first();
+ if (isflag)
+ flags = astClass.flags.first();
+ } else {
+ QCOMPARE(ast.enums.size(), 1);
+ enums = ast.enums.first();
+ if (isflag)
+ flags = ast.flags.first();
+ }
+ QVERIFY(enums.isScoped == isscoped);
+ QVERIFY(enums.flagIndex == (isflag ? 0 : -1));
+ QVERIFY(flags.name == (isflag ? "MyFlags" : QString{}));
+ QVERIFY(flags._enum == (isflag ? "preset" : QString{}));
+ QCOMPARE(enums.type, expectedtype);
+ const QList<ASTEnumParam> paramList = enums.params;
+ QVERIFY(paramList.size() == 1);
+ for (int i=0; i < paramList.size(); ++i) {
+ QCOMPARE(paramList.at(i).name, "presetNumber");
+ QCOMPARE(paramList.at(i).value, 0);
+ }
+ QCOMPARE(enums.max, 0);
+ QCOMPARE(enums.isSigned, false);
+}
+
+void tst_Parser::testModels_data()
+{
+ QTest::addColumn<QString>("modelDeclaration");
+ QTest::addColumn<QString>("expectedModel");
+ QTest::addColumn<QList<ASTModelRole>>("expectedRoles");
+ QTest::newRow("basicmodel") << "MODEL test(display)" << "test" << QList<ASTModelRole>({{"display"}});
+ QTest::newRow("basicmodelsemicolon") << "MODEL test(display);" << "test" << QList<ASTModelRole>({{"display"}});
+}
+
+void tst_Parser::testModels()
+{
+ QFETCH(QString, modelDeclaration);
+ QFETCH(QString, expectedModel);
+ QFETCH(QList<ASTModelRole>, expectedRoles);
+
+ QTemporaryFile file;
+ file.open();
+ QTextStream stream(&file);
+ stream << "class TestClass" << Qt::endl;
+ stream << "{" << Qt::endl;
+ stream << modelDeclaration << Qt::endl;
+ stream << "};" << Qt::endl;
+ file.seek(0);
+
+ RepParser parser(file);
+ QVERIFY(parser.parse());
+
+ const AST ast = parser.ast();
+ QCOMPARE(ast.classes.size(), 1);
+
+ const ASTClass astClass = ast.classes.first();
+ ASTModel model = astClass.modelMetadata.first();
+ ASTProperty property = astClass.properties.at(model.propertyIndex);
+ QCOMPARE(property.name, expectedModel);
+ int i = 0;
+ for (auto role : model.roles) {
+ QCOMPARE(role.name, expectedRoles.at(i).name);
+ i++;
+ }
+}
+
+void tst_Parser::testClasses_data()
+{
+ QTest::addColumn<QString>("classDeclaration");
+ QTest::addColumn<QString>("expectedType");
+ QTest::addColumn<QString>("expectedName");
+ QTest::newRow("basicclass") << "CLASS sub(subObject)" << "subObject" << "sub";
+}
+
+void tst_Parser::testClasses()
+{
+ QFETCH(QString, classDeclaration);
+ QFETCH(QString, expectedType);
+ QFETCH(QString, expectedName);
+
+ QTemporaryFile file;
+ file.open();
+ QTextStream stream(&file);
+ stream << "class subObject" << Qt::endl;
+ stream << "{" << Qt::endl;
+ stream << " PROP(int value)" << Qt::endl;
+ stream << "};" << Qt::endl;
+ stream << "class parentObject" << Qt::endl;
+ stream << "{" << Qt::endl;
+ stream << classDeclaration << Qt::endl;
+ stream << "};" << Qt::endl;
+ file.seek(0);
+
+ RepParser parser(file);
+ QVERIFY(parser.parse());
+
+ const AST ast = parser.ast();
+ QCOMPARE(ast.classes.size(), 2);
+
+ const ASTClass astSub = ast.classes.value(0);
+ const ASTClass astObj = ast.classes.value(1);
+ const ASTProperty property = astObj.properties.at(astObj.subClassPropertyIndices.at(0));
+ QCOMPARE(property.name, expectedName);
+ QCOMPARE(property.type, expectedType);
+}
+
+void tst_Parser::testInvalid_data()
+{
+ QTest::addColumn<QString>("content");
+ QTest::addColumn<QString>("warning");
+
+ QTest::newRow("pod_invalid") << "POD (int foo)" << ".?Unknown token encountered";
+ QTest::newRow("pod_unbalancedparens") << "POD foo(int foo" << ".?Unknown token encountered";
+ QTest::newRow("pod_inclass") << "class Foo\n{\nPOD foo(int)\n}" << ".?POD: Can only be used in global scope";
+ QTest::newRow("class_noidentifier") << "class\n{\n}" << ".?Unknown token encountered";
+ QTest::newRow("class_nested") << "class Foo\n{\nclass Bar\n}" << ".?class: Cannot be nested";
+ QTest::newRow("prop_outsideclass") << "PROP(int foo)" << ".?PROP: Can only be used in class scope";
+ QTest::newRow("prop_toomanyargs") << "class Foo\n{\nPROP(int int foo)\n}" << ".?Invalid property declaration: flag foo is unknown";
+ QTest::newRow("prop_toomanymodifiers") << "class Foo\n{\nPROP(int foo READWRITE, READONLY)\n}" << ".?Invalid property declaration: combination not allowed .READWRITE, READONLY.";
+ QTest::newRow("prop_noargs") << "class Foo\n{\nPROP()\n}" << ".?Unknown token encountered";
+ QTest::newRow("prop_unbalancedparens") << "class Foo\n{\nPROP(int foo\n}" << ".?Unknown token encountered";
+ QTest::newRow("signal_outsideclass") << "SIGNAL(foo())" << ".?SIGNAL: Can only be used in class scope";
+ QTest::newRow("signal_noargs") << "class Foo\n{\nSIGNAL()\n}" << ".?Unknown token encountered";
+ QTest::newRow("slot_outsideclass") << "SLOT(void foo())" << ".?SLOT: Can only be used in class scope";
+ QTest::newRow("slot_noargs") << "class Foo\n{\nSLOT()\n}" << ".?Unknown token encountered";
+ QTest::newRow("model_outsideclass") << "MODEL foo" << ".?Unknown token encountered";
+ QTest::newRow("class_outsideclass") << "CLASS foo" << ".?Unknown token encountered";
+ QTest::newRow("preprecessor_line_inclass") << "class Foo\n{\n#define foo\n}" << ".?Unknown token encountered";
+}
+
+void tst_Parser::testInvalid()
+{
+ QFETCH(QString, content);
+ QFETCH(QString, warning);
+
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(warning));
+ QTemporaryFile file;
+ file.open();
+ QTextStream stream(&file);
+ stream << content << Qt::endl;
+ file.seek(0);
+
+ RepParser parser(file);
+ QVERIFY(!parser.parse());
+}
+
+QTEST_APPLESS_MAIN(tst_Parser)
+
+#include "tst_parser.moc"
--- /dev/null
+
+add_subdirectory(client)
+add_subdirectory(server)
+add_subdirectory(tst)
--- /dev/null
+
+#####################################################################
+## restart_client Binary:
+#####################################################################
+
+qt_internal_add_executable(restart_client
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES
+ main.cpp
+ INCLUDE_DIRECTORIES
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+ Qt::Test
+)
+qt6_add_repc_replicas(restart_client
+ ../subclass.rep
+)
+
+#### Keys ignored in scope 1:.:.:client.pro:<TRUE>:
+# TEMPLATE = "app"
--- /dev/null
+// Copyright (C) 2019 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "rep_subclass_replica.h"
+
+#include <QCoreApplication>
+#include <QtRemoteObjects/qremoteobjectnode.h>
+#include <QtTest/QtTest>
+
+class tst_Client_Process : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void initTestCase()
+ {
+ QLoggingCategory::setFilterRules("qt.remoteobjects.warning=true");
+ m_repNode.reset(new QRemoteObjectNode);
+ m_repNode->connectToNode(QUrl(QStringLiteral("tcp://127.0.0.1:65217")));
+ m_rep.reset(m_repNode->acquire<ParentClassReplica>());
+ connect(m_rep.data(), &QRemoteObjectReplica::stateChanged, [this](QRemoteObjectReplica::State state, QRemoteObjectReplica::State previousState) {
+ qDebug() << "**** stateChanged" << state << previousState;
+ if (state == QRemoteObjectReplica::Suspect) {
+ qWarning() << "Replica suspect";
+ this->serverRestarted = true;
+ } else if (state == QRemoteObjectReplica::Valid) {
+ if (this->serverRestarted) {
+ qWarning() << "Replica valid again";
+ auto reply = m_rep->start();
+ }
+ }
+ });
+ QVERIFY(m_rep->waitForSource());
+ }
+
+ void testRun()
+ {
+ const auto objectMode = qEnvironmentVariable("ObjectMode");
+
+ qWarning() << "From client";
+ const MyPOD initialValue(42, 3.14f, QStringLiteral("SubClass"));
+ if (objectMode == QLatin1String("ObjectPointer")) {
+ QSignalSpy tracksSpy(m_rep->tracks(), &QAbstractItemModelReplica::initialized);
+ QVERIFY(m_rep->subClass() != nullptr);
+ QCOMPARE(m_rep->subClass()->myPOD(), initialValue);
+ QVERIFY(m_rep->tracks() != nullptr);
+ QVERIFY(tracksSpy.size() || tracksSpy.wait());
+ } else {
+ QVERIFY(m_rep->subClass() == nullptr);
+ QVERIFY(m_rep->tracks() == nullptr);
+ }
+ auto reply = m_rep->start();
+ QVERIFY(reply.waitForFinished());
+
+ QSignalSpy advanceSpy(m_rep.data(), SIGNAL(advance()));
+ QVERIFY(advanceSpy.wait(15000));
+ if (objectMode == QLatin1String("ObjectPointer")) {
+ QVERIFY(m_rep->subClass() != nullptr);
+ QCOMPARE(m_rep->subClass()->myPOD(), initialValue);
+ QVERIFY(m_rep->tracks() != nullptr);
+ } else {
+ QVERIFY(m_rep->subClass() == nullptr);
+ QVERIFY(m_rep->tracks() == nullptr);
+ }
+ }
+
+ void cleanupTestCase()
+ {
+ auto reply = m_rep->quit();
+ QVERIFY(reply.waitForFinished());
+ }
+
+private:
+ QScopedPointer<QRemoteObjectNode> m_repNode;
+ QScopedPointer<ParentClassReplica> m_rep;
+ bool serverRestarted = false;
+};
+
+QTEST_MAIN(tst_Client_Process)
+
+#include "main.moc"
--- /dev/null
+
+#####################################################################
+## restart_server Binary:
+#####################################################################
+
+qt_internal_add_executable(restart_server
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES
+ main.cpp
+ mytestserver.cpp mytestserver.h
+ INCLUDE_DIRECTORIES
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+ Qt::Test
+)
+qt6_add_repc_sources(restart_server
+ ../subclass.rep
+)
+
+#### Keys ignored in scope 1:.:.:server.pro:<TRUE>:
+# TEMPLATE = "app"
--- /dev/null
+// Copyright (C) 2019 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "mytestserver.h"
+#include "rep_subclass_source.h"
+
+#include <QCoreApplication>
+#include <QtTest/QtTest>
+
+class tst_Server_Process : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void testRun()
+ {
+ const auto runMode = qEnvironmentVariable("RunMode");
+ const auto objectMode = qEnvironmentVariable("ObjectMode");
+
+ QRemoteObjectHost srcNode(QUrl(QStringLiteral("tcp://127.0.0.1:65217")));
+ MyTestServer myTestServer;
+ SubClassSimpleSource subclass;
+ QStringListModel model;
+ const MyPOD initialValue(42, 3.14f, QStringLiteral("SubClass"));
+ if (objectMode == QLatin1String("ObjectPointer")) {
+ subclass.setMyPOD(initialValue);
+ model.setStringList(QStringList() << "Track1" << "Track2" << "Track3");
+ myTestServer.setSubClass(&subclass);
+ myTestServer.setTracks(&model);
+ }
+ srcNode.enableRemoting(&myTestServer);
+
+ qDebug() << "Waiting for incoming connections";
+
+ QSignalSpy waitForStartedSpy(&myTestServer, &MyTestServer::startedChanged);
+ QVERIFY(waitForStartedSpy.isValid());
+ QVERIFY(waitForStartedSpy.wait());
+ QCOMPARE(waitForStartedSpy.value(0).value(0).toBool(), true);
+
+ // wait for delivery of events
+ QTest::qWait(200);
+
+ qDebug() << "Client connected";
+ if (runMode != QLatin1String("Baseline")) {
+ qDebug() << "Server quitting" << runMode;
+ if (runMode == QLatin1String("ServerRestartFatal"))
+ qFatal("Fatal");
+ QCoreApplication::exit();
+ return;
+ }
+ emit myTestServer.advance();
+
+ // wait for quit
+ bool quit = false;
+ connect(&myTestServer, &MyTestServer::quitApp, [&quit]{quit = true;});
+ QTRY_VERIFY_WITH_TIMEOUT(quit, 5000);
+
+ // wait for delivery of events
+ QTest::qWait(200);
+
+ qDebug() << "Done. Shutting down.";
+ }
+};
+
+QTEST_MAIN(tst_Server_Process)
+
+#include "main.moc"
--- /dev/null
+// Copyright (C) 2019 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <qdebug.h>
+
+#include "mytestserver.h"
+#include "rep_subclass_source.h"
+
+MyTestServer::MyTestServer(QObject *parent)
+ : ParentClassSimpleSource(parent)
+{
+ qDebug() << "Server started";
+}
+
+MyTestServer::~MyTestServer()
+{
+ qDebug() << "Server stopped";
+}
+
+bool MyTestServer::start()
+{
+ setStarted(true);
+ return true;
+}
+
+bool MyTestServer::quit()
+{
+ emit quitApp();
+ return true;
+}
--- /dev/null
+// Copyright (C) 2019 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef MYTESTSERVER_H
+#define MYTESTSERVER_H
+
+#include <QTimer>
+
+#include <QtRemoteObjects/qremoteobjectnode.h>
+#include <QtRemoteObjects/qremoteobjectsource.h>
+
+#include "rep_subclass_source.h"
+
+class MyTestServer : public ParentClassSimpleSource
+{
+ Q_OBJECT
+
+public:
+ MyTestServer(QObject *parent = nullptr);
+ ~MyTestServer() override;
+
+public Q_SLOTS:
+ bool start() override;
+ bool quit() override;
+
+Q_SIGNALS:
+ void quitApp();
+};
+
+#endif // MYTESTSERVER_H
--- /dev/null
+POD MyPOD(int i, float f, QString s)
+
+class SubClass
+{
+ PROP(MyPOD myPOD)
+}
+
+class ParentClass
+{
+ PROP(bool started = false)
+
+ SLOT(bool start())
+ SLOT(bool quit())
+ SIGNAL(advance())
+
+ CLASS subClass(SubClass)
+ MODEL tracks(display)
+}
+
--- /dev/null
+
+#####################################################################
+## tst_restart Test:
+#####################################################################
+
+qt_internal_add_test(tst_restart
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES
+ tst_restart.cpp
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+)
--- /dev/null
+// Copyright (C) 2019 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <QtTest/QtTest>
+#include <QMetaType>
+#include <QProcess>
+
+#include "../../../shared/testutils.h"
+
+class tst_Restart: public QObject
+{
+ Q_OBJECT
+
+public:
+ enum RunMode { Baseline, ServerRestartGraceful, ServerRestartFatal };
+ enum ObjectMode { NullPointer, ObjectPointer };
+ Q_ENUM(RunMode)
+ Q_ENUM(ObjectMode)
+
+private slots:
+ void initTestCase()
+ {
+ QVERIFY(TestUtils::init("tst"));
+ QLoggingCategory::setFilterRules("qt.remoteobjects.warning=false");
+ }
+
+ void cleanup()
+ {
+ // wait for delivery of RemoveObject events to the source
+ QTest::qWait(200);
+ }
+
+ void testRun_data()
+ {
+ QTest::addColumn<RunMode>("runMode");
+ QTest::addColumn<ObjectMode>("objectMode");
+ auto runModeMeta = QMetaEnum::fromType<RunMode>();
+ auto objectModeMeta = QMetaEnum::fromType<ObjectMode>();
+ for (int i = 0; i < runModeMeta.keyCount(); i++) {
+ for (int j = 0; j < objectModeMeta.keyCount(); j++) {
+ auto ba = QByteArray(runModeMeta.valueToKey(i));
+ ba = ba.append("_").append(objectModeMeta.valueToKey(j));
+ QTest::newRow(ba.data()) << static_cast<RunMode>(i) << static_cast<ObjectMode>(j);
+ }
+ }
+ }
+
+ void testRun()
+ {
+#ifdef Q_OS_ANDROID
+ QSKIP("QProcess doesn't support running user bundled binaries on Android");
+#endif
+ QFETCH(RunMode, runMode);
+ QFETCH(ObjectMode, objectMode);
+
+ qDebug() << "Starting server process" << runMode;
+ bool serverRestart = runMode == ServerRestartFatal || runMode == ServerRestartGraceful;
+ QProcess serverProc;
+ serverProc.setProcessChannelMode(QProcess::ForwardedChannels);
+ QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
+ env.insert("RunMode", QVariant::fromValue(runMode).toString());
+ env.insert("ObjectMode", QVariant::fromValue(objectMode).toString());
+ serverProc.setProcessEnvironment(env);
+ QStringList arguments;
+ if (runMode == ServerRestartFatal)
+ arguments.append("-nocrashhandler");
+ serverProc.start(TestUtils::findExecutable("restart_server", "/server"), arguments);
+ QVERIFY(serverProc.waitForStarted());
+
+ // wait for server start
+ QTest::qWait(200);
+
+ qDebug() << "Starting client process";
+ QProcess clientProc;
+ clientProc.setProcessChannelMode(QProcess::ForwardedChannels);
+ clientProc.setProcessEnvironment(env);
+ clientProc.start(TestUtils::findExecutable("restart_client", "/client"),
+ QStringList());
+ QVERIFY(clientProc.waitForStarted());
+
+ if (serverRestart) {
+ env.insert("RunMode", QVariant::fromValue(Baseline).toString()); // Don't include ServerRestart environment variable this time
+ qDebug() << "Waiting for server exit";
+ QVERIFY(serverProc.waitForFinished());
+ if (runMode == ServerRestartFatal)
+ QVERIFY(serverProc.exitCode() != 0);
+ else
+ QCOMPARE(serverProc.exitCode(), 0);
+ qDebug() << "Restarting server";
+ serverProc.setProcessEnvironment(env);
+ serverProc.start(TestUtils::findExecutable("restart_server", "/server"),
+ QStringList());
+ QVERIFY(serverProc.waitForStarted());
+ }
+
+ QVERIFY(clientProc.waitForFinished());
+ QVERIFY(serverProc.waitForFinished());
+
+ QCOMPARE(serverProc.exitCode(), 0);
+ QCOMPARE(clientProc.exitCode(), 0);
+ }
+};
+
+QTEST_MAIN(tst_Restart)
+
+#include "tst_restart.moc"
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <QtTest/QtTest>
+#include <QModelIndex>
+
+// Helper class which can be used by tests for starting a task and
+// waiting for its completion. It takes care of running an event
+// loop while waiting, until finished() method is called (or the
+// timeout is reached).
+class WaitHelper : public QObject
+{
+ Q_OBJECT
+
+public:
+ WaitHelper() { m_promise.start(); }
+
+ ~WaitHelper()
+ {
+ if (m_promise.future().isRunning())
+ m_promise.finish();
+ }
+
+ /*
+ Starts an event loop and waits until finish() method is called
+ or the timeout is reached.
+ */
+ bool wait(int timeout = 30000)
+ {
+ if (m_promise.future().isFinished())
+ return true;
+
+ QFutureWatcher<void> watcher;
+ QSignalSpy watcherSpy(&watcher, &QFutureWatcher<void>::finished);
+ watcher.setFuture(m_promise.future());
+ return watcherSpy.wait(timeout);
+ }
+
+protected:
+ /*
+ The derived classes need to call this method to stop waiting.
+ */
+ void finish() { m_promise.finish(); }
+
+private:
+ QPromise<void> m_promise;
+};
+
+namespace {
+
+inline bool compareIndices(const QModelIndex &lhs, const QModelIndex &rhs)
+{
+ QModelIndex left = lhs;
+ QModelIndex right = rhs;
+ while (left.row() == right.row() && left.column() == right.column() && left.isValid() && right.isValid()) {
+ left = left.parent();
+ right = right.parent();
+ }
+ if (left.isValid() || right.isValid())
+ return false;
+ return true;
+}
+
+struct WaitForDataChanged : public WaitHelper
+{
+ WaitForDataChanged(const QAbstractItemModel *model, const QList<QModelIndex> &pending)
+ : WaitHelper(), m_model(model), m_pending(pending)
+ {
+ connect(m_model, &QAbstractItemModel::dataChanged, this,
+ [this](const QModelIndex &topLeft, const QModelIndex &bottomRight,
+ const QList<int> &roles) {
+ Q_UNUSED(roles)
+
+ checkAndRemoveRange(topLeft, bottomRight);
+ if (m_pending.isEmpty())
+ finish();
+ });
+ }
+
+ void checkAndRemoveRange(const QModelIndex &topLeft, const QModelIndex &bottomRight)
+ {
+ QVERIFY(topLeft.parent() == bottomRight.parent());
+ const auto isInRange = [topLeft, bottomRight] (const QModelIndex &pending) noexcept -> bool {
+ if (pending.isValid() && compareIndices(pending.parent(), topLeft.parent())) {
+ const bool fitLeft = topLeft.column() <= pending.column();
+ const bool fitRight = bottomRight.column() >= pending.column();
+ const bool fitTop = topLeft.row() <= pending.row();
+ const bool fitBottom = bottomRight.row() >= pending.row();
+ if (fitLeft && fitRight && fitTop && fitBottom)
+ return true;
+ }
+ return false;
+ };
+ m_pending.erase(std::remove_if(m_pending.begin(), m_pending.end(), isInRange),
+ m_pending.end());
+ }
+
+private:
+ const QAbstractItemModel *m_model = nullptr;
+ QList<QModelIndex> m_pending;
+};
+
+} // namespace
--- /dev/null
+
+#####################################################################
+## tst_subclassreplicatest Test:
+#####################################################################
+
+qt_internal_add_test(tst_subclassreplicatest
+ SOURCES
+ tst_subclassreplicatest.cpp
+ DEFINES
+ SRCDIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/\\\"
+ PUBLIC_LIBRARIES
+ Qt::RemoteObjects
+)
+qt6_add_repc_merged(tst_subclassreplicatest
+ class.rep
+)
+
+#### Keys ignored in scope 1:.:.:subclassreplica.pro:<TRUE>:
+# TEMPLATE = "app"
--- /dev/null
+class SubClass
+{
+ PROP(int value)
+}
+class ParentClass
+{
+ CLASS sub1(SubClass)
+ CLASS sub2(SubClass)
+}
+
+class OtherParentClass
+{
+ CLASS sub1(SubClass)
+}
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <QString>
+#include <QtTest>
+#include "rep_class_merged.h"
+
+#include "../../shared/testutils.h"
+
+class SubClassReplicaTest : public QObject
+{
+ Q_OBJECT
+
+public:
+ SubClassReplicaTest();
+
+private Q_SLOTS:
+ void basicFunctions();
+ void basicFunctions_data();
+};
+
+SubClassReplicaTest::SubClassReplicaTest()
+{
+}
+
+void SubClassReplicaTest::basicFunctions_data()
+{
+ QTest::addColumn<bool>("templated");
+ QTest::addColumn<bool>("nullobject");
+ QTest::newRow("non-templated pointer") << false << false;
+ QTest::newRow("templated pointer") << true << false;
+ QTest::newRow("non-templated nullptr") << false << true;
+ QTest::newRow("templated nullptr") << true << true;
+}
+
+void SubClassReplicaTest::basicFunctions()
+{
+ QFETCH(bool, templated);
+ QFETCH(bool, nullobject);
+
+ QRemoteObjectRegistryHost host(QUrl(LOCAL_SOCKET ":test"));
+ SubClassSimpleSource subclass1, subclass2;
+ ParentClassSimpleSource parent;
+ parent.setSub1(&subclass1);
+
+ SubClassSimpleSource subclass3;
+ subclass3.setValue(4);
+ OtherParentClassSimpleSource otherParent;
+ otherParent.setSub1(&subclass3);
+ host.enableRemoting(&otherParent, "OtherParent1");
+
+ SubClassSimpleSource subclass4;
+ subclass4.setValue(15);
+ OtherParentClassSimpleSource otherParent2;
+ otherParent2.setSub1(&subclass4);
+ host.enableRemoting(&otherParent2, "OtherParent2");
+
+ if (nullobject)
+ parent.setSub2(nullptr);
+ else
+ parent.setSub2(&subclass2);
+ if (templated)
+ host.enableRemoting<ParentClassSourceAPI>(&parent);
+ else
+ host.enableRemoting(&parent);
+
+ QRemoteObjectNode client(QUrl(LOCAL_SOCKET ":test"));
+ const QScopedPointer<ParentClassReplica> replica(client.acquire<ParentClassReplica>());
+ QVERIFY(replica->waitForSource(1000));
+
+ auto sub1 = replica->sub1();
+ QSignalSpy spy(sub1, &SubClassReplica::valueChanged);
+ subclass1.setValue(10);
+ QVERIFY(spy.wait());
+ QCOMPARE(subclass1.value(), sub1->value());
+ if (nullobject) {
+ QCOMPARE(replica->sub2(), nullptr);
+ QCOMPARE(parent.sub2(), nullptr);
+ } else
+ QCOMPARE(subclass2.value(), replica->sub2()->value());
+
+ const QScopedPointer<OtherParentClassReplica> otherReplica(client.acquire<OtherParentClassReplica>("OtherParent1"));
+ QVERIFY(otherReplica->waitForSource(1000));
+
+ const QScopedPointer<OtherParentClassReplica> otherReplica2(client.acquire<OtherParentClassReplica>("OtherParent2"));
+ QVERIFY(otherReplica2->waitForSource(1000));
+
+ QCOMPARE(otherReplica->sub1()->value(), 4);
+ QCOMPARE(otherReplica2->sub1()->value(), 15);
+ otherReplica->sub1()->pushValue(19);
+ otherReplica2->sub1()->pushValue(24);
+ QTRY_COMPARE(subclass4.value(), 24);
+ QTRY_COMPARE(subclass3.value(), 19);
+}
+
+QTEST_MAIN(SubClassReplicaTest)
+
+#include "tst_subclassreplicatest.moc"
--- /dev/null
+<config>
+<modules>
+<module name="QtRemoteObjects" qtname="remoteobjects"/>
+</modules>
+</config>
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTUTILS_H
+#define TESTUTILS_H
+
+#include <QDebug>
+#include <QString>
+#include <QDir>
+#include <QStandardPaths>
+#include <QCoreApplication>
+
+#ifdef Q_OS_ANDROID
+#define LOCAL_SOCKET "localabstract"
+#else
+#define LOCAL_SOCKET "local"
+#endif
+
+namespace TestUtils {
+
+QString subFolder;
+QString rootFolder;
+
+bool init(const QString &folder)
+{
+ if (!TestUtils::rootFolder.isEmpty())
+ return true;
+
+#ifdef Q_OS_ANDROID
+ // All libraries are at located at the native libraries folder
+ return true;
+#endif
+
+ const auto appPath = QCoreApplication::applicationDirPath();
+ auto dir = QDir(appPath);
+ while (dir.dirName() != folder) {
+ TestUtils::subFolder.prepend(dir.dirName()).prepend('/');
+ if (!dir.cdUp())
+ return false;
+ }
+ dir.cdUp();
+ TestUtils::rootFolder = dir.absolutePath();
+ return true;
+}
+
+QString findExecutable(const QString &executableName, const QString &folder)
+{
+ const auto path = QStandardPaths::findExecutable(executableName, {TestUtils::rootFolder + folder + TestUtils::subFolder});
+ if (!path.isEmpty()) {
+ return path;
+ }
+
+ qWarning() << "Could not find executable:" << executableName << "in " << TestUtils::rootFolder + folder + TestUtils::subFolder;
+ return QString();
+}
+
+}
+
+#endif // TESTUTILS_H
--- /dev/null
+# Generated from tools.pro.
+
+if(QT_FEATURE_commandlineparser)
+ add_subdirectory(repc)
+endif()
--- /dev/null
+# Generated from repc.pro.
+
+#####################################################################
+## repc Tool:
+#####################################################################
+
+set(REPPARSER_DIR "${PROJECT_SOURCE_DIR}/src/repparser")
+
+qt_get_tool_target_name(target_name repc)
+qt_internal_add_tool(${target_name}
+ TARGET_DESCRIPTION "Qt Remote Objects Compiler"
+ TOOLS_TARGET RemoteObjects # special case
+ INSTALL_DIR "${INSTALL_LIBEXECDIR}"
+ SOURCES
+ cppcodegenerator.cpp cppcodegenerator.h
+ main.cpp
+ repcodegenerator.cpp repcodegenerator.h
+ utils.cpp utils.h
+ DEFINES
+ QT_NO_CAST_FROM_ASCII
+ QT_NO_CAST_FROM_BYTEARRAY
+ QT_NO_CAST_TO_ASCII
+ QT_NO_URL_CAST_FROM_STRING
+ RO_INSTALL_HEADERS=\"$$[QT_INSTALL_HEADERS]/QtRemoteObjects\"
+ INCLUDE_DIRECTORIES
+ ${REPPARSER_DIR}
+ PUBLIC_LIBRARIES
+ Qt::CorePrivate
+)
+qt_internal_return_unless_building_tools()
+
+# QLALR Grammars:
+qt_process_qlalr(
+ ${target_name}
+ "${REPPARSER_DIR}/parser.g"
+ ""
+)
+
+#### Keys ignored in scope 1:.:.:repc.pro:<TRUE>:
+# QMAKE_TARGET_DESCRIPTION = "Qt Remote Objects Compiler"
+# _OPTION = "host_build"
+
+## Scopes:
+#####################################################################
+
+qt_internal_extend_target(${target_name} CONDITION MSVC
+ COMPILE_OPTIONS
+ /wd4129
+)
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <qiodevice.h>
+#include <qjsonarray.h>
+#include <qjsonvalue.h>
+
+#include "cppcodegenerator.h"
+#include "utils.h"
+
+QT_BEGIN_NAMESPACE
+
+CppCodeGenerator::CppCodeGenerator(QIODevice *outputDevice)
+ : m_outputDevice(outputDevice)
+{
+ Q_ASSERT(m_outputDevice);
+}
+
+void CppCodeGenerator::generate(const QJsonArray &classList, bool alwaysGenerateClass /* = false */)
+{
+ for (const QJsonValue cdef : classList)
+ m_outputDevice->write(generateClass(cdef, alwaysGenerateClass));
+
+ m_outputDevice->write("\n");
+}
+
+QT_END_NAMESPACE
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef CPPCODEGENERATOR_H
+#define CPPCODEGENERATOR_H
+
+QT_BEGIN_NAMESPACE
+class QJsonArray;
+class QIODevice;
+
+class CppCodeGenerator
+{
+public:
+ CppCodeGenerator(QIODevice *outputDevice);
+
+ void generate(const QJsonArray &classList, bool alwaysGenerateClass = false);
+
+private:
+ QIODevice *m_outputDevice;
+};
+
+QT_END_NAMESPACE
+
+#endif // CPPCODEGENERATOR_H
--- /dev/null
+// Copyright (C) 2017-2020 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <qcommandlineoption.h>
+#include <qcommandlineparser.h>
+#include <qcoreapplication.h>
+#include <qfileinfo.h>
+#include <qjsondocument.h>
+#include <qjsonobject.h>
+#include <qjsonarray.h>
+
+#include "cppcodegenerator.h"
+#include "repcodegenerator.h"
+#include "repparser.h"
+#include "utils.h"
+
+#include <cstdio>
+
+#define PROGRAM_NAME "repc"
+#define REPC_VERSION "1.0.0"
+
+enum Mode {
+ InRep = 1,
+ InJson = 2,
+ OutRep = 4,
+ OutSource = 8,
+ OutReplica = 16,
+ OutMerged = OutSource | OutReplica
+};
+
+static const QLatin1String REP("rep");
+static const QLatin1String JSON("json");
+static const QLatin1String REPLICA("replica");
+static const QLatin1String SOURCE("source");
+static const QLatin1String MERGED("merged");
+
+QT_USE_NAMESPACE
+
+int main(int argc, char **argv)
+{
+ QCoreApplication app(argc, argv);
+ QCoreApplication::setApplicationVersion(QString::fromLatin1(REPC_VERSION));
+
+ QString outputFile;
+ QString inputFile;
+ int mode = 0;
+ QCommandLineParser parser;
+ parser.setApplicationDescription(QStringLiteral("repc tool v%1 (Qt %2).\n")
+ .arg(QStringLiteral(REPC_VERSION), QString::fromLatin1(QT_VERSION_STR)));
+ parser.addHelpOption();
+ parser.addVersionOption();
+ QCommandLineOption inputTypeOption(QStringLiteral("i"));
+ inputTypeOption.setDescription(QLatin1String("Input file type:\n"
+ "rep: replicant template files.\n"
+ "json: JSON output from moc of a Qt header file."));
+ inputTypeOption.setValueName(QStringLiteral("rep|json"));
+ parser.addOption(inputTypeOption);
+
+ QCommandLineOption outputTypeOption(QStringLiteral("o"));
+ outputTypeOption.setDescription(QLatin1String("Output file type:\n"
+ "source: generates source header. Is incompatible with \"-i src\" option.\n"
+ "replica: generates replica header.\n"
+ "merged: generates combined replica/source header.\n"
+ "rep: generates replicant template file from C++ QOject classes. Is not compatible with \"-i rep\" option."));
+ outputTypeOption.setValueName(QStringLiteral("source|replica|merged|rep"));
+ parser.addOption(outputTypeOption);
+
+ QCommandLineOption includePathOption(QStringLiteral("I"));
+ includePathOption.setDescription(QStringLiteral("Add dir to the include path for header files. This parameter is needed only if the input file type is src (.h file)."));
+ includePathOption.setValueName(QStringLiteral("dir"));
+ parser.addOption(includePathOption);
+
+ QCommandLineOption alwaysClassOption(QStringLiteral("c"));
+ alwaysClassOption.setDescription(QStringLiteral("Always output `class` type for .rep files and never `POD`."));
+ parser.addOption(alwaysClassOption);
+
+ QCommandLineOption debugOption(QStringLiteral("d"));
+ debugOption.setDescription(QStringLiteral("Print out parsing debug information (for troubleshooting)."));
+ parser.addOption(debugOption);
+
+ parser.addPositionalArgument(QStringLiteral("[json-file/rep-file]"),
+ QStringLiteral("Input json/rep file to read from, otherwise stdin."));
+
+ parser.addPositionalArgument(QStringLiteral("[rep-file/header-file]"),
+ QStringLiteral("Output header/rep file to write to, otherwise stdout."));
+
+ parser.process(app.arguments());
+
+ const QStringList files = parser.positionalArguments();
+
+ if (files.size() > 2) {
+ fprintf(stderr, "%s", qPrintable(QLatin1String(PROGRAM_NAME ": Too many input, output files specified: '") + files.join(QStringLiteral("' '")) + QStringLiteral("\'.\n")));
+ parser.showHelp(1);
+ }
+
+ if (parser.isSet(inputTypeOption)) {
+ const QString &inputType = parser.value(inputTypeOption);
+ if (inputType == REP)
+ mode = InRep;
+ else if (inputType == JSON)
+ mode = InJson;
+ else {
+ fprintf(stderr, PROGRAM_NAME ": Unknown input type\"%s\".\n", qPrintable(inputType));
+ parser.showHelp(1);
+ }
+ }
+
+ if (parser.isSet(outputTypeOption)) {
+ const QString &outputType = parser.value(outputTypeOption);
+ if (outputType == REP)
+ mode |= OutRep;
+ else if (outputType == REPLICA)
+ mode |= OutReplica;
+ else if (outputType == SOURCE)
+ mode |= OutSource;
+ else if (outputType == MERGED)
+ mode |= OutMerged;
+ else {
+ fprintf(stderr, PROGRAM_NAME ": Unknown output type\"%s\".\n", qPrintable(outputType));
+ parser.showHelp(1);
+ }
+ }
+
+ switch (files.size()) {
+ case 2:
+ outputFile = files.last();
+ if (!(mode & (OutRep | OutSource | OutReplica))) {
+ // try to figure out the Out mode from file extension
+ if (outputFile.endsWith(QLatin1String(".rep")))
+ mode |= OutRep;
+ }
+ Q_FALLTHROUGH();
+ case 1:
+ inputFile = files.first();
+ if (!(mode & (InRep | InJson))) {
+ // try to figure out the In mode from file extension
+ if (inputFile.endsWith(QLatin1String(".rep")))
+ mode |= InRep;
+ else
+ mode |= InJson;
+ }
+ break;
+ }
+ // check mode sanity
+ if (!(mode & (InRep | InJson))) {
+ fprintf(stderr, PROGRAM_NAME ": Unknown input type, please use -i option to specify one.\n");
+ parser.showHelp(1);
+ }
+ if (!(mode & (OutRep | OutSource | OutReplica))) {
+ fprintf(stderr, PROGRAM_NAME ": Unknown output type, please use -o option to specify one.\n");
+ parser.showHelp(1);
+ }
+ if (mode & InRep && mode & OutRep) {
+ fprintf(stderr, PROGRAM_NAME ": Invalid input/output type combination, both are rep files.\n");
+ parser.showHelp(1);
+ }
+ if (mode & InJson && mode & OutSource) {
+ fprintf(stderr, PROGRAM_NAME ": Invalid input/output type combination, both are source header files.\n");
+ parser.showHelp(1);
+ }
+
+ QFile input;
+ if (inputFile.isEmpty()) {
+ inputFile = QStringLiteral("<stdin>");
+ input.open(stdin, QIODevice::ReadOnly);
+ } else {
+ input.setFileName(inputFile);
+ if (!input.open(QIODevice::ReadOnly)) {
+ fprintf(stderr, PROGRAM_NAME ": %s: No such file.\n", qPrintable(inputFile));
+ return 1;
+ }
+ }
+
+ QFile output;
+ if (outputFile.isEmpty()) {
+ output.open(stdout, QIODevice::WriteOnly);
+ } else {
+ output.setFileName(outputFile);
+ if (!output.open(QIODevice::WriteOnly)) {
+ fprintf(stderr, PROGRAM_NAME ": could not open output file '%s': %s.\n",
+ qPrintable(outputFile), qPrintable(output.errorString()));
+ return 1;
+ }
+ }
+
+ if (mode & InJson) {
+ QJsonDocument doc(QJsonDocument::fromJson(input.readAll()));
+ input.close();
+ if (!doc.isObject()) {
+ fprintf(stderr, PROGRAM_NAME ": Unable to read json input.\n");
+ return 0;
+ }
+
+ QJsonObject json = doc.object();
+
+ if (!json.contains(QLatin1String("classes")) || !json[QLatin1String("classes")].isArray()) {
+ fprintf(stderr, PROGRAM_NAME ": No QObject classes found.\n");
+ return 0;
+ }
+
+ QJsonArray classes = json[QLatin1String("classes")].toArray();
+
+ if (mode & OutRep) {
+ CppCodeGenerator generator(&output);
+ generator.generate(classes, parser.isSet(alwaysClassOption));
+ } else {
+ Q_ASSERT(mode & OutReplica);
+ RepCodeGenerator generator(&output, classList2AST(classes));
+ generator.generate(RepCodeGenerator::REPLICA, outputFile);
+ }
+ } else {
+ Q_ASSERT(!(mode & OutRep));
+ RepParser repparser(input);
+ if (parser.isSet(debugOption))
+ repparser.setDebug();
+ if (!repparser.parse()) {
+ fprintf(stderr, PROGRAM_NAME ": %s:%d: error: %s\n", qPrintable(inputFile), repparser.lineNumber(), qPrintable(repparser.errorString()));
+ // if everything is okay and only the input was malformed => remove the output file
+ // let's not create an empty file -- make sure the build system tries to run repc again
+ // this is the same behavior other code generators exhibit (e.g. flex)
+ output.remove();
+ return 1;
+ }
+
+ input.close();
+
+ RepCodeGenerator generator(&output, repparser.ast());
+ if ((mode & OutMerged) == OutMerged)
+ generator.generate(RepCodeGenerator::MERGED, outputFile);
+ else if (mode & OutReplica)
+ generator.generate(RepCodeGenerator::REPLICA, outputFile);
+ else if (mode & OutSource)
+ generator.generate(RepCodeGenerator::SOURCE, outputFile);
+ else {
+ fprintf(stderr, PROGRAM_NAME ": Unknown mode.\n");
+ return 1;
+ }
+ }
+
+ output.close();
+ return 0;
+}
--- /dev/null
+option(host_build)
+include(moc_copy/moc.pri)
+QT = core-private
+
+DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII QT_NO_CAST_FROM_BYTEARRAY QT_NO_URL_CAST_FROM_STRING
+DEFINES += RO_INSTALL_HEADERS=$$shell_quote(\"$$clean_path($$[QT_INSTALL_HEADERS]/QtRemoteObjects)\")
+msvc: QMAKE_CXXFLAGS += /wd4129
+
+CONFIG += qlalr
+QLALRSOURCES += $$QTRO_SOURCE_TREE/src/repparser/parser.g
+INCLUDEPATH += $$QTRO_SOURCE_TREE/src/repparser
+
+SOURCES += \
+ main.cpp \
+ repcodegenerator.cpp \
+ cppcodegenerator.cpp \
+ utils.cpp
+
+HEADERS += \
+ repcodegenerator.h \
+ cppcodegenerator.h \
+ utils.h
+
+QMAKE_TARGET_DESCRIPTION = "Qt Remote Objects Compiler"
+load(qt_tool)
--- /dev/null
+// Copyright (C) 2017-2020 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "repcodegenerator.h"
+
+#include <QFileInfo>
+#include <QMetaType>
+#include <QCryptographicHash>
+#include <QRegularExpression>
+
+using namespace Qt;
+
+QT_BEGIN_NAMESPACE
+
+template <typename C>
+static int accumulatedSizeOfNames(const C &c)
+{
+ int result = 0;
+ for (const auto &e : c)
+ result += e.name.size();
+ return result;
+}
+
+template <typename C>
+static int accumulatedSizeOfTypes(const C &c)
+{
+ int result = 0;
+ for (const auto &e : c)
+ result += e.type.size();
+ return result;
+}
+
+static QString cap(QString name)
+{
+ if (!name.isEmpty())
+ name[0] = name[0].toUpper();
+ return name;
+}
+
+static bool isClassEnum(const ASTClass &classContext, const QString &typeName)
+{
+ for (const ASTEnum &astEnum : classContext.enums) {
+ if (astEnum.name == typeName) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool hasScopedEnum(const ASTClass &classContext)
+{
+ for (const ASTEnum &astEnum : classContext.enums) {
+ if (astEnum.isScoped) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool hasScopedEnum(const POD &pod)
+{
+ for (const ASTEnum &astEnum : pod.enums) {
+ if (astEnum.isScoped) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static QString fullyQualifiedName(const ASTClass& classContext, const QString &className,
+ const QString &typeName)
+{
+ static const QRegularExpression re = QRegularExpression(QLatin1String("([^<>,\\s]+)"));
+ QString copy = typeName;
+ qsizetype offset = 0;
+ for (const QRegularExpressionMatch &match : re.globalMatch(typeName)) {
+ if (isClassEnum(classContext, match.captured(1))) {
+ copy.insert(match.capturedStart(1) + offset, className + QStringLiteral("::"));
+ offset += className.size() + 2;
+ }
+ }
+ return copy;
+}
+
+// for enums we need to transform signal/slot arguments to include the class scope
+static QList<ASTFunction> transformEnumParams(const ASTClass& classContext,
+ const QList<ASTFunction> &methodList,
+ const QString &typeName) {
+ QList<ASTFunction> localList = methodList;
+ for (ASTFunction &astFunction : localList) {
+ for (ASTDeclaration &astParam : astFunction.params) {
+ for (const ASTEnum &astEnum : classContext.enums) {
+ if (astEnum.name == astParam.type) {
+ astParam.type = typeName + QStringLiteral("::") + astParam.type;
+ }
+ }
+ }
+ }
+ return localList;
+}
+
+/*
+ Returns \c true if the type is a built-in type.
+*/
+static bool isBuiltinType(const QString &type)
+ {
+ const auto metaType = QMetaType::fromName(type.toLatin1().constData());
+ if (!metaType.isValid())
+ return false;
+ return (metaType.id() < QMetaType::User);
+}
+
+RepCodeGenerator::RepCodeGenerator(QIODevice *outputDevice, const AST &ast)
+ : m_stream(outputDevice), m_ast(ast)
+{
+}
+
+QByteArray RepCodeGenerator::classSignature(const ASTClass &ac)
+{
+ return m_ast.typeSignatures[ac.name];
+}
+
+void RepCodeGenerator::generate(Mode mode, QString fileName)
+{
+ if (fileName.isEmpty())
+ m_stream << "#pragma once" << Qt::endl << Qt::endl;
+ else {
+ fileName = QFileInfo(fileName).fileName();
+ fileName = fileName.toUpper();
+ fileName.replace(QLatin1Char('.'), QLatin1Char('_'));
+ m_stream << "#ifndef " << fileName << Qt::endl;
+ m_stream << "#define " << fileName << Qt::endl << Qt::endl;
+ }
+
+ generateHeader(mode);
+ for (const ASTEnum &en : m_ast.enums)
+ generateEnumGadget(en, QStringLiteral("%1Enum").arg(en.name));
+ for (const POD &pod : m_ast.pods)
+ generatePOD(pod);
+
+ QSet<QString> metaTypes;
+ QSet<QString> enumTypes;
+ for (const POD &pod : m_ast.pods) {
+ metaTypes << pod.name;
+ // We register from within the code generated for classes, not PODs
+ // Thus, for enums/flags in PODs, we need the to prefix with the POD
+ // name. The enumTypes set is used to make sure we don't try to
+ // register the non-prefixed name if it is used as a member variable
+ // type.
+ for (const ASTEnum &en : pod.enums) {
+ metaTypes << QLatin1String("%1::%2").arg(pod.name, en.name);
+ enumTypes << en.name;
+ }
+ for (const ASTFlag &flag : pod.flags) {
+ metaTypes << QLatin1String("%1::%2").arg(pod.name, flag.name);
+ enumTypes << flag.name;
+ }
+ for (const PODAttribute &attribute : pod.attributes) {
+ if (!enumTypes.contains(attribute.type))
+ metaTypes << attribute.type;
+ }
+ }
+ const QString metaTypeRegistrationCode = generateMetaTypeRegistration(metaTypes);
+
+ for (const ASTClass &astClass : m_ast.classes) {
+ QSet<QString> classMetaTypes;
+ QSet<QString> pendingMetaTypes;
+ for (const ASTEnum &en : astClass.enums)
+ classMetaTypes << en.name;
+ for (const ASTProperty &property : astClass.properties) {
+ if (property.isPointer)
+ continue;
+ classMetaTypes << property.type;
+ }
+ const auto extractClassMetaTypes = [&](const ASTFunction &function) {
+ classMetaTypes << function.returnType;
+ pendingMetaTypes << function.returnType;
+ for (const ASTDeclaration &decl : function.params) {
+ classMetaTypes << decl.type;
+
+ // Collect types packaged by Qt containers, to register their metatypes if needed
+ QRegularExpression re(
+ QStringLiteral("(QList|QMap|QHash)<\\s*([\\w]+)\\s*(,\\s*([\\w]+))?\\s*>"));
+ QRegularExpressionMatch m = re.match(decl.type);
+ if (m.hasMatch()) {
+ if (auto captured = m.captured(2);
+ !captured.isNull() && !metaTypes.contains(captured)) {
+ classMetaTypes << captured;
+ }
+ if (auto captured = m.captured(4);
+ !captured.isNull() && !metaTypes.contains(captured)) {
+ classMetaTypes << captured;
+ }
+ }
+ }
+ };
+ for (const ASTFunction &function : astClass.signalsList)
+ extractClassMetaTypes(function);
+ for (const ASTFunction &function : astClass.slotsList)
+ extractClassMetaTypes(function);
+
+ const QString classMetaTypeRegistrationCode = metaTypeRegistrationCode
+ + generateMetaTypeRegistration(classMetaTypes);
+ const QString replicaMetaTypeRegistrationCode = classMetaTypeRegistrationCode
+ + generateMetaTypeRegistrationForPending(pendingMetaTypes);
+
+ if (mode == MERGED) {
+ generateClass(REPLICA, astClass, replicaMetaTypeRegistrationCode);
+ generateClass(SOURCE, astClass, classMetaTypeRegistrationCode);
+ generateClass(SIMPLE_SOURCE, astClass, classMetaTypeRegistrationCode);
+ generateSourceAPI(astClass);
+ } else {
+ generateClass(mode, astClass, mode == REPLICA ? replicaMetaTypeRegistrationCode
+ : classMetaTypeRegistrationCode);
+ if (mode == SOURCE) {
+ generateClass(SIMPLE_SOURCE, astClass, classMetaTypeRegistrationCode);
+ generateSourceAPI(astClass);
+ }
+ }
+ }
+
+ m_stream << Qt::endl;
+ if (!fileName.isEmpty())
+ m_stream << "#endif // " << fileName << Qt::endl;
+}
+
+void RepCodeGenerator::generateHeader(Mode mode)
+{
+ m_stream <<
+ "// This is an autogenerated file.\n"
+ "// Do not edit this file, any changes made will be lost the next time it is generated.\n"
+ "\n"
+ "#include <QtCore/qobject.h>\n"
+ "#include <QtCore/qdatastream.h>\n"
+ "#include <QtCore/qvariant.h>\n"
+ "#include <QtCore/qmap.h>\n"
+ "#include <QtCore/qmetatype.h>\n";
+ bool hasModel = false;
+ for (auto c : m_ast.classes)
+ {
+ if (c.modelMetadata.size() > 0)
+ {
+ hasModel = true;
+ break;
+ }
+ }
+ if (hasModel)
+ m_stream << "#include <QtCore/qabstractitemmodel.h>\n";
+ m_stream << "\n"
+ "#include <QtRemoteObjects/qremoteobjectnode.h>\n";
+
+ if (mode == MERGED) {
+ m_stream << "#include <QtRemoteObjects/qremoteobjectpendingcall.h>\n";
+ m_stream << "#include <QtRemoteObjects/qremoteobjectreplica.h>\n";
+ m_stream << "#include <QtRemoteObjects/qremoteobjectsource.h>\n";
+ if (hasModel)
+ m_stream << "#include <QtRemoteObjects/qremoteobjectabstractitemmodelreplica.h>\n";
+ } else if (mode == REPLICA) {
+ m_stream << "#include <QtRemoteObjects/qremoteobjectpendingcall.h>\n";
+ m_stream << "#include <QtRemoteObjects/qremoteobjectreplica.h>\n";
+ if (hasModel)
+ m_stream << "#include <QtRemoteObjects/qremoteobjectabstractitemmodelreplica.h>\n";
+ } else
+ m_stream << "#include <QtRemoteObjects/qremoteobjectsource.h>\n";
+ m_stream << "\n";
+
+ m_stream << m_ast.preprocessorDirectives.join(QLatin1Char('\n'));
+ m_stream << "\n";
+}
+
+static QString formatTemplateStringArgTypeNameCapitalizedName(int numberOfTypeOccurrences,
+ int numberOfNameOccurrences,
+ QString templateString,
+ const POD &pod)
+{
+ QString out;
+ const int LengthOfPlaceholderText = 2;
+ Q_ASSERT(templateString.count(QRegularExpression(QStringLiteral("%\\d")))
+ == numberOfNameOccurrences + numberOfTypeOccurrences);
+ const auto expectedOutSize
+ = numberOfNameOccurrences * accumulatedSizeOfNames(pod.attributes)
+ + numberOfTypeOccurrences * accumulatedSizeOfTypes(pod.attributes)
+ + pod.attributes.size() * (templateString.size()
+ - (numberOfNameOccurrences + numberOfTypeOccurrences)
+ * LengthOfPlaceholderText);
+ out.reserve(expectedOutSize);
+ for (const PODAttribute &a : pod.attributes)
+ out += templateString.arg(a.type, a.name, cap(a.name));
+ return out;
+}
+
+QString RepCodeGenerator::formatQPropertyDeclarations(const POD &pod)
+{
+ QString prop = QStringLiteral(" Q_PROPERTY(%1 %2 READ %2 WRITE set%3)\n");
+ return formatTemplateStringArgTypeNameCapitalizedName(1, 3, prop, pod);
+}
+
+QString RepCodeGenerator::formatConstructors(const POD &pod)
+{
+ QString initializerString = QStringLiteral(": ");
+ QString defaultInitializerString = initializerString;
+ QString argString;
+ for (const PODAttribute &a : pod.attributes) {
+ initializerString += QString::fromLatin1("m_%1(%1), ").arg(a.name);
+ defaultInitializerString += QString::fromLatin1("m_%1(), ").arg(a.name);
+ argString += QString::fromLatin1("%1 %2, ").arg(a.type, a.name);
+ }
+ argString.chop(2);
+ initializerString.chop(2);
+ defaultInitializerString.chop(2);
+
+ return QString::fromLatin1(" %1() %2 {}\n"
+ " explicit %1(%3) %4 {}\n")
+ .arg(pod.name, defaultInitializerString, argString, initializerString);
+}
+
+QString RepCodeGenerator::formatPropertyGettersAndSetters(const POD &pod)
+{
+ QString templateString
+ = QString::fromLatin1(" %1 %2() const { return m_%2; }\n"
+ " void set%3(%1 %2) { if (%2 != m_%2) { m_%2 = %2; } }\n");
+ return formatTemplateStringArgTypeNameCapitalizedName(2, 8, qMove(templateString), pod);
+}
+
+QString RepCodeGenerator::formatDataMembers(const POD &pod)
+{
+ QString out;
+ const QString prefix = QStringLiteral(" ");
+ const QString infix = QStringLiteral(" m_");
+ const QString suffix = QStringLiteral(";\n");
+ const auto expectedOutSize
+ = accumulatedSizeOfNames(pod.attributes)
+ + accumulatedSizeOfTypes(pod.attributes)
+ + pod.attributes.size() * (prefix.size() + infix.size() + suffix.size());
+ out.reserve(expectedOutSize);
+ for (const PODAttribute &a : pod.attributes) {
+ out += prefix;
+ out += a.type;
+ out += infix;
+ out += a.name;
+ out += suffix;
+ }
+ Q_ASSERT(out.size() == expectedOutSize);
+ return out;
+}
+
+QString RepCodeGenerator::formatDebugOperator(const POD &pod)
+{
+ QString props;
+ int count = 0;
+ for (const PODAttribute &attribute : pod.attributes) {
+ if (count++ > 0)
+ props.append(QLatin1String(" << \", \""));
+ props.append(QLatin1String(" << \"%1: \" << obj.%1()").arg(attribute.name));
+ }
+
+ return QLatin1String("inline QDebug operator<<(QDebug dbg, const %1 &obj) {\n" \
+ " dbg.nospace() << \"%1(\" %2 << \")\";\n" \
+ " return dbg.maybeSpace();\n}\n\n").arg(pod.name, props);
+}
+
+QString RepCodeGenerator::formatMarshallingOperators(const POD &pod)
+{
+ return QLatin1String("inline QDataStream &operator<<(QDataStream &ds, const ") + pod.name
+ + QLatin1String(" &obj) {\n"
+ " QtRemoteObjects::copyStoredProperties(&obj, ds);\n"
+ " return ds;\n"
+ "}\n"
+ "\n"
+ "inline QDataStream &operator>>(QDataStream &ds, ") + pod.name
+ + QLatin1String(" &obj) {\n"
+ " QtRemoteObjects::copyStoredProperties(ds, &obj);\n"
+ " return ds;\n"
+ "}\n")
+ ;
+}
+
+QString RepCodeGenerator::typeForMode(const ASTProperty &property, RepCodeGenerator::Mode mode)
+{
+ if (!property.isPointer)
+ return property.type;
+
+ if (property.type.startsWith(QStringLiteral("QAbstractItemModel")))
+ return mode == REPLICA ? property.type + QStringLiteral("Replica*")
+ : property.type + QStringLiteral("*");
+
+ switch (mode) {
+ case REPLICA: return property.type + QStringLiteral("Replica*");
+ case SIMPLE_SOURCE:
+ Q_FALLTHROUGH();
+ case SOURCE: return property.type + QStringLiteral("Source*");
+ default: qCritical("Invalid mode");
+ }
+
+ return QStringLiteral("InvalidPropertyName");
+}
+
+void RepCodeGenerator::generateSimpleSetter(const ASTProperty &property, bool generateOverride)
+{
+ if (!generateOverride)
+ m_stream << " virtual ";
+ else
+ m_stream << " ";
+ m_stream << "void set" << cap(property.name) << "(" << typeForMode(property, SIMPLE_SOURCE)
+ << " " << property.name << ")";
+ if (generateOverride)
+ m_stream << " override";
+ m_stream << Qt::endl;
+ m_stream << " {" << Qt::endl;
+ m_stream << " if (" << property.name << " != m_" << property.name << ") {" << Qt::endl;
+ m_stream << " m_" << property.name << " = " << property.name << ";" << Qt::endl;
+ m_stream << " Q_EMIT " << property.name << "Changed(m_" << property.name << ");"
+ << Qt::endl;
+ m_stream << " }" << Qt::endl;
+ m_stream << " }" << Qt::endl;
+}
+
+void RepCodeGenerator::generatePOD(const POD &pod)
+{
+ QStringList equalityCheck;
+ for (const PODAttribute &attr : pod.attributes)
+ equalityCheck << QStringLiteral("left.%1() == right.%1()").arg(attr.name);
+ m_stream << "class " << pod.name << "\n"
+ "{\n"
+ " Q_GADGET\n"
+ << "\n"
+ << formatQPropertyDeclarations(pod);
+ if (hasScopedEnum(pod)) // See https://bugreports.qt.io/browse/QTBUG-73360
+ m_stream << " Q_CLASSINFO(\"RegisterEnumClassesUnscoped\", \"false\")\n";
+ m_stream << "public:\n";
+ generateDeclarationsForEnums(pod.enums);
+ for (auto &flag : pod.flags) {
+ m_stream << " Q_DECLARE_FLAGS(" << flag.name << ", " << flag._enum << ")\n";
+ m_stream << " Q_FLAG(" << flag.name << ")\n";
+ }
+ m_stream << formatConstructors(pod)
+ << formatPropertyGettersAndSetters(pod)
+ << "private:\n"
+ << formatDataMembers(pod)
+ << "};\n"
+ << "\n"
+ << "inline bool operator==(const " << pod.name << " &left, const " << pod.name <<
+ " &right) Q_DECL_NOTHROW {\n"
+ << " return " << equalityCheck.join(QStringLiteral(" && ")) << ";\n"
+ << "}\n"
+ << "inline bool operator!=(const " << pod.name << " &left, const " << pod.name <<
+ " &right) Q_DECL_NOTHROW {\n"
+ << " return !(left == right);\n"
+ << "}\n"
+ << "\n"
+ << formatDebugOperator(pod)
+ << formatMarshallingOperators(pod);
+ for (auto &flag : pod.flags)
+ m_stream << "Q_DECLARE_OPERATORS_FOR_FLAGS(" << pod.name << "::" << flag.name << ")\n";
+ m_stream << "\n";
+}
+
+QString getEnumType(const ASTEnum &en)
+{
+ if (!en.type.isEmpty())
+ return en.type;
+ if (en.isSigned) {
+ if (en.max < 0x7F)
+ return QStringLiteral("qint8");
+ if (en.max < 0x7FFF)
+ return QStringLiteral("qint16");
+ return QStringLiteral("qint32");
+ } else {
+ if (en.max < 0xFF)
+ return QStringLiteral("quint8");
+ if (en.max < 0xFFFF)
+ return QStringLiteral("quint16");
+ return QStringLiteral("quint32");
+ }
+}
+
+void RepCodeGenerator::generateDeclarationsForEnums(const QList<ASTEnum> &enums,
+ bool generateQENUM)
+{
+ if (!generateQENUM) {
+ m_stream << " // You need to add this enum as well as Q_ENUM to your" << Qt::endl;
+ m_stream << " // QObject class in order to use .rep enums over QtRO for" << Qt::endl;
+ m_stream << " // non-repc generated QObjects." << Qt::endl;
+ }
+
+ for (const ASTEnum &en : enums) {
+ m_stream << " enum " << (en.isScoped ? "class " : "") << en.name
+ << (en.type.isEmpty() ? "" : " : ") << en.type << " {\n";
+ for (const ASTEnumParam &p : en.params)
+ m_stream << " " << p.name << " = " << p.value << ",\n";
+
+ m_stream << " };\n";
+
+ if (generateQENUM)
+ m_stream << " Q_ENUM(" << en.name << ")\n";
+ }
+}
+
+void RepCodeGenerator::generateEnumGadget(const ASTEnum &en, const QString &className)
+{
+ m_stream << "class " << className << "\n"
+ "{\n"
+ " Q_GADGET\n";
+ if (en.isScoped)
+ m_stream << " Q_CLASSINFO(\"RegisterEnumClassesUnscoped\", \"false\")\n";
+ m_stream << " " << className << "();\n"
+ "\n"
+ "public:\n";
+
+ auto enums = QList<ASTEnum>() << en;
+ generateDeclarationsForEnums(enums);
+ if (en.flagIndex >= 0) {
+ auto flag = m_ast.flags.at(en.flagIndex);
+ m_stream << " Q_DECLARE_FLAGS(" << flag.name << ", " << flag._enum << ")\n";
+ m_stream << " Q_FLAG(" << flag.name << ")\n";
+ m_stream << "};\n\n";
+ m_stream << "Q_DECLARE_OPERATORS_FOR_FLAGS(" << className << "::" << flag.name << ")\n\n";
+ } else {
+ m_stream << "};\n\n";
+ }
+}
+
+QString RepCodeGenerator::generateMetaTypeRegistration(const QSet<QString> &metaTypes)
+{
+ QString out;
+ const QString qRegisterMetaType = QStringLiteral(" qRegisterMetaType<");
+ const QString lineEnding = QStringLiteral(">();\n");
+ for (const QString &metaType : metaTypes) {
+ if (isBuiltinType(metaType))
+ continue;
+
+ out += qRegisterMetaType;
+ out += metaType;
+ out += lineEnding;
+ }
+ return out;
+}
+
+QString RepCodeGenerator::generateMetaTypeRegistrationForPending(const QSet<QString> &metaTypes)
+{
+ QString out;
+ if (!metaTypes.isEmpty())
+ out += QLatin1String(" qRegisterMetaType<QRemoteObjectPendingCall>();\n");
+ const QString qRegisterMetaType =
+ QStringLiteral(" qRegisterMetaType<QRemoteObjectPendingReply<%1>>();\n");
+ const QString qRegisterConverterConditional =
+ QStringLiteral(" if (!QMetaType::hasRegisteredConverterFunction<"
+ "QRemoteObjectPendingReply<%1>, QRemoteObjectPendingCall>())\n");
+ const QString qRegisterConverter =
+ QStringLiteral(" QMetaType::registerConverter<QRemoteObjectPendingReply<%1>"
+ ", QRemoteObjectPendingCall>();\n");
+ for (const QString &metaType : metaTypes) {
+ out += qRegisterMetaType.arg(metaType);
+ out += qRegisterConverterConditional.arg(metaType);
+ out += qRegisterConverter.arg(metaType);
+ }
+ return out;
+}
+
+void RepCodeGenerator::generateClass(Mode mode, const ASTClass &astClass,
+ const QString &metaTypeRegistrationCode)
+{
+ const QString className = (astClass.name + (mode == REPLICA ?
+ QStringLiteral("Replica") : mode == SOURCE ?
+ QStringLiteral("Source") : QStringLiteral("SimpleSource")));
+ if (mode == REPLICA)
+ m_stream << "class " << className << " : public QRemoteObjectReplica" << Qt::endl;
+ else if (mode == SIMPLE_SOURCE)
+ m_stream << "class " << className << " : public " << astClass.name << "Source"
+ << Qt::endl;
+ else
+ m_stream << "class " << className << " : public QObject" << Qt::endl;
+
+ m_stream << "{\n";
+ m_stream << " Q_OBJECT\n";
+ if (hasScopedEnum(astClass)) // See https://bugreports.qt.io/browse/QTBUG-73360
+ m_stream << " Q_CLASSINFO(\"RegisterEnumClassesUnscoped\", \"false\")\n";
+ if (mode != SIMPLE_SOURCE) {
+ m_stream << " Q_CLASSINFO(QCLASSINFO_REMOTEOBJECT_TYPE, \"" << astClass.name
+ << "\")" << Qt::endl;
+ m_stream << " Q_CLASSINFO(QCLASSINFO_REMOTEOBJECT_SIGNATURE, \""
+ << QLatin1String(classSignature(astClass)) << "\")" << Qt::endl;
+ for (int i = 0; i < astClass.modelMetadata.size(); i++) {
+ const auto model = astClass.modelMetadata.at(i);
+ const auto modelName = astClass.properties.at(model.propertyIndex).name;
+ if (!model.roles.isEmpty()) {
+ QStringList list;
+ for (auto role : model.roles)
+ list << role.name;
+ m_stream << QString::fromLatin1(" Q_CLASSINFO(\"%1_ROLES\", \"%2\")")
+ .arg(modelName.toUpper(), list.join(QChar::fromLatin1('|')))
+ << Qt::endl;
+ }
+ }
+
+
+ //First output properties
+ for (const ASTProperty &property : astClass.properties) {
+ m_stream << " Q_PROPERTY(" << typeForMode(property, mode) << " " << property.name
+ << " READ " << property.name;
+ if (property.modifier == ASTProperty::Constant) {
+ if (mode == REPLICA) // We still need to notify when we get the initial value
+ m_stream << " NOTIFY " << property.name << "Changed";
+ else
+ m_stream << " CONSTANT";
+ } else if (property.modifier == ASTProperty::ReadOnly)
+ m_stream << " NOTIFY " << property.name << "Changed";
+ else if (property.modifier == ASTProperty::ReadWrite)
+ m_stream << " WRITE set" << cap(property.name) << " NOTIFY " << property.name
+ << "Changed";
+ else if (property.modifier == ASTProperty::ReadPush ||
+ property.modifier == ASTProperty::SourceOnlySetter) {
+ if (mode == REPLICA) // The setter slot isn't known to the PROP
+ m_stream << " NOTIFY " << property.name << "Changed";
+ else // The Source can use the setter, since non-asynchronous
+ m_stream << " WRITE set" << cap(property.name) << " NOTIFY "
+ << property.name << "Changed";
+ }
+ m_stream << ")" << Qt::endl;
+ }
+
+ if (!astClass.enums.isEmpty()) {
+ m_stream << "" << Qt::endl;
+ m_stream << "public:" << Qt::endl;
+ generateDeclarationsForEnums(astClass.enums);
+ for (const auto &flag : astClass.flags) {
+ m_stream << " Q_DECLARE_FLAGS(" << flag.name << ", " << flag._enum << ")\n";
+ m_stream << " Q_FLAG(" << flag.name << ")\n";
+ }
+ }
+ }
+
+ m_stream << "" << Qt::endl;
+ m_stream << "public:" << Qt::endl;
+
+ if (mode == REPLICA) {
+ m_stream << " " << className << "() : QRemoteObjectReplica() { initialize(); }"
+ << Qt::endl;
+ m_stream << " static void registerMetatypes()" << Qt::endl;
+ m_stream << " {" << Qt::endl;
+ m_stream << " static bool initialized = false;" << Qt::endl;
+ m_stream << " if (initialized)" << Qt::endl;
+ m_stream << " return;" << Qt::endl;
+ m_stream << " initialized = true;" << Qt::endl;
+
+ if (!metaTypeRegistrationCode.isEmpty())
+ m_stream << metaTypeRegistrationCode << Qt::endl;
+
+ m_stream << " }" << Qt::endl;
+
+ if (astClass.hasPointerObjects())
+ {
+ m_stream << " void setNode(QRemoteObjectNode *node) override" << Qt::endl;
+ m_stream << " {" << Qt::endl;
+ m_stream << " QRemoteObjectReplica::setNode(node);" << Qt::endl;
+ for (int index = 0; index < astClass.properties.size(); ++index) {
+ const ASTProperty &property = astClass.properties.at(index);
+ if (!property.isPointer)
+ continue;
+ const QString acquireName = astClass.name + QLatin1String("::") + property.name;
+ if (astClass.subClassPropertyIndices.contains(index))
+ m_stream << QString::fromLatin1(" setChild(%1, QVariant::fromValue("
+ "node->acquire<%2Replica>(QRemoteObjectStringLiterals::CLASS()"
+ ".arg(\"%3\"))));")
+ .arg(QString::number(index), property.type, acquireName)
+ << Qt::endl;
+ else
+ m_stream << QString::fromLatin1(" setChild(%1, QVariant::fromValue("
+ "node->acquireModel(QRemoteObjectStringLiterals::MODEL()"
+ ".arg(\"%2\"))));")
+ .arg(QString::number(index), acquireName) << Qt::endl;
+ m_stream << " Q_EMIT " << property.name << "Changed(" << property.name
+ << "()" << ");" << Qt::endl;
+
+ }
+ m_stream << " }" << Qt::endl;
+ }
+ m_stream << "" << Qt::endl;
+ m_stream << "private:" << Qt::endl;
+ m_stream << " " << className
+ << "(QRemoteObjectNode *node, const QString &name = QString())" << Qt::endl;
+ m_stream << " : QRemoteObjectReplica(ConstructWithNode)" << Qt::endl;
+ m_stream << " {" << Qt::endl;
+ m_stream << " initializeNode(node, name);" << Qt::endl;
+ for (int index = 0; index < astClass.properties.size(); ++index) {
+ const ASTProperty &property = astClass.properties.at(index);
+ if (!property.isPointer)
+ continue;
+ const QString acquireName = astClass.name + QLatin1String("::") + property.name;
+ if (astClass.subClassPropertyIndices.contains(index))
+ m_stream << QString::fromLatin1(" setChild(%1, QVariant::fromValue("
+ "node->acquire<%2Replica>(QRemoteObjectStringLiterals::CLASS()"
+ ".arg(\"%3\"))));")
+ .arg(QString::number(index), property.type, acquireName) << Qt::endl;
+ else
+ m_stream << QString::fromLatin1(" setChild(%1, QVariant::fromValue("
+ "node->acquireModel(QRemoteObjectStringLiterals::MODEL()"
+ ".arg(\"%2\"))));")
+ .arg(QString::number(index), acquireName) << Qt::endl;
+ }
+ m_stream << " }" << Qt::endl;
+
+ m_stream << "" << Qt::endl;
+
+ m_stream << " void initialize() override" << Qt::endl;
+ m_stream << " {" << Qt::endl;
+ m_stream << " " << className << "::registerMetatypes();" << Qt::endl;
+ m_stream << " QVariantList properties;" << Qt::endl;
+ m_stream << " properties.reserve(" << astClass.properties.size() << ");"
+ << Qt::endl;
+ for (const ASTProperty &property : astClass.properties) {
+ if (property.isPointer)
+ m_stream << " properties << QVariant::fromValue(("
+ << typeForMode(property, mode) << ")" << property.defaultValue
+ << ");" << Qt::endl;
+ else
+ m_stream << " properties << QVariant::fromValue("
+ << typeForMode(property, mode) << "(" << property.defaultValue
+ << "));" << Qt::endl;
+ }
+ int nPersisted = 0;
+ if (astClass.hasPersisted) {
+ m_stream << " QVariantList stored = retrieveProperties(QStringLiteral(\""
+ << astClass.name << "\"), \"" << classSignature(astClass) << "\");"
+ << Qt::endl;
+ m_stream << " if (!stored.isEmpty()) {" << Qt::endl;
+ for (int i = 0; i < astClass.properties.size(); i++) {
+ if (astClass.properties.at(i).persisted) {
+ m_stream << " properties[" << i << "] = stored.at(" << nPersisted
+ << ");" << Qt::endl;
+ nPersisted++;
+ }
+ }
+ m_stream << " }" << Qt::endl;
+ }
+ m_stream << " setProperties(std::move(properties));" << Qt::endl;
+ m_stream << " }" << Qt::endl;
+ } else if (mode == SOURCE) {
+ m_stream << " explicit " << className
+ << "(QObject *parent = nullptr) : QObject(parent)" << Qt::endl;
+ m_stream << " {" << Qt::endl;
+ if (!metaTypeRegistrationCode.isEmpty())
+ m_stream << metaTypeRegistrationCode << Qt::endl;
+ m_stream << " }" << Qt::endl;
+ } else {
+ QList<int> constIndices;
+ for (int index = 0; index < astClass.properties.size(); ++index) {
+ const ASTProperty &property = astClass.properties.at(index);
+ if (property.modifier == ASTProperty::Constant)
+ constIndices.append(index);
+ }
+ if (constIndices.isEmpty()) {
+ m_stream << " explicit " << className << "(QObject *parent = nullptr) : "
+ << astClass.name << "Source(parent)" << Qt::endl;
+ } else {
+ QStringList parameters;
+ for (int index : constIndices) {
+ const ASTProperty &property = astClass.properties.at(index);
+ parameters.append(QString::fromLatin1("%1 %2 = %3")
+ .arg(typeForMode(property, SOURCE), property.name,
+ property.defaultValue));
+ }
+ parameters.append(QStringLiteral("QObject *parent = nullptr"));
+ m_stream << " explicit " << className << "("
+ << parameters.join(QStringLiteral(", ")) << ") : " << astClass.name
+ << "Source(parent)" << Qt::endl;
+ }
+ for (const ASTProperty &property : astClass.properties) {
+ if (property.modifier == ASTProperty::Constant)
+ m_stream << " , m_" << property.name << "(" << property.name << ")"
+ << Qt::endl;
+ else
+ m_stream << " , m_" << property.name << "(" << property.defaultValue << ")"
+ << Qt::endl;
+ }
+ m_stream << " {" << Qt::endl;
+ m_stream << " }" << Qt::endl;
+ }
+
+ m_stream << "" << Qt::endl;
+ m_stream << "public:" << Qt::endl;
+
+ if (mode == REPLICA && astClass.hasPersisted) {
+ m_stream << " ~" << className << "() override {" << Qt::endl;
+ m_stream << " QVariantList persisted;" << Qt::endl;
+ for (int i = 0; i < astClass.properties.size(); i++) {
+ if (astClass.properties.at(i).persisted) {
+ m_stream << " persisted << propAsVariant(" << i << ");" << Qt::endl;
+ }
+ }
+ m_stream << " persistProperties(QStringLiteral(\"" << astClass.name << "\"), \""
+ << classSignature(astClass) << "\", persisted);" << Qt::endl;
+ m_stream << " }" << Qt::endl;
+ } else {
+ m_stream << " ~" << className << "() override = default;" << Qt::endl;
+ }
+ m_stream << "" << Qt::endl;
+
+ //Next output getter/setter
+ if (mode == REPLICA) {
+ int i = 0;
+ for (const ASTProperty &property : astClass.properties) {
+ auto type = typeForMode(property, mode);
+ if (type == QLatin1String("QVariant")) {
+ m_stream << " " << type << " " << property.name << "() const" << Qt::endl;
+ m_stream << " {" << Qt::endl;
+ m_stream << " return propAsVariant(" << i << ");" << Qt::endl;
+ m_stream << " }" << Qt::endl;
+ } else {
+ m_stream << " " << type << " " << property.name << "() const" << Qt::endl;
+ m_stream << " {" << Qt::endl;
+ m_stream << " const QVariant variant = propAsVariant(" << i << ");"
+ << Qt::endl;
+ m_stream << " if (!variant.canConvert<" << type << ">()) {" << Qt::endl;
+ m_stream << " qWarning() << \"QtRO cannot convert the property "
+ << property.name << " to type " << type << "\";" << Qt::endl;
+ m_stream << " }" << Qt::endl;
+ m_stream << " return variant.value<" << type << " >();" << Qt::endl;
+ m_stream << " }" << Qt::endl;
+ }
+ i++;
+ if (property.modifier == ASTProperty::ReadWrite) {
+ m_stream << "" << Qt::endl;
+ m_stream << " void set" << cap(property.name) << "(" << property.type << " "
+ << property.name << ")" << Qt::endl;
+ m_stream << " {" << Qt::endl;
+ m_stream << " static int __repc_index = " << className
+ << "::staticMetaObject.indexOfProperty(\"" << property.name << "\");"
+ << Qt::endl;
+ m_stream << " QVariantList __repc_args;" << Qt::endl;
+ m_stream << " __repc_args << QVariant::fromValue(" << property.name << ");"
+ << Qt::endl;
+ m_stream << " send(QMetaObject::WriteProperty, __repc_index, __repc_args);"
+ << Qt::endl;
+ m_stream << " }" << Qt::endl;
+ }
+ m_stream << "" << Qt::endl;
+ }
+ } else if (mode == SOURCE) {
+ for (const ASTProperty &property : astClass.properties)
+ m_stream << " virtual " << typeForMode(property, mode) << " " << property.name
+ << "() const = 0;" << Qt::endl;
+ for (const ASTProperty &property : astClass.properties) {
+ if (property.modifier == ASTProperty::ReadWrite ||
+ property.modifier == ASTProperty::ReadPush ||
+ property.modifier == ASTProperty::SourceOnlySetter)
+ m_stream << " virtual void set" << cap(property.name) << "("
+ << typeForMode(property, mode) << " " << property.name << ") = 0;"
+ << Qt::endl;
+ }
+ } else {
+ for (const ASTProperty &property : astClass.properties)
+ m_stream << " " << typeForMode(property, mode) << " " << property.name
+ << "() const override { return m_"
+ << property.name << "; }" << Qt::endl;
+ for (const ASTProperty &property : astClass.properties) {
+ if (property.modifier == ASTProperty::ReadWrite ||
+ property.modifier == ASTProperty::ReadPush ||
+ property.modifier == ASTProperty::SourceOnlySetter) {
+ generateSimpleSetter(property);
+ }
+ }
+ }
+
+ if (mode != SIMPLE_SOURCE) {
+ //Next output property signals
+ if (!astClass.properties.isEmpty() || !astClass.signalsList.isEmpty()) {
+ m_stream << "" << Qt::endl;
+ m_stream << "Q_SIGNALS:" << Qt::endl;
+ for (const ASTProperty &property : astClass.properties) {
+ if (property.modifier != ASTProperty::Constant)
+ m_stream
+ << " void " << property.name << "Changed("
+ << fullyQualifiedName(astClass, className, typeForMode(property, mode))
+ << " " << property.name << ");" << Qt::endl;
+ }
+
+ const auto signalsList = transformEnumParams(astClass, astClass.signalsList,
+ className);
+ for (const ASTFunction &signal : signalsList)
+ m_stream << " void " << signal.name << "(" << signal.paramsAsString() << ");"
+ << Qt::endl;
+
+ // CONSTANT source properties still need an onChanged signal on the Replica side to
+ // update (once) when the value is initialized. Put these last, so they don't mess
+ // up the signal index order
+ for (const ASTProperty &property : astClass.properties) {
+ if (mode == REPLICA && property.modifier == ASTProperty::Constant)
+ m_stream
+ << " void " << property.name << "Changed("
+ << fullyQualifiedName(astClass, className, typeForMode(property, mode))
+ << " " << property.name << ");" << Qt::endl;
+ }
+ }
+ bool hasWriteSlots = false;
+ for (const ASTProperty &property : astClass.properties) {
+ if (property.modifier == ASTProperty::ReadPush) {
+ hasWriteSlots = true;
+ break;
+ }
+ }
+ if (hasWriteSlots || !astClass.slotsList.isEmpty()) {
+ m_stream << "" << Qt::endl;
+ m_stream << "public Q_SLOTS:" << Qt::endl;
+ for (const ASTProperty &property : astClass.properties) {
+ if (property.modifier == ASTProperty::ReadPush) {
+ const auto type = fullyQualifiedName(astClass, className, property.type);
+ if (mode != REPLICA) {
+ m_stream << " virtual void push" << cap(property.name) << "(" << type
+ << " " << property.name << ")" << Qt::endl;
+ m_stream << " {" << Qt::endl;
+ m_stream << " set" << cap(property.name) << "(" << property.name
+ << ");" << Qt::endl;
+ m_stream << " }" << Qt::endl;
+ } else {
+ m_stream << " void push" << cap(property.name) << "(" << type << " "
+ << property.name << ")" << Qt::endl;
+ m_stream << " {" << Qt::endl;
+ m_stream << " static int __repc_index = " << className
+ << "::staticMetaObject.indexOfSlot(\"push" << cap(property.name)
+ << "(" << type << ")\");" << Qt::endl;
+ m_stream << " QVariantList __repc_args;" << Qt::endl;
+ m_stream << " __repc_args << QVariant::fromValue(" << property.name
+ << ");" << Qt::endl;
+ m_stream << " send(QMetaObject::InvokeMetaMethod, __repc_index,"
+ << " __repc_args);" << Qt::endl;
+ m_stream << " }" << Qt::endl;
+ }
+ }
+ }
+ const auto slotsList = transformEnumParams(astClass, astClass.slotsList, className);
+ for (const ASTFunction &slot : slotsList) {
+ const auto returnType = fullyQualifiedName(astClass, className, slot.returnType);
+ if (mode != REPLICA) {
+ m_stream << " virtual " << returnType << " " << slot.name << "("
+ << slot.paramsAsString() << ") = 0;" << Qt::endl;
+ } else {
+ // TODO: Discuss whether it is a good idea to special-case for void here,
+ const bool isVoid = slot.returnType == QStringLiteral("void");
+
+ if (isVoid)
+ m_stream << " void " << slot.name << "(" << slot.paramsAsString()
+ << ")" << Qt::endl;
+ else
+ m_stream << " QRemoteObjectPendingReply<" << returnType << "> "
+ << slot.name << "(" << slot.paramsAsString()<< ")" << Qt::endl;
+ m_stream << " {" << Qt::endl;
+ m_stream << " static int __repc_index = " << className
+ << "::staticMetaObject.indexOfSlot(\"" << slot.name << "("
+ << slot.paramsAsString(ASTFunction::Normalized) << ")\");"
+ << Qt::endl;
+ m_stream << " QVariantList __repc_args;" << Qt::endl;
+ const auto ¶mNames = slot.paramNames();
+ if (!paramNames.isEmpty()) {
+ m_stream << " __repc_args" << Qt::endl;
+ for (const QString &name : paramNames)
+ m_stream << " << " << "QVariant::fromValue(" << name << ")"
+ << Qt::endl;
+ m_stream << " ;" << Qt::endl;
+ }
+ if (isVoid)
+ m_stream << " send(QMetaObject::InvokeMetaMethod, __repc_index,"
+ << " __repc_args);" << Qt::endl;
+ else
+ m_stream << " return QRemoteObjectPendingReply<" << returnType
+ << ">(sendWithReply(QMetaObject::InvokeMetaMethod, __repc_index,"
+ << " __repc_args));" << Qt::endl;
+ m_stream << " }" << Qt::endl;
+ }
+ }
+ }
+ } else {
+ if (!astClass.properties.isEmpty()) {
+ bool addProtected = true;
+ for (const ASTProperty &property : astClass.properties) {
+ if (property.modifier == ASTProperty::ReadOnly) {
+ if (addProtected) {
+ m_stream << "" << Qt::endl;
+ m_stream << "protected:" << Qt::endl;
+ addProtected = false;
+ }
+ generateSimpleSetter(property, false);
+ }
+ }
+ }
+ }
+
+ m_stream << "" << Qt::endl;
+ m_stream << "private:" << Qt::endl;
+
+ //Next output data members
+ if (mode == SIMPLE_SOURCE) {
+ for (const ASTProperty &property : astClass.properties)
+ m_stream << " " << typeForMode(property, SOURCE) << " " << "m_" << property.name
+ << ";" << Qt::endl;
+ }
+
+ if (mode != SIMPLE_SOURCE)
+ m_stream << " friend class QT_PREPEND_NAMESPACE(QRemoteObjectNode);" << Qt::endl;
+
+ m_stream << "};\n\n";
+ if (mode != SIMPLE_SOURCE) {
+ for (const ASTFlag &flag : astClass.flags)
+ m_stream << "Q_DECLARE_OPERATORS_FOR_FLAGS(" << className << "::" << flag.name
+ << ")\n\n";
+ }
+}
+
+void RepCodeGenerator::generateSourceAPI(const ASTClass &astClass)
+{
+ const QString className = astClass.name + QStringLiteral("SourceAPI");
+ m_stream << QStringLiteral("template <class ObjectType>") << Qt::endl;
+ m_stream << QString::fromLatin1("struct %1 : public SourceApiMap").arg(className) << Qt::endl;
+ m_stream << QStringLiteral("{") << Qt::endl;
+ if (!astClass.enums.isEmpty()) {
+ // Include enum definition in SourceAPI
+ generateDeclarationsForEnums(astClass.enums, false);
+ }
+ for (const auto &flag : astClass.flags)
+ m_stream << QLatin1String(" typedef QFlags<typename ObjectType::%1> %2;")
+ .arg(flag._enum, flag.name) << Qt::endl;
+ m_stream << QString::fromLatin1(" %1(ObjectType *object, const QString &name = "
+ "QLatin1String(\"%2\"))").arg(className, astClass.name)
+ << Qt::endl;
+ m_stream << QStringLiteral(" : SourceApiMap(), m_name(name)") << Qt::endl;
+ m_stream << QStringLiteral(" {") << Qt::endl;
+ if (!astClass.hasPointerObjects())
+ m_stream << QStringLiteral(" Q_UNUSED(object)") << Qt::endl;
+
+ const auto enumCount = astClass.enums.size();
+ const auto totalCount = enumCount + astClass.flags.size();
+ for (int i : astClass.subClassPropertyIndices) {
+ const ASTProperty &child = astClass.properties.at(i);
+ m_stream << QString::fromLatin1(" using %1_type_t = typename std::remove_pointer<"
+ "decltype(object->%1())>::type;")
+ .arg(child.name) << Qt::endl;
+ }
+ m_stream << QString::fromLatin1(" m_enums[0] = %1;").arg(totalCount) << Qt::endl;
+ for (qsizetype i = 0; i < enumCount; ++i) {
+ const auto enumerator = astClass.enums.at(i);
+ m_stream << QString::fromLatin1(" m_enums[%1] = ObjectType::staticMetaObject."
+ "indexOfEnumerator(\"%2\");")
+ .arg(i+1).arg(enumerator.name) << Qt::endl;
+ }
+ for (qsizetype i = enumCount; i < totalCount; ++i) {
+ const auto flag = astClass.flags.at(i - enumCount);
+ m_stream << QString::fromLatin1(" m_enums[%1] = ObjectType::staticMetaObject."
+ "indexOfEnumerator(\"%2\");")
+ .arg(i+1).arg(flag.name) << Qt::endl;
+ }
+ const auto propCount = astClass.properties.size();
+ m_stream << QString::fromLatin1(" m_properties[0] = %1;").arg(propCount) << Qt::endl;
+ QList<ASTProperty> onChangeProperties;
+ QList<qsizetype> propertyChangeIndex;
+ for (qsizetype i = 0; i < propCount; ++i) {
+ const ASTProperty &prop = astClass.properties.at(i);
+ const QString propTypeName =
+ fullyQualifiedName(astClass, QStringLiteral("typename ObjectType"),
+ typeForMode(prop, SOURCE));
+ m_stream << QString::fromLatin1(" m_properties[%1] = "
+ "QtPrivate::qtro_property_index<ObjectType>("
+ "&ObjectType::%2, static_cast<%3 (QObject::*)()>(nullptr)"
+ ",\"%2\");")
+ .arg(QString::number(i+1), prop.name, propTypeName)
+ << Qt::endl;
+ if (prop.modifier == prop.ReadWrite) //Make sure we have a setter function
+ m_stream << QStringLiteral(" QtPrivate::qtro_method_test<ObjectType>("
+ "&ObjectType::set%1, static_cast<void (QObject::*)(%2)>"
+ "(nullptr));")
+ .arg(cap(prop.name), propTypeName) << Qt::endl;
+ if (prop.modifier != prop.Constant) { //Make sure we have an onChange signal
+ m_stream << QStringLiteral(" QtPrivate::qtro_method_test<ObjectType>("
+ "&ObjectType::%1Changed, static_cast<void (QObject::*)()>("
+ "nullptr));")
+ .arg(prop.name) << Qt::endl;
+ onChangeProperties << prop;
+ propertyChangeIndex << i + 1; //m_properties[0] is the count, so index is one higher
+ }
+ }
+ const auto signalCount = astClass.signalsList.size();
+ const auto changedCount = onChangeProperties.size();
+ m_stream << QString::fromLatin1(" m_signals[0] = %1;")
+ .arg(signalCount+onChangeProperties.size()) << Qt::endl;
+ for (qsizetype i = 0; i < changedCount; ++i)
+ m_stream
+ << QString::fromLatin1(" m_signals[%1] = QtPrivate::qtro_signal_index"
+ "<ObjectType>(&ObjectType::%2Changed, static_cast<void"
+ "(QObject::*)(%3)>(nullptr),m_signalArgCount+%4,"
+ "&m_signalArgTypes[%4]);")
+ .arg(QString::number(i+1), onChangeProperties.at(i).name,
+ fullyQualifiedName(astClass,
+ QStringLiteral("typename ObjectType"),
+ typeForMode(onChangeProperties.at(i),
+ SOURCE)),
+ QString::number(i))
+ << Qt::endl;
+
+ QList<ASTFunction> signalsList = transformEnumParams(astClass, astClass.signalsList,
+ QStringLiteral("typename ObjectType"));
+ for (qsizetype i = 0; i < signalCount; ++i) {
+ const ASTFunction &sig = signalsList.at(i);
+ m_stream << QString::fromLatin1(" m_signals[%1] = QtPrivate::qtro_signal_index"
+ "<ObjectType>(&ObjectType::%2, static_cast<void "
+ "(QObject::*)(%3)>(nullptr),m_signalArgCount+%4,"
+ "&m_signalArgTypes[%4]);")
+ .arg(QString::number(changedCount+i+1), sig.name,
+ sig.paramsAsString(ASTFunction::Normalized),
+ QString::number(changedCount+i))
+ << Qt::endl;
+ }
+ const auto slotCount = astClass.slotsList.size();
+ QList<ASTProperty> pushProps;
+ for (const ASTProperty &property : astClass.properties) {
+ if (property.modifier == ASTProperty::ReadPush)
+ pushProps << property;
+ }
+ const auto pushCount = pushProps.size();
+ const auto methodCount = slotCount + pushCount;
+ m_stream << QString::fromLatin1(" m_methods[0] = %1;").arg(methodCount) << Qt::endl;
+ const QString objType = QStringLiteral("typename ObjectType::");
+ for (qsizetype i = 0; i < pushCount; ++i) {
+ const ASTProperty &prop = pushProps.at(i);
+ const QString propTypeName = fullyQualifiedName(astClass,
+ QStringLiteral("typename ObjectType"),
+ prop.type);
+ m_stream <<
+ QString::fromLatin1(" m_methods[%1] = QtPrivate::qtro_method_index"
+ "<ObjectType>(&ObjectType::push%2, static_cast<void "
+ "(QObject::*)(%3)>(nullptr),\"push%2(%4)\","
+ "m_methodArgCount+%5,&m_methodArgTypes[%5]);")
+ .arg(QString::number(i+1), cap(prop.name), propTypeName,
+ // we don't want "typename ObjectType::" in the signature
+ QString(propTypeName).remove(objType),
+ QString::number(i))
+ << Qt::endl;
+ }
+
+ QList<ASTFunction> slotsList = transformEnumParams(astClass, astClass.slotsList,
+ QStringLiteral("typename ObjectType"));
+ for (qsizetype i = 0; i < slotCount; ++i) {
+ const ASTFunction &slot = slotsList.at(i);
+ const QString params = slot.paramsAsString(ASTFunction::Normalized);
+ m_stream << QString::fromLatin1(" m_methods[%1] = QtPrivate::qtro_method_index"
+ "<ObjectType>(&ObjectType::%2, static_cast<void "
+ "(QObject::*)(%3)>(nullptr),\"%2(%4)\","
+ "m_methodArgCount+%5,&m_methodArgTypes[%5]);")
+ .arg(QString::number(i+pushCount+1), slot.name, params,
+ // we don't want "typename ObjectType::" in the signature
+ QString(params).remove(objType),
+ QString::number(i+pushCount))
+ << Qt::endl;
+ }
+ for (const auto &model : astClass.modelMetadata) {
+ const ASTProperty &property = astClass.properties.at(model.propertyIndex);
+ m_stream << QString::fromLatin1(" m_models << ModelInfo({object->%1(),")
+ .arg(property.name) << Qt::endl;
+ m_stream << QString::fromLatin1(" QStringLiteral(\"%1\"),")
+ .arg(property.name) << Qt::endl;
+ QStringList list;
+ if (!model.roles.isEmpty()) {
+ for (auto role : model.roles)
+ list << role.name;
+ }
+ m_stream <<
+ QString::fromLatin1(" QByteArrayLiteral(\"%1\")});")
+ .arg(list.join(QChar::fromLatin1('|'))) << Qt::endl;
+ }
+ for (int i : astClass.subClassPropertyIndices) {
+ const ASTProperty &child = astClass.properties.at(i);
+ m_stream <<
+ QString::fromLatin1(" m_subclasses << new %2SourceAPI<%1_type_t>(object->%1(),"
+ " QStringLiteral(\"%1\"));")
+ .arg(child.name, child.type) << Qt::endl;
+ }
+ m_stream << QStringLiteral(" }") << Qt::endl;
+ m_stream << QStringLiteral("") << Qt::endl;
+ m_stream << QString::fromLatin1(" QString name() const override { return m_name; }")
+ << Qt::endl;
+ m_stream << QString::fromLatin1(" QString typeName() const override { "
+ "return QStringLiteral(\"%1\"); }")
+ .arg(astClass.name) << Qt::endl;
+ m_stream << QStringLiteral(" int enumCount() const override { return m_enums[0]; }")
+ << Qt::endl;
+ m_stream <<
+ QStringLiteral(" int propertyCount() const override { return m_properties[0]; }")
+ << Qt::endl;
+ m_stream << QStringLiteral(" int signalCount() const override { return m_signals[0]; }")
+ << Qt::endl;
+ m_stream << QStringLiteral(" int methodCount() const override { return m_methods[0]; }")
+ << Qt::endl;
+ m_stream << QStringLiteral(" int sourceEnumIndex(int index) const override") << Qt::endl;
+ m_stream << QStringLiteral(" {") << Qt::endl;
+ m_stream << QStringLiteral(" if (index < 0 || index >= m_enums[0]"
+ " || index + 1 >= int(std::size(m_enums)))")
+ << Qt::endl;
+ m_stream << QStringLiteral(" return -1;") << Qt::endl;
+ m_stream << QStringLiteral(" return m_enums[index+1];") << Qt::endl;
+ m_stream << QStringLiteral(" }") << Qt::endl;
+ m_stream << QStringLiteral(" int sourcePropertyIndex(int index) const override")
+ << Qt::endl;
+ m_stream << QStringLiteral(" {") << Qt::endl;
+ m_stream << QStringLiteral(" if (index < 0 || index >= m_properties[0]"
+ " || index + 1 >= int(std::size(m_properties)))")
+ << Qt::endl;
+ m_stream << QStringLiteral(" return -1;") << Qt::endl;
+ m_stream << QStringLiteral(" return m_properties[index+1];") << Qt::endl;
+ m_stream << QStringLiteral(" }") << Qt::endl;
+ m_stream << QStringLiteral(" int sourceSignalIndex(int index) const override") << Qt::endl;
+ m_stream << QStringLiteral(" {") << Qt::endl;
+ m_stream << QStringLiteral(" if (index < 0 || index >= m_signals[0]"
+ " || index + 1 >= int(std::size(m_signals)))")
+ << Qt::endl;
+ m_stream << QStringLiteral(" return -1;") << Qt::endl;
+ m_stream << QStringLiteral(" return m_signals[index+1];") << Qt::endl;
+ m_stream << QStringLiteral(" }") << Qt::endl;
+ m_stream << QStringLiteral(" int sourceMethodIndex(int index) const override") << Qt::endl;
+ m_stream << QStringLiteral(" {") << Qt::endl;
+ m_stream << QStringLiteral(" if (index < 0 || index >= m_methods[0]"
+ " || index + 1 >= int(std::size(m_methods)))")
+ << Qt::endl;
+ m_stream << QStringLiteral(" return -1;") << Qt::endl;
+ m_stream << QStringLiteral(" return m_methods[index+1];") << Qt::endl;
+ m_stream << QStringLiteral(" }") << Qt::endl;
+ if (signalCount+changedCount > 0) {
+ m_stream << QStringLiteral(" int signalParameterCount(int index) const override")
+ << Qt::endl;
+ m_stream << QStringLiteral(" {") << Qt::endl;
+ m_stream << QStringLiteral(" if (index < 0 || index >= m_signals[0])") << Qt::endl;
+ m_stream << QStringLiteral(" return -1;") << Qt::endl;
+ m_stream << QStringLiteral(" return m_signalArgCount[index];") << Qt::endl;
+ m_stream << QStringLiteral(" }") << Qt::endl;
+ m_stream << QStringLiteral(" int signalParameterType(int sigIndex, int paramIndex) "
+ "const override") << Qt::endl;
+ m_stream << QStringLiteral(" {") << Qt::endl;
+ m_stream << QStringLiteral(" if (sigIndex < 0 || sigIndex >= m_signals[0] || "
+ "paramIndex < 0 || paramIndex >= m_signalArgCount[sigIndex])")
+ << Qt::endl;
+ m_stream << QStringLiteral(" return -1;") << Qt::endl;
+ m_stream << QStringLiteral(" return m_signalArgTypes[sigIndex][paramIndex];")
+ << Qt::endl;
+ m_stream << QStringLiteral(" }") << Qt::endl;
+ } else {
+ m_stream << QStringLiteral(" int signalParameterCount(int index) const override "
+ "{ Q_UNUSED(index) return -1; }") << Qt::endl;
+ m_stream << QStringLiteral(" int signalParameterType(int sigIndex, int paramIndex) "
+ "const override") << Qt::endl;
+ m_stream << QStringLiteral(" { Q_UNUSED(sigIndex) Q_UNUSED(paramIndex) return -1; }")
+ << Qt::endl;
+ }
+ if (methodCount > 0) {
+ m_stream << QStringLiteral(" int methodParameterCount(int index) const override")
+ << Qt::endl;
+ m_stream << QStringLiteral(" {") << Qt::endl;
+ m_stream << QStringLiteral(" if (index < 0 || index >= m_methods[0])") << Qt::endl;
+ m_stream << QStringLiteral(" return -1;") << Qt::endl;
+ m_stream << QStringLiteral(" return m_methodArgCount[index];") << Qt::endl;
+ m_stream << QStringLiteral(" }") << Qt::endl;
+ m_stream << QStringLiteral(" int methodParameterType(int methodIndex, int paramIndex) "
+ "const override") << Qt::endl;
+ m_stream << QStringLiteral(" {") << Qt::endl;
+ m_stream <<
+ QStringLiteral(" if (methodIndex < 0 || methodIndex >= m_methods[0] || "
+ "paramIndex < 0 || paramIndex >= m_methodArgCount[methodIndex])")
+ << Qt::endl;
+ m_stream << QStringLiteral(" return -1;") << Qt::endl;
+ m_stream << QStringLiteral(" return m_methodArgTypes[methodIndex][paramIndex];")
+ << Qt::endl;
+ m_stream << QStringLiteral(" }") << Qt::endl;
+ } else {
+ m_stream << QStringLiteral(" int methodParameterCount(int index) const override { "
+ "Q_UNUSED(index) return -1; }") << Qt::endl;
+ m_stream << QStringLiteral(" int methodParameterType(int methodIndex, int paramIndex) "
+ "const override") << Qt::endl;
+ m_stream <<
+ QStringLiteral(" { Q_UNUSED(methodIndex) Q_UNUSED(paramIndex) return -1; }")
+ << Qt::endl;
+ }
+ //propertyIndexFromSignal method
+ m_stream << QStringLiteral(" int propertyIndexFromSignal(int index) const override")
+ << Qt::endl;
+ m_stream << QStringLiteral(" {") << Qt::endl;
+ if (!propertyChangeIndex.isEmpty()) {
+ m_stream << QStringLiteral(" switch (index) {") << Qt::endl;
+ for (int i = 0; i < propertyChangeIndex.size(); ++i)
+ m_stream << QString::fromLatin1(" case %1: return m_properties[%2];")
+ .arg(i).arg(propertyChangeIndex.at(i)) << Qt::endl;
+ m_stream << QStringLiteral(" }") << Qt::endl;
+ } else
+ m_stream << QStringLiteral(" Q_UNUSED(index)") << Qt::endl;
+ m_stream << QStringLiteral(" return -1;") << Qt::endl;
+ m_stream << QStringLiteral(" }") << Qt::endl;
+ //propertyRawIndexFromSignal method
+ m_stream << QStringLiteral(" int propertyRawIndexFromSignal(int index) const override")
+ << Qt::endl;
+ m_stream << QStringLiteral(" {") << Qt::endl;
+ if (!propertyChangeIndex.isEmpty()) {
+ m_stream << QStringLiteral(" switch (index) {") << Qt::endl;
+ for (int i = 0; i < propertyChangeIndex.size(); ++i)
+ m_stream << QString::fromLatin1(" case %1: return %2;").arg(i)
+ .arg(propertyChangeIndex.at(i)-1) << Qt::endl;
+ m_stream << QStringLiteral(" }") << Qt::endl;
+ } else
+ m_stream << QStringLiteral(" Q_UNUSED(index)") << Qt::endl;
+ m_stream << QStringLiteral(" return -1;") << Qt::endl;
+ m_stream << QStringLiteral(" }") << Qt::endl;
+
+ //signalSignature method
+ m_stream << QStringLiteral(" const QByteArray signalSignature(int index) const override")
+ << Qt::endl;
+ m_stream << QStringLiteral(" {") << Qt::endl;
+ if (signalCount+changedCount > 0) {
+ m_stream << QStringLiteral(" switch (index) {") << Qt::endl;
+ for (int i = 0; i < changedCount; ++i) {
+ const ASTProperty &prop = onChangeProperties.at(i);
+ if (isClassEnum(astClass, prop.type))
+ m_stream <<
+ QString::fromLatin1(" case %1: return QByteArrayLiteral(\"%2"
+ "Changed($1)\").replace(\"$1\", "
+ "QtPrivate::qtro_enum_signature<ObjectType>(\"%3\"));")
+ .arg(QString::number(i), prop.name, prop.type)
+ << Qt::endl;
+ else
+ m_stream <<
+ QString::fromLatin1(" case %1: return QByteArrayLiteral(\"%2"
+ "Changed(%3)\");")
+ .arg(QString::number(i), prop.name,
+ typeForMode(prop, SOURCE))
+ << Qt::endl;
+ }
+ for (int i = 0; i < signalCount; ++i)
+ {
+ const ASTFunction &sig = astClass.signalsList.at(i);
+ auto paramsAsString = sig.paramsAsString(ASTFunction::Normalized);
+ const auto paramsAsList = paramsAsString.split(QLatin1String(","));
+ int enumCount = 0;
+ QString enumString;
+ for (int j = 0; j < paramsAsList.size(); j++) {
+ auto const p = paramsAsList.at(j);
+ if (isClassEnum(astClass, p)) {
+ paramsAsString.replace(paramsAsString.indexOf(p), p.size(),
+ QStringLiteral("$%1").arg(enumCount));
+ enumString.append(QString::fromLatin1(".replace(\"$%1\", QtPrivate::"
+ "qtro_enum_signature<ObjectType>"
+ "(\"%2\"))")
+ .arg(enumCount++)
+ .arg(paramsAsList.at(j)));
+ }
+ }
+ m_stream <<
+ QString::fromLatin1(" case %1: return QByteArrayLiteral(\"%2(%3)\")%4;")
+ .arg(QString::number(i+changedCount), sig.name,
+ paramsAsString, enumString) << Qt::endl;
+ }
+ m_stream << QStringLiteral(" }") << Qt::endl;
+ } else
+ m_stream << QStringLiteral(" Q_UNUSED(index)") << Qt::endl;
+ m_stream << QStringLiteral(" return QByteArrayLiteral(\"\");") << Qt::endl;
+ m_stream << QStringLiteral(" }") << Qt::endl;
+
+ //signalParameterNames method
+ m_stream <<
+ QStringLiteral(" QByteArrayList signalParameterNames(int index) const override")
+ << Qt::endl;
+ m_stream << QStringLiteral(" {") << Qt::endl;
+ m_stream << QStringLiteral(" if (index < 0 || index >= m_signals[0]"
+ " || index + 1 >= int(std::size(m_signals)))")
+ << Qt::endl;
+ m_stream << QStringLiteral(" return QByteArrayList();") << Qt::endl;
+ m_stream << QStringLiteral(" return ObjectType::staticMetaObject.method(m_signals["
+ "index + 1]).parameterNames();") << Qt::endl;
+ m_stream << QStringLiteral(" }") << Qt::endl;
+
+ //methodSignature method
+ m_stream << QStringLiteral(" const QByteArray methodSignature(int index) const override")
+ << Qt::endl;
+ m_stream << QStringLiteral(" {") << Qt::endl;
+ if (methodCount > 0) {
+ m_stream << QStringLiteral(" switch (index) {") << Qt::endl;
+ for (int i = 0; i < pushCount; ++i)
+ {
+ const ASTProperty &prop = pushProps.at(i);
+ if (isClassEnum(astClass, prop.type))
+ m_stream << QString::fromLatin1(" case %1: return QByteArrayLiteral(\"push"
+ "%2($1)\").replace(\"$1\", QtPrivate::"
+ "qtro_enum_signature<ObjectType>(\"%3\"));")
+ .arg(QString::number(i), prop.name, prop.type)
+ << Qt::endl;
+ else
+ m_stream <<
+ QString::fromLatin1(" case %1: return QByteArrayLiteral(\"push"
+ "%2(%3)\");")
+ .arg(QString::number(i), cap(prop.name), prop.type)
+ << Qt::endl;
+ }
+ for (int i = 0; i < slotCount; ++i)
+ {
+ const ASTFunction &slot = astClass.slotsList.at(i);
+ auto paramsAsString = slot.paramsAsString(ASTFunction::Normalized);
+ const auto paramsAsList = paramsAsString.split(QLatin1String(","));
+ int enumCount = 0;
+ QString enumString;
+ for (int j = 0; j < paramsAsList.size(); j++) {
+ auto const p = paramsAsList.at(j);
+ if (isClassEnum(astClass, p)) {
+ paramsAsString.replace(paramsAsString.indexOf(p), p.size(),
+ QStringLiteral("$%1").arg(enumCount));
+ enumString.append(QString::fromLatin1(".replace(\"$%1\", QtPrivate::"
+ "qtro_enum_signature<ObjectType>"
+ "(\"%2\"))")
+ .arg(enumCount++)
+ .arg(paramsAsList.at(j)));
+ }
+ }
+ m_stream << QString::fromLatin1(" case %1: return QByteArrayLiteral(\"%2(%3)"
+ "\")%4;")
+ .arg(QString::number(i+pushCount), slot.name,
+ paramsAsString, enumString) << Qt::endl;
+ }
+ m_stream << QStringLiteral(" }") << Qt::endl;
+ } else
+ m_stream << QStringLiteral(" Q_UNUSED(index)") << Qt::endl;
+ m_stream << QStringLiteral(" return QByteArrayLiteral(\"\");") << Qt::endl;
+ m_stream << QStringLiteral(" }") << Qt::endl;
+
+ //methodType method
+ m_stream << QStringLiteral(" QMetaMethod::MethodType methodType(int) const override")
+ << Qt::endl;
+ m_stream << QStringLiteral(" {") << Qt::endl;
+ m_stream << QStringLiteral(" return QMetaMethod::Slot;") << Qt::endl;
+ m_stream << QStringLiteral(" }") << Qt::endl;
+
+ //methodParameterNames method
+ m_stream <<
+ QStringLiteral(" QByteArrayList methodParameterNames(int index) const override")
+ << Qt::endl;
+ m_stream << QStringLiteral(" {") << Qt::endl;
+ m_stream << QStringLiteral(" if (index < 0 || index >= m_methods[0]"
+ " || index + 1 >= int(std::size(m_methods)))")
+ << Qt::endl;
+ m_stream << QStringLiteral(" return QByteArrayList();") << Qt::endl;
+ m_stream << QStringLiteral(" return ObjectType::staticMetaObject.method(m_methods["
+ "index + 1]).parameterNames();") << Qt::endl;
+ m_stream << QStringLiteral(" }") << Qt::endl;
+
+ //typeName method
+ m_stream << QStringLiteral(" const QByteArray typeName(int index) const override")
+ << Qt::endl;
+ m_stream << QStringLiteral(" {") << Qt::endl;
+ if (methodCount > 0) {
+ m_stream << QStringLiteral(" switch (index) {") << Qt::endl;
+ for (int i = 0; i < pushCount; ++i)
+ {
+ m_stream <<
+ QString::fromLatin1(" case %1: return QByteArrayLiteral(\"void\");")
+ .arg(QString::number(i)) << Qt::endl;
+ }
+ for (int i = 0; i < slotCount; ++i)
+ {
+ const ASTFunction &slot = astClass.slotsList.at(i);
+ if (isClassEnum(astClass, slot.returnType))
+ m_stream <<
+ QString::fromLatin1(" case %1: return QByteArrayLiteral(\"$1\")"
+ ".replace(\"$1\", QtPrivate::qtro_enum_signature"
+ "<ObjectType>(\"%2\"));")
+ .arg(QString::number(i+pushCount), slot.returnType)
+ << Qt::endl;
+ else
+ m_stream <<
+ QString::fromLatin1(" case %1: return QByteArrayLiteral(\"%2\");")
+ .arg(QString::number(i+pushCount), slot.returnType) << Qt::endl;
+ }
+ m_stream << QStringLiteral(" }") << Qt::endl;
+ } else
+ m_stream << QStringLiteral(" Q_UNUSED(index)") << Qt::endl;
+ m_stream << QStringLiteral(" return QByteArrayLiteral(\"\");") << Qt::endl;
+ m_stream << QStringLiteral(" }") << Qt::endl;
+
+ //objectSignature method
+ m_stream <<
+ QStringLiteral(" QByteArray objectSignature() const override { return QByteArray{\"")
+ << QLatin1String(classSignature(astClass))
+ << QStringLiteral("\"}; }") << Qt::endl;
+
+ m_stream << QStringLiteral("") << Qt::endl;
+ m_stream << QString::fromLatin1(" int m_enums[%1];").arg(totalCount + 1) << Qt::endl;
+ m_stream << QString::fromLatin1(" int m_properties[%1];").arg(propCount+1) << Qt::endl;
+ m_stream << QString::fromLatin1(" int m_signals[%1];").arg(signalCount+changedCount+1)
+ << Qt::endl;
+ m_stream << QString::fromLatin1(" int m_methods[%1];").arg(methodCount+1) << Qt::endl;
+ m_stream << QString::fromLatin1(" const QString m_name;") << Qt::endl;
+ if (signalCount+changedCount > 0) {
+ m_stream << QString::fromLatin1(" int m_signalArgCount[%1];")
+ .arg(signalCount+changedCount) << Qt::endl;
+ m_stream << QString::fromLatin1(" const int* m_signalArgTypes[%1];")
+ .arg(signalCount+changedCount) << Qt::endl;
+ }
+ if (methodCount > 0) {
+ m_stream << QString::fromLatin1(" int m_methodArgCount[%1];").arg(methodCount)
+ << Qt::endl;
+ m_stream << QString::fromLatin1(" const int* m_methodArgTypes[%1];").arg(methodCount)
+ << Qt::endl;
+ }
+ m_stream << QStringLiteral("};") << Qt::endl;
+ m_stream << "" << Qt::endl;
+}
+
+QT_END_NAMESPACE
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef REPCODEGENERATOR_H
+#define REPCODEGENERATOR_H
+
+#include "repparser.h"
+
+#include <QList>
+#include <QSet>
+#include <QString>
+#include <QTextStream>
+
+QT_BEGIN_NAMESPACE
+
+class QIODevice;
+
+class RepCodeGenerator
+{
+public:
+ enum Mode
+ {
+ REPLICA,
+ SOURCE,
+ SIMPLE_SOURCE,
+ MERGED
+ };
+
+ RepCodeGenerator(QIODevice *outputDevice, const AST &ast);
+
+ void generate(Mode mode, QString fileName);
+
+ QByteArray classSignature(const ASTClass &ac);
+private:
+ void generateHeader(Mode mode);
+ QString generateMetaTypeRegistration(const QSet<QString> &metaTypes);
+ QString generateMetaTypeRegistrationForPending(const QSet<QString> &metaTypes);
+
+ void generateSimpleSetter(const ASTProperty &property, bool generateOverride = true);
+ void generatePOD(const POD &pod);
+ void generateEnumGadget(const ASTEnum &en, const QString &className);
+ void generateDeclarationsForEnums(const QList<ASTEnum> &enums, bool generateQENUM=true);
+ QString formatQPropertyDeclarations(const POD &pod);
+ QString formatConstructors(const POD &pod);
+ QString formatPropertyGettersAndSetters(const POD &pod);
+ QString formatSignals(const POD &pod);
+ QString formatDataMembers(const POD &pod);
+ QString formatDebugOperator(const POD &pod);
+ QString formatMarshallingOperators(const POD &pod);
+ QString typeForMode(const ASTProperty &property, Mode mode);
+
+ void generateClass(Mode mode, const ASTClass &astClasses,
+ const QString &metaTypeRegistrationCode);
+ void generateSourceAPI(const ASTClass &astClass);
+
+private:
+ QTextStream m_stream;
+ AST m_ast;
+};
+
+QT_END_NAMESPACE
+
+#endif
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <qjsonvalue.h>
+#include <qjsonarray.h>
+#include <qjsonobject.h>
+
+#include "utils.h"
+#include "repparser.h"
+
+
+#define _(X) QLatin1String(X)
+
+QT_BEGIN_NAMESPACE
+
+namespace JSON
+{
+ enum Types {
+ Any,
+ Array,
+ Object,
+ String,
+ Bool
+ };
+
+ static QJsonValue getItem(const QJsonValue &json, const char *key, JSON::Types type = JSON::Any)
+ {
+ if (json.isUndefined())
+ qCritical() << "Invalid metadata json file. Unexpected Undefined value when looking for key:" << key;
+ if (!json.isObject())
+ qCritical() << "Invalid metadata json file. Input (" << json << ") is not an object when looking for key:" << key;
+ QJsonValue value = json.toObject()[_(key)];
+ switch (type) {
+ case JSON::Any: break;
+ case JSON::Array:
+ if (!value.isArray())
+ qCritical() << "Invalid metadata json file. Value (" << value << ") is not an array when looking for key:" << key;
+ break;
+ case JSON::Object:
+ if (!value.isObject())
+ qCritical() << "Invalid metadata json file. Value (" << value << ") is not an object when looking for key:" << key;
+ break;
+ case JSON::String:
+ if (!value.isString())
+ qCritical() << "Invalid metadata json file. Value (" << value << ") is not a string when looking for key:" << key;
+ break;
+ case JSON::Bool:
+ if (!value.isBool())
+ qCritical() << "Invalid metadata json file. Value (" << value << ") is not a bool when looking for key:" << key;
+ break;
+ }
+ return value;
+ }
+
+ static bool containsKey(const QJsonValue &json, const char *key)
+ {
+ if (json.isUndefined())
+ qCritical() << "Invalid metadata json file. Unexpected Undefined value when looking for key:" << key;
+ if (!json.isObject())
+ qCritical() << "Invalid metadata json file. Input (" << json << ") is not an object when looking for key:" << key;
+ return json.toObject().contains(_(key));
+ }
+
+ static bool isEmptyArray(const QJsonValue &json, const char *key)
+ {
+ if (!containsKey(json, key))
+ return true;
+ const auto value = getItem(json, key);
+ if (!value.isArray())
+ qCritical() << "Invalid metadata json file." << key << "is not an array.";
+ return value.toArray().count() == 0;
+ }
+
+ static QJsonArray getArray(const QJsonValue &json, const char *key)
+ {
+ return getItem(json, key, JSON::Array).toArray();
+ }
+ static QString getString(const QJsonValue &json, const char *key)
+ {
+ return getItem(json, key, JSON::String).toString();
+ }
+ static QByteArray getBytes(const QJsonValue &json, const char *key)
+ {
+ return getItem(json, key, JSON::String).toString().toLatin1();
+ }
+ static bool getBool(const QJsonValue &json, const char *key)
+ {
+ return getItem(json, key, JSON::Bool).toBool();
+ }
+ static bool getBool(const QJsonValue &json, const char *key, bool missingValue)
+ {
+ if (!containsKey(json, key))
+ return missingValue;
+ bool res = getBool(json, key);
+ return res;
+ }
+}
+
+using namespace JSON;
+
+static QByteArray join(const QByteArrayList &array, const QByteArray &separator)
+{
+ QByteArray res;
+ const auto sz = array.size();
+ if (!sz)
+ return res;
+ for (qsizetype i = 0; i < sz - 1; i++)
+ res += array.at(i) + separator;
+ res += array.at(sz - 1);
+ return res;
+}
+
+static QByteArrayList generateProperties(const QJsonArray &properties, bool isPod=false)
+{
+ QByteArrayList ret;
+ for (const QJsonValue prop : properties) {
+ if (!isPod && !containsKey(prop, "notify") && !getBool(prop, "constant")) {
+ qWarning() << "Skipping property" << getString(prop, "name")
+ << "because it is non-notifiable & non-constant";
+ continue; // skip non-notifiable properties
+ }
+ QByteArray output = getBytes(prop, "type") + " " + getBytes(prop, "name");
+ if (getBool(prop, "constant"))
+ output += " CONSTANT";
+ if (!containsKey(prop, "write") && containsKey(prop, "read."))
+ output += " READONLY";
+ ret << output;
+ }
+ return ret;
+}
+
+static QByteArray generateFunctions(const QByteArray &type, const QJsonArray &functions)
+{
+ QByteArray ret;
+ for (const QJsonValue func : functions) {
+ ret += type + "(" + getBytes(func, "returnType") + " " + getBytes(func, "name") + "(";
+ const auto arguments = getArray(func, "arguments");
+ for (const QJsonValue arg : arguments)
+ ret += getBytes(arg, "type") + " " + getBytes(arg, "name") + ", ";
+ if (arguments.count())
+ ret.chop(2);
+ ret += "));\n";
+ }
+ return ret;
+}
+
+const auto filterNotPublic = [](const QJsonValue &value) {
+ return getString(value, "access") != QStringLiteral("public");
+};
+
+static QJsonArray cleanedSignalList(const QJsonValue &cls)
+{
+ if (isEmptyArray(cls, "signals"))
+ return QJsonArray();
+
+ auto signalList = getArray(cls, "signals");
+ if (isEmptyArray(cls, "properties"))
+ return signalList;
+
+ const auto props = getArray(cls, "properties");
+ const auto filterNotify = [&props](const QJsonValue &value) {
+ const auto filter = [&value](const QJsonValue &prop) {
+ return getItem(value, "name") == getItem(prop, "notify");
+ };
+ return std::find_if(props.begin(), props.end(), filter) != props.end();
+ };
+ for (auto it = signalList.begin(); it != signalList.end(); /* blank */ ) {
+ if (filterNotify(*it))
+ it = signalList.erase(it);
+ else if (filterNotPublic(*it))
+ it = signalList.erase(it);
+ else
+ it++;
+ }
+ return signalList;
+}
+
+static QJsonArray cleanedSlotList(const QJsonValue &cls)
+{
+ if (isEmptyArray(cls, "slots"))
+ return QJsonArray();
+
+ auto slotList = getArray(cls, "slots");
+ if (!isEmptyArray(cls, "properties"))
+ return slotList;
+
+ const auto props = getArray(cls, "properties");
+ const auto filterWrite = [&props](const QJsonValue &value) {
+ const auto filter = [&value](const QJsonValue &prop) {
+ const auto args = getArray(prop, "arguments");
+ return getItem(value, "name") == getItem(prop, "write") && args.count() == 1
+ && getItem(args.at(0), "type") == getItem(prop, "type");
+ };
+ return std::find_if(props.begin(), props.end(), filter) != props.end();
+ };
+ for (auto it = slotList.begin(); it != slotList.end(); /* blank */ ) {
+ if (filterWrite(*it))
+ it = slotList.erase(it);
+ else if (filterNotPublic(*it))
+ it = slotList.erase(it);
+ else
+ it++;
+ }
+ return slotList;
+}
+
+QByteArray generateClass(const QJsonValue &cls, bool alwaysGenerateClass)
+{
+ if (getBool(cls, "gadget", false) || alwaysGenerateClass
+ || (isEmptyArray(cls, "signals") && isEmptyArray(cls, "slots")))
+ return "POD " + getBytes(cls, "className") + "("
+ + join(generateProperties(getArray(cls, "properties"), true), ", ") + ")\n";
+
+ QByteArray ret("class " + getBytes(cls, "className") + "\n{\n");
+ if (!isEmptyArray(cls, "properties"))
+ ret += " PROP(" + join(generateProperties(getArray(cls, "properties")), ");\n PROP(")
+ + ");\n";
+ ret += generateFunctions(" SLOT", cleanedSlotList(cls));
+ ret += generateFunctions(" SIGNAL", cleanedSignalList(cls));
+ ret += "}\n";
+ return ret;
+}
+
+static QList<PODAttribute> propertyList2PODAttributes(const QJsonArray &list)
+{
+ QList<PODAttribute> ret;
+ for (const QJsonValue prop : list)
+ ret.push_back(PODAttribute(getString(prop, "type"), getString(prop, "name")));
+ return ret;
+}
+
+QList<ASTProperty> propertyList2AstProperties(const QJsonArray &list)
+{
+ QList<ASTProperty> ret;
+ for (const QJsonValue property : list) {
+ if (!containsKey(property, "notify") && !getBool(property, "constant")) {
+ qWarning() << "Skipping property" << getString(property, "name")
+ << "because it is non-notifiable & non-constant";
+ continue; // skip non-notifiable properties
+ }
+ ASTProperty prop;
+ prop.name = getString(property, "name");
+ prop.type = getString(property, "type");
+ prop.modifier = getBool(property, "constant") ? ASTProperty::Constant
+ : !containsKey(property, "write") && containsKey(property, "read")
+ ? ASTProperty::ReadOnly
+ : ASTProperty::ReadWrite;
+ ret.push_back(prop);
+ }
+ return ret;
+}
+
+QList<ASTFunction> functionList2AstFunctionList(const QJsonArray &list)
+{
+ QList<ASTFunction> ret;
+ for (const QJsonValue function : list) {
+ ASTFunction func;
+ func.name = getString(function, "name");
+ func.returnType = getString(function, "returnType");
+ const auto arguments = getArray(function, "arguments");
+ for (const QJsonValue arg : arguments)
+ func.params.push_back(ASTDeclaration(getString(arg, "type"), getString(arg, "name")));
+ ret.push_back(func);
+ }
+ return ret;
+}
+
+AST classList2AST(const QJsonArray &classes)
+{
+ AST ret;
+ for (const QJsonValue cls : classes) {
+ if (isEmptyArray(cls, "signals") && isEmptyArray(cls, "slots")) {
+ POD pod;
+ pod.name = getString(cls, "className");
+ pod.attributes = propertyList2PODAttributes(getArray(cls, "properties"));
+ ret.pods.push_back(pod);
+ } else {
+ ASTClass cl(getString(cls, "className"));
+ cl.properties = propertyList2AstProperties(getArray(cls, "properties"));
+ cl.signalsList = functionList2AstFunctionList(cleanedSignalList(cls));
+ cl.slotsList = functionList2AstFunctionList(cleanedSlotList(cls));
+ ret.classes.push_back(cl);
+ }
+ }
+ return ret;
+}
+
+QT_END_NAMESPACE
--- /dev/null
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <QByteArray>
+
+QT_BEGIN_NAMESPACE
+class QJsonValue;
+class QJsonArray;
+struct AST;
+
+QByteArray generateClass(const QJsonValue &cls, bool alwaysGenerateClass = false);
+AST classList2AST(const QJsonArray &classes);
+QT_END_NAMESPACE
+
+#endif // UTILS_H
--- /dev/null
+TEMPLATE = subdirs
+qtConfig(commandlineparser): {
+ SUBDIRS += repc
+}