From abe83923e1981f2d4a8512e03ab5fb4b103b2e44 Mon Sep 17 00:00:00 2001 From: "markh%activestate.com" Date: Mon, 19 Feb 2001 05:24:45 +0000 Subject: [PATCH] First checkin of the Python XPCOM bindings. git-svn-id: svn://10.0.0.236/trunk@87331 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/extensions/python/xpcom/LICENSE.txt | 567 +++++ mozilla/extensions/python/xpcom/__init__.py | 53 + .../python/xpcom/client/__init__.py | 247 ++ mozilla/extensions/python/xpcom/components.py | 183 ++ .../extensions/python/xpcom/doc/advanced.html | 135 ++ .../python/xpcom/doc/architecture.html | 84 + .../python/xpcom/doc/configure.html | 198 ++ .../extensions/python/xpcom/doc/credits.html | 54 + .../extensions/python/xpcom/doc/tutorial.html | 240 ++ mozilla/extensions/python/xpcom/file.py | 298 +++ .../python/xpcom/makefile.stupid.linux | 217 ++ .../python/xpcom/makefile.stupid.win | 223 ++ mozilla/extensions/python/xpcom/nsError.py | 115 + mozilla/extensions/python/xpcom/readme.html | 91 + mozilla/extensions/python/xpcom/register.py | 37 + .../python/xpcom/server/__init__.py | 38 + .../python/xpcom/server/enumerator.py | 24 + .../extensions/python/xpcom/server/factory.py | 36 + .../extensions/python/xpcom/server/loader.py | 208 ++ .../extensions/python/xpcom/server/module.py | 75 + .../extensions/python/xpcom/server/policy.py | 211 ++ .../python/xpcom/src/ErrorUtils.cpp | 219 ++ .../extensions/python/xpcom/src/PyGBase.cpp | 757 ++++++ .../python/xpcom/src/PyGInputStream.cpp | 143 ++ .../extensions/python/xpcom/src/PyGModule.cpp | 331 +++ .../extensions/python/xpcom/src/PyGStub.cpp | 144 ++ .../python/xpcom/src/PyGWeakReference.cpp | 51 + .../python/xpcom/src/PyIComponentManager.cpp | 165 ++ .../python/xpcom/src/PyIEnumerator.cpp | 198 ++ mozilla/extensions/python/xpcom/src/PyIID.cpp | 202 ++ .../python/xpcom/src/PyIInputStream.cpp | 127 + .../python/xpcom/src/PyIInterfaceInfo.cpp | 391 +++ .../xpcom/src/PyIInterfaceInfoManager.cpp | 167 ++ .../python/xpcom/src/PyIServiceManager.cpp | 101 + .../python/xpcom/src/PyISimpleEnumerator.cpp | 165 ++ .../python/xpcom/src/PyISupports.cpp | 343 +++ mozilla/extensions/python/xpcom/src/PyXPCOM.h | 557 +++++ .../extensions/python/xpcom/src/PyXPCOM_std.h | 49 + .../python/xpcom/src/Pyxpt_info.cpp | 136 ++ .../python/xpcom/src/TypeObject.cpp | 194 ++ .../python/xpcom/src/VariantUtils.cpp | 2092 +++++++++++++++++ .../extensions/python/xpcom/src/dllmain.cpp | 217 ++ .../python/xpcom/src/loader/pyloader.cpp | 392 +++ .../extensions/python/xpcom/src/readme.html | 66 + mozilla/extensions/python/xpcom/src/xpcom.cpp | 532 +++++ .../xpcom/test/output/test_com_exceptions | 8 + .../python/xpcom/test/output/test_comfile | 7 + .../python/xpcom/test/output/test_components | 4 + .../test/output/test_isupports_primitives | 2 + .../python/xpcom/test/output/test_misc | 9 + .../python/xpcom/test/output/test_streams | 1 + .../xpcom/test/output/test_test_component | 4 + .../xpcom/test/output/test_weakreferences | 2 + .../extensions/python/xpcom/test/regrtest.py | 18 + .../python/xpcom/test/test_com_exceptions.py | 70 + .../python/xpcom/test/test_comfile.py | 7 + .../test_component/py_test_component.html | 149 ++ .../test/test_component/py_test_component.idl | 183 ++ .../test/test_component/py_test_component.py | 344 +++ .../python/xpcom/test/test_components.py | 78 + .../xpcom/test/test_isupports_primitives.py | 116 + .../extensions/python/xpcom/test/test_misc.py | 171 ++ .../python/xpcom/test/test_streams.py | 86 + .../python/xpcom/test/test_test_component.js | 80 + .../python/xpcom/test/test_test_component.py | 435 ++++ .../python/xpcom/test/test_weakreferences.py | 52 + .../extensions/python/xpcom/tools/regxpcom.py | 33 + .../python/xpcom/tools/tracer_demo.py | 79 + .../extensions/python/xpcom/xpcom_consts.py | 202 ++ mozilla/extensions/python/xpcom/xpt.py | 435 ++++ 70 files changed, 13648 insertions(+) create mode 100644 mozilla/extensions/python/xpcom/LICENSE.txt create mode 100644 mozilla/extensions/python/xpcom/__init__.py create mode 100644 mozilla/extensions/python/xpcom/client/__init__.py create mode 100644 mozilla/extensions/python/xpcom/components.py create mode 100644 mozilla/extensions/python/xpcom/doc/advanced.html create mode 100644 mozilla/extensions/python/xpcom/doc/architecture.html create mode 100644 mozilla/extensions/python/xpcom/doc/configure.html create mode 100644 mozilla/extensions/python/xpcom/doc/credits.html create mode 100644 mozilla/extensions/python/xpcom/doc/tutorial.html create mode 100644 mozilla/extensions/python/xpcom/file.py create mode 100644 mozilla/extensions/python/xpcom/makefile.stupid.linux create mode 100644 mozilla/extensions/python/xpcom/makefile.stupid.win create mode 100644 mozilla/extensions/python/xpcom/nsError.py create mode 100644 mozilla/extensions/python/xpcom/readme.html create mode 100644 mozilla/extensions/python/xpcom/register.py create mode 100644 mozilla/extensions/python/xpcom/server/__init__.py create mode 100644 mozilla/extensions/python/xpcom/server/enumerator.py create mode 100644 mozilla/extensions/python/xpcom/server/factory.py create mode 100644 mozilla/extensions/python/xpcom/server/loader.py create mode 100644 mozilla/extensions/python/xpcom/server/module.py create mode 100644 mozilla/extensions/python/xpcom/server/policy.py create mode 100644 mozilla/extensions/python/xpcom/src/ErrorUtils.cpp create mode 100644 mozilla/extensions/python/xpcom/src/PyGBase.cpp create mode 100644 mozilla/extensions/python/xpcom/src/PyGInputStream.cpp create mode 100644 mozilla/extensions/python/xpcom/src/PyGModule.cpp create mode 100644 mozilla/extensions/python/xpcom/src/PyGStub.cpp create mode 100644 mozilla/extensions/python/xpcom/src/PyGWeakReference.cpp create mode 100644 mozilla/extensions/python/xpcom/src/PyIComponentManager.cpp create mode 100644 mozilla/extensions/python/xpcom/src/PyIEnumerator.cpp create mode 100644 mozilla/extensions/python/xpcom/src/PyIID.cpp create mode 100644 mozilla/extensions/python/xpcom/src/PyIInputStream.cpp create mode 100644 mozilla/extensions/python/xpcom/src/PyIInterfaceInfo.cpp create mode 100644 mozilla/extensions/python/xpcom/src/PyIInterfaceInfoManager.cpp create mode 100644 mozilla/extensions/python/xpcom/src/PyIServiceManager.cpp create mode 100644 mozilla/extensions/python/xpcom/src/PyISimpleEnumerator.cpp create mode 100644 mozilla/extensions/python/xpcom/src/PyISupports.cpp create mode 100644 mozilla/extensions/python/xpcom/src/PyXPCOM.h create mode 100644 mozilla/extensions/python/xpcom/src/PyXPCOM_std.h create mode 100644 mozilla/extensions/python/xpcom/src/Pyxpt_info.cpp create mode 100644 mozilla/extensions/python/xpcom/src/TypeObject.cpp create mode 100644 mozilla/extensions/python/xpcom/src/VariantUtils.cpp create mode 100644 mozilla/extensions/python/xpcom/src/dllmain.cpp create mode 100644 mozilla/extensions/python/xpcom/src/loader/pyloader.cpp create mode 100644 mozilla/extensions/python/xpcom/src/readme.html create mode 100644 mozilla/extensions/python/xpcom/src/xpcom.cpp create mode 100644 mozilla/extensions/python/xpcom/test/output/test_com_exceptions create mode 100644 mozilla/extensions/python/xpcom/test/output/test_comfile create mode 100644 mozilla/extensions/python/xpcom/test/output/test_components create mode 100644 mozilla/extensions/python/xpcom/test/output/test_isupports_primitives create mode 100644 mozilla/extensions/python/xpcom/test/output/test_misc create mode 100644 mozilla/extensions/python/xpcom/test/output/test_streams create mode 100644 mozilla/extensions/python/xpcom/test/output/test_test_component create mode 100644 mozilla/extensions/python/xpcom/test/output/test_weakreferences create mode 100644 mozilla/extensions/python/xpcom/test/regrtest.py create mode 100644 mozilla/extensions/python/xpcom/test/test_com_exceptions.py create mode 100644 mozilla/extensions/python/xpcom/test/test_comfile.py create mode 100644 mozilla/extensions/python/xpcom/test/test_component/py_test_component.html create mode 100644 mozilla/extensions/python/xpcom/test/test_component/py_test_component.idl create mode 100644 mozilla/extensions/python/xpcom/test/test_component/py_test_component.py create mode 100644 mozilla/extensions/python/xpcom/test/test_components.py create mode 100644 mozilla/extensions/python/xpcom/test/test_isupports_primitives.py create mode 100644 mozilla/extensions/python/xpcom/test/test_misc.py create mode 100644 mozilla/extensions/python/xpcom/test/test_streams.py create mode 100644 mozilla/extensions/python/xpcom/test/test_test_component.js create mode 100644 mozilla/extensions/python/xpcom/test/test_test_component.py create mode 100644 mozilla/extensions/python/xpcom/test/test_weakreferences.py create mode 100644 mozilla/extensions/python/xpcom/tools/regxpcom.py create mode 100644 mozilla/extensions/python/xpcom/tools/tracer_demo.py create mode 100644 mozilla/extensions/python/xpcom/xpcom_consts.py create mode 100644 mozilla/extensions/python/xpcom/xpt.py diff --git a/mozilla/extensions/python/xpcom/LICENSE.txt b/mozilla/extensions/python/xpcom/LICENSE.txt new file mode 100644 index 00000000000..18f8109b797 --- /dev/null +++ b/mozilla/extensions/python/xpcom/LICENSE.txt @@ -0,0 +1,567 @@ + MOZILLA PUBLIC LICENSE + Version 1.1 + + --------------- + +1. Definitions. + + 1.0.1. "Commercial Use" means distribution or otherwise making the + Covered Code available to a third party. + + 1.1. "Contributor" means each entity that creates or contributes to + the creation of Modifications. + + 1.2. "Contributor Version" means the combination of the Original + Code, prior Modifications used by a Contributor, and the Modifications + made by that particular Contributor. + + 1.3. "Covered Code" means the Original Code or Modifications or the + combination of the Original Code and Modifications, in each case + including portions thereof. + + 1.4. "Electronic Distribution Mechanism" means a mechanism generally + accepted in the software development community for the electronic + transfer of data. + + 1.5. "Executable" means Covered Code in any form other than Source + Code. + + 1.6. "Initial Developer" means the individual or entity identified + as the Initial Developer in the Source Code notice required by Exhibit + A. + + 1.7. "Larger Work" means a work which combines Covered Code or + portions thereof with code not governed by the terms of this License. + + 1.8. "License" means this document. + + 1.8.1. "Licensable" means having the right to grant, to the maximum + extent possible, whether at the time of the initial grant or + subsequently acquired, any and all of the rights conveyed herein. + + 1.9. "Modifications" means any addition to or deletion from the + substance or structure of either the Original Code or any previous + Modifications. When Covered Code is released as a series of files, a + Modification is: + A. Any addition to or deletion from the contents of a file + containing Original Code or previous Modifications. + + B. Any new file that contains any part of the Original Code or + previous Modifications. + + 1.10. "Original Code" means Source Code of computer software code + which is described in the Source Code notice required by Exhibit A as + Original Code, and which, at the time of its release under this + License is not already Covered Code governed by this License. + + 1.10.1. "Patent Claims" means any patent claim(s), now owned or + hereafter acquired, including without limitation, method, process, + and apparatus claims, in any patent Licensable by grantor. + + 1.11. "Source Code" means the preferred form of the Covered Code for + making modifications to it, including all modules it contains, plus + any associated interface definition files, scripts used to control + compilation and installation of an Executable, or source code + differential comparisons against either the Original Code or another + well known, available Covered Code of the Contributor's choice. The + Source Code can be in a compressed or archival form, provided the + appropriate decompression or de-archiving software is widely available + for no charge. + + 1.12. "You" (or "Your") means an individual or a legal entity + exercising rights under, and complying with all of the terms of, this + License or a future version of this License issued under Section 6.1. + For legal entities, "You" includes any entity which controls, is + controlled by, or is under common control with You. For purposes of + this definition, "control" means (a) the power, direct or indirect, + to cause the direction or management of such entity, whether by + contract or otherwise, or (b) ownership of more than fifty percent + (50%) of the outstanding shares or beneficial ownership of such + entity. + +2. Source Code License. + + 2.1. The Initial Developer Grant. + The Initial Developer hereby grants You a world-wide, royalty-free, + non-exclusive license, subject to third party intellectual property + claims: + (a) under intellectual property rights (other than patent or + trademark) Licensable by Initial Developer to use, reproduce, + modify, display, perform, sublicense and distribute the Original + Code (or portions thereof) with or without Modifications, and/or + as part of a Larger Work; and + + (b) under Patents Claims infringed by the making, using or + selling of Original Code, to make, have made, use, practice, + sell, and offer for sale, and/or otherwise dispose of the + Original Code (or portions thereof). + + (c) the licenses granted in this Section 2.1(a) and (b) are + effective on the date Initial Developer first distributes + Original Code under the terms of this License. + + (d) Notwithstanding Section 2.1(b) above, no patent license is + granted: 1) for code that You delete from the Original Code; 2) + separate from the Original Code; or 3) for infringements caused + by: i) the modification of the Original Code or ii) the + combination of the Original Code with other software or devices. + + 2.2. Contributor Grant. + Subject to third party intellectual property claims, each Contributor + hereby grants You a world-wide, royalty-free, non-exclusive license + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Contributor, to use, reproduce, modify, + display, perform, sublicense and distribute the Modifications + created by such Contributor (or portions thereof) either on an + unmodified basis, with other Modifications, as Covered Code + and/or as part of a Larger Work; and + + (b) under Patent Claims infringed by the making, using, or + selling of Modifications made by that Contributor either alone + and/or in combination with its Contributor Version (or portions + of such combination), to make, use, sell, offer for sale, have + made, and/or otherwise dispose of: 1) Modifications made by that + Contributor (or portions thereof); and 2) the combination of + Modifications made by that Contributor with its Contributor + Version (or portions of such combination). + + (c) the licenses granted in Sections 2.2(a) and 2.2(b) are + effective on the date Contributor first makes Commercial Use of + the Covered Code. + + (d) Notwithstanding Section 2.2(b) above, no patent license is + granted: 1) for any code that Contributor has deleted from the + Contributor Version; 2) separate from the Contributor Version; + 3) for infringements caused by: i) third party modifications of + Contributor Version or ii) the combination of Modifications made + by that Contributor with other software (except as part of the + Contributor Version) or other devices; or 4) under Patent Claims + infringed by Covered Code in the absence of Modifications made by + that Contributor. + +3. Distribution Obligations. + + 3.1. Application of License. + The Modifications which You create or to which You contribute are + governed by the terms of this License, including without limitation + Section 2.2. The Source Code version of Covered Code may be + distributed only under the terms of this License or a future version + of this License released under Section 6.1, and You must include a + copy of this License with every copy of the Source Code You + distribute. You may not offer or impose any terms on any Source Code + version that alters or restricts the applicable version of this + License or the recipients' rights hereunder. However, You may include + an additional document offering the additional rights described in + Section 3.5. + + 3.2. Availability of Source Code. + Any Modification which You create or to which You contribute must be + made available in Source Code form under the terms of this License + either on the same media as an Executable version or via an accepted + Electronic Distribution Mechanism to anyone to whom you made an + Executable version available; and if made available via Electronic + Distribution Mechanism, must remain available for at least twelve (12) + months after the date it initially became available, or at least six + (6) months after a subsequent version of that particular Modification + has been made available to such recipients. You are responsible for + ensuring that the Source Code version remains available even if the + Electronic Distribution Mechanism is maintained by a third party. + + 3.3. Description of Modifications. + You must cause all Covered Code to which You contribute to contain a + file documenting the changes You made to create that Covered Code and + the date of any change. You must include a prominent statement that + the Modification is derived, directly or indirectly, from Original + Code provided by the Initial Developer and including the name of the + Initial Developer in (a) the Source Code, and (b) in any notice in an + Executable version or related documentation in which You describe the + origin or ownership of the Covered Code. + + 3.4. Intellectual Property Matters + (a) Third Party Claims. + If Contributor has knowledge that a license under a third party's + intellectual property rights is required to exercise the rights + granted by such Contributor under Sections 2.1 or 2.2, + Contributor must include a text file with the Source Code + distribution titled "LEGAL" which describes the claim and the + party making the claim in sufficient detail that a recipient will + know whom to contact. If Contributor obtains such knowledge after + the Modification is made available as described in Section 3.2, + Contributor shall promptly modify the LEGAL file in all copies + Contributor makes available thereafter and shall take other steps + (such as notifying appropriate mailing lists or newsgroups) + reasonably calculated to inform those who received the Covered + Code that new knowledge has been obtained. + + (b) Contributor APIs. + If Contributor's Modifications include an application programming + interface and Contributor has knowledge of patent licenses which + are reasonably necessary to implement that API, Contributor must + also include this information in the LEGAL file. + + (c) Representations. + Contributor represents that, except as disclosed pursuant to + Section 3.4(a) above, Contributor believes that Contributor's + Modifications are Contributor's original creation(s) and/or + Contributor has sufficient rights to grant the rights conveyed by + this License. + + 3.5. Required Notices. + You must duplicate the notice in Exhibit A in each file of the Source + Code. If it is not possible to put such notice in a particular Source + Code file due to its structure, then You must include such notice in a + location (such as a relevant directory) where a user would be likely + to look for such a notice. If You created one or more Modification(s) + You may add your name as a Contributor to the notice described in + Exhibit A. You must also duplicate this License in any documentation + for the Source Code where You describe recipients' rights or ownership + rights relating to Covered Code. You may choose to offer, and to + charge a fee for, warranty, support, indemnity or liability + obligations to one or more recipients of Covered Code. However, You + may do so only on Your own behalf, and not on behalf of the Initial + Developer or any Contributor. You must make it absolutely clear than + any such warranty, support, indemnity or liability obligation is + offered by You alone, and You hereby agree to indemnify the Initial + Developer and every Contributor for any liability incurred by the + Initial Developer or such Contributor as a result of warranty, + support, indemnity or liability terms You offer. + + 3.6. Distribution of Executable Versions. + You may distribute Covered Code in Executable form only if the + requirements of Section 3.1-3.5 have been met for that Covered Code, + and if You include a notice stating that the Source Code version of + the Covered Code is available under the terms of this License, + including a description of how and where You have fulfilled the + obligations of Section 3.2. The notice must be conspicuously included + in any notice in an Executable version, related documentation or + collateral in which You describe recipients' rights relating to the + Covered Code. You may distribute the Executable version of Covered + Code or ownership rights under a license of Your choice, which may + contain terms different from this License, provided that You are in + compliance with the terms of this License and that the license for the + Executable version does not attempt to limit or alter the recipient's + rights in the Source Code version from the rights set forth in this + License. If You distribute the Executable version under a different + license You must make it absolutely clear that any terms which differ + from this License are offered by You alone, not by the Initial + Developer or any Contributor. You hereby agree to indemnify the + Initial Developer and every Contributor for any liability incurred by + the Initial Developer or such Contributor as a result of any such + terms You offer. + + 3.7. Larger Works. + You may create a Larger Work by combining Covered Code with other code + not governed by the terms of this License and distribute the Larger + Work as a single product. In such a case, You must make sure the + requirements of this License are fulfilled for the Covered Code. + +4. Inability to Comply Due to Statute or Regulation. + + If it is impossible for You to comply with any of the terms of this + License with respect to some or all of the Covered Code due to + statute, judicial order, or regulation then You must: (a) comply with + the terms of this License to the maximum extent possible; and (b) + describe the limitations and the code they affect. Such description + must be included in the LEGAL file described in Section 3.4 and must + be included with all distributions of the Source Code. Except to the + extent prohibited by statute or regulation, such description must be + sufficiently detailed for a recipient of ordinary skill to be able to + understand it. + +5. Application of this License. + + This License applies to code to which the Initial Developer has + attached the notice in Exhibit A and to related Covered Code. + +6. Versions of the License. + + 6.1. New Versions. + Netscape Communications Corporation ("Netscape") may publish revised + and/or new versions of the License from time to time. Each version + will be given a distinguishing version number. + + 6.2. Effect of New Versions. + Once Covered Code has been published under a particular version of the + License, You may always continue to use it under the terms of that + version. You may also choose to use such Covered Code under the terms + of any subsequent version of the License published by Netscape. No one + other than Netscape has the right to modify the terms applicable to + Covered Code created under this License. + + 6.3. Derivative Works. + If You create or use a modified version of this License (which you may + only do in order to apply it to code which is not already Covered Code + governed by this License), You must (a) rename Your license so that + the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape", + "MPL", "NPL" or any confusingly similar phrase do not appear in your + license (except to note that your license differs from this License) + and (b) otherwise make it clear that Your version of the license + contains terms which differ from the Mozilla Public License and + Netscape Public License. (Filling in the name of the Initial + Developer, Original Code or Contributor in the notice described in + Exhibit A shall not of themselves be deemed to be modifications of + this License.) + +7. DISCLAIMER OF WARRANTY. + + COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, + WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF + DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. + THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE + IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, + YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE + COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER + OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF + ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. + +8. TERMINATION. + + 8.1. This License and the rights granted hereunder will terminate + automatically if You fail to comply with terms herein and fail to cure + such breach within 30 days of becoming aware of the breach. All + sublicenses to the Covered Code which are properly granted shall + survive any termination of this License. Provisions which, by their + nature, must remain in effect beyond the termination of this License + shall survive. + + 8.2. If You initiate litigation by asserting a patent infringement + claim (excluding declatory judgment actions) against Initial Developer + or a Contributor (the Initial Developer or Contributor against whom + You file such action is referred to as "Participant") alleging that: + + (a) such Participant's Contributor Version directly or indirectly + infringes any patent, then any and all rights granted by such + Participant to You under Sections 2.1 and/or 2.2 of this License + shall, upon 60 days notice from Participant terminate prospectively, + unless if within 60 days after receipt of notice You either: (i) + agree in writing to pay Participant a mutually agreeable reasonable + royalty for Your past and future use of Modifications made by such + Participant, or (ii) withdraw Your litigation claim with respect to + the Contributor Version against such Participant. If within 60 days + of notice, a reasonable royalty and payment arrangement are not + mutually agreed upon in writing by the parties or the litigation claim + is not withdrawn, the rights granted by Participant to You under + Sections 2.1 and/or 2.2 automatically terminate at the expiration of + the 60 day notice period specified above. + + (b) any software, hardware, or device, other than such Participant's + Contributor Version, directly or indirectly infringes any patent, then + any rights granted to You by such Participant under Sections 2.1(b) + and 2.2(b) are revoked effective as of the date You first made, used, + sold, distributed, or had made, Modifications made by that + Participant. + + 8.3. If You assert a patent infringement claim against Participant + alleging that such Participant's Contributor Version directly or + indirectly infringes any patent where such claim is resolved (such as + by license or settlement) prior to the initiation of patent + infringement litigation, then the reasonable value of the licenses + granted by such Participant under Sections 2.1 or 2.2 shall be taken + into account in determining the amount or value of any payment or + license. + + 8.4. In the event of termination under Sections 8.1 or 8.2 above, + all end user license agreements (excluding distributors and resellers) + which have been validly granted by You or any distributor hereunder + prior to termination shall survive termination. + +9. LIMITATION OF LIABILITY. + + UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT + (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL + DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, + OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR + ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY + CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, + WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER + COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN + INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF + LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY + RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW + PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE + EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO + THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. + +10. U.S. GOVERNMENT END USERS. + + The Covered Code is a "commercial item," as that term is defined in + 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer + software" and "commercial computer software documentation," as such + terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 + C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), + all U.S. Government End Users acquire Covered Code with only those + rights set forth herein. + +11. MISCELLANEOUS. + + This License represents the complete agreement concerning subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. This License shall be governed by + California law provisions (except to the extent applicable law, if + any, provides otherwise), excluding its conflict-of-law provisions. + With respect to disputes in which at least one party is a citizen of, + or an entity chartered or registered to do business in the United + States of America, any litigation relating to this License shall be + subject to the jurisdiction of the Federal Courts of the Northern + District of California, with venue lying in Santa Clara County, + California, with the losing party responsible for costs, including + without limitation, court costs and reasonable attorneys' fees and + expenses. The application of the United Nations Convention on + Contracts for the International Sale of Goods is expressly excluded. + Any law or regulation which provides that the language of a contract + shall be construed against the drafter shall not apply to this + License. + +12. RESPONSIBILITY FOR CLAIMS. + + As between Initial Developer and the Contributors, each party is + responsible for claims and damages arising, directly or indirectly, + out of its utilization of rights under this License and You agree to + work with Initial Developer and Contributors to distribute such + responsibility on an equitable basis. Nothing herein is intended or + shall be deemed to constitute any admission of liability. + +13. MULTIPLE-LICENSED CODE. + + Initial Developer may designate portions of the Covered Code as + "Multiple-Licensed". "Multiple-Licensed" means that the Initial + Developer permits you to utilize portions of the Covered Code under + Your choice of the NPL or the alternative licenses, if any, specified + by the Initial Developer in the file described in Exhibit A. + +EXHIBIT A -Mozilla Public License. + + ``The contents of this file are subject to the Mozilla Public License + Version 1.1 (the "License"); you may not use this file except in + compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the + License for the specific language governing rights and limitations + under the License. + + The Original Code is ______________________________________. + + The Initial Developer of the Original Code is ________________________. + Portions created by ______________________ are Copyright (C) ______ + _______________________. All Rights Reserved. + + Contributor(s): ______________________________________. + + Alternatively, the contents of this file may be used under the terms + of the _____ license (the "[___] License"), in which case the + provisions of [______] License are applicable instead of those + above. If you wish to allow use of your version of this file only + under the terms of the [____] License and not to allow others to use + your version of this file under the MPL, indicate your decision by + deleting the provisions above and replace them with the notice and + other provisions required by the [___] License. If you do not delete + the provisions above, a recipient may use your version of this file + under either the MPL or the [___] License." + + [NOTE: The text of this Exhibit A may differ slightly from the text of + the notices in the Source Code files of the Original Code. You should + use the text of this Exhibit A rather than the text found in the + Original Code Source Code for Your Modifications.] + + ---------------------------------------------------------------------- + + AMENDMENTS + + The Netscape Public License Version 1.1 ("NPL") consists of the + Mozilla Public License Version 1.1 with the following Amendments, + including Exhibit A-Netscape Public License. Files identified with + "Exhibit A-Netscape Public License" are governed by the Netscape + Public License Version 1.1. + + Additional Terms applicable to the Netscape Public License. + I. Effect. + These additional terms described in this Netscape Public + License -- Amendments shall apply to the Mozilla Communicator + client code and to all Covered Code under this License. + + II. "Netscape's Branded Code" means Covered Code that Netscape + distributes and/or permits others to distribute under one or more + trademark(s) which are controlled by Netscape but which are not + licensed for use under this License. + + III. Netscape and logo. + This License does not grant any rights to use the trademarks + "Netscape", the "Netscape N and horizon" logo or the "Netscape + lighthouse" logo, "Netcenter", "Gecko", "Java" or "JavaScript", + "Smart Browsing" even if such marks are included in the Original + Code or Modifications. + + IV. Inability to Comply Due to Contractual Obligation. + Prior to licensing the Original Code under this License, Netscape + has licensed third party code for use in Netscape's Branded Code. + To the extent that Netscape is limited contractually from making + such third party code available under this License, Netscape may + choose to reintegrate such code into Covered Code without being + required to distribute such code in Source Code form, even if + such code would otherwise be considered "Modifications" under + this License. + + V. Use of Modifications and Covered Code by Initial Developer. + V.1. In General. + The obligations of Section 3 apply to Netscape, except to + the extent specified in this Amendment, Section V.2 and V.3. + + V.2. Other Products. + Netscape may include Covered Code in products other than the + Netscape's Branded Code which are released by Netscape + during the two (2) years following the release date of the + Original Code, without such additional products becoming + subject to the terms of this License, and may license such + additional products on different terms from those contained + in this License. + + V.3. Alternative Licensing. + Netscape may license the Source Code of Netscape's Branded + Code, including Modifications incorporated therein, without + such Netscape Branded Code becoming subject to the terms of + this License, and may license such Netscape Branded Code on + different terms from those contained in this License. + + VI. Litigation. + Notwithstanding the limitations of Section 11 above, the + provisions regarding litigation in Section 11(a), (b) and (c) of + the License shall apply to all disputes relating to this License. + + EXHIBIT A-Netscape Public License. + + "The contents of this file are subject to the Netscape Public + License Version 1.1 (the "License"); you may not use this file + except in compliance with the License. You may obtain a copy of + the License at http://www.mozilla.org/NPL/ + + Software distributed under the License is distributed on an "AS + IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + implied. See the License for the specific language governing + rights and limitations under the License. + + The Original Code is Mozilla Communicator client code, released + March 31, 1998. + + The Initial Developer of the Original Code is Netscape + Communications Corporation. Portions created by Netscape are + Copyright (C) 1998-1999 Netscape Communications Corporation. All + Rights Reserved. + + Contributor(s): ______________________________________. + + Alternatively, the contents of this file may be used under the + terms of the _____ license (the "[___] License"), in which case + the provisions of [______] License are applicable instead of + those above. If you wish to allow use of your version of this + file only under the terms of the [____] License and not to allow + others to use your version of this file under the NPL, indicate + your decision by deleting the provisions above and replace them + with the notice and other provisions required by the [___] + License. If you do not delete the provisions above, a recipient + may use your version of this file under either the NPL or the + [___] License." diff --git a/mozilla/extensions/python/xpcom/__init__.py b/mozilla/extensions/python/xpcom/__init__.py new file mode 100644 index 00000000000..a418c8e40f9 --- /dev/null +++ b/mozilla/extensions/python/xpcom/__init__.py @@ -0,0 +1,53 @@ +# Copyright (c) 2000-2001 ActiveState Tool Corporation. +# See the file LICENSE.txt for licensing information. + +# The XPCOM (Cross Platform COM) package. +import exceptions + +#import sys +#sys.stdout = open("pystdout.log", "w") + +# A global "verbose" flag - currently used by the +# server package to print trace messages +verbose = 0 +hr_map = {} + +# The standard XPCOM exception object. +# Instances of this class are raised by the XPCOM extension module. +class Exception(exceptions.Exception): + def __init__(self, errno, message = None): + assert int(errno) == errno, "The errno param must be an integer" + self.errno = errno + self.message = message + exceptions.Exception.__init__(self, errno) + def __str__(self): + if not hr_map: + import nsError + for name, val in nsError.__dict__.items(): + if type(val)==type(0): + hr_map[val] = name + message = self.message + if message is None: + message = hr_map.get(self.errno) + if message is None: + message = "" + return "0x%x (%s)" % (self.errno, message) + +# An alias for Exception - allows code to say "from xpcom import COMException" +# rather than "Exception" - thereby preventing clashes. +COMException = Exception + +# Exceptions thrown by servers. It can be good for diagnostics to +# differentiate between a ServerException (which was presumably explicitly thrown) +# and a normal exception which may simply be propagating down. +# (When ServerException objects are thrown across the XPConnect +# gateway they will be converted back to normal client exceptions if +# subsequently re-caught by Python +class ServerException(Exception): + def __init__(self, errno=None, *args, **kw): + if errno is None: + import nsError + errno = nsError.NS_ERROR_FAILURE + Exception.__init__(self, errno, *args, **kw) + +# Some global functions. diff --git a/mozilla/extensions/python/xpcom/client/__init__.py b/mozilla/extensions/python/xpcom/client/__init__.py new file mode 100644 index 00000000000..98ca1c1350a --- /dev/null +++ b/mozilla/extensions/python/xpcom/client/__init__.py @@ -0,0 +1,247 @@ +# Copyright (c) 2000-2001 ActiveState Tool Corporation. +# See the file LICENSE.txt for licensing information. + +import os +import new +from xpcom import xpt, _xpcom, COMException, nsError + +XPTC_InvokeByIndex = _xpcom.XPTC_InvokeByIndex + +_just_int_interfaces = ["nsISupportsPRInt32", "nsISupportsPRInt16", "nsISupportsPRUint32", "nsISupportsPRUint16", "nsISupportsPRUint8", "nsISupportsPRBool"] +_just_long_interfaces = ["nsISupportsPRInt64", "nsISupportsPRUint64"] +_just_float_interfaces = ["nsISupportsDouble", "nsISupportsFloat"] +# When doing a specific conversion, the order we try the interfaces in. +_int_interfaces = _just_int_interfaces + _just_float_interfaces +_long_interfaces = _just_long_interfaces + _just_int_interfaces + _just_float_interfaces +_float_interfaces = _just_float_interfaces + _just_long_interfaces + _just_int_interfaces + +method_template = """ +def %s(self, %s): + return XPTC_InvokeByIndex(self._comobj_, %d, + (%s, + (%s))) +""" +def _MakeMethodCode(method): + # Build a declaration + param_no = 0 + param_decls = [] + param_flags = [] + param_names = [] + used_default = 0 + for param in method.params: + param_no = param_no + 1 + param_name = "Param%d" % (param_no,) + param_default = "" + if not param.hidden_indicator and param.IsIn() and not param.IsDipper(): + if param.IsOut() or used_default: # If the param is "inout", provide a useful default for the "in" direction. + param_default = " = None" + used_default = 1 # Once we have used one once, we must for the rest! + param_decls.append(param_name + param_default) + param_names.append(param_name) + + type_repr = xpt.MakeReprForInvoke(param) + param_flags.append( (param.param_flags,) + type_repr ) + sep = ", " + param_decls = sep.join(param_decls) + if len(param_names)==1: # Damn tuple reprs. + param_names = param_names[0] + "," + else: + param_names = sep.join(param_names) + # A couple of extra newlines make them easier to read for debugging :-) + return method_template % (method.name, param_decls, method.method_index, tuple(param_flags), param_names) + +# Keyed by IID, each item is a tuple of (methods, getters, setters) +interface_cache = {} + +# Fully process the interface - generate method code, etc. +def BuildInterfaceInfo(iid): + ret = interface_cache.get(iid, None) + if ret is None: + # Build the data for the cache. + method_code_blocks = [] + getters = {} + setters = {} + method_names = [] + + interface = xpt.Interface(iid) + for m in interface.methods: + if not m.IsNotXPCOM() and \ + not m.IsHidden() and \ + not m.IsConstructor(): + # Yay - a method we can use! + if m.IsSetter(): + param_flags = map(lambda x: (x.param_flags,) + xpt.MakeReprForInvoke(x), m.params) + setters[m.name] = m.method_index, param_flags + elif m.IsGetter(): + param_flags = map(lambda x: (x.param_flags,) + xpt.MakeReprForInvoke(x), m.params) + getters[m.name] = m.method_index, param_flags + else: + method_names.append(m.name) + method_code_blocks.append(_MakeMethodCode(m)) + + # Build the constants. + constants = {} + for c in interface.constants: + constants[c.name] = c.value + # Build the methods - We only build function objects here + # - they are bound to each instance at instance creation. + methods = {} + if len(method_code_blocks)!=0: + method_code = "\n".join(method_code_blocks) +## print "Method Code:" +## print method_code + codeObject = compile(method_code, "","exec") + # Exec the code object + tempNameSpace = {} + exec codeObject in globals(), tempNameSpace # self.__dict__, self.__dict__ + for name in method_names: + methods[name] = tempNameSpace[name] + ret = methods, getters, setters, constants + interface_cache[iid] = ret + return ret + +def Component(ob, iid): + ob_name = None + if not hasattr(ob, "IID"): + ob_name = ob + cm = _xpcom.NS_GetGlobalComponentManager() + ob = cm.CreateInstanceByContractID(ob) + return Interface(ob, iid) + +class Interface: + """Implements a dynamic interface using xpcom reflection. + """ + def __init__(self, ob, iid, object_name = None): + ob = ob.QueryInterface(iid, 0) # zero means "no auto-wrap" + self.__dict__['_comobj_'] = ob + # Hack - the last interface only! + methods, getters, setters, constants = BuildInterfaceInfo(iid) + self.__dict__['_interface_infos_'] = getters, setters + self.__dict__['_interface_methods_'] = methods # Unbound methods. + + self.__dict__.update(constants) + # We remember the constant names to prevent the user trying to assign to them! + self.__dict__['_constant_names_'] = constants.keys() + + if object_name is None: + object_name = "object with interface '%s'" % (iid.name,) + self.__dict__['_object_name_'] = object_name + + def __cmp__(self, other): + try: + other = other._comobj_ + except AttributeError: + pass + return cmp(self._comobj_, other) + + def __hash__(self): + return hash(self._comobj_) + + def __repr__(self): + return "" % (self._comobj_.IID.name,) + + # See if the object support strings. + def __str__(self): + try: + self._comobj_.QueryInterface(_xpcom.IID_nsISupportsString) + return str(self._comobj_) + except COMException: + return self.__repr__() + + # Try the numeric support. + def _do_conversion(self, interface_names, cvt): + iim = _xpcom.XPTI_GetInterfaceInfoManager() + for interface_name in interface_names: + iid = iim.GetInfoForName(interface_name).GetIID() + try: + prim = self._comobj_.QueryInterface(iid) + return cvt(prim.data) + except COMException: + pass + raise ValueError, "This object does not support automatic numeric conversion to this type" + + def __int__(self): + return self._do_conversion(_int_interfaces, int) + + def __long__(self): + return self._do_conversion(_long_interfaces, long) + + def __float__(self): + return self._do_conversion(_float_interfaces, float) + + def __getattr__(self, attr): + # Allow the underlying interface to provide a better implementation if desired. + ret = getattr(self.__dict__['_comobj_'], attr, None) + if ret is not None: + return ret + # Do the function thing first. + unbound_method = self.__dict__['_interface_methods_'].get(attr) + if unbound_method is not None: + return new.instancemethod(unbound_method, self, self.__class__) + + getters, setters = self.__dict__['_interface_infos_'] + info = getters.get(attr) + if info is None: + raise AttributeError, "XPCOM component '%s' has no attribute '%s'" % (self._object_name_, attr) + method_index, param_infos = info + if len(param_infos)!=1: # Only expecting a retval + raise RuntimeError, "Can't get properties with this many args!" + args = ( param_infos, () ) + return XPTC_InvokeByIndex(self._comobj_, method_index, args) + + def __setattr__(self, attr, val): + # If we already have a __dict__ item of that name, and its not one of + # our constants, we just directly set it, and leave. + if self.__dict__.has_key(attr) and attr not in self.__dict__['_constant_names_']: + self.__dict__[attr] = val + return + # Start sniffing for what sort of attribute this might be? + getters, setters = self.__dict__['_interface_infos_'] + info = setters.get(attr) + if info is None: + raise AttributeError, "XPCOM component '%s' can not set attribute '%s'" % (self._object_name_, attr) + method_index, param_infos = info + if len(param_infos)!=1: # Only expecting a single input val + raise RuntimeError, "Can't set properties with this many args!" + real_param_infos = ( param_infos, (val,) ) + return XPTC_InvokeByIndex(self._comobj_, method_index, real_param_infos) + +# Called by the _xpcom C++ framework to wrap interfaces up just +# before they are returned. +def MakeInterfaceResult(ob, iid): + return Interface(ob, iid) + +class WeakReference: + """A weak-reference object. You construct a weak reference by passing + any COM object you like. If the object does not support weak + refs, you will get a standard NS_NOINTERFACE exception. + + Once you have a weak-reference, you can "call" the object to get + back a strong reference. Eg: + + >>> some_ob = components.classes['...] + >>> weak_ref = WeakReference(some_ob) + >>> new_ob = weak_ref() # new_ob is effectively "some_ob" at this point + >>> # EXCEPT: new_ob may be None of some_ob has already died - a + >>> # weak reference does not keep the object alive (that is the point) + + You should never hold onto this resulting strong object for a long time, + or else you defeat the purpose of the weak-reference. + """ + def __init__(self, ob, iid = None): + swr = Interface(ob, _xpcom.IID_nsISupportsWeakReference) + self._comobj_ = Interface(swr.GetWeakReference(), _xpcom.IID_nsIWeakReference) + if iid is None: + try: + iid = ob.IID + except AttributeError: + iid = _xpcom.IID_nsISupports + self._iid_ = iid + def __call__(self, iid = None): + if iid is None: iid = self._iid_ + try: + return Interface(self._comobj_.QueryReferent(iid), iid) + except COMException, details: + if details.errno != nsError.NS_ERROR_NULL_POINTER: + raise + return None diff --git a/mozilla/extensions/python/xpcom/components.py b/mozilla/extensions/python/xpcom/components.py new file mode 100644 index 00000000000..8f95e6f24c4 --- /dev/null +++ b/mozilla/extensions/python/xpcom/components.py @@ -0,0 +1,183 @@ +# Copyright (c) 2000-2001 ActiveState Tool Corporation. +# See the file LICENSE.txt for licensing information. + +# This module provides the JavaScript "components" interface +import xpt +import xpcom, _xpcom +import xpcom.client +import xpcom.server +import types + +StringTypes = [types.StringType, types.UnicodeType] + +# The "manager" object. +manager = xpcom.client.Interface(_xpcom.NS_GetGlobalComponentManager(), _xpcom.IID_nsIComponentManager) + +# The "interfaceInfoManager" object - JS doesnt have this. +interfaceInfoManager = _xpcom.XPTI_GetInterfaceInfoManager() + +# The "Exception" object +Exception = xpcom.Exception + +# Base class for our collections. +# It appears that all objects supports "." and "[]" notation. +# eg, "interface.nsISupports" or interfaces["nsISupports"] +class _ComponentCollection: + # Bases are to over-ride 2 methods. + # _get_one(self, name) - to return one object by name + # _build_dict - to return a dictionary which provide access into + def __init__(self): + self._dict_data = None + def keys(self): + if self._dict_data is None: + self._dict_data = self._build_dict() + return self._dict_data.keys() + def items(self): + if self._dict_data is None: + self._dict_data = self._build_dict() + return self._dict_data.items() + def values(self): + if self._dict_data is None: + self._dict_data = self._build_dict() + return self._dict_data.values() + def has_key(self, key): + if self._dict_data is None: + self._dict_data = self._build_dict() + return self._dict_data.has_key(key) + + def __len__(self): + if self._dict_data is None: + self._dict_data = self._build_dict() + return len(self._dict_data) + + def __getattr__(self, attr): + if self._dict_data is not None and self._dict_data.has_key(attr): + return self._dict_data[attr] + return self._get_one(attr) + def __getitem__(self, item): + if self._dict_data is not None and self._dict_data.has_key(item): + return self._dict_data[item] + return self._get_one(item) + + +class _Interface: + # An interface object. + def __init__(self, name, iid): + # Bypass self.__setattr__ when initializing attributes. + d = self.__dict__ + d['_iidobj_'] = iid # Allows the C++ framework to treat this as a native IID. + d['name'] = name + d['constants'] = None # Built first time an attribute is asked for. + def __cmp__(self, other): + this_iid = self._iidobj_ + other_iid = getattr(other, "_iidobj_", other) + return cmp(this_iid, other_iid) + def __hash__(self): + return hash(self._iidobj_) + def __str__(self): + return str(self._iidobj_) + def __getitem__(self, item): + raise TypeError, "components.interface objects are not subscriptable" + def __setitem__(self, item, value): + raise TypeError, "components.interface objects are not subscriptable" + def __setattr__(self, attr, value): + raise AttributeError, "Can not set attributes on components.Interface objects" + def __getattr__(self, attr): + # Support constants as attributes. + c = self.constants + if c is None: + c = {} + i = xpt.Interface(self._iidobj_) + for c_ob in i.constants: + c[c_ob.name] = c_ob.value + self.__dict__['constants'] = c + if c.has_key(attr): + return c[attr] + raise AttributeError, "'%s' interfaces do not define a constant '%s'" % (self.name, attr) + + +class _Interfaces(_ComponentCollection): + def _get_one(self, name): + item = interfaceInfoManager.GetInfoForName(name) + return _Interface(item.GetName(), item.GetIID()) + + def _build_dict(self): + ret = {} + enum = interfaceInfoManager.EnumerateInterfaces() + while not enum.IsDone(): + # Call the Python-specific FetchBlock, to keep the loop in C. + items = enum.FetchBlock(500, _xpcom.IID_nsIInterfaceInfo) + # This shouldnt be necessary, but appears to be so! + for item in items: + ret[item.GetName()] = _Interface(item.GetName(), item.GetIID()) + return ret + +# And the actual object people use. +interfaces = _Interfaces() + +del _Interfaces # Keep our namespace clean. + +################################################# +class _Class: + def __init__(self, contractid): + self.contractid = contractid + def __getattr__(self, attr): + if attr == "clsid": + rc = manager.contractIDToClassID(self.contractid) + # stash it away - it can never change! + self.clsid = rc + return rc + raise AttributeError, "%s class has no attribute '%s'" % (self.contractid, attr) + def createInstance(self, iid = None): + import xpcom.client + if iid is None: + iid = _xpcom.IID_nsISupports + elif type(iid) in StringTypes and len(iid)>0 and iid[0] != "{": + iid = getattr(interfaces, iid) + return xpcom.client.Component(self.contractid, iid) + def getService(self, iid): + return _xpcom.GetGlobalServiceManager().getService(self.contractid, iid) + +class _Classes(_ComponentCollection): + def __init__(self): + _ComponentCollection.__init__(self) + def _get_one(self, name): + # XXX - Need to check the contractid is valid! + return _Class(name) + + def _build_dict(self): + ret = {} + enum = manager.EnumerateContractIDs() + while not enum.IsDone(): + # Call the Python-specific FetchBlock, to keep the loop in C. + items = enum.FetchBlock(500) + for item in items: + name = str(item) + ret[name] = _Class(name) + return ret + +classes = _Classes() + +del _Classes + +del _ComponentCollection + +# The ID function +ID = _xpcom.IID + +# A helper to cleanup our namespace as xpcom shuts down. +class _ShutdownObserver: + _com_interfaces_ = interfaces.nsIObserver + def Observe(self, service, topic, extra): + global manager + global interfaceInfoManager + global _shutdownObserver + manager = interfaceInfoManager = _shutdownObserver = None + +svc = _xpcom.GetGlobalServiceManager().GetService("@mozilla.org/observer-service;1", interfaces.nsIObserverService) +# Observers will be QI'd for a weak-reference, so we must keep the +# observer alive ourself, and must keep the COM object alive, +# _not_ just the Python instance!!! +_shutdownObserver = xpcom.server.WrapObject(_ShutdownObserver(), interfaces.nsIObserver) +svc.AddObserver(_shutdownObserver, "xpcom-shutdown") +del svc, _ShutdownObserver diff --git a/mozilla/extensions/python/xpcom/doc/advanced.html b/mozilla/extensions/python/xpcom/doc/advanced.html new file mode 100644 index 00000000000..789077275f4 --- /dev/null +++ b/mozilla/extensions/python/xpcom/doc/advanced.html @@ -0,0 +1,135 @@ + + + + + + + + +Python XPCOM Advanced Topics + + + + +

Python XPCOM Advanced Topics

+ +

This document contains a series of tidbits that don't fit +anywhere else. As the Python XPCOM Package documentation matures, most of +these topics will have another home.

+ +

XPCOM Services

+

An XPCOM service is simply a singleton registered by name.  Python has +full support for both using and implementing XPCOM services.  To use a +service, use xpcom.components.services just like the JavaScript +counterpart.  There is nothing special about implementing a service in +Python; see the standard XPCOM documentation on services for more information.

+ +

nsISupports Primitives.

+ +

There is a set of interfaces described in nsISupportsPrimitives.idl, which I +term collectively the nsISupports Primitives Interfaces.  These +are a set of interfaces a component can support to allow automatic conversion to +and from many basic types.  For example, an interface can define that it +supports the nsISupportsString interface, and this could be used by any +program that wishes to get a string representation of the object.  If an +interface wishes to expose itself as a "boolean value", it may choose +to support the nsISupportsPRBool interface.

+

When you call an XPCOM object (i.e., you have an XPCOM interface you are +calling), you can use +the builtin functions str(), int(), long() etc., on the +object.  In the +case of str(), if the object does not support the nsISupportsString +or nsISupportsWString interfaces, the default string str() for the +object will be returned (i.e., what is normally returned for most XPCOM objects - +support for these interface is not very common!).  In the case of the numeric functions, a ValueError +exception will be raised if the objects do not support any interface that can be +used for the conversion. ValueError is used instead of TypeError, +as the type itself (i.e., an XPCOM object) can sometimes be used in this context - +hence it is the specific value of the object that is the problem.

+

The use of repr() on an XPCOM interface object prevents support +attempts for these interfaces, and allows you to see the +"real" object, rather than what the object wants you to see!

+

When you implement an XPCOM object, you have two choices for implementation +of these interfaces:

+
    +
  • You can explicitly handle these interfaces like any other interface.  + In this case, you have full control.  However, if you + implement only one of these standard interfaces, then you are only + overriding the default behavior for that specific interface - all other + interfaces not explicitly listed in your class will still get the behavior + described below.
    +
  • +
  • If your class does not define support for these interfaces, the framework + will use standard Python class semantics to implement them - i.e., if your + class provides a __str__ method, it will be used to implement nsISupportsString + and nsISupportsWString, if you provide __int__, __long__, + __float__ etc., methods, they will be used to implement the numeric + interfaces.  If your class defines no such special methods, then the + QueryInterface() for those interfaces fails (rather than the QI succeeding + and the operation to fetch the data failing).
  • +
+
+

This allows for an interesting feature that would not normally be +possible.  Consider Python code that does a str() on an  XPCOM +interface, and where the XPCOM interface itself is implemented in Python and +provides a __str__ method.  The str() on the original +interface queries for the nsISupportsString interface.  The +Python implemented object responds to this interface and delegates to the __str__ +method. At the end of all this, str() returns the same result +as if the objects were native Python objects with no XPCOM layer in between.

+ +
+ +

Enumerators

+ +

The primary enumerator used by XPCOM is nsISimpleEnumerator. +Although the Python XPCOM package has full support for nsIEnumerator, +since this interface is not "scriptable", you should avoided using it in interfaces +you design.

+ +

When you use nsISimpleEnumerator from Python, the following enhancements +are available:

+
    +
  • The GetNext() method takes an optional IID as a parameter. If + this is specified, the returned object will be of this interface.  This + prevents the manual QueryInterface() generally required from other + languages.
  • +
  • There is a FetchBlock(num, [iid]) method, which fetches the + specified number of elements in one operation and returns a Python + list. This can be useful for large enumerator sets, so the loop + iterating the elements runs at full C++ speed.
  • +
+

nsIEnumerator has similar enhancements.

+

When implementing a Python XPCOM object, the Python class xpcom.server.enumerator.SimpleEnumerator() +can be used.  You can pass a standard Python sequence (list, etc), and it +will be correctly wrapped in an nsISimpleEnumerator interface.

+

Files

+

The Python XPCOM package provides an xpcom.file module.  This implements +a Python-like file object on top of the XPCOM/Mozilla stream interfaces.  +When run from within the Mozilla environment, this allows you to open almost any +URL supported by Mozilla (including "chrome://" etc.,).

+

See this module for more information, including test code.

+

XPCOM Object Identity

+

XPCOM has defined rules for object identity and for how objects must behave +in their QueryInterface() implementations.  The Python XPCOM framework +manages this for you; your code can return new Python instances etc., when +responding to new interfaces, and the framework itself will ensure the XPCOM +semantics are followed.  Critically, the framework provides no mechanism +for breaking these rules.

+

Policies

+

The Python XPCOM framework has the concept of "policies" that +define how XPCOM semantics are mapped to Python objects.  It is the policy +that implements delegation of QueryInterface(), translates property +references into direct property references, and failing that, "get_name" +and "set_name" calls, decides how to handle exceptions in the +component, and so on.

+

The default policy is very flexible and suitable for most purposes. +Indeed, the Komodo project has never had to implement a custom policy. +However, you should be aware the feature exists should you wish to do some +bizarre things, such as using Python as a bridge between XPCOM and some other +component technology.

+ + + + diff --git a/mozilla/extensions/python/xpcom/doc/architecture.html b/mozilla/extensions/python/xpcom/doc/architecture.html new file mode 100644 index 00000000000..c04e4962d93 --- /dev/null +++ b/mozilla/extensions/python/xpcom/doc/architecture.html @@ -0,0 +1,84 @@ + + + + + + + + +Architecture + + + + +

Python XPCOM Package Architecture

+

Architecture

+

Much of the design for the Python XPCOM Package has been borrowed from the Python MS-COM +extensions in win32com. Most of the major limitations and drawbacks in the win32com +design have been addressed, mainly "auto-wrapping" of +interface objects, which is not supported by win32com.

+

Like win32com, this architecture includes the concept of client COM and server +COM.

+

Client COM:

+
    +
  • calls other interfaces
  • +
  • is supported by PyInterfaces implemented in C++, which assists +in making the COM calls
  • +
  • is supported by PyGateways, which assists in receiving +external COM calls and dispatching them to the correct Python object
  • +
  • is supported in the xpcom/client package
  • +
+

Server COM:

+
    +
  • implements interfaces for use by other XPCOM applications or components
  • +
  • is +supported in the xpcom/server package
  • +
+

The XPConnect framework is very powerful, and far exceeds what COM's +IDispatch can offer.  Thus, we are able to get by with far fewer interfaces +supported in the C++ level, and defer most things to the Python code that uses +XPConnect.  As a result, the requirement for a huge number of interfaces to +exist in the .pyd does not exist.  There are, however, a number of +interfaces that do require native C++ support: these are interfaces +required to "boot" the XPConnect support (i.e., the interfaces that are +used to get information about interfaces), and also two gateways that need to +work without interface information available. This last requirement is +due to the XPCOM shutdown-ordering - it may be a bug, but is not an unreasonable +amount of code anyway.

+

Auto-wrapping of COM objects is supported by both client COM and +server COM. For client COM, auto-wrapping means that the +Python programmer always sees Python "component" objects, rather than +raw C++ interface objects; to the user, it all appears to "just +work".  This is a major source of frustration in the win32com +framework.

+

For server COM, auto-wrapping means that you can +pass Python instances wherever a COM object is expected. If the Python +instance supports COM interfaces, by virtue of having a _com_interfaces_ +attribute that lists the interface requested, it will be automatically wrapped +in the correct COM object. 

+

Error Handling: The C++ framework has good error handling support, +and uses the XPCOM console service to log debug messages, Python exceptions and +tracebacks.  win32com does not have good exception/traceback support +at the C++ level, mainly because COM does not define a service like +the console where debug messages can go.  This does mean that in Mozilla +release builds, these debug messages are likely to be lost, but the --console +command line option to a release Mozilla will get them back.  Therefore, +the other error-support utilities, such as the error callbacks made on the +policy object, may be used.

+

Component Loader, Modules and Factories:  XPCOM has the concept +of a component loader - a module used to load all components of a +particular type.  For example, the moz.jsloader.1 component loads all +the JavaScript components. Similarly, the moz.pyloader.1 +component loads all Python components.  However, unlike +JavaScript, the Python component loader is actually implemented in Python +itself! Since the Python component loader can not be used to load +itself, this component has some special code, pyloader.dll, to boot-strap itself.

+

This means is that all XPCOM components, including the Python loader itself and all +XPCOM module and factory interfaces, are implemented in +Python. There are no components or interfaces implemented purely in C++ +in this entire package!

+ + + + diff --git a/mozilla/extensions/python/xpcom/doc/configure.html b/mozilla/extensions/python/xpcom/doc/configure.html new file mode 100644 index 00000000000..b27e46ab04f --- /dev/null +++ b/mozilla/extensions/python/xpcom/doc/configure.html @@ -0,0 +1,198 @@ + + + + + + + + +Configuring your Environment + + + + +

Building, Configuring and Testing Python XPCOM Package

+

This document attempts to explain how to build, configure and test the +Python XPCOM Package. This document assumes you have already successfully +built +Mozilla from source and your environment is currently set up for such a build - +see the Mozilla build documentation +for more information.

+

This section covers:

+ +

Configuring your Environment

+

In addition to the existing environment requirements for building Mozilla itself, the +Python XPCOM package has the following requirements:

+
    +
  • +Mozilla Directory - Ensure the Mozilla + bin directory is on your PATH +(LD_LIBRARY_PATH +on Unix) so Python can locate the xpcom.dll module needed for +core XPCOM services.
  • +
  • PYTHONPATH - PYTHONPATH needs to be set appropriately. Ensure +the parent to this xpcom directory is on your path so "import +xpcom" locates this directory.  Note that the Mozilla components directory does not need to be on the PYTHONPATH, +but any modules used by your components must be set correctly.  If anything +is wrong here you should get a normal ImportError.
  • +
+
+

Note that on Windows, the PYTHONPATH is generally maintained in the + Registry; however, you can set this variable at a DOS prompt, and it will still be +added to the core PYTHONPATH. +

+

Building

+

The initial release of the Python XPCOM package has a very simple, hard-coded +build process.  The intention is to include this package in the Mozilla +source tree, and integrate the build with the Mozilla build.  Until this +happens, perform the following steps:

+
    +
  • Ensure you can successfully build Mozilla itself from source-code.
  • +
  • Unpack this source code, and change to the xpcom directory under + the root PyXPCOM directory.
  • +
  • For Windows, edit makefile.stupid.win, while for Linux, edit makefile.stupid.linux
  • +
  • As per the instructions at the top of the makefile, edit the MOZ_SRC, + INSTALLDIR and PYTHON_SRC definitions appropriately.
  • +
+

Build Example: Building on Windows

+

For this example, we will assume that the source-code for Mozilla is in C:\src\mozilla, +the source-code for the PyXPCOM package is in c:\src\pyxpcom. Further, we assume +that you have Python 2.0 installed in C:\Python20, and wish to install +the built Python XPCOM package so it exists in C:\Python20\xpcom.

+

To build the package in this environment, you would perform the following +steps:

+
    +
  • Unpack the PyXPCOM package source-code into the appropriate directory
  • +
  • Edit makefile.stupid.win with the following changes:
    + MOZ_SRC=c:\src
    + INSTALLDIR=C:\Python20
    + PYTHON_SRC=C:\Python20
  • +
  •  From the top-level Python xpcom source directory (i.e., the + directory with makefile.stupid.win), execute the command:
    + nmake -f makefile.stupid.win install
  • +
+

NOTE: To build a Debug version of the Python XPCOM library, you can add DEBUG=1 +to the nmake command-line.

+

If everything appears to work and you are brave, you may also like to execute +make -f makefile.stupid.win test to execute the test script.  +Otherwise, continue to the following section where we confirm the installation +step-by-step.

+

Build Example: Building on Linux

+

For this example, we will assume that the source-code for Mozilla is in ~/src/mozilla, +the source-code for the PyXPCOM package is in ~/src/pyxpcom. Further, we assume +that you have ActivePython 2.0 installed in /usr/local/ActivePython2.0, +and wish to install the built Python XPCOM package so it exists in /usr/local/ActivePython2.0/lib/python2.0/site-packages/xpcom.

+

To build the package in this environment, you would perform the following +steps:

+
    +
  • Unpack the PyXPCOM package source-code into the appropriate directory
  • +
  • Edit makefile.stupid.linux with the following changes (substituting + username accordingly):
    + MOZ_SRC=/home/username/src
    + INSTALLDIR=/usr/local/ActivePython-2.0/site-packages
    + PYTHON_SRC=/usr/local/ActivePython-2.0
  • +
  • Build: As a regular user, from the top-level Python xpcom source + directory (i.e., the directory with makefile.stupid.linux), execute + the command:
    + make -f makefile.stupid.linux DEBUG=1
  • +
  • Install: Log in as a user with permissions to install into the relevant directories + (usually the root user).  From the top-level Python xpcom source + directory (i.e., the directory with makefile.stupid.linux), execute + the command:
    + make -f makefile.stupid.linux DEBUG=1 install
  • +
  • Switch back to a regular user, ready for testing!
  • +
+

NOTE: The instructions above are for a Debug build, as this is the default +Mozilla build type.  If you have configured Mozilla to build a Release +version of Mozilla, you can drop the DEBUG=1 option.  It is important that +PyXPCOM and Mozilla itself are consistent with respect to Release and Debug +builds.  For more details, please consult the makefile.

+

If everything appears to work and you are brave, you may also like to execute +make -f makefile.stupid.linux test to execute the test script.  +Otherwise, continue to the following section where we confirm the installation +step--by-step.

+

Testing your Setup

+

The Python XPCOM Package has a complete test suite.  If you are +impatient, you can simply execute make -f makefile.stupid.linux test (for +Linux) or nmake -f makefile.stupid.linux test (for Windows).  If +this command indicates that the tests succeeded, then you can ignore the rest of +this section.

+

In the rest of this section, we walk through some simpler tests a step at a time, +to help diagnose any problems.

+

Note: We recommend you do all your testing outside of mozilla.exe; it is far simpler to test all of +this using the PyXPCOM package stand-alone.

+

Note: On Windows, if you use a debug build of Mozilla (i.e., in dist\WIN32_D.OBJ\bin), + you must use python_d.exe; if you use a release build (i.e., in + a dist\WIN32_O.OBJ\bin directory), you must use python.exe.  +makefile.stupid.win handles this automatically.

+

To test your setup:

+
    +
  1. Start Python, and check
    + >>> import xpcom
    + works. If not, check your PYTHONPATH - the + main PyXPCOM package can not be located..
  2. +
  3. Check
    + >>> import xpcom._xpcom

    + +works. If not, then most likely your Mozilla + directory is not on your path, or something is wrong with _xpcom(_d).pyd/_xpcommodule.so.
  4. + +
  5. Next run a simple test: test/test_misc.py. With a Windows debug build, the command may look like:
    + C:\Anywhere> python_d \src\python\xpcom\test\test_misc.py
    +
    or on Linux
    + /home/user/src/mozilla/dist/bin$ python /home/user/src/python/xpcom/test/test_misc.py
  6. +
+

If you can't get this going, you won't get much further! If you do, the +next step is to register our test component and run our full test suite.

+

Registering the Loader and Test Component

+

First register the generic Python loader. For instructions, see the architecture +document. Do this only once, regardless of how many +Python components you have.  Then install the test component itself, and +finally you can test it!

+

Registering the Python Loader and Component

+

To register the Python Loader and Component:

+
    +
  1. Ensure the build process has put pyloader.dll (or modpyloader.so + for Unix), and the files py_test_component.py and py_test_component.idl into + the Mozilla bin/components directory.  If not, copy the files + there manually.
  2. +
  3. Run regxpcomregxpcom is a standard Mozilla + executable, found in the bin directory, that detects whether the DLL and .py + files have been added and registers them accordingly.  You should + see a few messages that include the following:
  4. +
+
+
Registering: PythonComponentLoader
+Registered 1 Python components in pyloader.dll
+nsNativeComponentLoader: autoregistering succeeded
+Auto-registering all Python components in F:\src\mozilla\dist\WIN32_D.OBJ\bin\components
+Registering: PythonTestComponent
+Registered 1 Python components in py_test_component.py
+
+

If so (or you see no message at all), you are ready to run the test suite.

+

Note: If you execute this same step a second time, you will not +see any of the above mentioned messages. XPCOM knows that nothing has +changed since you last ran regxpcom, so nothing is registered.  If +you do not see these messages the first time you run it, there is the +possibility that some other process, possibly the build process, has already +executed this step.

+

Running the Test Suite

+

To run the test suite, run xpcom/test/regrtest.py.  This runs the +tests and ensures that the test output is as expected.  If all tests +pass, you have a fully functioning Python XPCOM package.  Enjoy!

+ + + + diff --git a/mozilla/extensions/python/xpcom/doc/credits.html b/mozilla/extensions/python/xpcom/doc/credits.html new file mode 100644 index 00000000000..046dd3ff0ae --- /dev/null +++ b/mozilla/extensions/python/xpcom/doc/credits.html @@ -0,0 +1,54 @@ + + + + + + + + + +Credits and Acknowledgements + + + + +

Credits and Acknowledgements

+

ActiveState Tool Corporation

+

The Python XPCOM Package was developed primarily by Mark +Hammond of ActiveState Tool Corporation.

+

The developers on the Komodo +project deserve high praise for putting up with early versions when almost +nothing worked, and for believing in Python as a viable XPCOM language.  +Their feedback and patience has allowed the first public release to be amazingly +functional and bug-free.

+

Komodo Development Team (at December 2000)

+

David Ascher, Aaron Bingham, +Bin Du, Mark +Hammond, Trent Mick (build god),  +Paul PrescodEric Promislow, +Ken Simpson, Neil Watkiss, +Audrey Schumacher.

+

Mozilla/Netscape

+

The following people at Netscape and Mozilla +(or not there but still heavily involved in the project) have provided enormous +help in getting things integrated with their build system, answering us on the +newsgroup, teaching us the finer points of XPCOM, gently slapping us for accidentally +referring to jscript, etc., and otherwise lending us a clue in the +Mozilla/Netscape/XPCOM world.

+

John Bandhauer, Brendan +Eich, Mike Shaver, Eric Vaughan, +David Hyatt

+

External Contributors

+

The following people have made contributions to the project, simply because +they find it useful and enjoy supporting Open Source projects.

+

Christof Meerwald

+

Documentation Credits

+

The following people have contributed to the Python XPCOM Package +documentation 

+

Mark +Hammond, Audrey Schumacher

+ + + + diff --git a/mozilla/extensions/python/xpcom/doc/tutorial.html b/mozilla/extensions/python/xpcom/doc/tutorial.html new file mode 100644 index 00000000000..6fac965b4e6 --- /dev/null +++ b/mozilla/extensions/python/xpcom/doc/tutorial.html @@ -0,0 +1,240 @@ + + + + + + + + +Python XPCOM Package Tutorial + + + + +

Python XPCOM Package Tutorial

+

This is a quick introduction to the Python XPCOM Package. We assume that you have a good understanding of Python and XPCOM, +and have experience both using and implementing XPCOM objects in some other +language (e.g., C++ or JavaScript). We do not attempt to +provide a tutorial to XPCOM or Python itself, only to using Python and + XPCOM.

+

This tutorial contains the following sections:

+ +

For anything not covered here, try the advanced +documentation, and if that fails, use the source, Luke!

+

Using XPCOM object and interfaces.

+

The techniques for using XPCOM in Python have been borrowed from JavaScript - +thus, the model described here should be quite familiar to existing JavaScript +XPCOM programmers.

+

xpcom.components module

+

When using an XPCOM object, the primary module used is the xpcom.components + module.  Using this module, you can get a Python object that supports any +scriptable XPCOM interface. Once you have this Python object, you can +simply call XPCOM methods on the object, as normal.

+

The xpcom.components module defines the following public +members:

+ + + + + + + + + + + + + +
NameDescription
classesA mapping (dictionary-like object) used to get XPCOM + "classes".  These are indexed by XPCOM contract ID, just + like the JavaScript object of the same name.   +

Example:

+
cls = components.classes["@mozilla.org/sample;1"]
+ob = cls.createInstance() # Now have an nsISupports
+
interfacesAn object that exposes all XPCOM interface IDs (IIDs).  + Like the JavaScript object of the same name, this object uses + "dot" notation, as demonstrated below. +

Example:

+
ob = cls.createInstance(components.interfaces.nsISample) 
+# Now have an nsISample
+
+

For many people, this is all you need to know. Consider the Mozilla Sample Component.  The Mozilla Sample +Component has a contract ID of @mozilla.org/sample;1, +and implements the nsISample interface.

+

Thus, a complete Python program that uses this component is shown below.

+
from xpcom import components
+cls = components.classes["@mozilla.org/sample;1"]
+ob = cls.createInstance(components.interfaces.nsISample)
+# nsISample defines a "value" property - let's use it!
+ob.value = "new value"
+if ob.value != "new value":
+    print "Eeek - what happened?"
+

And that is it - a complete Python program that uses XPCOM.

+

Implementing XPCOM Objects and Interfaces.

+

Implementing XPCOM objects is almost as simple as using them. The +basic strategy is this:

+
    +
  1. Create a standard Python source file, with a standard Python class.
  2. +
  3. Add some special attributes to your class for use by the Python XPCOM + framework. This controls the XPCOM behavior of your object.
  4. +
  5. Implement the XPCOM properties and methods of your classes as normal.
  6. +
  7. Put the Python source file in the Mozilla components directory.
  8. +
  9. Run regxpcom.
  10. +
+

Your component is now ready to be used.

+

Attributes

+

There are two classes of attributes: those used at runtime to define the object +behavior and those used at registration time to control object +registration.  Not all objects require registration, thus not all +Python XPCOM objects will have registration-related attributes.

+ + + + + + + + + + + + + + + + + + + + + + + + + +
AttributeDescription
_com_interfaces_The interface IDs (IIDs) supported by the component.  + For simplicity, this may be either a single IID, or a list of IIDs.  + There is no need to specify base interfaces, as all parent interfaces are + automatically supported. Thus, it is never necessary to nominate + nsISupports in the list of interfaces. +

This attribute is required. Objects without such an attribute are + deemed unsuitable for use as a XPCOM object.

_reg_contractid_The contract ID of the component.  Required if the + component requires registration (i.e., exists in the components directory).
_reg_clsid_The Class ID (CLSID) of the component, as a string in the + standard "{XXX-XXX-XXX-XXX}" format. Required if the + component requires registration (i.e., exists in the components directory).
_reg_registrar_Nominates a function that is called at registration + time. The default is for no extra function to be called. This can + be useful if a component has special registration requirements and needs + to hook into the registration process.
_reg_desc_The description of the XPCOM object. This may be used by + browsers or other such objects.  If not specified, the contract ID + is used.
+

Properties

+

A Python class can support XPCOM properties in one of two ways.  Either +a standard Python property of the same name can exist - our sample +component demonstrates this with the boolean_value property.  +Alternatively, the class can provide the get_propertyName(self) and set_propertyName(self, +value) functions (with propertyName changed to the appropriate value for the +property), and these functions will be called instead.

+

Example:  The Python XPCOM Test Component

+

As an example, examine the Python XPCOM Test Component.  This +code can be found in py_test_component.py.

+
from xpcom import components
+
+class PythonTestComponent:
+    _com_interfaces_ = components.interfaces.nsIPythonTestInterface
+    _reg_clsid_ = "{7EE4BDC6-CB53-42c1-A9E4-616B8E012ABA}"
+    _reg_contractid_ = "Python.TestComponent"
+    def __init__(self):
+        self.boolean_value = 1
+        ...
+    def do_boolean(self, p1, p2):
+        ret = p1 ^ p2
+        return ret, not ret, ret
+...
+

Note: This component only specifies the mandatory attributes - _com_interfaces, +_reg_clsid_ and _reg_contractid_.

+

This sample code demonstrates supporting the boolean_value attribute, +supported implicitly, as it is defined in the IDL and exists as a real Python +attribute of that name, and a method called do_boolean.

+

Tip: The xpcom/xpt.py Script

+

The xpcom/xpt.py script is a useful script that can generate the skeleton of a class for +any XPCOM interface.  Just specify the interface name on the command-line, +and paste the output into your source file.

+

This is the output of running this program over the nsISample +interface (i.e., assuming we wanted to implement a component that supported this +interface):

+
class nsISample:
+    _com_interfaces_ = xpcom.components.interfaces.nsISample
+    # If this object needs to be registered, the following 2 are also needed.
+    # _reg_clsid_ = {a new clsid generated for this object}
+    # _reg_contractid_ = "The.Object.Name"
+
+    def get_value( self ):
+        # Result: string
+        pass
+    def set_value( self, param0 ):
+        # Result: void - None
+        # In: param0: string
+        pass
+    def writeValue( self, param0 ):
+        # Result: void - None
+        # In: param0: string
+        pass
+    def poke( self, param0 ):
+        # Result: void - None
+        # In: param0: string
+        pass
+

Note: The types of the parameters and the function itself are included in +the comments. You need to implement the functions +themselves.  Another advantage of this script is that the hidden +parameters are handled for you; the comments indicate when parameters +have been hidden.

+

Parameters and Types

+

This section briefly describes the XPCOM type support in +Python.

+

All XPCOM interfaces define parameters of a specific type.  There is +currently no concept of a variant, or union of all types. Thus, the +conversion rules are very straightforward, and generally surprise free: for +any given XPCOM method, there is only one possible type for a given parameter.

+

Type Conversion Rules:

+ + + + + + + diff --git a/mozilla/extensions/python/xpcom/file.py b/mozilla/extensions/python/xpcom/file.py new file mode 100644 index 00000000000..c45e3f8f45b --- /dev/null +++ b/mozilla/extensions/python/xpcom/file.py @@ -0,0 +1,298 @@ +# Copyright (c) 2000-2001 ActiveState Tool Corporation. +# See the file LICENSE.txt for licensing information. + +"""Implementation of Python file objects for Mozilla/xpcom. + +Introduction: + This module defines various class that are implemented using + Mozilla streams. This allows you to open Mozilla URI's, and + treat them as Python file object. + +Example: +>>> file = URIFile("chrome://whatever") +>>> data = file.read(5) # Pass no arg to read everything. + +Known Limitations: + * Not all URL schemes will work from "python.exe" - most notably + "chrome://" and "http://" URLs - this is because a simple initialization of + xpcom by Python does not load up the full set of Mozilla URL handlers. + If you can work out how to correctly initialize the chrome registry and + setup a message queue. + +Known Bugs: + * Only read ("r") mode is supported. Although write ("w") mode doesnt make + sense for HTTP type URLs, it potentially does for file:// etc type ones. + * No concept of text mode vs binary mode. It appears Mozilla takes care of + this internally (ie, all "text/???" mime types are text, rest are binary) + +""" + +from xpcom import components, Exception, _xpcom +import os +import threading # for locks. + +NS_RDONLY = components.interfaces.nsIFileChannel.NS_RDONLY +NS_WRONLY = components.interfaces.nsIFileChannel.NS_WRONLY +NS_RDWR = components.interfaces.nsIFileChannel.NS_RDWR +NS_CREATE_FILE = components.interfaces.nsIFileChannel.NS_CREATE_FILE +NS_APPEND = components.interfaces.nsIFileChannel.NS_APPEND +NS_TRUNCATE = components.interfaces.nsIFileChannel.NS_TRUNCATE +NS_SYNC = components.interfaces.nsIFileChannel.NS_SYNC +NS_EXCL = components.interfaces.nsIFileChannel.NS_EXCL + +# A helper function - you pass a progID, an interface, plus optionally the +# name of the "constructor" and its args. +def _construct(progid, interface, ctor = None, *args): + assert (ctor is None and not args) or (ctor is not None and args), \ + "no point having a ctor with no args, or if you provide args, there must be a ctor!" + instance = components.classes[progid] \ + .createInstance(interface) + if ctor is not None: + ctor = getattr(instance, ctor) + apply(ctor, args) + return instance + +# A helper function that may come in useful +def LocalFileToURL(localFileName): + "Convert a filename to an XPCOM nsIFileURL object." + # Create an nsILocalFile + localFile = components.classes["@mozilla.org/file/local;1"] \ + .createInstance(components.interfaces.nsILocalFile) + localFile.initWithPath(localFileName) + + # Create a standard URL, but we QI for the FileURL interface! + url = components.classes["@mozilla.org/network/standard-url;1"] \ + .createInstance(components.interfaces.nsIFileURL) + # Setting the "file" attribute causes initialization... + url.file = localFile + return url + +# A base class for file objects. +class _File: + def __init__(self, name_thingy = None, mode="r"): + self.lockob = threading.Lock() + self.inputStream = self.outputStream = None + if name_thingy is not None: + self.init(name_thingy, mode) + + def __del__(self): + self.close() + + # The Moz file streams are not thread safe. + def _lock(self): + self.lockob.acquire() + def _release(self): + self.lockob.release() + def read(self, n = -1): + assert self.inputStream is not None, "Not setup for read!" + self._lock() + try: + return str(self.inputStream.read(n)) + finally: + self._release() + + def readlines(self): + # Not part of the xpcom interface, but handy for direct Python users. + # Not 100% faithful, but near enough for now! + lines = self.read().split("\n") + if len(lines) and len(lines[-1]) == 0: + lines = lines[:-1] + return [s+"\n" for s in lines ] + + def write(self, data): + assert self.outputStream is not None, "Not setup for write!" + self._lock() + try: + self.outputStream.write(data, len(data)) + finally: + self._release() + + def close(self): + self._lock() + try: + if self.inputStream is not None: + self.inputStream.close() + self.inputStream = None + if self.outputStream is not None: + self.outputStream.close() + self.outputStream = None + self.channel = None + # leave self.fileInst alone - it should be available after close. + finally: + self._release() + + def flush(self): + self._lock() + try: + if self.outputStream is not None: self.outputStream.flush() + finally: + self._release() + +# A synchronous "file object" used to open a URI. +class URIFile(_File): + def init(self, url, mode="r"): + self.close() + if mode != "r": + raise ValueError, "only 'r' mode supported'" + io_service = components.classes["@mozilla.org/network/io-service;1"] \ + .createInstance("nsIIOService") + if hasattr(url, "queryInterface"): + url_ob = url + else: + url_ob = components.classes["@mozilla.org/network/standard-url;1"] \ + .createInstance(components.interfaces.nsIURL) + url_ob.spec = url + # Mozilla asserts and starts saying "NULL POINTER" if this is wrong! + if not url_ob.scheme: + raise ValueError, ("The URI '%s' is invalid (no scheme)" + % (url_ob.spec,)) + self.channel = io_service.newChannelFromURI(url_ob) + self.inputStream = self.channel.openInputStream() + +# A "file object" implemented using Netscape's native file support. +# Based on io.js - http://lxr.mozilla.org/seamonkey/source/xpcom/tests/utils/io.js +# You open this file using a local file name (as a string) so it really is pointless - +# you may as well be using a standard Python file object! +class LocalFile(_File): + def init(self, name, mode = "r"): + name = os.path.abspath(name) # Moz libraries under Linux fail with relative paths. + self.close() + self.fileInst = _construct('@mozilla.org/file/local;1', "nsILocalFile", "initWithPath", name) + self.inputStream = None + self.channel = None + if mode in ["w","a"]: + if mode== "w": + if self.fileInst.exists(): + self.fileInst.delete(0) + moz_mode = NS_CREATE_FILE | NS_WRONLY + elif mode=="a": + moz_mode = NS_APPEND + else: + assert 0, "Can't happen!" + perms = 0644 + if not self.fileInst.exists(): + self.fileInst.create(components.interfaces.nsILocalFile.NORMAL_FILE_TYPE, 0644) + self.channel = _construct('@mozilla.org/network/local-file-channel;1', "nsIFileChannel", "init", + self.fileInst, moz_mode, perms) + self.outputStream = self.channel.openOutputStream() + self.fileInst.permissions = perms + + elif mode == "r": + self.channel = _construct('@mozilla.org/network/local-file-channel;1', "nsIFileChannel", "init", + self.fileInst, NS_RDONLY, self.fileInst.permissions) + self.inputStream = self.channel.openInputStream() + else: + raise ValueError, "Unknown mode" + + def read(self, n = -1): + if n == -1: + n = self.fileInst.fileSize + return _File.read(self, n) + + +########################################################## +## +## Test Code +## +########################################################## +def _DoTestRead(file, expected): + # read in a couple of chunks, just to test that our various arg combinations work. + got = file.read(3) + got = got + file.read(300) + got = got + file.read(0) + got = got + file.read() + if got != expected: + raise RuntimeError, "Reading '%s' failed - got %d bytes, but expected %d bytes" % (file, len(got), len(expected)) + +def _DoTestBufferRead(file, expected): + # read in a couple of chunks, just to test that our various arg combinations work. + buffer = _xpcom.AllocateBuffer(50) + got = '' + while 1: + # Note - we need to reach into the file object so we + # can get at the native buffer supported function. + num = file.inputStream.read(buffer) + if num == 0: + break + got = got + str(buffer[:num]) + if got != expected: + raise RuntimeError, "Reading '%s' failed - got %d bytes, but expected %d bytes" % (file, len(got), len(expected)) + +def _TestLocalFile(): + import tempfile, os + fname = tempfile.mktemp() + data = "Hello from Python" + test_file = LocalFile(fname, "w") + try: + test_file.write(data) + test_file.close() + # Make sure Python can read it OK. + f = open(fname, "r") + if f.read() != data: + print "Eeek - Python could not read the data back correctly!" + f.close() + # For the sake of the test, try a re-init. + test_file.init(fname, "r") + got = str(test_file.read()) + if got != data: + print "Read the wrong data back - %r" % (got,) + else: + print "Read the correct data." + test_file.close() + # Try reading in chunks. + test_file = LocalFile(fname, "r") + got = test_file.read(10) + test_file.read() + if got != data: + print "Chunks the wrong data back - %r" % (got,) + else: + print "Chunks read the correct data." + test_file.close() + # XXX - todo - test "a" mode! + finally: + try: + os.unlink(fname) + except OSError, details: + print "Error removing temp test file:", details + +def _TestAll(): + # A mini test suite. + # Get a test file, and convert it to a file:// URI. + # check what we read is the same as when + # we read this file "normally" + fname = components.__file__ + if fname[-1] in "cCoO": # fix .pyc/.pyo + fname = fname[:-1] + expected = open(fname, "rb").read() + # convert the fname to a URI. + url = LocalFileToURL(fname) + # First try passing a URL as a string. + _DoTestRead( URIFile( url.spec), expected) + print "Open as string test worked." + # Now with a URL object. + _DoTestRead( URIFile( url ), expected) + print "Open as URL test worked." + + _DoTestBufferRead( URIFile( url ), expected) + print "File test using buffers worked." + + # For the sake of testing, do our pointless, demo object! + _DoTestRead( LocalFile(fname), expected ) + print "Local file read test worked." + + # Now do the full test of our pointless, demo object! + _TestLocalFile() + +def _TestURI(url): + test_file = URIFile(url) + print "Opened file is", test_file + got = test_file.read() + print "Read %d bytes of data from %r" % (len(got), url) + test_file.close() + +if __name__=='__main__': + import sys + if len(sys.argv) < 2: + print "No URL specified on command line - performing self-test" + _TestAll() + else: + _TestURI(sys.argv[1]) diff --git a/mozilla/extensions/python/xpcom/makefile.stupid.linux b/mozilla/extensions/python/xpcom/makefile.stupid.linux new file mode 100644 index 00000000000..ca9f1ce29e8 --- /dev/null +++ b/mozilla/extensions/python/xpcom/makefile.stupid.linux @@ -0,0 +1,217 @@ +# Copyright (c) 2000-2001 ActiveState Tool Corporation. +# See the file LICENSE.txt for licensing information. + +# Dumb makefile to build PyXPCOM on linux +# +# The build is by no means clean. I just kept shoving in compiler and linker +# options until it worked. :) +# +# USAGE: +# 1. edit top section as appropriate +# 2. build, install, and test PyXPCOM +# > make -f makefile.stupid.linux DEBUG=1 +# > make -f makefile.stupid.linux DEBUG=1 install +# > make -f makefile.stupid.linux DEBUG=1 test +# A debug build is suggested (DEBUG=1) by default because a default +# mozilla build is a debug build. Exclude the "DEBUG=1" to build for a +# release build of mozilla. +# + +#------------------------- +# You must edit the variables in this section as appropriate for your machine +# The most common edits will just be: +# MOZ_SRC, INSTALLDIR, and PYTHON_SRC +# =========== START OF SECTION FOR COMMON EDITS ===================== + +# We expect a "mozilla" directory under this +MOZ_SRC=/home/skip/src + +# this will have an "xpcom" subdir on install +INSTALLDIR=/usr/local/ActivePython-2.0/lib/python2.0/site-packages + +PYTHON_SRC=/usr/local/ActivePython-2.0 + +# =========== START OF SECTION FOR COMMON EDITS ===================== + +MOZCOMPONENTSDIR=$(MOZ_SRC)/mozilla/dist/bin/components +MOZINCLUDES=-I$(MOZ_SRC)/mozilla/dist/include +MOZLIBDIR=$(MOZ_SRC)/mozilla/dist/lib + +# this is setup to use the *installed* Python directory structure +# - To use the development Python dir structure some changes are +# necessary here *and* below (below, because there two lib dir +# to include in LDFLAGS for the dev dir structure) +PYTHON_SRC=/usr/local/ActivePython-2.0 +PYTHONINCLUDES=-I$(PYTHON_SRC)/include/python2.0 +PYTHONLIBDIR=$(PYTHON_SRC)/lib/python2.0/config + +XPIDL=$(MOZ_SRC)/mozilla/dist/bin/xpidl +IDLINCLUDES=-I$(MOZ_SRC)/mozilla/dist/idl + + +#------------------------- +# You should not need to edit anything beyond this point. +# + +# the main PyXPCOM engine library +ENGINE=src/_xpcommodule.so +# the PyXPCOM loader +LOADER=src/loader/libpyloader.so + +all:: $(ENGINE) $(LOADER) + + +#---- build the PyXPCOM loader + +LOADER_CFLAGS=-fpic -fno-rtti -fno-exceptions -Wconversion \ + -Wpointer-arith -Wbad-function-cast -Wcast-align -Woverloaded-virtual \ + -Wsynth -pedantic -Wno-long-long -pthread -DTRIMMED +ifdef DEBUG +LOADER_CFLAGS += -DDEBUG +endif + +# NOTE: not sure if using -rpath is the best way to do this +LOADER_LDFLAGS=-Xlinker -rpath -Xlinker $(MOZLIBDIR) \ + -Xlinker -rpath -Xlinker $(MOZLIBDIR)/components \ + -Xlinker -rpath -Xlinker $(PYTHONLIBDIR) \ + -L$(MOZLIBDIR) -L$(PYTHONLIBDIR) +# NOTE: can't remember if "-shared" is necessary here +LOADER_LIBS=-lpython2.0 -ldl -lpthread -ldb -lutil -shared -lxpcom +$(LOADER): src/loader/pyloader.cpp + c++ $(LOADER_CFLAGS) $(MOZINCLUDES) $(PYTHONINCLUDES) \ + -c src/loader/pyloader.cpp \ + -o src/loader/pyloader.o + c++ $(LOADER_CFLAGS) $(LOADER_LDFLAGS) $(LOADER_LIBS) \ + -o $(LOADER) \ + src/loader/pyloader.o + + +#---- build the PyXPCOM engine + +ENGINE_CFLAGS=-fpic -fno-rtti -fno-exceptions -Wconversion \ + -Wpointer-arith -Wbad-function-cast -Wcast-align -Woverloaded-virtual \ + -Wsynth -pedantic -Wno-long-long -pthread -DTRIMMED -nostdlib +ifdef DEBUG +ENGINE_CFLAGS += -DDEBUG +endif + +# -DXPCOM_EXPORTS +XPCOM_SRC_OBJECTS = \ + src/ErrorUtils.o \ + src/PyGBase.o \ + src/PyGModule.o \ + src/PyGStub.o \ + src/PyGInputStream.o \ + src/PyGWeakReference.o \ + src/PyIComponentManager.o \ + src/PyIInputStream.o \ + src/PyIEnumerator.o \ + src/PyIID.o \ + src/PyIInterfaceInfo.o \ + src/PyIInterfaceInfoManager.o \ + src/PyIServiceManager.o \ + src/PyISimpleEnumerator.o \ + src/PyISupports.o \ + src/Pyxpt_info.o \ + src/TypeObject.o \ + src/VariantUtils.o \ + src/dllmain.o \ + src/xpcom.o + +%.o: %.cpp + c++ $(ENGINE_CFLAGS) $(MOZINCLUDES) $(PYTHONINCLUDES) \ + -c $< -o $@ + +# NOTE: not sure if using -rpath is the best way to do this +# NOTE: can't remember if "-shared" is necessary here +ENGINE_LDFLAGS=-Xlinker -rpath -Xlinker $(MOZLIBDIR) \ + -Xlinker -rpath -Xlinker $(MOZLIBDIR)/components \ + -Xlinker -rpath -Xlinker $(PYTHONLIBDIR) \ + -Xlinker -shared +ENGINE_LIBS=-lpython2.0 -ldl -lpthread -ldb -lutil -lxpcom \ + -lnspr4 -lgtk -lgdk -rdynamic -lgmodule -lglib -lm -lplc4 \ + -lpthread -lnsl -lresolv -lm -lc -lgcc + +$(ENGINE): $(XPCOM_SRC_OBJECTS) + c++ $(ENGINE_CFLAGS) $(ENGINE_LDFLAGS) -o $(ENGINE) \ + $(XPCOM_SRC_OBJECTS) -L$(MOZLIBDIR) -L$(PYTHONLIBDIR) $(ENGINE_LIBS) + + +#---- install PyXPCOM + +XPCOM_PACKAGE_FILES = \ + __init__.py \ + components.py \ + file.py \ + nsError.py \ + register.py \ + xpcom_consts.py \ + xpt.py \ + client/__init__.py \ + server/__init__.py \ + server/enumerator.py \ + server/factory.py \ + server/loader.py \ + server/module.py \ + server/policy.py + +# this is a cheasy install +# - no attention to permissions +# - doesn't explicitly use $(XPCOM_PACKAGE_FILES) +install:: all $(XPCOM_PACKAGE_FILES) + mkdir -p $(INSTALLDIR)/xpcom/client + mkdir -p $(INSTALLDIR)/xpcom/server + cp -f *.py $(INSTALLDIR)/xpcom + cp -f client/*.py $(INSTALLDIR)/xpcom/client + cp -f server/*.py $(INSTALLDIR)/xpcom/server + cp -f $(ENGINE) $(INSTALLDIR)/xpcom + mkdir -p $(MOZCOMPONENTSDIR) + cp -f $(LOADER) $(MOZCOMPONENTSDIR) + ( \ + export PYTHONPATH=$(INSTALLDIR):$(PYTHONPATH) ; \ + export MOZILLA_FIVE_HOME=$(MOZ_SRC)/mozilla/dist/bin ; \ + export LD_LIBRARY_PATH=$(MOZ_SRC)/mozilla/dist/bin ; \ + $(MOZ_SRC)/mozilla/dist/bin/regxpcom \ + ) + + +#---- build and run the PyXPCOM test suite + +all:: test/test_component/py_test_component.xpt + +test/test_component/py_test_component.xpt: test/test_component/py_test_component.idl + $(XPIDL) -m typelib -w $(IDLINCLUDES) -o test/test_component/py_test_component $< + +install:: + mkdir -p $(MOZCOMPONENTSDIR) + cp -f test/test_component/py_test_component.xpt $(MOZCOMPONENTSDIR) + cp -f test/test_component/py_test_component.py $(MOZCOMPONENTSDIR) + ( \ + export PYTHONPATH=$(INSTALLDIR):$(PYTHONPATH) ; \ + export MOZILLA_FIVE_HOME=$(MOZ_SRC)/mozilla/dist/bin ; \ + export LD_LIBRARY_PATH=$(MOZ_SRC)/mozilla/dist/bin ; \ + $(MOZ_SRC)/mozilla/dist/bin/regxpcom \ + ) + +test:: install + ( \ + export PYTHONPATH=$(INSTALLDIR):$(PYTHONPATH) ; \ + export MOZILLA_FIVE_HOME=$(MOZ_SRC)/mozilla/dist/bin ; \ + export LD_LIBRARY_PATH=$(MOZ_SRC)/mozilla/dist/bin ; \ + python test/regrtest.py \ + ) + +runpython: install + ( \ + export PYTHONPATH=$(INSTALLDIR):$(PYTHONPATH) ; \ + export MOZILLA_FIVE_HOME=$(MOZ_SRC)/mozilla/dist/bin ; \ + export LD_LIBRARY_PATH=$(MOZ_SRC)/mozilla/dist/bin ; \ + python \ + ) + +clean: + find . -name "*~" | xargs rm -f + find . -name "*.o" | xargs rm -f + find . -name "*.pyc" | xargs rm -f + find . -name "*.so" | xargs rm -f + diff --git a/mozilla/extensions/python/xpcom/makefile.stupid.win b/mozilla/extensions/python/xpcom/makefile.stupid.win new file mode 100644 index 00000000000..e791b59cea8 --- /dev/null +++ b/mozilla/extensions/python/xpcom/makefile.stupid.win @@ -0,0 +1,223 @@ +# Copyright (c) 2000-2001 ActiveState Tool Corporation. +# See the file LICENSE.txt for licensing information. + +# Dumb makefile to build PyXPCOM on Windows +# +# The build is by no means clean. I just kept shoving in compiler and linker +# options until it worked. :) +# +# USAGE: +# 1. edit top section as appropriate +# 2. build, install, and test PyXPCOM +# > nmake -f makefile.stupid.win +# > nmake -f makefile.stupid.win install +# > nmake -f makefile.stupid.win test + +#---------------------------------------------------------------------------- +# You must edit the variables in this section as appropriate for your machine +# The most common edits will just be: +# MOZ_SRC, INSTALLDIR, and PYTHON_SRC +# ========= START OF SECTION FOR COMMON EDITS ============== + +# We expect a "mozilla" directory under this +MOZ_SRC=c:\src + +# this will have an "xpcom" subdir on install +INSTALLDIR=C:\Python20 + +# this is setup to use the *installed* Python directory structure +PYTHON_SRC=C:\Python20 + + +# ========= END OF SECTION FOR COMMON EDITS ============== + +!IF DEFINED(DEBUG) +MOZ_BUILD_DIR=$(MOZ_SRC)\mozilla\dist\WIN32_D.OBJ +PYTHON_EXE=python_d.exe +!ELSE +MOZ_BUILD_DIR=$(MOZ_SRC)\mozilla\dist\WIN32_O.OBJ +PYTHON_EXE=python.exe +!ENDIF + +MOZCOMPONENTSDIR=$(MOZ_BUILD_DIR)\bin\components +MOZINCLUDES=/I$(MOZ_SRC)\mozilla\dist\include /I$(MOZ_BUILD_DIR)\include +MOZLIBS=/LIBPATH:$(MOZ_BUILD_DIR)\lib + +# this is setup to use the *installed* Python directory structure +# - To use the development Python dir structure some changes are +# necessary here *and* below (below, because there two lib dir +# to include in LDFLAGS for the dev dir structure) +PYTHONINCLUDES=/I$(PYTHON_SRC)\include +PYTHONLIBS=/LIBPATH:$(PYTHON_SRC)\libs + +MOZ_BIN=$(MOZ_BUILD_DIR)\bin + +XPIDL=$(MOZ_BIN)\xpidl.exe +IDLINCLUDES=-I$(MOZ_SRC)\mozilla\dist\idl +REGXPCOM=$(MOZ_BIN)\regxpcom.exe + + +#---------------------------------------------------------------------------- +# You should not need to edit anything beyond this point. +# + +# the main PyXPCOM engine library +!IF DEFINED(DEBUG) +ENGINE=src\_xpcom_d.pyd +# the PyXPCOM loader +LOADER=src\loader\pyloader_d.dll +!ELSE +ENGINE=src\_xpcom.pyd +# the PyXPCOM loader +LOADER=src\loader\pyloader.dll +!ENDIF + + +all:: $(ENGINE) $(LOADER) + + +#---- build the PyXPCOM loader + +!IF DEFINED(DEBUG) +LOADER_CFLAGS_THIS_BUILD=/ZI /MDd /Od /DDEBUG /D_DEBUG +LOADER_LDFLAGS_THIS_BUILD=/debug +!ELSE +LOADER_CFLAGS_THIS_BUILD=/MD /Ox /DNDEBUG /D_NDEBUG +LOADER_LDFLAGS_THIS_BUILD= +!ENDIF + +LOADER_CFLAGS=/W3 /D WIN32 /D _WINDOWS /D XPCOM_EXPORTS $(LOADER_CFLAGS_THIS_BUILD) +LOADER_LDFLAGS=/dll $(LOADER_LDFLAGS_THIS_BUILD) + +$(LOADER): src\loader\pyloader.cpp + cl /nologo $(LOADER_CFLAGS) $(MOZINCLUDES) $(PYTHONINCLUDES) \ + /c src\loader\pyloader.cpp \ + /Fosrc\loader\pyloader.obj + link $(LOADER_LDFLAGS) $(PYTHONLIBS) $(MOZLIBS) \ + /out:$(LOADER) \ + src\loader\pyloader.obj \ + xpcom.lib + + +#---- build the PyXPCOM engine + +!IF DEFINED(DEBUG) +ENGINE_CFLAGS_THIS_BUILD=/ZI /MDd /Od /DDEBUG /D_DEBUG +!ELSE +ENGINE_CFLAGS_THIS_BUILD=/MD /Ox /DNDEBUG /D_NDEBUG +!ENDIF + +ENGINE_CFLAGS=/D _USRDLL /W3 /D WIN32 /D _WINDOWS /D XPCOM_EXPORTS $(ENGINE_CFLAGS_THIS_BUILD) + +XPCOM_SRC_OBJECTS = \ + src\ErrorUtils.obj \ + src\PyGBase.obj \ + src\PyGModule.obj \ + src\PyGStub.obj \ + src\PyGInputStream.obj \ + src\PyGWeakReference.obj \ + src\PyIComponentManager.obj \ + src\PyIInputStream.obj \ + src\PyIEnumerator.obj \ + src\PyIID.obj \ + src\PyIInterfaceInfo.obj \ + src\PyIInterfaceInfoManager.obj \ + src\PyIServiceManager.obj \ + src\PyISimpleEnumerator.obj \ + src\PyISupports.obj \ + src\Pyxpt_info.obj \ + src\TypeObject.obj \ + src\VariantUtils.obj \ + src\dllmain.obj \ + src\xpcom.obj + +.cpp.obj: + cl /nologo $(ENGINE_CFLAGS) $(MOZINCLUDES) $(PYTHONINCLUDES) -c $< -Fo$@ + +!IF DEFINED(DEBUG) +ENGINE_LDFLAGS_THIS_BUILD=/debug +!ELSE +ENGINE_LDFLAGS_THIS_BUILD= +!ENDIF + +#XXX for debug: ENGINE_LDFLAGS_DEBUG=/DEBUG +ENGINE_LDFLAGS=/dll /export:init_xpcom $(ENGINE_LDFLAGS_THIS_BUILD) + +$(ENGINE): $(XPCOM_SRC_OBJECTS) + link $(ENGINE_LDFLAGS) $(XPCOM_SRC_OBJECTS) $(MOZLIBS) $(PYTHONLIBS) /out:$(ENGINE) + + +#---- install PyXPCOM + +XPCOM_PACKAGE_FILES = \ + __init__.py \ + components.py \ + file.py \ + nsError.py \ + register.py \ + xpcom_consts.py \ + xpt.py \ + client\__init__.py \ + server\__init__.py \ + server\enumerator.py \ + server\factory.py \ + server\loader.py \ + server\module.py \ + server\policy.py + +# this is a cheasy install +# - no attention to permissions +# - doesn't explicitly use $(XPCOM_PACKAGE_FILES) +install:: all $(XPCOM_PACKAGE_FILES) + if not exist $(INSTALLDIR) mkdir $(INSTALLDIR) + if not exist $(INSTALLDIR)\xpcom mkdir $(INSTALLDIR)\xpcom + if not exist $(INSTALLDIR)\xpcom\client mkdir $(INSTALLDIR)\xpcom\client + if not exist $(INSTALLDIR)\xpcom\server mkdir $(INSTALLDIR)\xpcom\server + if not exist $(INSTALLDIR)\xpcom\test mkdir $(INSTALLDIR)\xpcom\test + if not exist $(INSTALLDIR)\xpcom\test\output mkdir $(INSTALLDIR)\xpcom\test\output + copy /v /y *.py $(INSTALLDIR)\xpcom + copy /v /y client\*.py $(INSTALLDIR)\xpcom\client + copy /v /y server\*.py $(INSTALLDIR)\xpcom\server + copy /v /y test\*.py $(INSTALLDIR)\xpcom\test + copy /v /y test\output\* $(INSTALLDIR)\xpcom\test\output + copy /v /y $(ENGINE) $(INSTALLDIR)\xpcom + if not exist $(MOZCOMPONENTSDIR) mkdir $(MOZCOMPONENTSDIR) + copy /v /y $(LOADER) $(MOZCOMPONENTSDIR) + set PYTHONPATH=$(INSTALLDIR);$(PYTHONPATH) + set PATH=$(MOZ_BIN);$(PATH) + $(REGXPCOM) + + +#---- build and run the PyXPCOM test suite + +all:: test\test_component\py_test_component.xpt + +test\test_component\py_test_component.xpt: test\test_component\py_test_component.idl + $(XPIDL) -m typelib -w $(IDLINCLUDES) -o test\test_component\py_test_component test\test_component\py_test_component.idl + +install:: + if not exist $(MOZCOMPONENTSDIR) mkdir $(MOZCOMPONENTSDIR) + copy /v /y test\test_component\py_test_component.xpt $(MOZCOMPONENTSDIR) + copy /v /y test\test_component\py_test_component.py $(MOZCOMPONENTSDIR) + set PYTHONPATH=$(INSTALLDIR);$(PYTHONPATH) + set PATH=$(MOZ_BIN);$(PATH) + $(REGXPCOM) + +test:: install + set PATH=$(MOZ_BIN);$(PATH) + set PYTHONPATH=$(INSTALLDIR);$(PYTHONPATH) + $(PYTHON_EXE) test\regrtest.py + +runpython:: install + set PATH=$(MOZ_BIN);$(PATH) + set PYTHONPATH=$(INSTALLDIR);$(PYTHONPATH) + $(PYTHON_EXE) + +clean: + -del /f /q src\*.obj + -del /f /q src\loader\*.obj + -del /f /q *.pyc + -del /f /q client\*.pyc + -del /f /q server\*.pyc + -del /f /q test\*.pyc + diff --git a/mozilla/extensions/python/xpcom/nsError.py b/mozilla/extensions/python/xpcom/nsError.py new file mode 100644 index 00000000000..02f5e088bcf --- /dev/null +++ b/mozilla/extensions/python/xpcom/nsError.py @@ -0,0 +1,115 @@ +# Copyright (c) 2000-2001 ActiveState Tool Corporation. +# See the file LICENSE.txt for licensing information. + +# Generated by h2py from nsError.h +# CMD line: h2py.py -i (nsresult) nsError.h + +# XXX - NOTE - some manual code at the end, and all literals moved back to ints +NS_ERROR_MODULE_XPCOM = 1 +NS_ERROR_MODULE_BASE = 2 +NS_ERROR_MODULE_GFX = 3 +NS_ERROR_MODULE_WIDGET = 4 +NS_ERROR_MODULE_CALENDAR = 5 +NS_ERROR_MODULE_NETWORK = 6 +NS_ERROR_MODULE_PLUGINS = 7 +NS_ERROR_MODULE_LAYOUT = 8 +NS_ERROR_MODULE_HTMLPARSER = 9 +NS_ERROR_MODULE_RDF = 10 +NS_ERROR_MODULE_UCONV = 11 +NS_ERROR_MODULE_REG = 12 +NS_ERROR_MODULE_FILES = 13 +NS_ERROR_MODULE_DOM = 14 +NS_ERROR_MODULE_IMGLIB = 15 +NS_ERROR_MODULE_MAILNEWS = 16 +NS_ERROR_MODULE_EDITOR = 17 +NS_ERROR_MODULE_XPCONNECT = 18 +NS_ERROR_MODULE_PROFILE = 19 +def NS_FAILED(_nsresult): return ((_nsresult) & 0x80000000) + +NS_ERROR_SEVERITY_SUCCESS = 0 +NS_ERROR_SEVERITY_ERROR = 1 +NS_ERROR_MODULE_BASE_OFFSET = 0x45 +def NS_ERROR_GET_CODE(err): return ((err) & 0xffff) + +def NS_ERROR_GET_MODULE(err): return (((((err) >> 16) - NS_ERROR_MODULE_BASE_OFFSET) & 0x1fff)) + +def NS_ERROR_GET_SEVERITY(err): return (((err) >> 31) & 0x1) + +NS_OK = 0 +NS_COMFALSE = 1 +NS_ERROR_BASE = ( 0xC1F30000) +NS_ERROR_NOT_INITIALIZED = (NS_ERROR_BASE + 1) +NS_ERROR_ALREADY_INITIALIZED = (NS_ERROR_BASE + 2) +NS_ERROR_NOT_IMPLEMENTED = ( 0x80004001) +NS_NOINTERFACE = ( 0x80004002) +NS_ERROR_NO_INTERFACE = NS_NOINTERFACE +NS_ERROR_INVALID_POINTER = ( 0x80004003) +NS_ERROR_NULL_POINTER = NS_ERROR_INVALID_POINTER +NS_ERROR_ABORT = ( 0x80004004) +NS_ERROR_FAILURE = ( 0x80004005) +NS_ERROR_UNEXPECTED = ( 0x8000ffff) +NS_ERROR_OUT_OF_MEMORY = ( 0x8007000e) +NS_ERROR_ILLEGAL_VALUE = ( 0x80070057) +NS_ERROR_INVALID_ARG = NS_ERROR_ILLEGAL_VALUE +NS_ERROR_NO_AGGREGATION = ( 0x80040110) +NS_ERROR_NOT_AVAILABLE = ( 0x80040111) +NS_ERROR_FACTORY_NOT_REGISTERED = ( 0x80040154) +NS_ERROR_FACTORY_REGISTER_AGAIN = ( 0x80040155) +NS_ERROR_FACTORY_NOT_LOADED = ( 0x800401f8) +NS_ERROR_FACTORY_NO_SIGNATURE_SUPPORT = \ + (NS_ERROR_BASE + 0x101) +NS_ERROR_FACTORY_EXISTS = (NS_ERROR_BASE + 0x100) +NS_ERROR_PROXY_INVALID_IN_PARAMETER = ( 0x80010010) +NS_ERROR_PROXY_INVALID_OUT_PARAMETER = ( 0x80010011) + +##### END OF GENERATED CODE +##### +def NS_ERROR_GENERATE_FAILURE(module,code): + return (NS_ERROR_SEVERITY_ERROR<<31) | ((module+NS_ERROR_MODULE_BASE_OFFSET)<<16) | (code) + +NS_BASE_STREAM_CLOSED = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_BASE, 2) +NS_BASE_STREAM_OSERROR = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_BASE, 3) +NS_BASE_STREAM_ILLEGAL_ARGS = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_BASE, 4) +NS_BASE_STREAM_NO_CONVERTER = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_BASE, 5) +NS_BASE_STREAM_BAD_CONVERSION = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_BASE, 6) +NS_BASE_STREAM_WOULD_BLOCK = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_BASE, 7) +NS_ERROR_FILE_UNRECOGNIZED_PATH = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES, 1) +NS_ERROR_FILE_UNRESOLVABLE_SYMLINK = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES, 2) +NS_ERROR_FILE_EXECUTION_FAILED = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES, 3) +NS_ERROR_FILE_UNKNOWN_TYPE = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES, 4) +NS_ERROR_FILE_DESTINATION_NOT_DIR = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES, 5) +NS_ERROR_FILE_TARGET_DOES_NOT_EXIST = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES, 6) +NS_ERROR_FILE_COPY_OR_MOVE_FAILED = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES, 7) +NS_ERROR_FILE_ALREADY_EXISTS = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES, 8) +NS_ERROR_FILE_INVALID_PATH = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES, 9) +NS_ERROR_FILE_DISK_FULL = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES, 10) +NS_ERROR_FILE_CORRUPTED = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES, 11) +NS_ERROR_FILE_NOT_DIRECTORY = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES, 12) +NS_ERROR_FILE_IS_DIRECTORY = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES, 13) +NS_ERROR_FILE_IS_LOCKED = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES, 14) +NS_ERROR_FILE_TOO_BIG = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES, 15) +NS_ERROR_FILE_NO_DEVICE_SPACE = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES, 16) +NS_ERROR_FILE_NAME_TOO_LONG = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES, 17) +NS_ERROR_FILE_NOT_FOUND = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES, 18) +NS_ERROR_FILE_READ_ONLY = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES, 19) +NS_ERROR_FILE_DIR_NOT_EMPTY = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES, 20) +NS_ERROR_FILE_ACCESS_DENIED = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES, 21) + +## from netCore.h +NS_ERROR_ALREADY_CONNECTED = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 11) + +NS_ERROR_NOT_CONNECTED = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 12) +NS_ERROR_IN_PROGRESS = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 15) +NS_ERROR_OFFLINE = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 16) + +## from nsISocketTransportService.idl +NS_ERROR_CONNECTION_REFUSED = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 13) + +NS_ERROR_NET_TIMEOUT = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 14) + +# Status nsresult codes: used with nsIProgressEventSink::OnStatus +NS_NET_STATUS_RESOLVING_HOST = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 3) +NS_NET_STATUS_CONNECTED_TO = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 4) +NS_NET_STATUS_SENDING_TO = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 5) +NS_NET_STATUS_RECEIVING_FROM = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 6) +NS_NET_STATUS_CONNECTING_TO = NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 7) diff --git a/mozilla/extensions/python/xpcom/readme.html b/mozilla/extensions/python/xpcom/readme.html new file mode 100644 index 00000000000..af985f39f20 --- /dev/null +++ b/mozilla/extensions/python/xpcom/readme.html @@ -0,0 +1,91 @@ + + + + + + + + +Python XPCOM module + + + + +

Python XPCOM Package

+ +

Version 0.91 - January 2001

+

This is the readme for the Python interface to XPCOM.

+

XPCOM is an acronym for "Cross Platform COM".  It has +come out of the Mozilla project, which +maintains the main XPCOM +project pages.  The Python XPCOM package is a set of Python bindings to +XPCOM, allowing a Python programmer to both use and implement XPCOM +interfaces.  If you don't know what Python +is, then none of this probably interests you at all!

+

This readme has links to the following information:

+ +

Note: This package requires Python 1.6 or later; we recommend using +the latest +official Python version (currently 2.0).  This package works +very well with the latest ActivePython, +and does not require any external modules or packages beyond what is provided in +the core Python release for each platform.

+

About the Python XPCOM Package

+

The Python XPCOM Package was developed by ActiveState +Tool Corporation, and came out of their Komodo +project.  The Python XPCOM package is released under the Mozilla +Public License (MPL)

+

Please see the credits file for a list of +contributors.

+

Known Bugs/Issues

+
    +
  • No attempt is made to recurse sub-directories of the main +"components" directory.  This is because we may decide on some +smart scheme for recursion (similar to Python packages), and don't want people +to rely on simple recursive searches.
  • +
  • No management of the PythonPath is done by the package.  You must +arrange for the Python xpcom package to be on your PythonPath.  +Significantly, the XPCOM components directory is not on the PythonPath and +generally cannot be, as Python will often find other DLLs in this directory and +attempt to use them as Python modules.  This means that Python module +files will not be found in the components directory, even when referenced by +another component - thus, a component can  not import another component +source file as a regular module!  It is thought that when we know what to +do with sub-directories of the components directory (as described above), some +automated PythonPath support will be provided, so Python components and regular +Python modules the component depends on can exist in the same directory +structure.
  • +
  • No unregistration support at all. The main Python Component Loader supports + unregistration, but the actual Python objects themselves do not support unregistration. It is unclear if the Component Loader + unregistration process needs to manually remove each component it is responsible +for.
  • +
  • All Python-implemented components unconditionally support +weak-references.  There is no way to disable this feature for any or all +components.  It is unclear if there is a need to prevent this, but it is +documented here just in case!
  • +
+

Release History

+

Version 0.90 - January 2001

+
    +
  • First public release.
  • +
+

Version 0.91 - January 2001

+
    +
  • Fix a seg fault on Linux when PYTHONPATH is not set.
  • +
  • Changes to allow building with stand-alone XPCOM.
  • +
+ + + + diff --git a/mozilla/extensions/python/xpcom/register.py b/mozilla/extensions/python/xpcom/register.py new file mode 100644 index 00000000000..53f1e411b69 --- /dev/null +++ b/mozilla/extensions/python/xpcom/register.py @@ -0,0 +1,37 @@ +# Copyright (c) 2000-2001 ActiveState Tool Corporation. +# See the file LICENSE.txt for licensing information. + +import os, sys + +def addSelf(fname): + #XXX the path hardcoding and env var reliance limits the usefulness + #XXX I don't even know if this is used, or usable, at all anymore. + try: + mozSrc = os.environ['MOZ_SRC'] + except KeyError: + print "register.py: need MOZ_SRC to be set" + sys.exit(-1) + try: + komododist = os.environ['KOMODO'] + except KeyError: + print "Set KOMODO" + sys.exit(-1) + + bindir = os.path.join(mozSrc, "dist", "WIN32_D.OBJ", "bin") + idldir = os.path.join(mozSrc, "dist", "idl") + idl2dir = os.path.join(komododist, "SciMoz") + componentdir = os.path.normpath(os.path.join(bindir, 'components')) + + base, ext = os.path.splitext(fname) + idlfile = base+'.idl' + pyfile = base+'.py' + xptfile = base+'.xpt' + if os.path.exists(idlfile): + # IDL file of same name exists, assume it needs to be updated + print r'%(bindir)s\xpidl -I %(idldir)s -I %(idl2dir)s -m typelib %(idlfile)s' % vars() + os.system(r'%(bindir)s\xpidl -I %(idldir)s -I %(idl2dir)s -m typelib %(idlfile)s' % vars()) + print r'cp %(xptfile)s %(componentdir)s' % vars() + os.system(r'cp %(xptfile)s %(componentdir)s' % vars()) + print 'cp %(pyfile)s %(componentdir)s' % vars() + os.system('cp %(pyfile)s %(componentdir)s' % vars()) + diff --git a/mozilla/extensions/python/xpcom/server/__init__.py b/mozilla/extensions/python/xpcom/server/__init__.py new file mode 100644 index 00000000000..475ad0687e1 --- /dev/null +++ b/mozilla/extensions/python/xpcom/server/__init__.py @@ -0,0 +1,38 @@ +# Copyright (c) 2000-2001 ActiveState Tool Corporation. +# See the file LICENSE.txt for licensing information. + +# The xpcom.server package. + +from policy import DefaultPolicy +from xpcom import _xpcom + +# We define the concept of a single "tracer" object - similar to the single +# Python "trace hook" for debugging. Someone can set +# xpcom.server.tracer to some class/function, and it will be used in place +# of the real xpcom object. Presumably this "trace" object will delegate +# to the real object, but presumably also taking some other action, such +# as calling a profiler or debugger. +tracer = None + +# Wrap an instance in an interface (via a policy) +def WrapObject(ob, iid, policy = None): + """Called by the framework to attempt to wrap + an object in a policy. + If iid is None, it will use the first interface the object indicates it supports. + """ + if policy is None: + policy = DefaultPolicy + if tracer is not None: + ob = tracer(ob) + return _xpcom.WrapObject(policy( ob, iid ), iid) + +# Create the main module for the Python loader. +# This is a once only init process, and the returned object +# if used to load all other Python components. + +# This means that we keep all factories, modules etc implemented in +# Python! +def NS_GetModule( serviceManager, nsIFile ): + import loader + iid = _xpcom.IID_nsIModule + return WrapObject(loader.MakePythonComponentLoaderModule(serviceManager, nsIFile), iid) diff --git a/mozilla/extensions/python/xpcom/server/enumerator.py b/mozilla/extensions/python/xpcom/server/enumerator.py new file mode 100644 index 00000000000..97baed26d05 --- /dev/null +++ b/mozilla/extensions/python/xpcom/server/enumerator.py @@ -0,0 +1,24 @@ +# Copyright (c) 2000-2001 ActiveState Tool Corporation. +# See the file LICENSE.txt for licensing information. + +from xpcom import components + +# This class is created by Python components when it +# needs to return an enumerator. +# For example, a component may implement a function: +# nsISimpleEnumerator enumSomething(); +# This could could simply say: +# return SimpleEnumerator([something1, something2, something3]) +class SimpleEnumerator: + _com_interfaces_ = [components.interfaces.nsISimpleEnumerator] + + def __init__(self, data): + self._data = data + self._index = 0 + + def hasMoreElements(self): + return self._index < len(self._data) + + def getNext(self): + self._index = self._index + 1 + return self._data[self._index-1] diff --git a/mozilla/extensions/python/xpcom/server/factory.py b/mozilla/extensions/python/xpcom/server/factory.py new file mode 100644 index 00000000000..f48d60696a5 --- /dev/null +++ b/mozilla/extensions/python/xpcom/server/factory.py @@ -0,0 +1,36 @@ +# Copyright (c) 2000-2001 ActiveState Tool Corporation. +# See the file LICENSE.txt for licensing information. + +# Class factory +# +# Hardly worth its own source file! +import xpcom +from xpcom import components, nsError, _xpcom + + +class Factory: + _com_interfaces_ = components.interfaces.nsIFactory + # This will only ever be constructed via other Python code, + # so we can have ctor args. + def __init__(self, klass): + self.klass = klass + + def createInstance(self, outer, iid): + if outer is not None: + raise xpcom.ServerException(nsError.NS_ERROR_NO_AGGREGATION) + + if xpcom.verbose: + print "Python Factory creating", self.klass.__name__ + try: + return self.klass() + except: + # An exception here may not be obvious to the user - none + # of their code has been called yet. It can be handy on + # failure to tell the user what class failed! + _xpcom.LogWarning("Creation of class '%r' failed!\nException details follow\n" % (self.klass,)) + raise + + def lockServer(self, lock): + if xpcom.verbose: + print "Python Factory LockServer called -", lock + diff --git a/mozilla/extensions/python/xpcom/server/loader.py b/mozilla/extensions/python/xpcom/server/loader.py new file mode 100644 index 00000000000..a6f109587ba --- /dev/null +++ b/mozilla/extensions/python/xpcom/server/loader.py @@ -0,0 +1,208 @@ +# Copyright (c) 2000-2001 ActiveState Tool Corporation. +# See the file LICENSE.txt for licensing information. + +import xpcom +from xpcom import components + +import factory +import module + +import glob, os, types + +from xpcom.client import Component + +fileSizeValueName = "FileSize" +lastModValueName = "LastModTimeStamp" +xpcomKeyName = "software/mozilla/XPCOM/components" + +# Until we get interface constants. +When_Startup = 0 +When_Component = 1 +When_Timer = 2 + +def _has_good_attr(object, attr): + # Actually allows "None" to be specified to disable inherited attributes. + return getattr(object, attr, None) is not None + +def FindCOMComponents(py_module): + # For now, just run over all classes looking for likely candidates. + comps = [] + for name, object in py_module.__dict__.items(): + if type(object)==types.ClassType and \ + _has_good_attr(object, "_com_interfaces_") and \ + _has_good_attr(object, "_reg_clsid_") and \ + _has_good_attr(object, "_reg_contractid_"): + comps.append(object) + return comps + +def register_self(compMgr, location, registryLocation, componentType): + pcl = PythonComponentLoader + from xpcom import _xpcom + svc = _xpcom.GetGlobalServiceManager().GetService("@mozilla.org/categorymanager;1", components.interfaces.nsICategoryManager) + svc.addCategoryEntry("component-loader", pcl._reg_component_type_, pcl._reg_contractid_, 1, 1) + +class PythonComponentLoader: + _com_interfaces_ = components.interfaces.nsIComponentLoader + _reg_clsid_ = "{63B68B1E-3E62-45f0-98E3-5E0B5797970C}" # Never copy these! + _reg_contractid_ = "moz.pyloader.1" + _reg_desc_ = "Python component loader" + # Optional function which performs additional special registration + # Appears that no special unregistration is needed for ComponentLoaders, hence no unregister function. + _reg_registrar_ = (register_self,None) + # Custom attributes for ComponentLoader registration. + _reg_component_type_ = "script/python" + + def __init__(self): + self.com_modules = {} # Keyed by module's FQN as obtained from nsIFile.path + + def _getCOMModuleForLocation(self, componentFile): + fqn = componentFile.path + mod = self.com_modules.get(fqn) + if mod is not None: + return mod + import ihooks, sys + base_name = os.path.splitext(os.path.basename(fqn))[0] + loader = ihooks.ModuleLoader() + + module_name_in_sys = "component:%s" % (base_name,) + stuff = loader.find_module(base_name, [componentFile.parent.path]) + assert stuff is not None, "Couldnt find the module '%s'" % (base_name,) + py_mod = loader.load_module( module_name_in_sys, stuff ) + + # Make and remember the COM module. + comps = FindCOMComponents(py_mod) + mod = module.Module(comps) + + self.com_modules[fqn] = mod + return mod + + def getFactory(self, clsid, location, type): + # return the factory + assert type == self._reg_component_type_, "Being asked to create an object not of my type:%s" % (type,) + file_interface = components.manager.specForRegistryLocation(location) + # delegate to the module. + m = self._getCOMModuleForLocation(file_interface) + return m.getClassObject(components.manager, clsid, components.interfaces.nsIFactory) + + def init(self, comp_mgr, registry): + # void + registry = registry.QueryInterface(components.interfaces.nsIRegistry) + try: + self.xpcom_registry_key = registry.getSubtree( + components.interfaces.nsIRegistry.Common, + xpcomKeyName) + # If we worked, we can use the registry! + self.registry = registry + except xpcom.Exception, details: + print "Registry failed", details + self.registry = None # no registry ops allowed + + self.comp_mgr = comp_mgr + if xpcom.verbose: + print "Python component loader init() called" + + # Called when a component of the appropriate type is registered, + # to give the component loader an opportunity to do things like + # annotate the registry and such. + def onRegister (self, clsid, type, className, proId, location, replace, persist): + if xpcom.verbose: + print "Python component loader - onRegister() called" + + def autoRegisterComponents (self, when, directory): + directory_path = directory.path + print "Auto-registering all Python components in", directory_path + import traceback + + # ToDo - work out the right thing here + # eg - do we recurse? + # - do we support packages? + entries = directory.directoryEntries + while entries.HasMoreElements(): + entry = entries.GetNext(components.interfaces.nsIFile) + if os.path.splitext(entry.path)[1]==".py": + try: + self.autoRegisterComponent(when, entry) + except: + print "** Registration of '%s' failed!" % (entry.path,) + traceback.print_exc() + + def autoRegisterComponent (self, when, componentFile): + # bool return + + reg_loc = components.manager.registryLocationForSpec(componentFile) + # Use the registry to see if we actually need to do anything + if not self._hasChanged(reg_loc, componentFile): + return 1 + + # Sheesh - it appears we should also use the observer service + # to let the system know of our auto-register progress. + + # auto-register via the module. + m = self._getCOMModuleForLocation(componentFile) + m.registerSelf(components.manager, componentFile, reg_loc, self._reg_component_type_) + self._setRegistryInfo(reg_loc, componentFile) + return 1 + + def autoUnregisterComponent (self, when, componentFile): + # bool return + # auto-unregister via the module. + m = self._getCOMModuleForLocation(componentFile) + reg_loc = components.manager.registryLocationForSpec(componentFile) + try: + m.unregisterSelf(components.manager, componentFile, reg_loc) + finally: + self._removeRegistryInfo( reg_loc, componentFile) + return 1 + + def registerDeferredComponents (self, when): + # bool return + if xpcom.verbose: + print "Python component loader - registerDeferred() called" + return 0 # no more to register + def unloadAll (self, when): + if xpcom.verbose: + print "Python component loader being asked to unload all components!" + self.registry = None + self.comp_mgr = None + self.com_modules = {} + # Internal Helpers + def _setRegistryInfo(self, registry_location, nsIFile): + if self.registry is None: + return # No registry work allowed. + e_location = self.registry.escapeKey(registry_location, 1) + if e_location is None: # No escaped key needed. + e_location = registry_location + key = self.registry.addSubtreeRaw(self.xpcom_registry_key, e_location) + self.registry.setLongLong(key, lastModValueName, nsIFile.lastModificationDate) + self.registry.setLongLong(key, fileSizeValueName, nsIFile.fileSize) + def _hasChanged(self, registry_location, nsIFile): + if self.registry is None: + # Can't cache in registry - assume it has changed. + return 1 + e_location = self.registry.escapeKey(registry_location, 1) + if e_location is None: # No escaped key needed. + e_location = registry_location + try: + key = self.registry.getSubtreeRaw(self.xpcom_registry_key, e_location) + if nsIFile.lastModificationDate != self.registry.getLongLong(key, lastModValueName): + return 1 + if nsIFile.fileSize != self.registry.getLongLong(key, fileSizeValueName): + return 1 + return 0 + except xpcom.Exception, details: + return 1 + + def _removeRegistryInfo(self, registry_location, nsIFile): + if self.registry is None: + return # No registry work allowed. + e_location = self.registry.escapeKey(registry_location, 1) + if e_location is None: # No escaped key needed. + e_location = registry_location + try: + key = self.registry.removeSubtreeRaw(self.xpcom_registry_key, e_location) + except xpcom.Exception, details: + pass + +def MakePythonComponentLoaderModule(serviceManager, nsIFile): + import module + return module.Module( [PythonComponentLoader] ) diff --git a/mozilla/extensions/python/xpcom/server/module.py b/mozilla/extensions/python/xpcom/server/module.py new file mode 100644 index 00000000000..ee3f24d57bc --- /dev/null +++ b/mozilla/extensions/python/xpcom/server/module.py @@ -0,0 +1,75 @@ +# Copyright (c) 2000-2001 ActiveState Tool Corporation. +# See the file LICENSE.txt for licensing information. + +from xpcom import components +from xpcom import ServerException, Exception +from xpcom import nsError + +import factory + +import types +import os + +class Module: + _com_interfaces_ = components.interfaces.nsIModule + def __init__(self, comps): + # Build a map of classes we can provide factories for. + c = self.components = {} + for klass in comps: + c[components.ID(klass._reg_clsid_)] = klass + + def getClassObject(self, compMgr, clsid, iid): + # Single retval result. + try: + klass = self.components[clsid] + except KeyError: + raise ServerException(nsError.NS_ERROR_FACTORY_NOT_REGISTERED) + + # We can ignore the IID - the auto-wrapp process will automatically QI us. + return factory.Factory(klass) + + def registerSelf(self, compMgr, location, registryLocation, componentType): + # void function. + for klass in self.components.values(): + print "Registering: %s" % (klass.__name__,) + reg_contractid = klass._reg_contractid_ + reg_desc = getattr(klass, "_reg_desc_", reg_contractid) + compMgr.registerComponentWithType(klass._reg_clsid_, + reg_desc, + reg_contractid, + location, + registryLocation, + 1, + 1, + componentType) + + # See if this class nominates custom register_self + extra_func = getattr(klass, "_reg_registrar_", (None,None))[0] + if extra_func is not None: + extra_func(compMgr, location, registryLocation, componentType) + print "Registered %d Python components in %s" % (len(self.components),os.path.basename(location.path)) + + def unregisterSelf(self, compMgr, location, registryLocation): + # void function. + for klass in self.components.values(): + ok = 1 + try: + compMgr.unregisterComponentSpec(klass._reg_clsid_, location) + except Exception: + ok = 0 + # Give the class a bash even if we failed! + extra_func = getattr(klass, "_reg_registrar_", (None,None))[1] + if extra_func is not None: + try: + extra_func(compMgr, location, registryLocation) + except Exception: + ok = 0 + if ok: + print "Successfully unregistered", klass.__name__ + else: + print "Unregistration of", klass.__name__, "failed. (probably just not already registered)" + + def canUnload(self, compMgr): + # single bool result + return 0 # we can never unload! + diff --git a/mozilla/extensions/python/xpcom/server/policy.py b/mozilla/extensions/python/xpcom/server/policy.py new file mode 100644 index 00000000000..3986fd37d78 --- /dev/null +++ b/mozilla/extensions/python/xpcom/server/policy.py @@ -0,0 +1,211 @@ +# Copyright (c) 2000-2001 ActiveState Tool Corporation. +# See the file LICENSE.txt for licensing information. + +from xpcom import xpcom_consts, _xpcom, client, nsError, ServerException, COMException +import xpcom +import traceback +import xpcom.server +import operator + +_supports_primitives_map_ = {} # Filled on first use. + +def _GetNominatedInterfaces(obj): + ret = getattr(obj, "_com_interfaces_", None) + if ret is None: return None + # See if the user only gave one. + try: + ret[0] + except TypeError: + ret = [ret] + real_ret = [] + # For each interface, walk to the root of the interface tree. + iim = _xpcom.XPTI_GetInterfaceInfoManager() + for interface in ret: + try: + interface_info = iim.GetInfoForIID(interface) + except COMException: + # Allow an interface name. + interface_info = iim.GetInfoForName(interface) + real_ret.append(interface_info.GetIID()) + parent = interface_info.GetParent() + while parent is not None: + real_ret.append(parent.GetIID()) + parent = parent.GetParent() + return real_ret + +class DefaultPolicy: + def __init__(self, instance, iid): + self._obj_ = instance + self._nominated_interfaces_ = ni = _GetNominatedInterfaces(instance) + self._iid_ = iid + if ni is None: + raise ValueError, "The object '%r' can not be used as a COM object" % (instance,) + if iid not in ni: + # The object may delegate QI. + try: + delegate_qi = instance._query_interface_ + except AttributeError: + delegate_qi = None + # Perform the actual QI and throw away the result - the _real_ + # QI performed by the framework will set things right! + if delegate_qi is None or not delegate_qi(iid): + raise ServerException(nsError.NS_ERROR_NO_INTERFACE) + # Stuff for the magic interface conversion. + self._interface_info_ = None + self._interface_iid_map_ = {} # Cache - Indexed by (method_index, param_index) + + def _QueryInterface_(self, com_object, iid): + # Framework allows us to return a single boolean integer, + # or a COM object. + if iid in self._nominated_interfaces_: + # We return the underlying object re-wrapped + # in a new gateway - which is desirable, as one gateway should only support + # one interface (this wont affect the users of this policy - we can have as many + # gateways as we like pointing to the same Python objects - the users never + # see what object the call came in from. + # NOTE: We could have simply returned the instance and let the framework + # do the auto-wrap for us - but this way we prevent a round-trip back into Python + # code just for the autowrap. + return xpcom.server.WrapObject(self._obj_, iid) + + # See if the instance has a QI + # use lower-case "_query_interface_" as win32com does, and it doesnt really matter. + delegate = getattr(self._obj_, "_query_interface_", None) + if delegate is not None: + # The COM object itself doesnt get passed to the child + # (again, as win32com doesnt). It is rarely needed + # (in win32com, we dont even pass it to the policy, although we have identified + # one place where we should - for marshalling - so I figured I may as well pass it + # to the policy layer here, but no all the way down to the object. + return delegate(iid) + # Finally see if we are being queried for one of the "nsISupports primitives" + if not _supports_primitives_map_: + iim = _xpcom.XPTI_GetInterfaceInfoManager() + for (iid_name, attr, cvt) in _supports_primitives_data_: + special_iid = iim.GetInfoForName(iid_name).GetIID() + _supports_primitives_map_[special_iid] = (attr, cvt) + attr, cvt = _supports_primitives_map_.get(iid, (None,None)) + if attr is not None and hasattr(self._obj_, attr): + return xpcom.server.WrapObject(SupportsPrimitive(iid, self._obj_, attr, cvt), iid) + # Out of clever things to try! + return None # We dont support this IID. + + def _MakeInterfaceParam_(self, interface, iid, method_index, mi, param_index): + # Wrap a "raw" interface object in a nice object. The result of this + # function will be passed to one of the gateway methods. + if iid is None: + # look up the interface info - this will be true for all xpcom called interfaces. + if self._interface_info_ is None: + import xpcom.xpt + self._interface_info_ = xpcom.xpt.Interface( self._iid_ ) + iid = self._interface_iid_map_.get( (method_index, param_index)) + if iid is None: + iid = self._interface_info_.GetIIDForParam(method_index, param_index) + self._interface_iid_map_[(method_index, param_index)] = iid +# iid = _xpcom.IID_nsISupports + return client.Interface(interface, iid) + + def _CallMethod_(self, com_object, index, info, params): +# print "_CallMethod_", index, info, params + flags, name, param_descs, ret = info + assert ret[1][0] == xpcom_consts.TD_UINT32, "Expected an nsresult (%s)" % (ret,) + if xpcom_consts.XPT_MD_IS_GETTER(flags): + # Look for a function of that name + func = getattr(self._obj_, "get_" + name, None) + if func is None: + assert len(param_descs)==1 and len(params)==0, "Can only handle a single [out] arg for a default getter" + ret = getattr(self._obj_, name) # Let attribute error go here! + else: + ret = func(*params) + return 0, ret + elif xpcom_consts.XPT_MD_IS_SETTER(flags): + # Look for a function of that name + func = getattr(self._obj_, "set_" + name, None) + if func is None: + assert len(param_descs)==1 and len(params)==1, "Can only handle a single [in] arg for a default setter" + setattr(self._obj_, name, params[0]) # Let attribute error go here! + else: + func(*params) + return 0 + else: + # A regular method. + func = getattr(self._obj_, name) + return 0, func(*params) + + def _doHandleException(self, func_name, exc_info): + exc_val = exc_info[1] + is_server_exception = isinstance(exc_val, ServerException) + if is_server_exception: + if xpcom.verbose: + print "** Information: '%s' raised COM Exception %s" % (func_name, exc_val) + traceback.print_exception(exc_info[0], exc_val, exc_info[2]) + print "** Returning nsresult from existing exception", exc_val + return exc_val.errno + # Unhandled exception - always print a warning. + print "** Unhandled exception calling '%s'" % (func_name,) + traceback.print_exception(exc_info[0], exc_val, exc_info[2]) + print "** Returning nsresult of NS_ERROR_FAILURE" + return nsError.NS_ERROR_FAILURE + + + # Called whenever an unhandled Python exception is detected as a result + # of _CallMethod_ - this exception may have been raised during the _CallMethod_ + # invocation, or after its return, but when unpacking the results + # eg, type errors, such as a Python integer being used as a string "out" param. + def _CallMethodException_(self, com_object, index, info, params, exc_info): + # Later we may want to have some smart "am I debugging" flags? + # Or maybe just delegate to the actual object - it's probably got the best + # idea what to do with them! + flags, name, param_descs, ret = info + exc_typ, exc_val, exc_tb = exc_info + # use the xpt module to get a better repr for the method. + # But if we fail, ignore it! + try: + import xpcom.xpt + m = xpcom.xpt.Method(info, index, None) + func_repr = m.Describe().lstrip() + except: + func_repr = "%s(%r)" % (name, param_descs) + return self._doHandleException(func_repr, exc_info) + + # Called whenever a gateway fails due to anything other than _CallMethod_. + # Really only used for the component loader etc objects, so most + # users should never see exceptions triggered here. + def _GatewayException_(self, name, exc_info): + return self._doHandleException(name, exc_info) + +_supports_primitives_data_ = [ + ("nsISupportsString", "__str__", str), + ("nsISupportsWString", "__str__", str), + ("nsISupportsPRUint64", "__long__", long), + ("nsISupportsPRInt64", "__long__", long), + ("nsISupportsPRUint32", "__int__", int), + ("nsISupportsPRInt32", "__int__", int), + ("nsISupportsPRUint16", "__int__", int), + ("nsISupportsPRInt16", "__int__", int), + ("nsISupportsPRUint8", "__int__", int), + ("nsISupportsPRBool", "__nonzero__", operator.truth), + ("nsISupportsDouble", "__float__", float), + ("nsISupportsFloat", "__float__", float), +] + +# Support for the nsISupports primitives: +class SupportsPrimitive: + _com_interfaces_ = ["nsISupports"] + def __init__(self, iid, base_ob, attr_name, converter): + self.iid = iid + self.base_ob = base_ob + self.attr_name = attr_name + self.converter = converter + def _query_interface_(self, iid): + if iid == self.iid: + return 1 + return None + def get_data(self): + method = getattr(self.base_ob, self.attr_name) + val = method() + return self.converter(val) + def set_data(self, val): + raise ServerException(nsError.NS_ERROR_NOT_IMPLEMENTED) + def toString(self): + return str(self.get_data()) diff --git a/mozilla/extensions/python/xpcom/src/ErrorUtils.cpp b/mozilla/extensions/python/xpcom/src/ErrorUtils.cpp new file mode 100644 index 00000000000..d2135b61f1a --- /dev/null +++ b/mozilla/extensions/python/xpcom/src/ErrorUtils.cpp @@ -0,0 +1,219 @@ +/* Copyright (c) 2000-2001 ActiveState Tool Corporation. + See the file LICENSE.txt for licensing information. */ + +// +// This code is part of the XPCOM extensions for Python. +// +// Written May 2000 by Mark Hammond. +// +// Based heavily on the Python COM support, which is +// (c) Mark Hammond and Greg Stein. +// +// (c) 2000, ActiveState corp. + +#include "PyXPCOM_std.h" +#include + +static char *PyTraceback_AsString(PyObject *exc_tb); + +// The internal helper that actually moves the +// formatted string to the target! + +void LogMessage(const char *prefix, const char *pszMessageText) +{ + nsOutputConsoleStream console; + console << prefix << pszMessageText; +} + +// A helper for the various logging routines. +static void VLogF(const char *prefix, const char *fmt, va_list argptr) +{ + char buff[512]; + + vsprintf(buff, fmt, argptr); + + LogMessage(prefix, buff); +} + +void PyXPCOM_LogError(const char *fmt, ...) +{ + va_list marker; + va_start(marker, fmt); + VLogF("PyXPCOM Error: ", fmt, marker); + // If we have a Python exception, also log that: + PyObject *exc_typ = NULL, *exc_val = NULL, *exc_tb = NULL; + PyErr_Fetch( &exc_typ, &exc_val, &exc_tb); + if (exc_typ) { + PyErr_NormalizeException( &exc_typ, &exc_val, &exc_tb); + char *string1 = nsnull; + nsOutputStringStream streamout(string1); + + if (exc_tb) { + const char *szTraceback = PyTraceback_AsString(exc_tb); + if (szTraceback == NULL) + streamout << "Can't get the traceback info!"; + else { + streamout << "Traceback (most recent call last):\n"; + streamout << szTraceback; + PyMem_Free((ANY *)szTraceback); + } + } + PyObject *temp = PyObject_Str(exc_typ); + if (temp) { + streamout << PyString_AsString(temp); + Py_DECREF(temp); + } else + streamout << "Can't convert exception to a string!"; + streamout << ": "; + if (exc_val != NULL) { + temp = PyObject_Str(exc_val); + if (temp) { + streamout << PyString_AsString(temp); + Py_DECREF(temp); + } else + streamout << "Can't convert exception value to a string!"; + } + streamout << "\n"; + LogMessage("PyXPCOM Exception:", string1); + } + PyErr_Restore(exc_typ, exc_val, exc_tb); +} + +void PyXPCOM_LogWarning(const char *fmt, ...) +{ + va_list marker; + va_start(marker, fmt); + VLogF("PyXPCOM Warning: ", fmt, marker); +} + +#ifdef DEBUG +void PyXPCOM_LogDebug(const char *fmt, ...) +{ + va_list marker; + va_start(marker, fmt); + VLogF("PyXPCOM Debug: ", fmt, marker); +} +#endif + + +PyObject *PyXPCOM_BuildPyException(nsresult r) +{ + // Need the message etc. + PyObject *evalue = Py_BuildValue("i", r); + PyErr_SetObject(PyXPCOM_Error, evalue); + Py_XDECREF(evalue); + return NULL; +} + +nsresult PyXPCOM_SetCOMErrorFromPyException() +{ + if (!PyErr_Occurred()) + // No error occurred + return NS_OK; + return NS_ERROR_FAILURE; +} + +/* Obtains a string from a Python traceback. + This is the exact same string as "traceback.print_exc" would return. + + Pass in a Python traceback object (probably obtained from PyErr_Fetch()) + Result is a string which must be free'd using PyMem_Free() +*/ +#define TRACEBACK_FETCH_ERROR(what) {errMsg = what; goto done;} + +char *PyTraceback_AsString(PyObject *exc_tb) +{ + char *errMsg = NULL; /* a static that hold a local error message */ + char *result = NULL; /* a valid, allocated result. */ + PyObject *modStringIO = NULL; + PyObject *modTB = NULL; + PyObject *obFuncStringIO = NULL; + PyObject *obStringIO = NULL; + PyObject *obFuncTB = NULL; + PyObject *argsTB = NULL; + PyObject *obResult = NULL; + + /* Import the modules we need - cStringIO and traceback */ + modStringIO = PyImport_ImportModule("cStringIO"); + if (modStringIO==NULL) + TRACEBACK_FETCH_ERROR("cant import cStringIO\n"); + + modTB = PyImport_ImportModule("traceback"); + if (modTB==NULL) + TRACEBACK_FETCH_ERROR("cant import traceback\n"); + /* Construct a cStringIO object */ + obFuncStringIO = PyObject_GetAttrString(modStringIO, "StringIO"); + if (obFuncStringIO==NULL) + TRACEBACK_FETCH_ERROR("cant find cStringIO.StringIO\n"); + obStringIO = PyObject_CallObject(obFuncStringIO, NULL); + if (obStringIO==NULL) + TRACEBACK_FETCH_ERROR("cStringIO.StringIO() failed\n"); + /* Get the traceback.print_exception function, and call it. */ + obFuncTB = PyObject_GetAttrString(modTB, "print_tb"); + if (obFuncTB==NULL) + TRACEBACK_FETCH_ERROR("cant find traceback.print_tb\n"); + + argsTB = Py_BuildValue("OOO", + exc_tb ? exc_tb : Py_None, + Py_None, + obStringIO); + if (argsTB==NULL) + TRACEBACK_FETCH_ERROR("cant make print_tb arguments\n"); + + obResult = PyObject_CallObject(obFuncTB, argsTB); + if (obResult==NULL) + TRACEBACK_FETCH_ERROR("traceback.print_tb() failed\n"); + /* Now call the getvalue() method in the StringIO instance */ + Py_DECREF(obFuncStringIO); + obFuncStringIO = PyObject_GetAttrString(obStringIO, "getvalue"); + if (obFuncStringIO==NULL) + TRACEBACK_FETCH_ERROR("cant find getvalue function\n"); + Py_DECREF(obResult); + obResult = PyObject_CallObject(obFuncStringIO, NULL); + if (obResult==NULL) + TRACEBACK_FETCH_ERROR("getvalue() failed.\n"); + + /* And it should be a string all ready to go - duplicate it. */ + if (!PyString_Check(obResult)) + TRACEBACK_FETCH_ERROR("getvalue() did not return a string\n"); + + { // a temp scope so I can use temp locals. + char *tempResult = PyString_AsString(obResult); + result = (char *)PyMem_Malloc(strlen(tempResult)+1); + if (result==NULL) + TRACEBACK_FETCH_ERROR("memory error duplicating the traceback string"); + + strcpy(result, tempResult); + } // end of temp scope. +done: + /* All finished - first see if we encountered an error */ + if (result==NULL && errMsg != NULL) { + result = (char *)PyMem_Malloc(strlen(errMsg)+1); + if (result != NULL) + /* if it does, not much we can do! */ + strcpy(result, errMsg); + } + Py_XDECREF(modStringIO); + Py_XDECREF(modTB); + Py_XDECREF(obFuncStringIO); + Py_XDECREF(obStringIO); + Py_XDECREF(obFuncTB); + Py_XDECREF(argsTB); + Py_XDECREF(obResult); + return result; +} + +// See comments in PyXPCOM.h for why we need this! +void PyXPCOM_MakePendingCalls() +{ + while (1) { + int rc = Py_MakePendingCalls(); + if (rc == 0) + break; + // An exception - just report it as normal. + // Note that a traceback is very unlikely! + PyXPCOM_LogError("Unhandled exception detected before entering Python.\n"); + PyErr_Clear(); + // And loop around again until we are told everything is done! + } +} diff --git a/mozilla/extensions/python/xpcom/src/PyGBase.cpp b/mozilla/extensions/python/xpcom/src/PyGBase.cpp new file mode 100644 index 00000000000..cf47e00faa9 --- /dev/null +++ b/mozilla/extensions/python/xpcom/src/PyGBase.cpp @@ -0,0 +1,757 @@ +/* Copyright (c) 2000-2001 ActiveState Tool Corporation. + See the file LICENSE.txt for licensing information. */ + +// PyGBase.cpp - implementation of the PyG_Base class +// +// This code is part of the XPCOM extensions for Python. +// +// Written May 2000 by Mark Hammond. +// +// Based heavily on the Python COM support, which is +// (c) Mark Hammond and Greg Stein. +// +// (c) 2000, ActiveState corp. + +#include "PyXPCOM_std.h" +#include +#include +#include + +static PRInt32 cGateways = 0; +PRInt32 _PyXPCOM_GetGatewayCount(void) +{ + return cGateways; +} + +extern PyG_Base *MakePyG_nsIModule(PyObject *); +extern PyG_Base *MakePyG_nsIComponentLoader(PyObject *instance); +extern PyG_Base *MakePyG_nsIInputStream(PyObject *instance); + +static char *PyXPCOM_szDefaultGatewayAttributeName = "_com_instance_default_gateway_"; +nsresult GetDefaultGateway(PyObject *instance, REFNSIID iid, void **ret); +void AddDefaultGateway(PyObject *instance, nsISupports *gateway); +PRBool CheckDefaultGateway(PyObject *real_inst, REFNSIID iid, nsISupports **ret_gateway); + +/*static*/ nsresult +PyG_Base::CreateNew(PyObject *pPyInstance, const nsIID &iid, void **ppResult) +{ + NS_PRECONDITION(ppResult && *ppResult==NULL, "NULL or uninitialized pointer"); + if (ppResult==nsnull) + return NS_ERROR_NULL_POINTER; + + PyG_Base *ret; + // Hack for few extra gateways we support. + if (iid.Equals(NS_GET_IID(nsIModule))) + ret = MakePyG_nsIModule(pPyInstance); + else if (iid.Equals(NS_GET_IID(nsIComponentLoader))) + ret = MakePyG_nsIComponentLoader(pPyInstance); + else if (iid.Equals(NS_GET_IID(nsIInputStream))) + ret = MakePyG_nsIInputStream(pPyInstance); + else + ret = new PyXPCOM_XPTStub(pPyInstance, iid); + if (ret==nsnull) + return NS_ERROR_OUT_OF_MEMORY; + ret->AddRef(); // The first reference for the caller. + *ppResult = ret->ThisAsIID(iid); + NS_ABORT_IF_FALSE(*ppResult != NULL, "ThisAsIID() gave NULL, but we know it supports it!"); + return *ppResult ? NS_OK : NS_ERROR_FAILURE; +} + +PyG_Base::PyG_Base(PyObject *instance, const nsIID &iid) +{ + // Note that "instance" is the _policy_ instance!! + NS_INIT_REFCNT(); + PR_AtomicIncrement(&cGateways); + m_pBaseObject = NULL; + // m_pWeakRef is an nsCOMPtr and needs no init. + m_iid = iid; + m_pPyObject = instance; + NS_PRECONDITION(instance, "NULL PyObject for PyXPCOM_XPTStub!"); +#ifdef DEBUG_LIFETIMES + { + char *iid_repr; + nsCOMPtr iim = XPTI_GetInterfaceInfoManager(); + if (iim!=nsnull) + iim->GetNameForIID(&iid, &iid_repr); + PyObject *real_instance = PyObject_GetAttrString(instance, "_obj_"); + PyObject *real_repr = PyObject_Repr(real_instance); + + PYXPCOM_LOG_DEBUG("PyG_Base created at %p\n instance_repr=%s\n IID=%s\n", this, PyString_AsString(real_repr), iid_repr); + nsAllocator::Free(iid_repr); + Py_XDECREF(real_instance); + Py_XDECREF(real_repr); + } +#endif // DEBUG_LIFETIMES + Py_XINCREF(instance); // instance should never be NULL - but whats an X between friends! + + PyXPCOM_DLLAddRef(); + +#ifdef DEBUG_FULL + LogF("PyGatewayBase: created %s", m_pPyObject ? m_pPyObject->ob_type->tp_name : ""); +#endif +} + +PyG_Base::~PyG_Base() +{ + PR_AtomicDecrement(&cGateways); +#ifdef DEBUG_LIFETIMES + PYXPCOM_LOG_DEBUG("PyG_Base: deleted %p", this); +#endif + if ( m_pPyObject ) { + CEnterLeavePython celp; + Py_DECREF(m_pPyObject); + } + if (m_pBaseObject) + m_pBaseObject->Release(); + if (m_pWeakRef) { + // Need to ensure some other thread isnt doing a QueryReferent on + // our weak reference at the same time + CEnterLeaveXPCOMFramework _celf; + PyXPCOM_GatewayWeakReference *p = (PyXPCOM_GatewayWeakReference *)(nsISupports *)m_pWeakRef; + p->m_pBase = nsnull; + m_pWeakRef = nsnull; + } + PyXPCOM_DLLRelease(); +} + +// Get the correct interface pointer for this object given the IID. +void *PyG_Base::ThisAsIID( const nsIID &iid ) +{ + if (this==NULL) return NULL; + if (iid.Equals(NS_GET_IID(nsISupports))) + return (nsISupports *)(nsIInternalPython *)this; + if (iid.Equals(NS_GET_IID(nsISupportsWeakReference))) + return (nsISupportsWeakReference *)this; + if (iid.Equals(NS_GET_IID(nsIInternalPython))) + return (nsISupports *)(nsIInternalPython *)this; + return NULL; +}; + +// Call back into Python, passing a Python instance, and get back +// an interface object that wraps the instance. +/*static*/ PRBool +PyG_Base::AutoWrapPythonInstance(PyObject *ob, const nsIID &iid, nsISupports **ppret) +{ + NS_PRECONDITION(ppret!=NULL, "null pointer when wrapping a Python instance!"); + NS_PRECONDITION(ob && PyInstance_Check(ob), "AutoWrapPythonInstance is expecting an non-NULL instance!"); + PRBool ok = PR_FALSE; + // XXX - todo - this static object leaks! (but Python on Windows leaks 2000+ objects as it is ;-) + static PyObject *func = NULL; // fetch this once and remember! + PyObject *obIID = NULL; + PyObject *wrap_ret = NULL; + PyObject *args = NULL; + if (func==NULL) { // not thread-safe, but nothing bad can happen, except an extra reference leak + PyObject *mod = PyImport_ImportModule("xpcom.server"); + if (mod) + func = PyObject_GetAttrString(mod, "WrapObject"); + Py_XDECREF(mod); + if (func==NULL) goto done; + } + // See if the instance has previously been wrapped. + if (CheckDefaultGateway(ob, iid, ppret)) { + ok = PR_TRUE; // life is good! + } else { + PyErr_Clear(); + + obIID = Py_nsIID::PyObjectFromIID(iid); + if (obIID==NULL) goto done; + args = Py_BuildValue("OO", ob, obIID); + if (args==NULL) goto done; + wrap_ret = PyEval_CallObject(func, args); + if (wrap_ret==NULL) goto done; + ok = Py_nsISupports::InterfaceFromPyObject(wrap_ret, iid, ppret, PR_FALSE, PR_FALSE); +#ifdef DEBUG + if (ok) + // Check we _now_ have a default gateway + { + nsISupports *temp = NULL; + NS_ABORT_IF_FALSE(CheckDefaultGateway(ob, iid, &temp), "Auto-wrapped object didnt get a default gateway!"); + if (temp) temp->Release(); + } +#endif + } +done: +// Py_XDECREF(func); -- func is static for performance reasons. + Py_XDECREF(obIID); + Py_XDECREF(wrap_ret); + Py_XDECREF(args); + return ok; +} + +// Call back into Python, passing a raw nsIInterface object, getting back +// the object to actually use as the gateway parameter for this interface. +// For example, it is expected that the policy will wrap the interface +// object in one of the xpcom.client.Interface objects, allowing +// natural usage of the interface from Python clients. +// Note that piid will usually be NULL - this is because the runtime +// reflection interfaces dont provide this information to me. +// In this case, the Python code may choose to lookup the complete +// interface info to obtain the IID. +// It is expected (but should not be assumed) that the method info +// or the IID will be NULL. +// Worst case, the code should provide a wrapper for the nsiSupports interface, +// so at least the user can simply QI the object. +PyObject * +PyG_Base::MakeInterfaceParam(nsISupports *pis, + const nsIID *piid, + int methodIndex /* = -1 */, + const XPTParamDescriptor *d /* = NULL */, + int paramIndex /* = -1 */) +{ + if (pis==NULL) { + Py_INCREF(Py_None); + return Py_None; + } + // This condition is true today, but not necessarily so. + // But if it ever triggers, the poor Python code has no real hope + // of returning something useful, so we should at least do our + // best to provide the useful data. + NS_WARN_IF_FALSE( ((piid != NULL) ^ (d != NULL)) == 1, "No information on the interface available - Python's gunna have a hard time doing much with it!"); + PyObject *obIID = NULL; + PyObject *obISupports = NULL; + PyObject *obParamDesc = NULL; + PyObject *result = NULL; + + // get the basic interface first, as if we fail, we can try and use this. + nsIID iid_check = piid ? *piid : NS_GET_IID(nsISupports); + obISupports = Py_nsISupports::PyObjectFromInterface(pis, iid_check, PR_TRUE, PR_FALSE); + if (!obISupports) + goto done; + if (piid==NULL) { + obIID = Py_None; + Py_INCREF(Py_None); + } else + obIID = Py_nsIID::PyObjectFromIID(*piid); + if (obIID==NULL) + goto done; + obParamDesc = PyObject_FromXPTParamDescriptor(d); + if (obParamDesc==NULL) + goto done; + + result = PyObject_CallMethod(m_pPyObject, + "_MakeInterfaceParam_", + "OOiOi", + obISupports, + obIID, + methodIndex, + obParamDesc, + paramIndex); +done: + if (PyErr_Occurred()) { + NS_WARN_IF_FALSE(result==NULL, "Have an error, but also a result!"); + PyXPCOM_LogError("Wrapping an interface object for the gateway failed\n"); + } + Py_XDECREF(obIID); + Py_XDECREF(obParamDesc); + if (result==NULL) // we had an error. + // return our obISupports. If NULL, we are really hosed and nothing we can do. + return obISupports; + // Dont need to return this - we have a better result. + Py_XDECREF(obISupports); + return result; +} + +NS_IMETHODIMP +PyG_Base::QueryInterface(REFNSIID iid, void** ppv) +{ +#ifdef PYXPCOM_DEBUG_FULL + { + char *sziid = iid.ToString(); + LogF("PyGatewayBase::QueryInterface: %s", sziid); + Allocator::Free(sziid); + } +#endif + NS_PRECONDITION(ppv, "NULL pointer"); + if (ppv==nsnull) + return NS_ERROR_NULL_POINTER; + *ppv = nsnull; + // If one of our native interfaces (but NOT nsISupports if we have a base) + // return this. + // It is important is that nsISupports come from the base object + // to ensure that we live by XPCOM identity rules (other interfaces need + // not abide by this rule - only nsISupports.) + if ( (m_pBaseObject==NULL || !iid.Equals(NS_GET_IID(nsISupports))) + && (*ppv=ThisAsIID(iid)) != NULL ) { + AddRef(); + return NS_OK; + } + // If we have a "base object", then we need to delegate _every_ remaining + // QI to it. + if (m_pBaseObject != NULL && (m_pBaseObject->QueryInterface(iid, ppv)==NS_OK)) + return NS_OK; + + // Call the Python policy to see if it (says it) supports the interface + PRBool supports = PR_FALSE; + { // temp scope for Python lock + CEnterLeavePython celp; + + PyObject * ob = Py_nsIID::PyObjectFromIID(iid); + PyObject * this_interface_ob = Py_nsISupports::PyObjectFromInterface((nsIInternalPython *)this, NS_GET_IID(nsISupports), PR_TRUE, PR_FALSE); + if ( !ob || !this_interface_ob) { + Py_XDECREF(ob); + Py_XDECREF(this_interface_ob); + return NS_ERROR_OUT_OF_MEMORY; + } + + PyObject *result = PyObject_CallMethod(m_pPyObject, "_QueryInterface_", + "OO", + this_interface_ob, ob); + Py_DECREF(ob); + Py_DECREF(this_interface_ob); + + if ( result ) { + if (Py_nsISupports::InterfaceFromPyObject(result, iid, (nsISupports **)ppv, PR_TRUE)) { + // If OK, but NULL, _QI_ returned None, which simply means + // "no such interface" + supports = (*ppv!=NULL); + // result has been QI'd and AddRef'd all ready for return. + } else { + // Dump this message and any Python exception before + // reporting the fact that QI failed - this error + // may provide clues! + PyXPCOM_LogError("The _QueryInterface_ method returned an object of type '%s', but an interface was expected\n", result->ob_type->tp_name); + // supports remains false + } + Py_DECREF(result); + } else { + NS_ABORT_IF_FALSE(PyErr_Occurred(), "Got NULL result, but no Python error flagged!"); + NS_WARN_IF_FALSE(!supports, "Have failure with success flag set!"); + PyXPCOM_LogError("The _QueryInterface_ processing failed.\n"); + // supports remains false. + // We have reported the error, and are returning to COM, + // so we should clear it. + PyErr_Clear(); + } + } // end of temp scope for Python lock - lock released here! + if ( !supports ) + return NS_ERROR_NO_INTERFACE; + + // Now setup the base object pointer back to me. + // We do a QI on our internal one to ensure we can safely cast + // the result to a PyG_Base (both from the POV that is may not + // be a Python object, and that the vtables offsets may screw + // us even if it is!) + nsISupports *pLook = (nsISupports *)(*ppv); + nsIInternalPython *pTemp; + if (pLook->QueryInterface(NS_GET_IID(nsIInternalPython), (void **)&pTemp)==NS_OK) { + // One of our objects, so set the base object if it doesnt already have one + PyG_Base *pG = (PyG_Base *)pTemp; + // Eeek - just these few next lines need to be thread-safe :-( + CEnterLeaveXPCOMFramework _celf; + if (pG->m_pBaseObject==NULL && pG != (PyG_Base *)this) { + pG->m_pBaseObject = this; + pG->m_pBaseObject->AddRef(); +#ifdef DEBUG_LIFETIMES + PYXPCOM_LOG_DEBUG("PyG_Base setting BaseObject of %p to %p\n", pG, this); +#endif + } + pTemp->Release(); + } + return NS_OK; +} + +nsrefcnt +PyG_Base::AddRef(void) +{ + nsrefcnt cnt = (nsrefcnt) PR_AtomicIncrement((PRInt32*)&mRefCnt); + NS_LOG_ADDREF(this, cnt, "PyG_Base", sizeof(*this)); + return cnt; +} + +nsrefcnt +PyG_Base::Release(void) +{ + nsrefcnt cnt = (nsrefcnt) PR_AtomicDecrement((PRInt32*)&mRefCnt); + NS_LOG_RELEASE(this, cnt, "PyG_Base"); + if ( cnt == 0 ) + delete this; + return cnt; +} + +NS_IMETHODIMP +PyG_Base::GetWeakReference(nsIWeakReference **ret) +{ + NS_PRECONDITION(ret, "null pointer"); + if (ret==nsnull) return NS_ERROR_INVALID_POINTER; + if (!m_pWeakRef) { + // First query for a weak reference - create it. + m_pWeakRef = new PyXPCOM_GatewayWeakReference(this); + NS_ABORT_IF_FALSE(m_pWeakRef, "Shouldn't be able to fail creating a weak reference!"); + if (!m_pWeakRef) + return NS_ERROR_UNEXPECTED; + } + *ret = m_pWeakRef; + (*ret)->AddRef(); + return NS_OK; +} + +nsresult PyG_Base::HandleNativeGatewayError(const char *szMethodName) +{ + nsresult rc = NS_OK; + if (PyErr_Occurred()) { + // The error handling - fairly involved, but worth it as + // good error reporting is critical for users to know WTF + // is going on - especially with TypeErrors etc in their + // return values (ie, after the Python code has successfully + // existed, but we encountered errors unpacking their + // result values for the COM caller - there is literally no + // way to catch these exceptions from Python code, as their + // is no Python function on the call-stack) + + // First line of attack in an error is to call-back on the policy. + // If the callback of the error handler succeeds and returns an + // integer (for the nsresult), we take no further action. + + // If this callback fails, we log _2_ exceptions - the error handler + // error, and the original error. + + PRBool bProcessMainError = PR_TRUE; // set to false if our exception handler does its thing! + PyObject *exc_typ, *exc_val, *exc_tb; + PyErr_Fetch(&exc_typ, &exc_val, &exc_tb); + + PyObject *err_result = PyObject_CallMethod(m_pPyObject, + "_GatewayException_", + "z(OOO)", + szMethodName, + exc_typ ? exc_typ : Py_None, // should never be NULL, but defensive programming... + exc_val ? exc_val : Py_None, // may well be NULL. + exc_tb ? exc_tb : Py_None); // may well be NULL. + if (err_result == NULL) { + PyXPCOM_LogError("The exception handler _CallMethodException_ failed!\n"); + } else if (err_result == Py_None) { + // The exception handler has chosen not to do anything with + // this error, so we still need to print it! + ; + } else if (PyInt_Check(err_result)) { + // The exception handler has given us the nresult. + rc = PyInt_AsLong(err_result); + bProcessMainError = PR_FALSE; + } else { + // The exception handler succeeded, but returned other than + // int or None. + PyXPCOM_LogError("The _CallMethodException_ handler returned object of type '%s' - None or an integer expected\n", err_result->ob_type->tp_name); + } + Py_XDECREF(err_result); + PyErr_Restore(exc_typ, exc_val, exc_tb); + if (bProcessMainError) { + PyXPCOM_LogError("The function '%s' failed\n", szMethodName); + rc = PyXPCOM_SetCOMErrorFromPyException(); + } + PyErr_Clear(); + } + return rc; +} + +static nsresult do_dispatch( + PyObject *pPyObject, + PyObject **ppResult, + const char *szMethodName, + const char *szFormat, + va_list va + ) +{ + NS_PRECONDITION(ppResult, "Must provide a result buffer"); + *ppResult = nsnull; + // Build the Invoke arguments... + PyObject *args = NULL; + PyObject *method = NULL; + PyObject *real_ob = NULL; + nsresult ret = NS_ERROR_FAILURE; + if ( szFormat ) + args = Py_VaBuildValue((char *)szFormat, va); + else + args = PyTuple_New(0); + if ( !args ) + goto done; + + // make sure a tuple. + if ( !PyTuple_Check(args) ) { + PyObject *a = PyTuple_New(1); + if ( a == NULL ) + { + Py_DECREF(args); + goto done; + } + PyTuple_SET_ITEM(a, 0, args); + args = a; + } + // Bit to a hack here to maintain the use of a policy. + // We actually get the policies underlying object + // to make the call on. + real_ob = PyObject_GetAttrString(pPyObject, "_obj_"); + if (real_ob == NULL) { + PyErr_Format(PyExc_AttributeError, "The policy object does not have an '_obj_' attribute."); + goto done; + } + method = PyObject_GetAttrString(real_ob, (char *)szMethodName); + if ( !method ) { + PyErr_Clear(); + ret = NS_COMFALSE; + goto done; + } + // Make the call + *ppResult = PyEval_CallObject(method, args); + ret = *ppResult ? NS_OK : NS_ERROR_FAILURE; +done: + Py_XDECREF(method); + Py_XDECREF(real_ob); + Py_XDECREF(args); + return ret; +} + + +nsresult PyG_Base::InvokeNativeViaPolicyInternal( + const char *szMethodName, + PyObject **ppResult, + const char *szFormat, + va_list va + ) +{ + if ( m_pPyObject == NULL || szMethodName == NULL ) + return NS_ERROR_NULL_POINTER; + + PyObject *temp = nsnull; + if (ppResult == nsnull) + ppResult = &temp; + nsresult nr = do_dispatch(m_pPyObject, ppResult, szMethodName, szFormat, va); + + // If temp is NULL, they provided a buffer, and we dont touch it. + // If not NULL, *ppResult = temp, and _we_ do own it. + Py_XDECREF(temp); + return nr; +} + +nsresult PyG_Base::InvokeNativeViaPolicy( + const char *szMethodName, + PyObject **ppResult /* = NULL */, + const char *szFormat /* = NULL */, + ... + ) +{ + va_list va; + va_start(va, szFormat); + nsresult nr = InvokeNativeViaPolicyInternal(szMethodName, ppResult, szFormat, va); + va_end(va); + + if (nr==NS_COMFALSE) { + // Only problem was missing method. + PyErr_Format(PyExc_AttributeError, "The object does not have a '%s' function.", szMethodName); + } + return nr == NS_OK ? NS_OK : HandleNativeGatewayError(szMethodName); +} + +nsresult PyG_Base::InvokeNativeGetViaPolicy( + const char *szPropertyName, + PyObject **ppResult /* = NULL */ + ) +{ + PyObject *ob_ret = NULL; + nsresult ret = NS_OK; + PyObject *real_ob = NULL; + if ( m_pPyObject == NULL || szPropertyName == NULL ) + return NS_ERROR_NULL_POINTER; + // First see if we have a method of that name. + char buf[256]; + strcpy(buf, "get_"); + strncat(buf, szPropertyName, sizeof(buf)*sizeof(buf[0])-strlen(buf)-1); + buf[sizeof(buf)/sizeof(buf[0])-1] = '\0'; + ret = InvokeNativeViaPolicyInternal(buf, ppResult, nsnull, nsnull); + if (ret == NS_COMFALSE) { + // No method of that name - just try a property. + // Bit to a hack here to maintain the use of a policy. + // We actually get the policies underlying object + // to make the call on. + real_ob = PyObject_GetAttrString(m_pPyObject, "_obj_"); + if (real_ob == NULL) { + PyErr_Format(PyExc_AttributeError, "The policy object does not have an '_obj_' attribute."); + ret = HandleNativeGatewayError(szPropertyName); + goto done; + } + ob_ret = PyObject_GetAttrString(real_ob, (char *)szPropertyName); + if (ob_ret==NULL) { + PyErr_Format(PyExc_AttributeError, + "The object does not have a 'get_%s' function, or a '%s attribute.", + szPropertyName, szPropertyName); + } else { + ret = NS_OK; + if (ppResult) + *ppResult = ob_ret; + else + Py_XDECREF(ob_ret); + } + } + if (ret != NS_OK) + ret = HandleNativeGatewayError(szPropertyName); + +done: + Py_XDECREF(real_ob); + return ret; +} + +nsresult PyG_Base::InvokeNativeSetViaPolicy( + const char *szPropertyName, + ... + ) +{ + if ( m_pPyObject == NULL || szPropertyName == NULL ) + return NS_ERROR_NULL_POINTER; + PyObject *ob_ret = NULL; + nsresult ret = NS_OK; + PyObject *real_ob = NULL; + char buf[256]; + strcpy(buf, "set_"); + strncat(buf, szPropertyName, sizeof(buf)*sizeof(buf[0])-strlen(buf)-1); + buf[sizeof(buf)/sizeof(buf[0])-1] = '\0'; + va_list va; + va_start(va, szPropertyName); + ret = InvokeNativeViaPolicyInternal(buf, NULL, "O", va); + va_end(va); + if (ret == NS_COMFALSE) { + // No method of that name - just try a property. + // Bit to a hack here to maintain the use of a policy. + // We actually get the policies underlying object + // to make the call on. + real_ob = PyObject_GetAttrString(m_pPyObject, "_obj_"); + if (real_ob == NULL) { + PyErr_Format(PyExc_AttributeError, "The policy object does not have an '_obj_' attribute."); + ret = HandleNativeGatewayError(szPropertyName); + goto done; + } + va_list va2; + va_start(va2, szPropertyName); + PyObject *arg = va_arg( va2, PyObject *); + va_end(va2); + if (PyObject_SetAttrString(real_ob, (char *)szPropertyName, arg) == 0) + ret = NS_OK; + else { + PyErr_Format(PyExc_AttributeError, + "The object does not have a 'set_%s' function, or a '%s attribute.", + szPropertyName, szPropertyName); + } + } + if (ret != NS_OK) + ret = HandleNativeGatewayError(szPropertyName); +done: + Py_XDECREF(real_ob); + return ret; +} + +/****************************************************** + + Some special support to help with object identity. + + In the simplest case, assume a Python COM object is + supporting a function "nsIWhatever GetWhatever()", + so implements it as: + return this + it is almost certain they intend returning + the same COM OBJECT to the caller! Thus, if a user of this COM + object does: + + p1 = foo.GetWhatever(); + p2 = foo.GetWhatever(); + + We almost certainly expect p1==p2==foo. + + We previously _did_ have special support for the "this" + example above, but this implements a generic scheme that + works for _all_ objects. + + Whenever we are asked to "AutoWrap" a Python object, the + first thing we do is see if it has been auto-wrapped before. + + If not, we create a new wrapper, then make a COM weak reference + to that wrapper, and store it directly back into the instance + we are auto-wrapping! The use of a weak-reference prevents + cycles. + + The existance of this attribute in an instance indicates if it + has been previously auto-wrapped. + + If it _has_ previously been auto-wrapped, we de-reference the + weak reference, and use that gateway. + +*********************************************************************/ + +nsresult GetDefaultGateway(PyObject *instance, REFNSIID iid, void **ret) +{ + // NOTE: Instance is the real instance, _not_ the policy. + PyObject *ob_existing_weak = PyObject_GetAttrString(instance, PyXPCOM_szDefaultGatewayAttributeName); + if (ob_existing_weak != NULL) { + PRBool ok = PR_TRUE; + nsCOMPtr pWeakRef; + ok = NS_SUCCEEDED(Py_nsISupports::InterfaceFromPyObject(ob_existing_weak, + NS_GET_IID(nsIWeakReference), + getter_AddRefs(pWeakRef), + PR_FALSE)); + Py_DECREF(ob_existing_weak); + if (ok) + return pWeakRef->QueryReferent( iid, ret); + } else + PyErr_Clear(); + return NS_ERROR_FAILURE; +} + +PRBool CheckDefaultGateway(PyObject *real_inst, REFNSIID iid, nsISupports **ret_gateway) +{ + NS_ABORT_IF_FALSE(real_inst, "Did not have an _obj_ attribute"); + if (real_inst==NULL) { + PyErr_Clear(); + return PR_FALSE; + } + PyObject *ob_existing_weak = PyObject_GetAttrString(real_inst, PyXPCOM_szDefaultGatewayAttributeName); + if (ob_existing_weak != NULL) { + // We have an existing default, but as it is a weak reference, it + // may no longer be valid. Check it. + PRBool ok = PR_TRUE; + nsCOMPtr pWeakRef; + ok = NS_SUCCEEDED(Py_nsISupports::InterfaceFromPyObject(ob_existing_weak, + NS_GET_IID(nsIWeakReference), + getter_AddRefs(pWeakRef), + PR_FALSE)); + Py_DECREF(ob_existing_weak); + if (ok) { + Py_BEGIN_ALLOW_THREADS; + ok = NS_SUCCEEDED(pWeakRef->QueryReferent( iid, (void **)(ret_gateway))); + Py_END_ALLOW_THREADS; + } + if (!ok) { + // We have the attribute, but not valid - wipe it + // before restoring it. + if (0 != PyObject_DelAttrString(real_inst, PyXPCOM_szDefaultGatewayAttributeName)) + PyErr_Clear(); + } + return ok; + } + PyErr_Clear(); + return PR_FALSE; +} + +void AddDefaultGateway(PyObject *instance, nsISupports *gateway) +{ + // NOTE: Instance is the _policy_! + PyObject *real_inst = PyObject_GetAttrString(instance, "_obj_"); + NS_ABORT_IF_FALSE(real_inst, "Could not get the '_obj_' element"); + if (!real_inst) return; + if (!PyObject_HasAttrString(real_inst, PyXPCOM_szDefaultGatewayAttributeName)) { + nsCOMPtr swr( do_QueryInterface((nsISupportsWeakReference *)(gateway)) ); + NS_ABORT_IF_FALSE(swr, "Our gateway failed with a weak reference query"); + // Create the new default gateway - get a weak reference for our gateway. + if (swr) { + nsIWeakReference *pWeakReference = NULL; + swr->GetWeakReference( &pWeakReference ); + if (pWeakReference) { + PyObject *ob_new_weak = Py_nsISupports::PyObjectFromInterface(pWeakReference, + NS_GET_IID(nsIWeakReference), + PR_FALSE, /* bAddRef */ + PR_FALSE ); /* bMakeNicePyObject */ + // pWeakReference reference consumed. + if (ob_new_weak) { + PyObject_SetAttrString(real_inst, PyXPCOM_szDefaultGatewayAttributeName, ob_new_weak); + Py_DECREF(ob_new_weak); + } + } + } + } + Py_DECREF(real_inst); +} diff --git a/mozilla/extensions/python/xpcom/src/PyGInputStream.cpp b/mozilla/extensions/python/xpcom/src/PyGInputStream.cpp new file mode 100644 index 00000000000..b366464974c --- /dev/null +++ b/mozilla/extensions/python/xpcom/src/PyGInputStream.cpp @@ -0,0 +1,143 @@ +/* Copyright (c) 2000-2001 ActiveState Tool Corporation. + See the file LICENSE.txt for licensing information. */ + +// PyGInputStream.cpp +// +// This code is part of the XPCOM extensions for Python. +// +// Written October 2000 by Mark Hammond. +// +// Based heavily on the Python COM support, which is +// (c) Mark Hammond and Greg Stein. +// +// (c) 2000, ActiveState corp. + +#include "PyXPCOM_std.h" +#include + +class PyG_nsIInputStream : public PyG_Base, public nsIInputStream +{ +public: + PyG_nsIInputStream(PyObject *instance) : PyG_Base(instance, NS_GET_IID(nsIInputStream)) {;} + PYGATEWAY_BASE_SUPPORT(nsIInputStream, PyG_Base); + + NS_IMETHOD Close(void); + NS_IMETHOD Available(PRUint32 *_retval); + NS_IMETHOD Read(char * buf, PRUint32 count, PRUint32 *_retval); + NS_IMETHOD ReadSegments(nsWriteSegmentFun writer, void * closure, PRUint32 count, PRUint32 *_retval); + NS_IMETHOD GetNonBlocking(PRBool *aNonBlocking); + NS_IMETHOD GetObserver(nsIInputStreamObserver * *aObserver); + NS_IMETHOD SetObserver(nsIInputStreamObserver * aObserver); +}; + + +PyG_Base *MakePyG_nsIInputStream(PyObject *instance) +{ + return new PyG_nsIInputStream(instance); +} + +NS_IMETHODIMP +PyG_nsIInputStream::Close() +{ + CEnterLeavePython _celp; + const char *methodName = "close"; + return InvokeNativeViaPolicy(methodName, NULL); +} + +NS_IMETHODIMP +PyG_nsIInputStream::Available(PRUint32 *_retval) +{ + NS_PRECONDITION(_retval, "null pointer"); + CEnterLeavePython _celp; + PyObject *ret; + const char *methodName = "available"; + nsresult nr = InvokeNativeViaPolicy(methodName, &ret); + if (NS_SUCCEEDED(nr)) { + *_retval = PyInt_AsLong(ret); + if (PyErr_Occurred()) + nr = HandleNativeGatewayError(methodName); + Py_XDECREF(ret); + } + return nr; +} + +NS_IMETHODIMP +PyG_nsIInputStream::Read(char * buf, PRUint32 count, PRUint32 *_retval) +{ + NS_PRECONDITION(_retval, "null pointer"); + NS_PRECONDITION(buf, "null pointer"); + CEnterLeavePython _celp; + PyObject *ret; + const char *methodName = "read"; + nsresult nr = InvokeNativeViaPolicy(methodName, &ret, "i", count); + if (NS_SUCCEEDED(nr)) { + PRUint32 py_size; + const void *py_buf; + if (PyObject_AsReadBuffer(ret, &py_buf, (int *)&py_size)!=0) { + PyErr_Format(PyExc_TypeError, "nsIInputStream::read() method must return a buffer object - not a '%s' object", ret->ob_type->tp_name); + nr = HandleNativeGatewayError(methodName); + } else { + if (py_size > count) { + PyXPCOM_LogWarning("nsIInputStream::read() was asked for %d bytes, but the string returned is %d bytes - truncating!\n", count, py_size); + py_size = count; + } + memcpy(buf, py_buf, py_size); + *_retval = py_size; + } + } + return nr; +} + + +NS_IMETHODIMP +PyG_nsIInputStream::ReadSegments(nsWriteSegmentFun writer, void * closure, PRUint32 count, PRUint32 *_retval) +{ + NS_WARNING("ReadSegments() not implemented!!!"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +PyG_nsIInputStream::GetNonBlocking(PRBool *aNonBlocking) +{ + NS_PRECONDITION(aNonBlocking, "null pointer"); + CEnterLeavePython _celp; + PyObject *ret; + const char *propName = "nonBlocking"; + nsresult nr = InvokeNativeGetViaPolicy(propName, &ret); + if (NS_SUCCEEDED(nr)) { + *aNonBlocking = PyInt_AsLong(ret); + if (PyErr_Occurred()) + nr = HandleNativeGatewayError(propName); + Py_XDECREF(ret); + } + return nr; +} + +NS_IMETHODIMP +PyG_nsIInputStream::GetObserver(nsIInputStreamObserver * *aObserver) +{ + NS_PRECONDITION(aObserver, "null pointer"); + CEnterLeavePython _celp; + PyObject *ret; + const char *propName = "observer"; + nsresult nr = InvokeNativeGetViaPolicy(propName, &ret); + if (NS_SUCCEEDED(nr)) { + Py_nsISupports::InterfaceFromPyObject(ret, NS_GET_IID(nsIInputStreamObserver), (nsISupports **)aObserver, PR_FALSE); + if (PyErr_Occurred()) + nr = HandleNativeGatewayError(propName); + } + return nr; +} + +NS_IMETHODIMP +PyG_nsIInputStream::SetObserver(nsIInputStreamObserver * aObserver) +{ + CEnterLeavePython _celp; + const char *propName = "observer"; + PyObject *obObserver = MakeInterfaceParam(aObserver, &NS_GET_IID(nsIInputStreamObserver)); + if (obObserver==NULL) + return HandleNativeGatewayError(propName); + nsresult nr = InvokeNativeSetViaPolicy(propName, obObserver); + Py_DECREF(obObserver); + return nr; +} diff --git a/mozilla/extensions/python/xpcom/src/PyGModule.cpp b/mozilla/extensions/python/xpcom/src/PyGModule.cpp new file mode 100644 index 00000000000..be6d7d06e60 --- /dev/null +++ b/mozilla/extensions/python/xpcom/src/PyGModule.cpp @@ -0,0 +1,331 @@ +/* Copyright (c) 2000-2001 ActiveState Tool Corporation. + See the file LICENSE.txt for licensing information. */ + +// +// This code is part of the XPCOM extensions for Python. +// +// Written May 2000 by Mark Hammond. +// +// Based heavily on the Python COM support, which is +// (c) Mark Hammond and Greg Stein. +// +// (c) 2000, ActiveState corp. + +// Unfortunately, we can not use an XPConnect object for +// the nsiModule and nsiComponentLoader interfaces. +// As XPCOM shuts down, it shuts down the interface manager before +// it releases all the modules. This is a bit of a problem for +// us, as it means we can't get runtime info on the interface at shutdown time. + +#include "PyXPCOM_std.h" +#include +#include + +#ifdef XP_WIN +// Can only assume dynamic loading on Windows. +#define LOADER_LINKS_WITH_PYTHON + +#endif // XP_WIN + +extern void PyXPCOM_InterpreterState_Ensure(); + +//////////////////////////////////////////////////////////// +// This is the main entry point called by the Python component +// loader. +extern "C" NS_EXPORT nsresult PyXPCOM_NSGetModule(nsIComponentManager *servMgr, + nsIFile* location, + nsIModule** result) +{ + NS_PRECONDITION(result!=NULL, "null result pointer in PyXPCOM_NSGetModule!"); + NS_PRECONDITION(location!=NULL, "null nsIFile pointer in PyXPCOM_NSGetModule!"); + NS_PRECONDITION(servMgr!=NULL, "null servMgr pointer in PyXPCOM_NSGetModule!"); +#ifndef LOADER_LINKS_WITH_PYTHON + if (!Py_IsInitialized()) { + Py_Initialize(); + if (!Py_IsInitialized()) { + PyXPCOM_LogError("Python initialization failed!\n"); + return NS_ERROR_FAILURE; + } + PyEval_InitThreads(); + PyXPCOM_InterpreterState_Ensure(); + PyEval_SaveThread(); + } +#endif // LOADER_LINKS_WITH_PYTHON + CEnterLeavePython _celp; + PyObject *func = NULL; + PyObject *obServMgr = NULL; + PyObject *obLocation = NULL; + PyObject *wrap_ret = NULL; + PyObject *args = NULL; + PyObject *mod = PyImport_ImportModule("xpcom.server"); + if (!mod) goto done; + func = PyObject_GetAttrString(mod, "NS_GetModule"); + if (func==NULL) goto done; + obServMgr = Py_nsISupports::PyObjectFromInterface(servMgr, NS_GET_IID(nsIComponentManager), PR_TRUE); + if (obServMgr==NULL) goto done; + obLocation = Py_nsISupports::PyObjectFromInterface(location, NS_GET_IID(nsIFile), PR_TRUE); + if (obLocation==NULL) goto done; + args = Py_BuildValue("OO", obServMgr, obLocation); + if (args==NULL) goto done; + wrap_ret = PyEval_CallObject(func, args); + if (wrap_ret==NULL) goto done; + Py_nsISupports::InterfaceFromPyObject(wrap_ret, NS_GET_IID(nsIModule), (nsISupports **)result, PR_FALSE, PR_FALSE); +done: + nsresult nr = NS_OK; + if (PyErr_Occurred()) { + PyXPCOM_LogError("Obtaining the module object from Python failed.\n"); + nr = PyXPCOM_SetCOMErrorFromPyException(); + } + Py_XDECREF(func); + Py_XDECREF(obServMgr); + Py_XDECREF(obLocation); + Py_XDECREF(wrap_ret); + Py_XDECREF(mod); + Py_XDECREF(args); + return nr; +} + +class PyG_nsIModule : public PyG_Base, public nsIModule +{ +public: + PyG_nsIModule(PyObject *instance) : PyG_Base(instance, NS_GET_IID(nsIModule)) {;} + PYGATEWAY_BASE_SUPPORT(nsIModule, PyG_Base); + + NS_IMETHOD GetClassObject(nsIComponentManager *aCompMgr, const nsCID & aClass, const nsIID & aIID, void * *result); + NS_IMETHOD RegisterSelf(nsIComponentManager *aCompMgr, nsIFile *location, const char *registryLocation, const char *componentType); + NS_IMETHOD UnregisterSelf(nsIComponentManager *aCompMgr, nsIFile *location, const char *registryLocation); + NS_IMETHOD CanUnload(nsIComponentManager *aCompMgr, PRBool *_retval); +}; + +PyG_Base *MakePyG_nsIModule(PyObject *instance) +{ + return new PyG_nsIModule(instance); +} + + +// Create a factory object for creating instances of aClass. +NS_IMETHODIMP +PyG_nsIModule::GetClassObject(nsIComponentManager *aCompMgr, + const nsCID& aClass, + const nsIID& aIID, + void** r_classObj) +{ + NS_PRECONDITION(r_classObj, "null pointer"); + *r_classObj = nsnull; + CEnterLeavePython _celp; + PyObject *cm = MakeInterfaceParam(aCompMgr, &NS_GET_IID(nsIComponentManager)); + PyObject *iid = Py_nsIID::PyObjectFromIID(aIID); + PyObject *clsid = Py_nsIID::PyObjectFromIID(aClass); + const char *methodName = "getClassObject"; + PyObject *ret = NULL; + nsresult nr = InvokeNativeViaPolicy(methodName, &ret, "OOO", cm, clsid, iid); + Py_XDECREF(cm); + Py_XDECREF(iid); + Py_XDECREF(clsid); + if (NS_SUCCEEDED(nr)) { + nr = Py_nsISupports::InterfaceFromPyObject(ret, aIID, (nsISupports **)r_classObj, PR_FALSE); + if (PyErr_Occurred()) + nr = HandleNativeGatewayError(methodName); + } + if (NS_FAILED(nr)) { + NS_ABORT_IF_FALSE(*r_classObj==NULL, "returning error result with an interface - probable leak!"); + } + Py_XDECREF(ret); + return nr; +} + +NS_IMETHODIMP +PyG_nsIModule::RegisterSelf(nsIComponentManager *aCompMgr, + nsIFile* aPath, + const char* registryLocation, + const char* componentType) +{ + NS_PRECONDITION(aCompMgr, "null pointer"); + NS_PRECONDITION(aPath, "null pointer"); + CEnterLeavePython _celp; + PyObject *cm = MakeInterfaceParam(aCompMgr, &NS_GET_IID(nsIComponentManager)); + PyObject *path = MakeInterfaceParam(aPath, &NS_GET_IID(nsIFile)); + const char *methodName = "registerSelf"; + nsresult nr = InvokeNativeViaPolicy(methodName, NULL, "OOzz", cm, path, registryLocation, componentType); + Py_XDECREF(cm); + Py_XDECREF(path); + return nr; +} + +NS_IMETHODIMP +PyG_nsIModule::UnregisterSelf(nsIComponentManager* aCompMgr, + nsIFile* aPath, + const char* registryLocation) +{ + NS_PRECONDITION(aCompMgr, "null pointer"); + NS_PRECONDITION(aPath, "null pointer"); + CEnterLeavePython _celp; + PyObject *cm = MakeInterfaceParam(aCompMgr, &NS_GET_IID(nsIComponentManager)); + PyObject *path = MakeInterfaceParam(aPath, &NS_GET_IID(nsIFile)); + const char *methodName = "unregisterSelf"; + nsresult nr = InvokeNativeViaPolicy(methodName, NULL, "OOz", cm, path, registryLocation); + Py_XDECREF(cm); + Py_XDECREF(path); + return nr; +} + +NS_IMETHODIMP +PyG_nsIModule::CanUnload(nsIComponentManager *aCompMgr, PRBool *okToUnload) +{ + NS_PRECONDITION(aCompMgr, "null pointer"); + NS_PRECONDITION(okToUnload, "null pointer"); + CEnterLeavePython _celp; + PyObject *cm = MakeInterfaceParam(aCompMgr, &NS_GET_IID(nsIComponentManager)); + const char *methodName = "canUnload"; + PyObject *ret = NULL; + nsresult nr = InvokeNativeViaPolicy(methodName, &ret, "O", cm); + Py_XDECREF(cm); + if (NS_SUCCEEDED(nr)) { + *okToUnload = PyInt_AsLong(ret); + if (PyErr_Occurred()) + nr = HandleNativeGatewayError(methodName); + } + Py_XDECREF(ret); + return nr; +} + +/////////////////////////////////////////////////////////////////////////////////// + +class PyG_nsIComponentLoader : public PyG_Base, public nsIComponentLoader +{ +public: + PyG_nsIComponentLoader(PyObject *instance) : PyG_Base(instance, NS_GET_IID(nsIComponentLoader)) {;} + PYGATEWAY_BASE_SUPPORT(nsIComponentLoader, PyG_Base); + + NS_DECL_NSICOMPONENTLOADER +}; + +PyG_Base *MakePyG_nsIComponentLoader(PyObject *instance) +{ + return new PyG_nsIComponentLoader(instance); +} + +/* nsIFactory getFactory (in nsIIDRef aCID, in string aLocation, in string aType); */ +NS_IMETHODIMP PyG_nsIComponentLoader::GetFactory(const nsIID & aCID, const char *aLocation, const char *aType, nsIFactory **_retval) +{ + CEnterLeavePython _celp; + const char *methodName = "getFactory"; + PyObject *iid = Py_nsIID::PyObjectFromIID(aCID); + PyObject *ret = NULL; + nsresult nr = InvokeNativeViaPolicy(methodName, &ret, "Ozz", + iid, + aLocation, + aType); + Py_XDECREF(iid); + if (NS_SUCCEEDED(nr)) { + Py_nsISupports::InterfaceFromPyObject(ret, NS_GET_IID(nsIFactory), (nsISupports **)_retval, PR_FALSE); + if (PyErr_Occurred()) + nr = HandleNativeGatewayError(methodName); + } + Py_XDECREF(ret); + return nr; +} + +/* void init (in nsIComponentManager aCompMgr, in nsISupports aRegistry); */ +NS_IMETHODIMP PyG_nsIComponentLoader::Init(nsIComponentManager *aCompMgr, nsISupports *aRegistry) +{ + CEnterLeavePython _celp; + const char *methodName = "init"; + PyObject *c = MakeInterfaceParam(aCompMgr, &NS_GET_IID(nsIComponentManager)); + PyObject *r = MakeInterfaceParam(aRegistry, &NS_GET_IID(nsISupports)); + nsresult nr = InvokeNativeViaPolicy(methodName, NULL, "OO", c, r); + Py_XDECREF(c); + Py_XDECREF(r); + return nr; +} + +/* void onRegister (in nsIIDRef aCID, in string aType, in string aClassName, in string aContractID, in string aLocation, in boolean aReplace, in boolean aPersist); */ +NS_IMETHODIMP PyG_nsIComponentLoader::OnRegister(const nsIID & aCID, const char *aType, const char *aClassName, const char *aContractID, const char *aLocation, PRBool aReplace, PRBool aPersist) +{ + CEnterLeavePython _celp; + const char *methodName = "onRegister"; + PyObject *iid = Py_nsIID::PyObjectFromIID(aCID); + nsresult nr = InvokeNativeViaPolicy(methodName, NULL, "Ossssii", + iid, + aType, + aClassName, + aContractID, + aLocation, + aReplace, + aPersist); + Py_XDECREF(iid); + return nr; +} + +/* void autoRegisterComponents (in long aWhen, in nsIFile aDirectory); */ +NS_IMETHODIMP PyG_nsIComponentLoader::AutoRegisterComponents(PRInt32 aWhen, nsIFile *aDirectory) +{ + CEnterLeavePython _celp; + const char *methodName = "autoRegisterComponents"; + PyObject *c = MakeInterfaceParam(aDirectory, &NS_GET_IID(nsIFile)); + nsresult nr = InvokeNativeViaPolicy(methodName, NULL, "iO", aWhen, c); + Py_XDECREF(c); + return nr; +} + +/* boolean autoRegisterComponent (in long aWhen, in nsIFile aComponent); */ +NS_IMETHODIMP PyG_nsIComponentLoader::AutoRegisterComponent(PRInt32 aWhen, nsIFile *aComponent, PRBool *_retval) +{ + CEnterLeavePython _celp; + const char *methodName = "autoRegisterComponent"; + PyObject *ret = NULL; + PyObject *c = MakeInterfaceParam(aComponent, &NS_GET_IID(nsIFile)); + nsresult nr = InvokeNativeViaPolicy(methodName, &ret, "iO", aWhen, c); + Py_XDECREF(c); + if (NS_SUCCEEDED(nr)) { + *_retval = PyInt_AsLong(ret); + if (PyErr_Occurred()) + nr = HandleNativeGatewayError(methodName); + } + Py_XDECREF(ret); + return nr; +} + +/* boolean autoUnregisterComponent (in long aWhen, in nsIFile aComponent); */ +NS_IMETHODIMP PyG_nsIComponentLoader::AutoUnregisterComponent(PRInt32 aWhen, nsIFile *aComponent, PRBool *_retval) +{ + CEnterLeavePython _celp; + const char *methodName = "autoUnregisterComponent"; + PyObject *ret = NULL; + PyObject *c = MakeInterfaceParam(aComponent, &NS_GET_IID(nsIFile)); + nsresult nr = InvokeNativeViaPolicy(methodName, &ret, "iO", aWhen, c); + Py_XDECREF(c); + if (NS_SUCCEEDED(nr)) { + *_retval = PyInt_AsLong(ret); + if (PyErr_Occurred()) + nr = HandleNativeGatewayError(methodName); + } + Py_XDECREF(ret); + return nr; +} + +/* boolean registerDeferredComponents (in long aWhen); */ +NS_IMETHODIMP PyG_nsIComponentLoader::RegisterDeferredComponents(PRInt32 aWhen, PRBool *_retval) +{ + CEnterLeavePython _celp; + const char *methodName = "registerDeferredComponents"; + PyObject *ret = NULL; + nsresult nr = InvokeNativeViaPolicy(methodName, &ret, "i", aWhen); + if (NS_SUCCEEDED(nr)) { + *_retval = PyInt_AsLong(ret); + if (PyErr_Occurred()) + nr = HandleNativeGatewayError(methodName); + } + Py_XDECREF(ret); + return nr; +} + +/* void unloadAll (in long aWhen); */ +NS_IMETHODIMP PyG_nsIComponentLoader::UnloadAll(PRInt32 aWhen) +{ + CEnterLeavePython _celp; + const char *methodName = "unloadAll"; + return InvokeNativeViaPolicy(methodName, NULL, "i", aWhen); +} + + diff --git a/mozilla/extensions/python/xpcom/src/PyGStub.cpp b/mozilla/extensions/python/xpcom/src/PyGStub.cpp new file mode 100644 index 00000000000..65dae702a42 --- /dev/null +++ b/mozilla/extensions/python/xpcom/src/PyGStub.cpp @@ -0,0 +1,144 @@ +/* Copyright (c) 2000-2001 ActiveState Tool Corporation. + See the file LICENSE.txt for licensing information. */ + +// PyXPTStub - the stub for implementing interfaces. +// +// This code is part of the XPCOM extensions for Python. +// +// Written May 2000 by Mark Hammond. +// +// Based heavily on the Python COM support, which is +// (c) Mark Hammond and Greg Stein. +// +// (c) 2000, ActiveState corp. + +#include "PyXPCOM_std.h" +#include + +void *PyXPCOM_XPTStub::ThisAsIID(const nsIID &iid) +{ + if (iid.Equals(NS_GET_IID(nsISupports))) + return (nsISupports *)(nsXPTCStubBase *)this; + else if (iid.Equals(m_iid)) + return (nsISupports *)(nsXPTCStubBase *)this; + else + return PyG_Base::ThisAsIID(iid); +} + + +NS_IMETHODIMP +PyXPCOM_XPTStub::GetInterfaceInfo(nsIInterfaceInfo** info) +{ + NS_PRECONDITION(info, "NULL pointer"); + if (info==nsnull) + return NS_ERROR_NULL_POINTER; + // Simply get the XPCOM runtime to provide this + // (but there must be some reason why they dont get it themselves!? + // Maybe because they dont know the IID? + nsCOMPtr iim = XPTI_GetInterfaceInfoManager(); + NS_ABORT_IF_FALSE(iim != nsnull, "Cant get interface from IIM!"); + if (iim==nsnull) + return NS_ERROR_FAILURE; + + return iim->GetInfoForIID( &m_iid, info); +} + +// call this method and return result +NS_IMETHODIMP +PyXPCOM_XPTStub::CallMethod(PRUint16 methodIndex, + const nsXPTMethodInfo* info, + nsXPTCMiniVariant* params) +{ + nsresult rc = NS_ERROR_FAILURE; + NS_PRECONDITION(info, "NULL methodinfo pointer"); + NS_PRECONDITION(params, "NULL variant pointer"); + CEnterLeavePython _celp; + PyObject *obParams = NULL; + PyObject *result = NULL; + PyObject *obThisObject = NULL; + PyObject *obMI = PyObject_FromXPTMethodDescriptor(info); + PyXPCOM_GatewayVariantHelper arg_helper(this, methodIndex, info, params); + if (obMI==NULL) + goto done; + // base object is passed raw. + obThisObject = Py_nsISupports::PyObjectFromInterface((nsIInternalPython*)this, NS_GET_IID(nsISupports), PR_TRUE, PR_FALSE); + obParams = arg_helper.MakePyArgs(); + if (obParams==NULL) + goto done; + result = PyObject_CallMethod(m_pPyObject, + "_CallMethod_", + "OiOO", + obThisObject, + (int)methodIndex, + obMI, + obParams); + if (result!=NULL) { + rc = arg_helper.ProcessPythonResult(result); + // Use an xor to check failure && pyerr, or !failure && !pyerr. + NS_ABORT_IF_FALSE( ((NS_FAILED(rc)!=0)^(PyErr_Occurred()!=0)) == 0, "We must have failure with a Python error, or success without a Python error."); + } +done: + if (PyErr_Occurred()) { + // The error handling - fairly involved, but worth it as + // good error reporting is critical for users to know WTF + // is going on - especially with TypeErrors etc in their + // return values (ie, after the Python code has successfully + // existed, but we encountered errors unpacking their + // result values for the COM caller - there is literally no + // way to catch these exceptions from Python code, as their + // is no Python function on the call-stack) + + // First line of attack in an error is to call-back on the policy. + // If the callback of the error handler succeeds and returns an + // integer (for the nsresult), we take no further action. + + // If this callback fails, we log _2_ exceptions - the error handler + // error, and the original error. + + PRBool bProcessMainError = PR_TRUE; // set to false if our exception handler does its thing! + PyObject *exc_typ, *exc_val, *exc_tb; + PyErr_Fetch(&exc_typ, &exc_val, &exc_tb); + PyErr_NormalizeException( &exc_typ, &exc_val, &exc_tb); + + PyObject *err_result = PyObject_CallMethod(m_pPyObject, + "_CallMethodException_", + "OiOO(OOO)", + obThisObject, + (int)methodIndex, + obMI, + obParams, + exc_typ ? exc_typ : Py_None, // should never be NULL, but defensive programming... + exc_val ? exc_val : Py_None, // may well be NULL. + exc_tb ? exc_tb : Py_None); // may well be NULL. + if (err_result == NULL) { + PyXPCOM_LogError("The exception handler _CallMethodException_ failed!\n"); + } else if (err_result == Py_None) { + // The exception handler has chosen not to do anything with + // this error, so we still need to print it! + ; + } else if (PyInt_Check(err_result)) { + // The exception handler has given us the nresult. + rc = PyInt_AsLong(err_result); + bProcessMainError = PR_FALSE; + } else { + // The exception handler succeeded, but returned other than + // int or None. + PyXPCOM_LogError("The _CallMethodException_ handler returned object of type '%s' - None or an integer expected\n", err_result->ob_type->tp_name); + } + Py_XDECREF(err_result); + PyErr_Restore(exc_typ, exc_val, exc_tb); + if (bProcessMainError) { + PyXPCOM_LogError("The function '%s' failed\n", info->GetName()); + rc = PyXPCOM_SetCOMErrorFromPyException(); + } + // else everything is already setup, + // just clear the Python error state. + PyErr_Clear(); + } + + Py_XDECREF(obMI); + Py_XDECREF(obParams); + Py_XDECREF(obThisObject); + Py_XDECREF(result); + return rc; +} diff --git a/mozilla/extensions/python/xpcom/src/PyGWeakReference.cpp b/mozilla/extensions/python/xpcom/src/PyGWeakReference.cpp new file mode 100644 index 00000000000..78227c78578 --- /dev/null +++ b/mozilla/extensions/python/xpcom/src/PyGWeakReference.cpp @@ -0,0 +1,51 @@ +/* Copyright (c) 2000-2001 ActiveState Tool Corporation. + See the file LICENSE.txt for licensing information. */ + +// PyGWeakReference - implements weak references for gateways. +// +// This code is part of the XPCOM extensions for Python. +// +// Written November 2000 by Mark Hammond. +// +// Based heavily on the Python COM support, which is +// (c) Mark Hammond and Greg Stein. +// +// (c) 2000, ActiveState corp. + +#include "PyXPCOM_std.h" + +PyXPCOM_GatewayWeakReference::PyXPCOM_GatewayWeakReference( PyG_Base *base ) +{ + m_pBase = base; + NS_INIT_REFCNT(); +} + +PyXPCOM_GatewayWeakReference::~PyXPCOM_GatewayWeakReference() +{ + // Simply zap my reference to the gateway! + // No need to zap my gateway's reference to me, as + // it already holds a reference, so if we are destructing, + // then it can't possibly hold one. + m_pBase = NULL; +} + +NS_IMPL_THREADSAFE_ADDREF(PyXPCOM_GatewayWeakReference); +NS_IMPL_THREADSAFE_RELEASE(PyXPCOM_GatewayWeakReference); +NS_IMPL_THREADSAFE_QUERY_INTERFACE(PyXPCOM_GatewayWeakReference, NS_GET_IID(nsIWeakReference)); + +NS_IMETHODIMP +PyXPCOM_GatewayWeakReference::QueryReferent(REFNSIID iid, void * *ret) +{ + { + // Temp scope for lock. We can't hold the lock during + // a QI, as this may itself need the lock. + // Make sure our object isn't dieing right now on another thread. + CEnterLeaveXPCOMFramework _celf; + if (m_pBase == NULL) + return NS_ERROR_NULL_POINTER; + m_pBase->AddRef(); // Can't die while we have a ref. + } // end of lock scope - lock unlocked. + nsresult nr = m_pBase->QueryInterface(iid, ret); + m_pBase->Release(); + return nr; +} diff --git a/mozilla/extensions/python/xpcom/src/PyIComponentManager.cpp b/mozilla/extensions/python/xpcom/src/PyIComponentManager.cpp new file mode 100644 index 00000000000..14246aa3eed --- /dev/null +++ b/mozilla/extensions/python/xpcom/src/PyIComponentManager.cpp @@ -0,0 +1,165 @@ +/* Copyright (c) 2000-2001 ActiveState Tool Corporation. + See the file LICENSE.txt for licensing information. */ + +// +// This code is part of the XPCOM extensions for Python. +// +// Written May 2000 by Mark Hammond. +// +// Based heavily on the Python COM support, which is +// (c) Mark Hammond and Greg Stein. +// +// (c) 2000, ActiveState corp. + +#include "PyXPCOM_std.h" +#include + +static nsIComponentManager *GetI(PyObject *self) { + nsIID iid = NS_GET_IID(nsIComponentManager); + + if (!Py_nsISupports::Check(self, iid)) { + PyErr_SetString(PyExc_TypeError, "This object is not the correct interface"); + return NULL; + } + return (nsIComponentManager *)Py_nsISupports::GetI(self); +} + +static PyObject *PyCreateInstanceByContractID(PyObject *self, PyObject *args) +{ + char *pid, *notyet = NULL; + PyObject *obIID = NULL; + if (!PyArg_ParseTuple(args, "s|zO", &pid, ¬yet, &obIID)) + return NULL; + if (notyet != NULL) { + PyErr_SetString(PyExc_ValueError, "2nd arg must be none"); + return NULL; + } + nsIComponentManager *pI = GetI(self); + if (pI==NULL) + return NULL; + + nsIID iid; + if (obIID==NULL) + iid = NS_GET_IID(nsISupports); + else + if (!Py_nsIID::IIDFromPyObject(obIID, &iid)) + return NULL; + + nsISupports *pis; + nsresult r; + Py_BEGIN_ALLOW_THREADS; + r = pI->CreateInstanceByContractID(pid, NULL, iid, (void **)&pis); + Py_END_ALLOW_THREADS; + if ( NS_FAILED(r) ) + return PyXPCOM_BuildPyException(r); + + /* Return a type based on the IID (with no extra ref) */ + return Py_nsISupports::PyObjectFromInterface(pis, iid, PR_FALSE); +} + +static PyObject *PyContractIDToClassID(PyObject *self, PyObject *args) +{ + char *pid; + if (!PyArg_ParseTuple(args, "s", &pid)) + return NULL; + nsIComponentManager *pI = GetI(self); + if (pI==NULL) + return NULL; + + nsIID iid; + nsresult r; + Py_BEGIN_ALLOW_THREADS; + r = pI->ContractIDToClassID(pid, &iid); + Py_END_ALLOW_THREADS; + if ( NS_FAILED(r) ) + return PyXPCOM_BuildPyException(r); + + return Py_nsIID::PyObjectFromIID(iid); +} + +static PyObject *PyCLSIDToContractID(PyObject *self, PyObject *args) +{ + PyObject *obIID; + if (!PyArg_ParseTuple(args, "O", &obIID)) + return NULL; + + nsIID iid; + if (!Py_nsIID::IIDFromPyObject(obIID, &iid)) + return NULL; + char *ret_pid = nsnull; + char *ret_class = nsnull; + nsIComponentManager *pI = GetI(self); + if (pI==NULL) + return NULL; + + nsresult r; + Py_BEGIN_ALLOW_THREADS; + r = pI->CLSIDToContractID(iid, &ret_class, &ret_pid); + Py_END_ALLOW_THREADS; + if ( NS_FAILED(r) ) + return PyXPCOM_BuildPyException(r); + + PyObject *ob_pid = PyString_FromString(ret_pid); + PyObject *ob_class = PyString_FromString(ret_class); + PyObject *ret = Py_BuildValue("OO", ob_pid, ob_class); + nsAllocator::Free(ret_pid); + nsAllocator::Free(ret_class); + Py_XDECREF(ob_pid); + Py_XDECREF(ob_class); + return ret; +} + +static PyObject *PyEnumerateCLSIDs(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) + return NULL; + + nsIComponentManager *pI = GetI(self); + if (pI==NULL) + return NULL; + + nsIEnumerator *pRet; + nsresult r; + Py_BEGIN_ALLOW_THREADS; + r = pI->EnumerateCLSIDs(&pRet); + Py_END_ALLOW_THREADS; + if ( NS_FAILED(r) ) + return PyXPCOM_BuildPyException(r); + + return Py_nsISupports::PyObjectFromInterface(pRet, NS_GET_IID(nsIEnumerator), PR_FALSE); +} + +static PyObject *PyEnumerateContractIDs(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) + return NULL; + + nsIComponentManager *pI = GetI(self); + if (pI==NULL) + return NULL; + + nsIEnumerator *pRet; + nsresult r; + Py_BEGIN_ALLOW_THREADS; + r = pI->EnumerateContractIDs(&pRet); + Py_END_ALLOW_THREADS; + if ( NS_FAILED(r) ) + return PyXPCOM_BuildPyException(r); + + return Py_nsISupports::PyObjectFromInterface(pRet, NS_GET_IID(nsIEnumerator), PR_FALSE); +} + +struct PyMethodDef +PyMethods_IComponentManager[] = +{ + { "CreateInstanceByContractID", PyCreateInstanceByContractID, 1}, + { "createInstanceByContractID", PyCreateInstanceByContractID, 1}, + { "EnumerateCLSIDs", PyEnumerateCLSIDs, 1}, + { "enumerateCLSIDs", PyEnumerateCLSIDs, 1}, + { "EnumerateContractIDs", PyEnumerateContractIDs, 1}, + { "enumerateContractIDs", PyEnumerateContractIDs, 1}, + { "ContractIDToClassID", PyContractIDToClassID, 1}, + { "contractIDToClassID", PyContractIDToClassID, 1}, + { "CLSIDToContractID", PyCLSIDToContractID, 1}, + {NULL} +}; diff --git a/mozilla/extensions/python/xpcom/src/PyIEnumerator.cpp b/mozilla/extensions/python/xpcom/src/PyIEnumerator.cpp new file mode 100644 index 00000000000..e2f428c394e --- /dev/null +++ b/mozilla/extensions/python/xpcom/src/PyIEnumerator.cpp @@ -0,0 +1,198 @@ +/* Copyright (c) 2000-2001 ActiveState Tool Corporation. + See the file LICENSE.txt for licensing information. */ + +// +// This code is part of the XPCOM extensions for Python. +// +// Written May 2000 by Mark Hammond. +// +// Based heavily on the Python COM support, which is +// (c) Mark Hammond and Greg Stein. +// +// (c) 2000, ActiveState corp. + +#include "PyXPCOM_std.h" +#include + +static nsIEnumerator *GetI(PyObject *self) { + nsIID iid = NS_GET_IID(nsIEnumerator); + + if (!Py_nsISupports::Check(self, iid)) { + PyErr_SetString(PyExc_TypeError, "This object is not the correct interface"); + return NULL; + } + return (nsIEnumerator *)Py_nsISupports::GetI(self); +} + +static PyObject *PyFirst(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":First")) + return NULL; + + nsIEnumerator *pI = GetI(self); + if (pI==NULL) + return NULL; + + nsresult r; + Py_BEGIN_ALLOW_THREADS; + r = pI->First(); + Py_END_ALLOW_THREADS; + return PyInt_FromLong(r); +} + +static PyObject *PyNext(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":Next")) + return NULL; + + nsIEnumerator *pI = GetI(self); + if (pI==NULL) + return NULL; + + nsresult r; + Py_BEGIN_ALLOW_THREADS; + r = pI->Next(); + Py_END_ALLOW_THREADS; + return PyInt_FromLong(r); +} + +static PyObject *PyCurrentItem(PyObject *self, PyObject *args) +{ + PyObject *obIID = NULL; + if (!PyArg_ParseTuple(args, "|O:CurrentItem", &obIID)) + return NULL; + + nsIID iid(NS_GET_IID(nsISupports)); + if (obIID != NULL && !Py_nsIID::IIDFromPyObject(obIID, &iid)) + return NULL; + nsIEnumerator *pI = GetI(self); + if (pI==NULL) + return NULL; + + nsISupports *pRet = nsnull; + nsresult r; + Py_BEGIN_ALLOW_THREADS; + r = pI->CurrentItem(&pRet); + Py_END_ALLOW_THREADS; + if ( NS_FAILED(r) ) + return PyXPCOM_BuildPyException(r); + if (obIID) { + nsISupports *temp; + Py_BEGIN_ALLOW_THREADS; + r = pRet->QueryInterface(iid, (void **)&temp); + pRet->Release(); + Py_END_ALLOW_THREADS; + if ( NS_FAILED(r) ) { + return PyXPCOM_BuildPyException(r); + } + pRet = temp; + } + return Py_nsISupports::PyObjectFromInterface(pRet, iid, PR_FALSE); +} + +// A method added for Python performance if you really need +// it. Allows you to fetch a block of objects in one +// hit, allowing the loop to remain implemented in C. +static PyObject *PyFetchBlock(PyObject *self, PyObject *args) +{ + PyObject *obIID = NULL; + int n_wanted; + int n_fetched = 0; + if (!PyArg_ParseTuple(args, "i|O:FetchBlock", &n_wanted, &obIID)) + return NULL; + + nsIID iid(NS_GET_IID(nsISupports)); + if (obIID != NULL && !Py_nsIID::IIDFromPyObject(obIID, &iid)) + return NULL; + nsIEnumerator *pI = GetI(self); + if (pI==NULL) + return NULL; + + // We want to fetch with the thread-lock released, + // but this means we can not append to the PyList + nsISupports **fetched = new nsISupports*[n_wanted]; + if (fetched==nsnull) { + PyErr_NoMemory(); + return NULL; + } + memset(fetched, 0, sizeof(nsISupports *) * n_wanted); + nsresult r = NS_OK; + Py_BEGIN_ALLOW_THREADS; + for (;n_fetchedCurrentItem(&pNew); + if (NS_FAILED(r)) { + r = 0; // Normal enum end + break; + } + if (obIID) { + nsISupports *temp; + r = pNew->QueryInterface(iid, (void **)&temp); + pNew->Release(); + if ( NS_FAILED(r) ) { + break; + } + pNew = temp; + } + fetched[n_fetched] = pNew; + n_fetched++; // must increment before breaking out. + if (NS_FAILED(pI->Next())) + break; // not an error condition. + } + Py_END_ALLOW_THREADS; + PyObject *ret; + if (NS_SUCCEEDED(r)) { + ret = PyList_New(n_fetched); + if (ret) + for (int i=0;iRelease(); + + } + delete [] fetched; + return ret; +} + +static PyObject *PyIsDone(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":IsDone")) + return NULL; + + nsIEnumerator *pI = GetI(self); + nsresult r; + if (pI==NULL) + return NULL; + + Py_BEGIN_ALLOW_THREADS; + r = pI->IsDone(); + Py_END_ALLOW_THREADS; + if (NS_FAILED(r)) + return PyXPCOM_BuildPyException(r); + PyObject *ret = r==NS_OK ? Py_True : Py_False; + Py_INCREF(ret); + return ret; +} + +struct PyMethodDef +PyMethods_IEnumerator[] = +{ + { "First", PyFirst, 1}, + { "first", PyFirst, 1}, + { "Next", PyNext, 1}, + { "next", PyNext, 1}, + { "CurrentItem", PyCurrentItem, 1}, + { "currentItem", PyCurrentItem, 1}, + { "IsDone", PyIsDone, 1}, + { "isDone", PyIsDone, 1}, + { "FetchBlock", PyFetchBlock, 1}, + { "fetchBlock", PyFetchBlock, 1}, + {NULL} +}; diff --git a/mozilla/extensions/python/xpcom/src/PyIID.cpp b/mozilla/extensions/python/xpcom/src/PyIID.cpp new file mode 100644 index 00000000000..5cc443e4461 --- /dev/null +++ b/mozilla/extensions/python/xpcom/src/PyIID.cpp @@ -0,0 +1,202 @@ +/* Copyright (c) 2000-2001 ActiveState Tool Corporation. + See the file LICENSE.txt for licensing information. */ + +// Py_nsIID.cpp -- IID type for Python/XPCOM +// +// This code is part of the XPCOM extensions for Python. +// +// Written May 2000 by Mark Hammond. +// +// Based heavily on the Python COM support, which is +// (c) Mark Hammond and Greg Stein. +// +// (c) 2000, ActiveState corp. +// +// @doc + +#include "PyXPCOM_std.h" +#include + +nsIID Py_nsIID_NULL = {0,0,0,{0,0,0,0,0,0,0,0}}; + +// @pymethod |xpcom|IID|Creates a new IID object +PyObject *PyXPCOMMethod_IID(PyObject *self, PyObject *args) +{ + PyObject *obIID; + PyObject *obBuf; + if ( PyArg_ParseTuple(args, "O", &obBuf)) { + if (PyBuffer_Check(obBuf)) { + PyBufferProcs *pb = NULL; + pb = obBuf->ob_type->tp_as_buffer; + void *buf = NULL; + int size = (*pb->bf_getreadbuffer)(obBuf, 0, &buf); + if (size != sizeof(nsIID) || buf==NULL) { + PyErr_Format(PyExc_ValueError, "A buffer object to be converted to an IID must be exactly %d bytes long", sizeof(nsIID)); + return NULL; + } + nsIID iid; + unsigned char *ptr = (unsigned char *)buf; + iid.m0 = XPT_SWAB32(*((PRUint32 *)ptr)); + ptr = ((unsigned char *)buf) + offsetof(nsIID, m1); + iid.m1 = XPT_SWAB16(*((PRUint16 *)ptr)); + ptr = ((unsigned char *)buf) + offsetof(nsIID, m2); + iid.m2 = XPT_SWAB16(*((PRUint16 *)ptr)); + ptr = ((unsigned char *)buf) + offsetof(nsIID, m3); + for (int i=0;i<8;i++) { + iid.m3[i] = *((PRUint8 *)ptr); + ptr += sizeof(PRUint8); + } + return new Py_nsIID(iid); + } + } + PyErr_Clear(); + // @pyparm string/Unicode|iidString||A string representation of an IID, or a ContractID. + if ( !PyArg_ParseTuple(args, "O", &obIID) ) + return NULL; + + nsIID iid; + if (!Py_nsIID::IIDFromPyObject(obIID, &iid)) + return NULL; + return new Py_nsIID(iid); +} + +/*static*/ PRBool +Py_nsIID::IIDFromPyObject(PyObject *ob, nsIID *pRet) { + PRBool ok = PR_TRUE; + nsIID iid; + if (ob==NULL) { + PyErr_SetString(PyExc_RuntimeError, "The object is invalid!"); + return PR_FALSE; + } + if (PyString_Check(ob)) { + ok = iid.Parse(PyString_AsString(ob)); + if (!ok) { + PyXPCOM_BuildPyException(NS_ERROR_ILLEGAL_VALUE); + return PR_FALSE; + } + } else if (ob->ob_type == &type) { + iid = ((Py_nsIID *)ob)->m_iid; + } else if (PyInstance_Check(ob)) { + // Get the _iidobj_ attribute + PyObject *use_ob = PyObject_GetAttrString(ob, "_iidobj_"); + if (use_ob==NULL) { + PyErr_SetString(PyExc_TypeError, "Only instances with _iidobj_ attributes can be used as IID objects"); + return PR_FALSE; + } + if (use_ob->ob_type != &type) { + Py_DECREF(use_ob); + PyErr_SetString(PyExc_TypeError, "instance _iidobj_ attributes must be raw IID object"); + return PR_FALSE; + } + iid = ((Py_nsIID *)use_ob)->m_iid; + Py_DECREF(use_ob); + } else { + PyErr_Format(PyExc_TypeError, "Objects of type '%s' can not be converted to an IID", ob->ob_type->tp_name); + ok = PR_FALSE; + } + if (ok) *pRet = iid; + return ok; +} + + +// @object Py_nsIID|A Python object, representing an IID/CLSID. +// All pythoncom functions that return a CLSID/IID will return one of these +// objects. However, in almost all cases, functions that expect a CLSID/IID +// as a param will accept either a string object, or a native Py_nsIID object. +PyTypeObject Py_nsIID::type = +{ + PyObject_HEAD_INIT(&PyType_Type) + 0, + "IID", + sizeof(Py_nsIID), + 0, + PyTypeMethod_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + PyTypeMethod_getattr, /* tp_getattr */ + 0, /* tp_setattr */ + PyTypeMethod_compare, /* tp_compare */ + PyTypeMethod_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + PyTypeMethod_hash, /* tp_hash */ + 0, /* tp_call */ + PyTypeMethod_str, /* tp_str */ +}; + +Py_nsIID::Py_nsIID(const nsIID &riid) +{ + ob_type = &type; + _Py_NewReference(this); + m_iid = riid; +} + +/*static*/PyObject * +Py_nsIID::PyTypeMethod_getattr(PyObject *self, char *name) +{ + Py_nsIID *me = (Py_nsIID *)self; + if (strcmp(name, "name")==0) { + char *iid_repr = nsnull; + nsCOMPtr iim = XPTI_GetInterfaceInfoManager(); + if (iim!=nsnull) + iim->GetNameForIID(&me->m_iid, &iid_repr); + if (iid_repr==nsnull) + iid_repr = me->m_iid.ToString(); + PyObject *ret; + if (iid_repr != nsnull) { + ret = PyString_FromString(iid_repr); + nsAllocator::Free(iid_repr); + } else + ret = PyString_FromString(""); + return ret; + } + return PyErr_Format(PyExc_AttributeError, "IID objects have no attribute '%s'", name); +} + +/* static */ int +Py_nsIID::PyTypeMethod_compare(PyObject *self, PyObject *other) +{ + Py_nsIID *s_iid = (Py_nsIID *)self; + Py_nsIID *o_iid = (Py_nsIID *)other; + return memcmp(&s_iid->m_iid, &o_iid->m_iid, sizeof(s_iid->m_iid)); +} + +/* static */ PyObject * +Py_nsIID::PyTypeMethod_repr(PyObject *self) +{ + Py_nsIID *s_iid = (Py_nsIID *)self; + char buf[256]; + char *sziid = s_iid->m_iid.ToString(); + sprintf(buf, "_xpcom.IID('%s')", sziid); + nsAllocator::Free(sziid); + return PyString_FromString(buf); +} + +/* static */ PyObject * +Py_nsIID::PyTypeMethod_str(PyObject *self) +{ + Py_nsIID *s_iid = (Py_nsIID *)self; + char *sziid = s_iid->m_iid.ToString(); + PyObject *ret = PyString_FromString(sziid); + nsAllocator::Free(sziid); + return ret; +} + +/* static */long +Py_nsIID::PyTypeMethod_hash(PyObject *self) +{ + const nsIID &iid = ((Py_nsIID *)self)->m_iid; + + long ret = iid.m0 + iid.m1 + iid.m2; + for (int i=0;i<7;i++) + ret += iid.m3[i]; + if ( ret == -1 ) + return -2; + return ret; +} + +/*static*/ void +Py_nsIID::PyTypeMethod_dealloc(PyObject *ob) +{ + delete (Py_nsIID *)ob; +} diff --git a/mozilla/extensions/python/xpcom/src/PyIInputStream.cpp b/mozilla/extensions/python/xpcom/src/PyIInputStream.cpp new file mode 100644 index 00000000000..323ebcaa87b --- /dev/null +++ b/mozilla/extensions/python/xpcom/src/PyIInputStream.cpp @@ -0,0 +1,127 @@ +/* Copyright (c) 2000-2001 ActiveState Tool Corporation. + See the file LICENSE.txt for licensing information. */ + +// +// This code is part of the XPCOM extensions for Python. +// +// Written September 2000 by Mark Hammond. +// +// Based heavily on the Python COM support, which is +// (c) Mark Hammond and Greg Stein. +// +// (c) 2000, ActiveState corp. + +#include "PyXPCOM_std.h" +#include "nsIInputStream.h" + +// Prevents us needing to use an nsIScriptableInputStream +// (and even that can't read binary data!!!) + +static nsIInputStream *GetI(PyObject *self) { + nsIID iid = NS_GET_IID(nsIInputStream); + + if (!Py_nsISupports::Check(self, iid)) { + PyErr_SetString(PyExc_TypeError, "This object is not the correct interface"); + return NULL; + } + return (nsIInputStream *)Py_nsISupports::GetI(self); +} + +static PyObject *DoPyRead_Buffer(nsIInputStream *pI, PyObject *obBuffer, PRUint32 n) +{ + PRUint32 nread; + void *buf; + PRUint32 buf_len; + if (PyObject_AsWriteBuffer(obBuffer, &buf, (int *)&buf_len) != 0) { + PyErr_Clear(); + PyErr_SetString(PyExc_TypeError, "The buffer object does not have a write buffer!"); + return NULL; + } + if (n==(PRUint32)-1) { + n = buf_len; + } else { + if (n > buf_len) { + NS_WARNING("Warning: PyIInputStream::read() was passed an integer size greater than the size of the passed buffer! Buffer size used.\n"); + n = buf_len; + } + } + nsresult r; + Py_BEGIN_ALLOW_THREADS; + r = pI->Read((char *)buf, n, &nread); + Py_END_ALLOW_THREADS; + if ( NS_FAILED(r) ) + return PyXPCOM_BuildPyException(r); + return PyInt_FromLong(nread); +} + +static PyObject *DoPyRead_Size(nsIInputStream *pI, PRUint32 n) +{ + if (n==-1) { + nsresult r; + Py_BEGIN_ALLOW_THREADS; + r = pI->Available(&n); + Py_END_ALLOW_THREADS; + if (NS_FAILED(r)) + return PyXPCOM_BuildPyException(r); + } + char *buf = (char *)nsAllocator::Alloc(n); + if (buf==NULL) { + PyErr_NoMemory(); + return NULL; + } + nsresult r; + PRUint32 nread; + Py_BEGIN_ALLOW_THREADS; + r = pI->Read(buf, n, &nread); + Py_END_ALLOW_THREADS; + PyObject *rc = NULL; + if ( NS_SUCCEEDED(r) ) { + rc = PyBuffer_New(nread); + if (rc != NULL) { + void *ob_buf; + PRUint32 buf_len; + if (PyObject_AsWriteBuffer(rc, &ob_buf, (int *)&buf_len) != 0) { + // should never fail - we just created it! + return NULL; + } + if (buf_len != nread) { + PyErr_SetString(PyExc_RuntimeError, "New buffer isnt the size we create it!"); + return NULL; + } + memcpy(ob_buf, buf, nread); + } + } else + PyXPCOM_BuildPyException(r); + nsAllocator::Free(buf); + return rc; +} + +static PyObject *PyRead(PyObject *self, PyObject *args) +{ + PyObject *obBuffer = NULL; + PRUint32 n = (PRUint32)-1; + + nsIInputStream *pI = GetI(self); + if (pI==NULL) + return NULL; + if (PyArg_ParseTuple(args, "|i", (int *)&n)) + // This worked - no args, or just an int. + return DoPyRead_Size(pI, n); + // try our other supported arg format. + PyErr_Clear(); + if (!PyArg_ParseTuple(args, "O|i", &obBuffer, (int *)&n)) { + PyErr_Clear(); + PyErr_SetString(PyExc_TypeError, "'read()' must be called as (buffer_ob, int_size=-1) or (int_size=-1)"); + return NULL; + } + return DoPyRead_Buffer(pI, obBuffer, n); +} + + +struct PyMethodDef +PyMethods_IInputStream[] = +{ + { "read", PyRead, 1}, + // The rest are handled as normal + {NULL} +}; diff --git a/mozilla/extensions/python/xpcom/src/PyIInterfaceInfo.cpp b/mozilla/extensions/python/xpcom/src/PyIInterfaceInfo.cpp new file mode 100644 index 00000000000..a82df7754a1 --- /dev/null +++ b/mozilla/extensions/python/xpcom/src/PyIInterfaceInfo.cpp @@ -0,0 +1,391 @@ +/* Copyright (c) 2000-2001 ActiveState Tool Corporation. + See the file LICENSE.txt for licensing information. */ + +// +// This code is part of the XPCOM extensions for Python. +// +// Written May 2000 by Mark Hammond. +// +// Based heavily on the Python COM support, which is +// (c) Mark Hammond and Greg Stein. +// +// (c) 2000, ActiveState corp. + +#include "PyXPCOM_std.h" + + +static nsIInterfaceInfo *GetI(PyObject *self) { + nsIID iid = NS_GET_IID(nsIInterfaceInfo); + + if (!Py_nsISupports::Check(self, iid)) { + PyErr_SetString(PyExc_TypeError, "This object is not the correct interface"); + return NULL; + } + return (nsIInterfaceInfo *)Py_nsISupports::GetI(self); +} + +static PyObject *PyGetName(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":GetName")) + return NULL; + + nsIInterfaceInfo *pI = GetI(self); + if (pI==NULL) + return NULL; + + char *name; + nsresult r; + Py_BEGIN_ALLOW_THREADS; + r = pI->GetName(&name); + Py_END_ALLOW_THREADS; + if ( NS_FAILED(r) ) + return PyXPCOM_BuildPyException(r); + PyObject *ret = PyString_FromString(name); + nsAllocator::Free(name); + return ret; +} + +static PyObject *PyGetIID(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":GetIID")) + return NULL; + + nsIInterfaceInfo *pI = GetI(self); + if (pI==NULL) + return NULL; + + nsIID *iid_ret; + nsresult r; + Py_BEGIN_ALLOW_THREADS; + r = pI->GetIID(&iid_ret); + Py_END_ALLOW_THREADS; + if ( NS_FAILED(r) ) + return PyXPCOM_BuildPyException(r); + PyObject *ret = Py_nsIID::PyObjectFromIID(*iid_ret); + nsAllocator::Free(iid_ret); + return ret; +} + +static PyObject *PyIsScriptable(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":IsScriptable")) + return NULL; + + nsIInterfaceInfo *pI = GetI(self); + if (pI==NULL) + return NULL; + + PRBool b_ret; + nsresult r; + Py_BEGIN_ALLOW_THREADS; + r = pI->IsScriptable(&b_ret); + Py_END_ALLOW_THREADS; + if ( NS_FAILED(r) ) + return PyXPCOM_BuildPyException(r); + return PyInt_FromLong(b_ret); +} + +static PyObject *PyGetParent(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":GetParent")) + return NULL; + + nsIInterfaceInfo *pI = GetI(self); + if (pI==NULL) + return NULL; + + nsIInterfaceInfo *pRet; + nsresult r; + Py_BEGIN_ALLOW_THREADS; + r = pI->GetParent(&pRet); + Py_END_ALLOW_THREADS; + if ( NS_FAILED(r) ) + return PyXPCOM_BuildPyException(r); + return Py_nsISupports::PyObjectFromInterface(pRet, NS_GET_IID(nsIInterfaceInfo), PR_FALSE); +} + +static PyObject *PyGetMethodCount(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":GetMethodCount")) + return NULL; + + nsIInterfaceInfo *pI = GetI(self); + if (pI==NULL) + return NULL; + + PRUint16 ret; + nsresult r; + Py_BEGIN_ALLOW_THREADS; + r = pI->GetMethodCount(&ret); + Py_END_ALLOW_THREADS; + if ( NS_FAILED(r) ) + return PyXPCOM_BuildPyException(r); + return PyInt_FromLong(ret); +} + + +static PyObject *PyGetConstantCount(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":GetConstantCount")) + return NULL; + + nsIInterfaceInfo *pI = GetI(self); + if (pI==NULL) + return NULL; + + PRUint16 ret; + nsresult r; + Py_BEGIN_ALLOW_THREADS; + r = pI->GetConstantCount(&ret); + Py_END_ALLOW_THREADS; + if ( NS_FAILED(r) ) + return PyXPCOM_BuildPyException(r); + return PyInt_FromLong(ret); +} + +static PyObject *PyGetMethodInfo(PyObject *self, PyObject *args) +{ + PRUint16 index; + if (!PyArg_ParseTuple(args, "h:GetMethodInfo", &index)) + return NULL; + + nsIInterfaceInfo *pI = GetI(self); + if (pI==NULL) + return NULL; + + PRUint16 nmethods; + pI->GetMethodCount(&nmethods); + if (index>=nmethods) { + PyErr_SetString(PyExc_ValueError, "The method index is out of range"); + return NULL; + } + + const nsXPTMethodInfo *pRet; + nsresult r; + Py_BEGIN_ALLOW_THREADS; + r = pI->GetMethodInfo(index, &pRet); + Py_END_ALLOW_THREADS; + if ( NS_FAILED(r) ) + return PyXPCOM_BuildPyException(r); + return PyObject_FromXPTMethodDescriptor(pRet); +} + +static PyObject *PyGetMethodInfoForName(PyObject *self, PyObject *args) +{ + char *name; + if (!PyArg_ParseTuple(args, "s:GetMethodInfoForName", &name)) + return NULL; + + nsIInterfaceInfo *pI = GetI(self); + if (pI==NULL) + return NULL; + + const nsXPTMethodInfo *pRet; + PRUint16 index; + nsresult r; + Py_BEGIN_ALLOW_THREADS; + r = pI->GetMethodInfoForName(name, &index, &pRet); + Py_END_ALLOW_THREADS; + if ( NS_FAILED(r) ) + return PyXPCOM_BuildPyException(r); + PyObject *ret_i = PyObject_FromXPTMethodDescriptor(pRet); + if (ret_i==NULL) + return NULL; + PyObject *real_ret = Py_BuildValue("iO", (int)index, ret_i); + Py_DECREF(ret_i); + return real_ret; +} + + +static PyObject *PyGetConstant(PyObject *self, PyObject *args) +{ + PRUint16 index; + if (!PyArg_ParseTuple(args, "h:GetConstant", &index)) + return NULL; + + nsIInterfaceInfo *pI = GetI(self); + if (pI==NULL) + return NULL; + + const nsXPTConstant *pRet; + nsresult r; + Py_BEGIN_ALLOW_THREADS; + r = pI->GetConstant(index, &pRet); + Py_END_ALLOW_THREADS; + if ( NS_FAILED(r) ) + return PyXPCOM_BuildPyException(r); + return PyObject_FromXPTConstant(pRet); +} + +static PRBool __GetMethodInfoHelper(nsIInterfaceInfo *pii, int mi, int pi, const nsXPTMethodInfo **ppmi) +{ + PRUint16 nmethods=0; + pii->GetMethodCount(&nmethods); + if (mi<0 || mi>=nmethods) { + PyErr_SetString(PyExc_ValueError, "The method index is out of range"); + return PR_FALSE; + } + const nsXPTMethodInfo *pmi; + nsresult r = pii->GetMethodInfo(mi, &pmi); + if ( NS_FAILED(r) ) { + PyXPCOM_BuildPyException(r); + return PR_FALSE; + } + + int nparams=0; + nparams = pmi->GetParamCount(); + if (pi<0 || pi>=nparams) { + PyErr_SetString(PyExc_ValueError, "The param index is out of range"); + return PR_FALSE; + } + *ppmi = pmi; + return PR_TRUE; +} + +static PyObject *PyGetInfoForParam(PyObject *self, PyObject *args) +{ + nsIInterfaceInfo *pii = GetI(self); + if (pii==NULL) + return NULL; + PRUint16 mi, pi; + if (!PyArg_ParseTuple(args, "hh:GetInfoForParam", &mi, &pi)) + return NULL; + const nsXPTMethodInfo *pmi; + if (!__GetMethodInfoHelper(pii, mi, pi, &pmi)) + return NULL; + const nsXPTParamInfo& param_info = pmi->GetParam((PRUint8)pi); + nsIInterfaceInfo *pnewii = nsnull; + nsresult n = pii->GetInfoForParam(mi, ¶m_info, &pnewii); + if (NS_FAILED(n)) + return PyXPCOM_BuildPyException(n); + return Py_nsISupports::PyObjectFromInterface(pnewii, NS_GET_IID(nsIInterfaceInfo), PR_FALSE); +} + +static PyObject *PyGetIIDForParam(PyObject *self, PyObject *args) +{ + nsIInterfaceInfo *pii = GetI(self); + if (pii==NULL) + return NULL; + PRUint16 mi, pi; + if (!PyArg_ParseTuple(args, "hh:GetIIDForParam", &mi, &pi)) + return NULL; + const nsXPTMethodInfo *pmi; + if (!__GetMethodInfoHelper(pii, mi, pi, &pmi)) + return NULL; + const nsXPTParamInfo& param_info = pmi->GetParam((PRUint8)pi); + nsIID *piid; + nsresult n = pii->GetIIDForParam(mi, ¶m_info, &piid); + if (NS_FAILED(n) || piid==nsnull) + return PyXPCOM_BuildPyException(n); + return Py_nsIID::PyObjectFromIID(*piid); +} + +static PyObject *PyGetTypeForParam(PyObject *self, PyObject *args) +{ + nsIInterfaceInfo *pii = GetI(self); + if (pii==NULL) + return NULL; + PRUint16 mi, pi, dim; + if (!PyArg_ParseTuple(args, "hhh:GetTypeForParam", &mi, &pi, &dim)) + return NULL; + const nsXPTMethodInfo *pmi; + if (!__GetMethodInfoHelper(pii, mi, pi, &pmi)) + return NULL; + nsXPTType datumType; + const nsXPTParamInfo& param_info = pmi->GetParam((PRUint8)pi); + nsresult n = pii->GetTypeForParam(mi, ¶m_info, dim, &datumType); + if (NS_FAILED(n)) + return PyXPCOM_BuildPyException(n); + return PyObject_FromXPTTypeDescriptor((const XPTTypeDescriptor *)&datumType); +} + +static PyObject *PyGetSizeIsArgNumberForParam(PyObject *self, PyObject *args) +{ + nsIInterfaceInfo *pii = GetI(self); + if (pii==NULL) + return NULL; + PRUint16 mi, pi, dim; + if (!PyArg_ParseTuple(args, "hhh:GetSizeIsArgNumberForParam", &mi, &pi, &dim)) + return NULL; + const nsXPTMethodInfo *pmi; + if (!__GetMethodInfoHelper(pii, mi, pi, &pmi)) + return NULL; + PRUint8 ret; + const nsXPTParamInfo& param_info = pmi->GetParam((PRUint8)pi); + nsresult n = pii->GetSizeIsArgNumberForParam(mi, ¶m_info, dim, &ret); + if (NS_FAILED(n)) + return PyXPCOM_BuildPyException(n); + return PyInt_FromLong(ret); +} + +static PyObject *PyGetLengthIsArgNumberForParam(PyObject *self, PyObject *args) +{ + nsIInterfaceInfo *pii = GetI(self); + if (pii==NULL) + return NULL; + PRUint16 mi, pi, dim; + if (!PyArg_ParseTuple(args, "hhh:GetLengthIsArgNumberForParam", &mi, &pi, &dim)) + return NULL; + const nsXPTMethodInfo *pmi; + if (!__GetMethodInfoHelper(pii, mi, pi, &pmi)) + return NULL; + PRUint8 ret; + const nsXPTParamInfo& param_info = pmi->GetParam((PRUint8)pi); + nsresult n = pii->GetLengthIsArgNumberForParam(mi, ¶m_info, dim, &ret); + if (NS_FAILED(n)) + return PyXPCOM_BuildPyException(n); + return PyInt_FromLong(ret); +} + +static PyObject *PyGetInterfaceIsArgNumberForParam(PyObject *self, PyObject *args) +{ + nsIInterfaceInfo *pii = GetI(self); + if (pii==NULL) + return NULL; + PRUint16 mi, pi; + if (!PyArg_ParseTuple(args, "hhh:GetInterfaceIsArgNumberForParam", &mi, &pi)) + return NULL; + const nsXPTMethodInfo *pmi; + if (!__GetMethodInfoHelper(pii, mi, pi, &pmi)) + return NULL; + PRUint8 ret; + const nsXPTParamInfo& param_info = pmi->GetParam((PRUint8)pi); + nsresult n = pii->GetInterfaceIsArgNumberForParam(mi, ¶m_info, &ret); + if (NS_FAILED(n)) + return PyXPCOM_BuildPyException(n); + return PyInt_FromLong(ret); +} + +struct PyMethodDef +PyMethods_IInterfaceInfo[] = +{ + { "GetName", PyGetName, 1}, + { "GetIID", PyGetIID, 1}, + { "IsScriptable", PyIsScriptable, 1}, + { "GetParent", PyGetParent, 1}, + { "GetMethodCount", PyGetMethodCount, 1}, + { "GetConstantCount", PyGetConstantCount, 1}, + { "GetMethodInfo", PyGetMethodInfo, 1}, + { "GetMethodInfoForName", PyGetMethodInfoForName, 1}, + { "GetConstant", PyGetConstant, 1}, + { "GetInfoForParam", PyGetInfoForParam, 1}, + { "GetIIDForParam", PyGetIIDForParam, 1}, + { "GetTypeForParam", PyGetTypeForParam, 1}, + { "GetSizeIsArgNumberForParam", PyGetSizeIsArgNumberForParam, 1}, + { "GetLengthIsArgNumberForParam", PyGetLengthIsArgNumberForParam, 1}, + { "GetInterfaceIsArgNumberForParam", PyGetInterfaceIsArgNumberForParam, 1}, + {NULL} +}; + +/* + NS_IMETHOD GetMethodInfo(PRUint16 index, const nsXPTMethodInfo * *info) = 0; + NS_IMETHOD GetMethodInfoForName(const char *methodName, PRUint16 *index, const nsXPTMethodInfo * *info) = 0; + NS_IMETHOD GetConstant(PRUint16 index, const nsXPTConstant * *constant) = 0; + NS_IMETHOD GetInfoForParam(PRUint16 methodIndex, const nsXPTParamInfo * param, nsIInterfaceInfo **_retval) = 0; + NS_IMETHOD GetIIDForParam(PRUint16 methodIndex, const nsXPTParamInfo * param, nsIID * *_retval) = 0; + NS_IMETHOD GetTypeForParam(PRUint16 methodIndex, const nsXPTParamInfo * param, PRUint16 dimension, nsXPTType *_retval) = 0; + NS_IMETHOD GetSizeIsArgNumberForParam(PRUint16 methodIndex, const nsXPTParamInfo * param, PRUint16 dimension, PRUint8 *_retval) = 0; + NS_IMETHOD GetLengthIsArgNumberForParam(PRUint16 methodIndex, const nsXPTParamInfo * param, PRUint16 dimension, PRUint8 *_retval) = 0; + NS_IMETHOD GetInterfaceIsArgNumberForParam(PRUint16 methodIndex, const nsXPTParamInfo * param, PRUint8 *_retval) = 0; + +*/ diff --git a/mozilla/extensions/python/xpcom/src/PyIInterfaceInfoManager.cpp b/mozilla/extensions/python/xpcom/src/PyIInterfaceInfoManager.cpp new file mode 100644 index 00000000000..3ffc4c03a98 --- /dev/null +++ b/mozilla/extensions/python/xpcom/src/PyIInterfaceInfoManager.cpp @@ -0,0 +1,167 @@ +/* Copyright (c) 2000-2001 ActiveState Tool Corporation. + See the file LICENSE.txt for licensing information. */ + +// +// This code is part of the XPCOM extensions for Python. +// +// Written May 2000 by Mark Hammond. +// +// Based heavily on the Python COM support, which is +// (c) Mark Hammond and Greg Stein. +// +// (c) 2000, ActiveState corp. + +#include "PyXPCOM_std.h" +#include + +static nsIInterfaceInfoManager *GetI(PyObject *self) { + nsIID iid = NS_GET_IID(nsIInterfaceInfoManager); + + if (!Py_nsISupports::Check(self, iid)) { + PyErr_SetString(PyExc_TypeError, "This object is not the correct interface"); + return NULL; + } + return (nsIInterfaceInfoManager *)Py_nsISupports::GetI(self); +} + +static PyObject *PyGetInfoForIID(PyObject *self, PyObject *args) +{ + PyObject *obIID = NULL; + if (!PyArg_ParseTuple(args, "O", &obIID)) + return NULL; + + nsIInterfaceInfoManager *pI = GetI(self); + if (pI==NULL) + return NULL; + + nsIID iid; + if (!Py_nsIID::IIDFromPyObject(obIID, &iid)) + return NULL; + + nsIInterfaceInfo *pi; + nsresult r; + Py_BEGIN_ALLOW_THREADS; + r = pI->GetInfoForIID(&iid, &pi); + Py_END_ALLOW_THREADS; + if ( NS_FAILED(r) ) + return PyXPCOM_BuildPyException(r); + + /* Return a type based on the IID (with no extra ref) */ + nsIID new_iid = NS_GET_IID(nsIInterfaceInfo); + // Can not auto-wrap the interface info manager as it is critical to + // building the support we need for autowrap. + return Py_nsISupports::PyObjectFromInterface(pi, new_iid, PR_FALSE, PR_FALSE); +} + +static PyObject *PyGetInfoForName(PyObject *self, PyObject *args) +{ + char *name; + if (!PyArg_ParseTuple(args, "s", &name)) + return NULL; + + nsIInterfaceInfoManager *pI = GetI(self); + if (pI==NULL) + return NULL; + + nsIInterfaceInfo *pi; + nsresult r; + Py_BEGIN_ALLOW_THREADS; + r = pI->GetInfoForName(name, &pi); + Py_END_ALLOW_THREADS; + if ( NS_FAILED(r) ) + return PyXPCOM_BuildPyException(r); + + /* Return a type based on the IID (with no extra ref) */ + // Can not auto-wrap the interface info manager as it is critical to + // building the support we need for autowrap. + return Py_nsISupports::PyObjectFromInterface(pi, NS_GET_IID(nsIInterfaceInfo), PR_FALSE, PR_FALSE); +} + +static PyObject *PyGetNameForIID(PyObject *self, PyObject *args) +{ + PyObject *obIID = NULL; + if (!PyArg_ParseTuple(args, "O", &obIID)) + return NULL; + + nsIInterfaceInfoManager *pI = GetI(self); + if (pI==NULL) + return NULL; + + nsIID iid; + if (!Py_nsIID::IIDFromPyObject(obIID, &iid)) + return NULL; + + char *ret_name = NULL; + nsresult r; + Py_BEGIN_ALLOW_THREADS; + r = pI->GetNameForIID(&iid, &ret_name); + Py_END_ALLOW_THREADS; + if ( NS_FAILED(r) ) + return PyXPCOM_BuildPyException(r); + + PyObject *ret = PyString_FromString(ret_name); + nsAllocator::Free(ret_name); + return ret; +} + +static PyObject *PyGetIIDForName(PyObject *self, PyObject *args) +{ + char *name; + if (!PyArg_ParseTuple(args, "s", &name)) + return NULL; + + nsIInterfaceInfoManager *pI = GetI(self); + if (pI==NULL) + return NULL; + + nsIID *iid_ret; + nsresult r; + Py_BEGIN_ALLOW_THREADS; + r = pI->GetIIDForName(name, &iid_ret); + Py_END_ALLOW_THREADS; + if ( NS_FAILED(r) ) + return PyXPCOM_BuildPyException(r); + + PyObject *ret = Py_nsIID::PyObjectFromIID(*iid_ret); + nsAllocator::Free(iid_ret); + return ret; +} + +static PyObject *PyEnumerateInterfaces(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) + return NULL; + + nsIInterfaceInfoManager *pI = GetI(self); + if (pI==NULL) + return NULL; + + nsIEnumerator *pRet; + nsresult r; + Py_BEGIN_ALLOW_THREADS; + r = pI->EnumerateInterfaces(&pRet); + Py_END_ALLOW_THREADS; + if ( NS_FAILED(r) ) + return PyXPCOM_BuildPyException(r); + + return Py_nsISupports::PyObjectFromInterface(pRet, NS_GET_IID(nsIEnumerator), PR_FALSE); +} + +// TODO: +// void autoRegisterInterfaces(); + +PyMethodDef +PyMethods_IInterfaceInfoManager[] = +{ + { "GetInfoForIID", PyGetInfoForIID, 1}, + { "getInfoForIID", PyGetInfoForIID, 1}, + { "GetInfoForName", PyGetInfoForName, 1}, + { "getInfoForName", PyGetInfoForName, 1}, + { "GetIIDForName", PyGetIIDForName, 1}, + { "getIIDForName", PyGetIIDForName, 1}, + { "GetNameForIID", PyGetNameForIID, 1}, + { "getNameForIID", PyGetNameForIID, 1}, + { "EnumerateInterfaces", PyEnumerateInterfaces, 1}, + { "enumerateInterfaces", PyEnumerateInterfaces, 1}, + {NULL} +}; diff --git a/mozilla/extensions/python/xpcom/src/PyIServiceManager.cpp b/mozilla/extensions/python/xpcom/src/PyIServiceManager.cpp new file mode 100644 index 00000000000..0fbe2746d2c --- /dev/null +++ b/mozilla/extensions/python/xpcom/src/PyIServiceManager.cpp @@ -0,0 +1,101 @@ +/* Copyright (c) 2000-2001 ActiveState Tool Corporation. + See the file LICENSE.txt for licensing information. */ + +// +// This code is part of the XPCOM extensions for Python. +// +// Written May 2000 by Mark Hammond. +// +// Based heavily on the Python COM support, which is +// (c) Mark Hammond and Greg Stein. +// +// (c) 2000, ActiveState corp. + +#include "PyXPCOM_std.h" +#include + + +static nsIServiceManager *GetI(PyObject *self) { + nsIID iid = NS_GET_IID(nsIServiceManager); + + if (!Py_nsISupports::Check(self, iid)) { + PyErr_SetString(PyExc_TypeError, "This object is not the correct interface"); + return NULL; + } + return (nsIServiceManager *)Py_nsISupports::GetI(self); +} + +static PyObject *PyRegisterService(PyObject *self, PyObject *args) +{ + nsIServiceManager *pI = GetI(self); + if (pI==NULL) + return NULL; + PyObject *obCID, *obInterface; + if (!PyArg_ParseTuple(args, "OO", &obCID, &obInterface)) + return NULL; + + nsCOMPtr pis; + if (!Py_nsISupports::InterfaceFromPyObject(obInterface, NS_GET_IID(nsISupports), getter_AddRefs(pis), PR_FALSE)) + return NULL; + nsresult r; + if (PyString_Check(obCID) || PyUnicode_Check(obCID)) { + const char *val = PyString_AsString(obCID); + Py_BEGIN_ALLOW_THREADS; + r = pI->RegisterService(val, pis); + Py_END_ALLOW_THREADS; + } else { + nsCID cid; + if (!Py_nsIID::IIDFromPyObject(obCID, &cid)) + return NULL; + Py_BEGIN_ALLOW_THREADS; + r = pI->RegisterService(cid, pis); + Py_END_ALLOW_THREADS; + } + if ( NS_FAILED(r) ) + return PyXPCOM_BuildPyException(r); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *PyGetService(PyObject *self, PyObject *args) +{ + nsIServiceManager *pI = GetI(self); + if (pI==NULL) + return NULL; + PyObject *obIID, *obCID; + if (!PyArg_ParseTuple(args, "OO", &obCID, &obIID)) + return NULL; + nsIID cid, iid; + if (!Py_nsIID::IIDFromPyObject(obIID, &iid)) + return NULL; + + nsISupports *pis; + nsresult r; + if (PyString_Check(obCID) || PyUnicode_Check(obCID)) { + char *val = PyString_AsString(obCID); + Py_BEGIN_ALLOW_THREADS; + r = pI->GetService(val, iid, &pis); + Py_END_ALLOW_THREADS; + } else { + if (!Py_nsIID::IIDFromPyObject(obCID, &cid)) + return NULL; + Py_BEGIN_ALLOW_THREADS; + r = pI->GetService(cid, iid, &pis); + Py_END_ALLOW_THREADS; + } + if ( NS_FAILED(r) ) + return PyXPCOM_BuildPyException(r); + + /* Return a type based on the IID (with no extra ref) */ + return Py_nsISupports::PyObjectFromInterface(pis, iid, PR_FALSE); +} + +struct PyMethodDef +PyMethods_IServiceManager[] = +{ + { "GetService", PyGetService, 1}, + { "getService", PyGetService, 1}, + { "RegisterService", PyRegisterService, 1}, + { "registerService", PyRegisterService, 1}, + {NULL} +}; diff --git a/mozilla/extensions/python/xpcom/src/PyISimpleEnumerator.cpp b/mozilla/extensions/python/xpcom/src/PyISimpleEnumerator.cpp new file mode 100644 index 00000000000..00446a32359 --- /dev/null +++ b/mozilla/extensions/python/xpcom/src/PyISimpleEnumerator.cpp @@ -0,0 +1,165 @@ +/* Copyright (c) 2000-2001 ActiveState Tool Corporation. + See the file LICENSE.txt for licensing information. */ + +// +// This code is part of the XPCOM extensions for Python. +// +// Written May 2000 by Mark Hammond. +// +// Based heavily on the Python COM support, which is +// (c) Mark Hammond and Greg Stein. +// +// (c) 2000, ActiveState corp. + +#include "PyXPCOM_std.h" +#include + +static nsISimpleEnumerator *GetI(PyObject *self) { + nsIID iid = NS_GET_IID(nsISimpleEnumerator); + + if (!Py_nsISupports::Check(self, iid)) { + PyErr_SetString(PyExc_TypeError, "This object is not the correct interface"); + return NULL; + } + return (nsISimpleEnumerator *)Py_nsISupports::GetI(self); +} + + +static PyObject *PyHasMoreElements(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":HasMoreElements")) + return NULL; + + nsISimpleEnumerator *pI = GetI(self); + if (pI==NULL) + return NULL; + + nsresult r; + PRBool more; + Py_BEGIN_ALLOW_THREADS; + r = pI->HasMoreElements(&more); + Py_END_ALLOW_THREADS; + if ( NS_FAILED(r) ) + return PyXPCOM_BuildPyException(r); + return PyInt_FromLong(more); +} + +static PyObject *PyGetNext(PyObject *self, PyObject *args) +{ + PyObject *obIID = NULL; + if (!PyArg_ParseTuple(args, "|O:GetNext", &obIID)) + return NULL; + + nsIID iid(NS_GET_IID(nsISupports)); + if (obIID != NULL && !Py_nsIID::IIDFromPyObject(obIID, &iid)) + return NULL; + nsISimpleEnumerator *pI = GetI(self); + if (pI==NULL) + return NULL; + + nsISupports *pRet = nsnull; + nsresult r; + Py_BEGIN_ALLOW_THREADS; + r = pI->GetNext(&pRet); + Py_END_ALLOW_THREADS; + if ( NS_FAILED(r) ) + return PyXPCOM_BuildPyException(r); + if (obIID) { + nsISupports *temp; + Py_BEGIN_ALLOW_THREADS; + r = pRet->QueryInterface(iid, (void **)&temp); + pRet->Release(); + Py_END_ALLOW_THREADS; + if ( NS_FAILED(r) ) { + return PyXPCOM_BuildPyException(r); + } + pRet = temp; + } + return Py_nsISupports::PyObjectFromInterface(pRet, iid, PR_FALSE); +} + +// A method added for Python performance if you really need +// it. Allows you to fetch a block of objects in one +// hit, allowing the loop to remain implemented in C. +static PyObject *PyFetchBlock(PyObject *self, PyObject *args) +{ + PyObject *obIID = NULL; + int n_wanted; + int n_fetched = 0; + if (!PyArg_ParseTuple(args, "i|O:FetchBlock", &n_wanted, &obIID)) + return NULL; + + nsIID iid(NS_GET_IID(nsISupports)); + if (obIID != NULL && !Py_nsIID::IIDFromPyObject(obIID, &iid)) + return NULL; + nsISimpleEnumerator *pI = GetI(self); + if (pI==NULL) + return NULL; + + // We want to fetch with the thread-lock released, + // but this means we can not append to the PyList + nsISupports **fetched = new nsISupports*[n_wanted]; + if (fetched==nsnull) { + PyErr_NoMemory(); + return NULL; + } + memset(fetched, 0, sizeof(nsISupports *) * n_wanted); + nsresult r; + PRBool more; + Py_BEGIN_ALLOW_THREADS; + for (;n_fetchedHasMoreElements(&more); + if (NS_FAILED(r)) + break; // this _is_ an error! + if (!more) + break; // Normal enum end. + nsISupports *pNew; + r = pI->GetNext(&pNew); + if (NS_FAILED(r)) // IS an error + break; + if (obIID) { + nsISupports *temp; + r = pNew->QueryInterface(iid, (void **)&temp); + pNew->Release(); + if ( NS_FAILED(r) ) { + break; + } + pNew = temp; + } + fetched[n_fetched] = pNew; + n_fetched++; + } + Py_END_ALLOW_THREADS; + PyObject *ret; + if (NS_SUCCEEDED(r)) { + ret = PyList_New(n_fetched); + if (ret) + for (int i=0;iRelease(); + + } + delete [] fetched; + return ret; +} + + +struct PyMethodDef +PyMethods_ISimpleEnumerator[] = +{ + { "HasMoreElements", PyHasMoreElements, 1}, + { "hasMoreElements", PyHasMoreElements, 1}, + { "GetNext", PyGetNext, 1}, + { "getNext", PyGetNext, 1}, + { "FetchBlock", PyFetchBlock, 1}, + { "fetchBlock", PyFetchBlock, 1}, + {NULL} +}; diff --git a/mozilla/extensions/python/xpcom/src/PyISupports.cpp b/mozilla/extensions/python/xpcom/src/PyISupports.cpp new file mode 100644 index 00000000000..75da99c8831 --- /dev/null +++ b/mozilla/extensions/python/xpcom/src/PyISupports.cpp @@ -0,0 +1,343 @@ +/* Copyright (c) 2000-2001 ActiveState Tool Corporation. + See the file LICENSE.txt for licensing information. */ + +// +// This code is part of the XPCOM extensions for Python. +// +// Written May 2000 by Mark Hammond. +// +// Based heavily on the Python COM support, which is +// (c) Mark Hammond and Greg Stein. +// +// (c) 2000, ActiveState corp. + +#include "PyXPCOM_std.h" + +static PRInt32 cInterfaces=0; +static PyObject *g_obFuncMakeInterfaceCount = NULL; // XXX - never released!!! + +PRInt32 +_PyXPCOM_GetInterfaceCount(void) +{ + return cInterfaces; +} + +Py_nsISupports::Py_nsISupports(nsISupports *punk, const nsIID &iid, PyTypeObject *this_type) +{ + ob_type = this_type; + m_obj = punk; + m_iid = iid; + // refcnt of object managed by caller. + PR_AtomicIncrement(&cInterfaces); + PyXPCOM_DLLAddRef(); + _Py_NewReference(this); +} + +Py_nsISupports::~Py_nsISupports() +{ + SafeRelease(this); + PR_AtomicDecrement(&cInterfaces); + PyXPCOM_DLLRelease(); +} + +/*static*/ nsISupports * +Py_nsISupports::GetI(PyObject *self, nsIID *ret_iid) +{ + if (self==NULL) { + PyErr_SetString(PyExc_ValueError, "The Python object is invalid"); + return NULL; + } + Py_nsISupports *pis = (Py_nsISupports *)self; + if (pis->m_obj==NULL) { + // This should never be able to happen. + PyErr_SetString(PyExc_ValueError, "Internal Error - The XPCOM object has been released."); + return NULL; + } + if (ret_iid) + *ret_iid = pis->m_iid; + return pis->m_obj; +} + +/*static*/ void +Py_nsISupports::SafeRelease(Py_nsISupports *ob) +{ + if (!ob) + return; + if (ob->m_obj) + { + long rcnt; + Py_BEGIN_ALLOW_THREADS; + rcnt = ob->m_obj->Release(); + Py_END_ALLOW_THREADS; + +#ifdef _DEBUG_LIFETIMES + LogF(buf, " SafeRelease(%ld) -> %s at 0x%0lx, nsISupports at 0x%0lx - Release() returned %ld",GetCurrentThreadId(), ob->ob_type->tp_name,ob, ob->m_obj,rcnt); +#endif + ob->m_obj = NULL; + } +} + +/*static*/ Py_nsISupports * +Py_nsISupports::Constructor(nsISupports *pInitObj, const nsIID &iid) +{ + return new Py_nsISupports(pInitObj, + iid, + type); +} + +/*static*/PRBool +Py_nsISupports::InterfaceFromPyObject(PyObject *ob, + const nsIID &iid, + nsISupports **ppv, + PRBool bNoneOK, + PRBool bTryAutoWrap /* = PR_TRUE */) +{ + if ( ob == NULL ) + { + // don't overwrite an error message + if ( !PyErr_Occurred() ) + PyErr_SetString(PyExc_TypeError, "The Python object is invalid"); + return PR_FALSE; + } + if ( ob == Py_None ) + { + if ( bNoneOK ) + { + *ppv = NULL; + return PR_TRUE; + } + else + { + PyErr_SetString(PyExc_TypeError, "None is not a invalid interface object in this context"); + return PR_FALSE; + } + } + + if (PyInstance_Check(ob)) { + // Get the _comobj_ attribute + PyObject *use_ob = PyObject_GetAttrString(ob, "_comobj_"); + if (use_ob==NULL) { + PyErr_Clear(); + if (bTryAutoWrap) + // Try and auto-wrap it - errors will leave Py exception set, + return PyXPCOM_XPTStub::AutoWrapPythonInstance(ob, iid, ppv); + PyErr_SetString(PyExc_TypeError, "The Python instance can not be converted to an XP COM object"); + return PR_FALSE; + } else + ob = use_ob; + + } else { + Py_XINCREF(ob); + } + + nsISupports *pis; + PRBool rc = PR_FALSE; + if ( !Check(ob) ) + { + PyErr_Format(PyExc_TypeError, "Objects of type '%s' can not be used as COM objects", ob->ob_type->tp_name); + goto done; + } + nsIID already_iid; + pis = GetI(ob, &already_iid); + if ( !pis ) + goto done; /* exception was set by GetI() */ + /* note: we don't (yet) explicitly hold a reference to pis */ + if (iid.Equals(Py_nsIID_NULL)) { + // a bit of a hack - we are asking for the arbitary interface + // wrapped by this object, not some other specific interface - + // so no QI, just an AddRef(); + Py_BEGIN_ALLOW_THREADS + pis->AddRef(); + Py_END_ALLOW_THREADS + *ppv = pis; + } else { + // specific interface requested - if it is not already the + // specific interface, QI for it and discard pis. + if (iid.Equals(already_iid)) { + *ppv = pis; + pis->AddRef(); + } else { + nsresult r; + Py_BEGIN_ALLOW_THREADS + r = pis->QueryInterface(iid, (void **)ppv); + Py_END_ALLOW_THREADS + if ( NS_FAILED(r) ) + { + PyXPCOM_BuildPyException(r); + goto done; + } + /* note: the QI added a ref for the return value */ + } + } + + rc = PR_TRUE; +done: + Py_XDECREF(ob); + return rc; +} + +// Interface conversions +/*static*/void +Py_nsISupports::RegisterInterface( const nsIID &iid, PyTypeObject *t) +{ + if (mapIIDToType==NULL) + mapIIDToType = PyDict_New(); + + if (mapIIDToType) { + PyObject *key = Py_nsIID::PyObjectFromIID(iid); + if (key) + PyDict_SetItem(mapIIDToType, key, (PyObject *)t); + Py_XDECREF(key); + } +} + +/*static */PyObject * +Py_nsISupports::PyObjectFromInterface(nsISupports *pis, + const nsIID &riid, + PRBool bAddRef, + PRBool bMakeNicePyObject /* = PR_TRUE */) +{ + // Quick exit. + if (pis==NULL) { + Py_INCREF(Py_None); + return Py_None; + } + PyTypeObject *createType = NULL; + // If the IID is for nsISupports, dont bother with + // a map lookup as we know the type! + if (!riid.Equals(NS_GET_IID(nsISupports))) { + + // Look up the map + PyObject *obiid = Py_nsIID::PyObjectFromIID(riid); + if (!obiid) return NULL; + + if (mapIIDToType != NULL) + createType = (PyTypeObject *)PyDict_GetItem(mapIIDToType, obiid); + Py_DECREF(obiid); + } + if (createType==NULL) + createType = Py_nsISupports::type; + // Check it is indeed one of our types. + if (!PyXPCOM_TypeObject::IsType(createType)) { + PyErr_SetString(PyExc_RuntimeError, "The type map is invalid"); + return NULL; + } + // we can now safely cast the thing to a PyComTypeObject and use it + PyXPCOM_TypeObject *myCreateType = (PyXPCOM_TypeObject *)createType; + if (myCreateType->ctor==NULL) { + PyErr_SetString(PyExc_TypeError, "The type does not declare a PyCom constructor"); + return NULL; + } + + Py_nsISupports *ret = (*myCreateType->ctor)(pis, riid); +#ifdef _DEBUG_LIFETIMES + PyXPCOM_LogF("XPCOM Object created at 0x%0xld, nsISupports at 0x%0xld", + ret, ret->m_obj); +#endif + if (ret && bAddRef && pis) pis->AddRef(); + if (ret && bMakeNicePyObject) + return MakeInterfaceResult(ret, riid); + return ret; +} + +// Call back into Python, passing a raw nsIInterface object, getting back +// the object to actually pass to Python. +PyObject * +Py_nsISupports::MakeInterfaceResult(PyObject *pyis, + const nsIID &iid) +{ + NS_PRECONDITION(pyis, "NULL pyobject!"); + PyObject *obIID = NULL; + PyObject *args = NULL; + PyObject *func = NULL; + PyObject *mod = NULL; + PyObject *ret = NULL; + + obIID = Py_nsIID::PyObjectFromIID(iid); + if (obIID==NULL) + goto done; + + if (g_obFuncMakeInterfaceCount==NULL) { + PyObject *mod = PyImport_ImportModule("xpcom.client"); + if (mod) + g_obFuncMakeInterfaceCount = PyObject_GetAttrString(mod, "MakeInterfaceResult"); + Py_XDECREF(mod); + } + if (g_obFuncMakeInterfaceCount==NULL) goto done; + + args = Py_BuildValue("OO", pyis, obIID); + if (args==NULL) goto done; + ret = PyEval_CallObject(g_obFuncMakeInterfaceCount, args); +done: + if (PyErr_Occurred()) { + NS_ABORT_IF_FALSE(ret==NULL, "Have an error, but also a return val!"); + PyXPCOM_LogError("Creating an interface object to be used as a parameter failed\n"); + PyErr_Clear(); + } + Py_XDECREF(mod); + Py_XDECREF(args); + Py_XDECREF(obIID); + if (ret==NULL) // eek - error - return the original with no refcount mod. + ret = pyis; + else + // no error - decref the old object + Py_DECREF(pyis); + // return our obISupports. If NULL, we are really hosed and nothing we can do. + return ret; +} + +// @pymethod |Py_nsISupports|QueryInterface|Queries an object for a specific interface. +PyObject * +Py_nsISupports::QueryInterface(PyObject *self, PyObject *args) +{ + PyObject *obiid; + int bWrap = 1; + // @pyparm IID|iid||The IID requested. + // @rdesc The result is always a object. + // Any error (including E_NOINTERFACE) will generate a exception. + if (!PyArg_ParseTuple(args, "O|i:QueryInterface", &obiid, &bWrap)) + return NULL; + + nsIID iid; + if (!Py_nsIID::IIDFromPyObject(obiid, &iid)) + return NULL; + + nsISupports *pMyIS = GetI(self); + if (pMyIS==NULL) return NULL; + + nsISupports *pis; + nsresult r; + Py_BEGIN_ALLOW_THREADS; + r = pMyIS->QueryInterface(iid, (void **)&pis); + Py_END_ALLOW_THREADS; + + /* Note that this failure may include E_NOINTERFACE */ + if ( NS_FAILED(r) ) + return PyXPCOM_BuildPyException(r); + + /* Return a type based on the IID (with no extra ref) */ + return PyObjectFromInterface(pis, iid, PR_FALSE, (PRBool)bWrap); +} + + +// @object Py_nsISupports|The base object for all PythonCOM objects. Wraps a COM nsISupports interface. +/*static*/ struct PyMethodDef +Py_nsISupports::methods[] = +{ + { "queryInterface", Py_nsISupports::QueryInterface, 1, "Queries the object for an interface."}, + { "QueryInterface", Py_nsISupports::QueryInterface, 1, "An alias for queryInterface."}, + {NULL} +}; + +/*static*/void Py_nsISupports::InitType(void) +{ + type = new PyXPCOM_TypeObject( + "nsISupports", + NULL, + sizeof(Py_nsISupports), + methods, + Constructor); +} + +PyXPCOM_TypeObject *Py_nsISupports::type = NULL; +PyObject *Py_nsISupports::mapIIDToType = NULL; diff --git a/mozilla/extensions/python/xpcom/src/PyXPCOM.h b/mozilla/extensions/python/xpcom/src/PyXPCOM.h new file mode 100644 index 00000000000..026cbc516f8 --- /dev/null +++ b/mozilla/extensions/python/xpcom/src/PyXPCOM.h @@ -0,0 +1,557 @@ +/* Copyright (c) 2000-2001 ActiveState Tool Corporation. + See the file LICENSE.txt for licensing information. */ + +// PyXPCOM.h - the main header file for the Python XPCOM support. +// +// This code is part of the XPCOM extensions for Python. +// +// Written May 2000 by Mark Hammond. +// +// Based heavily on the Python COM support, which is +// (c) Mark Hammond and Greg Stein. +// +// (c) 2000, ActiveState corp. + +#ifndef __PYXPCOM_H__ +#define __PYXPCOM_H__ + +#ifdef XP_WIN +# ifdef BUILD_PYXPCOM + /* We are building the main dll */ +# define PYXPCOM_EXPORT __declspec(dllexport) +# else + /* This module uses the dll */ +# define PYXPCOM_EXPORT __declspec(dllimport) +# endif // BUILD_PYXPCOM + + // We need these libs! +# pragma comment(lib, "xpcom.lib") +# pragma comment(lib, "nspr4.lib") + +#else // XP_WIN +# define PYXPCOM_EXPORT +#endif // XP_WIN + + +// An IID we treat as NULL when passing as a reference. +extern nsIID Py_nsIID_NULL; + +/************************************************************************* +************************************************************************** + + Error and exception related function. + +************************************************************************** +*************************************************************************/ + +// The exception object (loaded from the xpcom .py code) +extern PYXPCOM_EXPORT PyObject *PyXPCOM_Error; + +// Client related functions - generally called by interfaces before +// they return NULL back to Python to indicate the error. +// All these functions return NULL so interfaces can generally +// just "return PyXPCOM_BuildPyException(hr, punk, IID_IWhatever)" +PYXPCOM_EXPORT PyObject *PyXPCOM_BuildPyException(nsresult res); + +// Used in gateways to handle the current Python exception +// NOTE: this function assumes it is operating within the Python context +PYXPCOM_EXPORT nsresult PyXPCOM_SetCOMErrorFromPyException(); + +// A couple of logging/error functions. These probably end up +// being written to the console service. + +// Log a warning for the user - something at runtime +// they may care about, but nothing that prevents us actually +// working. +// As it's designed for user error/warning, it exists in non-debug builds. +PYXPCOM_EXPORT void PyXPCOM_LogWarning(const char *fmt, ...); + +// Log an error for the user - something that _has_ prevented +// us working. This is probably accompanied by a traceback. +// As it's designed for user error/warning, it exists in non-debug builds. +PYXPCOM_EXPORT void PyXPCOM_LogError(const char *fmt, ...); + +#ifdef DEBUG +// Mainly designed for developers of the XPCOM package. +// Only enabled in debug builds. +PYXPCOM_EXPORT void PyXPCOM_LogDebug(const char *fmt, ...); +#define PYXPCOM_LOG_DEBUG PyXPCOM_LogDebug +#else +#define PYXPCOM_LOG_DEBUG() +#endif // DEBUG + +/************************************************************************* +************************************************************************** + + Support for CALLING (ie, using) interfaces. + +************************************************************************** +*************************************************************************/ + +class Py_nsISupports; + +typedef Py_nsISupports* (* PyXPCOM_I_CTOR)(nsISupports *, const nsIID &); + +////////////////////////////////////////////////////////////////////////// +// class PyXPCOM_TypeObject +// Base class for (most of) the type objects. + +class PYXPCOM_EXPORT PyXPCOM_TypeObject : public PyTypeObject { +public: + PyXPCOM_TypeObject( + const char *name, + PyXPCOM_TypeObject *pBaseType, + int typeSize, + struct PyMethodDef* methodList, + PyXPCOM_I_CTOR ctor); + ~PyXPCOM_TypeObject(); + + PyMethodChain chain; + PyXPCOM_TypeObject *baseType; + PyXPCOM_I_CTOR ctor; + + static PRBool IsType(PyTypeObject *t); + // Static methods for the Python type. + static void Py_dealloc(PyObject *ob); + static PyObject *Py_repr(PyObject *ob); + static PyObject *Py_str(PyObject *ob); + static PyObject *Py_getattr(PyObject *self, char *name); + static int Py_setattr(PyObject *op, char *name, PyObject *v); + static int Py_cmp(PyObject *ob1, PyObject *ob2); + static long Py_hash(PyObject *self); +}; + +////////////////////////////////////////////////////////////////////////// +// class Py_nsISupports +// This class serves 2 purposes: +// * It is a base class for other interfaces we support "natively" +// * It is instantiated for _all_ other interfaces. +// +// This is different than win32com, where a PyIUnknown only +// ever holds an IUnknown - but here, we could be holding +// _any_ interface. +class PYXPCOM_EXPORT Py_nsISupports : public PyObject +{ +public: + static PRBool Check( PyObject *ob, const nsIID &checkIID = Py_nsIID_NULL) { + Py_nsISupports *self = static_cast(ob); + if (ob==NULL || !PyXPCOM_TypeObject::IsType(ob->ob_type )) + return PR_FALSE; + if (!checkIID.Equals(Py_nsIID_NULL)) + return self->m_iid.Equals(checkIID) != 0; + return PR_TRUE; + } + // Get the nsISupports interface from the PyObject WITH NO REF COUNT ADDED + static nsISupports *GetI(PyObject *self, nsIID *ret_iid = NULL); + nsISupports *m_obj; + nsIID m_iid; + + // Given an nsISupports and an Interface ID, create and return an object + // Does not QI the object - the caller must ensure the nsISupports object + // is really a pointer to an object identified by the IID. + // PRBool bAddRef indicates if a COM reference count should be added to the interface. + // This depends purely on the context in which it is called. If the interface is obtained + // from a function that creates a new ref (eg, ???) then you should use + // FALSE. If you receive the pointer as (eg) a param to a gateway function, then + // you normally need to pass TRUE, as this is truly a new reference. + // *** ALWAYS take the time to get this right. *** + // PRBool bMakeNicePyObject indicates if we should call back into + // Python to wrap the object. This allows Python code to + // see the correct xpcom.client.Interface object even when calling + // xpcom function directly. + static PyObject *PyObjectFromInterface(nsISupports *ps, + const nsIID &iid, + PRBool bAddRef, + PRBool bMakeNicePyObject = PR_TRUE); + + // Given a Python object that is a registered COM type, return a given + // interface pointer on its underlying object, with a NEW REFERENCE ADDED. + // bTryAutoWrap indicates if a Python instance object should attempt to + // be automatically wrapped in an XPCOM object. This is really only + // provided to stop accidental recursion should the object returned by + // the wrap process itself be in instance (where it should already be + // a COM object. + static PRBool InterfaceFromPyObject( + PyObject *ob, + const nsIID &iid, + nsISupports **ppret, + PRBool bNoneOK, + PRBool bTryAutoWrap = PR_TRUE); + + static Py_nsISupports *Constructor(nsISupports *pInitObj, const nsIID &iid); + // The Python methods + static PyObject *QueryInterface(PyObject *self, PyObject *args); + + // Internal (sort-of) objects. + static PyXPCOM_TypeObject *type; + static PyMethodDef methods[]; + static PyObject *mapIIDToType; + static void SafeRelease(Py_nsISupports *ob); + static void RegisterInterface( const nsIID &iid, PyTypeObject *t); + static void InitType(); + + ~Py_nsISupports(); +protected: + // ctor is protected - must create objects via + // PyObjectFromInterface() + Py_nsISupports(nsISupports *p, + const nsIID &iid, + PyTypeObject *type); + + static PyObject *MakeInterfaceResult(PyObject *pyis, const nsIID &iid); + +}; + +// Python/XPCOM IID support +class PYXPCOM_EXPORT Py_nsIID : public PyObject +{ +public: + Py_nsIID(const nsIID &riid); + nsIID m_iid; + + PRBool + IsEqual(const nsIID &riid) { + return m_iid.Equals(riid); + } + + PRBool + IsEqual(PyObject *ob) { + return ob && + ob->ob_type== &type && + m_iid.Equals(((Py_nsIID *)ob)->m_iid); + } + + PRBool + IsEqual(Py_nsIID &iid) { + return m_iid.Equals(iid.m_iid); + } + + static PyObject * + PyObjectFromIID(const nsIID &iid) { + return new Py_nsIID(iid); + } + + static PRBool IIDFromPyObject(PyObject *ob, nsIID *pRet); + /* Python support */ + static PyObject *PyTypeMethod_getattr(PyObject *self, char *name); + static int PyTypeMethod_compare(PyObject *self, PyObject *ob); + static PyObject *PyTypeMethod_repr(PyObject *self); + static long PyTypeMethod_hash(PyObject *self); + static PyObject *PyTypeMethod_str(PyObject *self); + static void PyTypeMethod_dealloc(PyObject *self); + static PyTypeObject type; + static PyMethodDef methods[]; +}; + +/////////////////////////////////////////////////////// +// +// Helper classes for managing arrays of variants. +class PythonTypeDescriptor; // Forward declare. + +class PYXPCOM_EXPORT PyXPCOM_InterfaceVariantHelper { +public: + PyXPCOM_InterfaceVariantHelper(); + ~PyXPCOM_InterfaceVariantHelper(); + PRBool Init(PyObject *obParams); + PRBool FillArray(); + + PyObject *MakePythonResult(); + + nsXPTCVariant *m_var_array; + int m_num_array; +protected: + PyObject *MakeSinglePythonResult(int index); + PRBool FillInVariant(const PythonTypeDescriptor &, int, int); + PRBool PrepareOutVariant(const PythonTypeDescriptor &td, int value_index); + PRBool SetSizeIs( int var_index, PRBool is_arg1, PRUint32 new_size); + PRUint32 GetSizeIs( int var_index, PRBool is_arg1); + + PyObject *m_pyparams; // sequence of actual params passed (ie, not including hidden) + PyObject *m_typedescs; // desc of _all_ params, including hidden. + PythonTypeDescriptor *m_python_type_desc_array; + void **m_buffer_array; + +}; + +/************************************************************************* +************************************************************************** + + Support for IMPLEMENTING interfaces. + +************************************************************************** +*************************************************************************/ +#define NS_IINTERNALPYTHON_IID_STR "AC7459FC-E8AB-4f2e-9C4F-ADDC53393A20" +#define NS_IINTERNALPYTHON_IID \ + { 0xac7459fc, 0xe8ab, 0x4f2e, { 0x9c, 0x4f, 0xad, 0xdc, 0x53, 0x39, 0x3a, 0x20 } } + +class PyXPCOM_GatewayWeakReference; + +// This interface is needed primarily to give us a known vtable base. +// If we QI a Python object for this interface, we can safely cast the result +// to a PyG_Base. Any other interface, we do now know which vtable we will get. +// Later, we may get some internal functions +// (eg, win32com allows us to get the underlying Python object, but +// we should try and avoid that if possible. +class nsIInternalPython : public nsISupports { + public: + NS_DEFINE_STATIC_IID_ACCESSOR(NS_IINTERNALPYTHON_IID) +}; + +// This is roughly equivilent to PyGatewayBase in win32com +// +class PYXPCOM_EXPORT PyG_Base : public nsIInternalPython, public nsISupportsWeakReference +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSISUPPORTSWEAKREFERENCE + + // A static "constructor" - the real ctor is protected. + static nsresult CreateNew(PyObject *pPyInstance, + const nsIID &iid, + void **ppResult); + + // A utility to auto-wrap an arbitary Python instance + // in a COM gateway. + static PRBool AutoWrapPythonInstance(PyObject *ob, + const nsIID &iid, + nsISupports **ppret); + + + // A helper that creates objects to be passed for nsISupports + // objects. See extensive comments in PyG_Base.cpp. + PyObject *MakeInterfaceParam(nsISupports *pis, + const nsIID *piid, + int methodIndex = -1, + const XPTParamDescriptor *d = NULL, + int paramIndex = -1); + + // A helper that ensures all casting and vtable offsetting etc + // done against this object happens in the one spot! + virtual void *ThisAsIID( const nsIID &iid ) = 0; + + // Helpers for "native" interfaces. + // Not used by the generic stub interface. + nsresult HandleNativeGatewayError(const char *szMethodName); + // These data members used by the converter helper functions - hence public + nsIID m_iid; + PyObject * m_pPyObject; + // We keep a reference count on this object, and the object + // itself uses normal refcount rules - thus, it will only + // die when we die, and all external references are removed. + // This means that once we have created it (and while we + // are alive) it will never die. + nsCOMPtr m_pWeakRef; +protected: + PyG_Base(PyObject *instance, const nsIID &iid); + virtual ~PyG_Base(); + PyG_Base *m_pBaseObject; // A chain to implement identity rules. + nsresult InvokeNativeViaPolicy( const char *szMethodName, + PyObject **ppResult = NULL, + const char *szFormat = NULL, + ... + ); + nsresult InvokeNativeViaPolicyInternal( const char *szMethodName, + PyObject **ppResult, + const char *szFormat, + va_list va); + nsresult InvokeNativeGetViaPolicy(const char *szPropertyName, + PyObject **ppResult = NULL + ); + nsresult InvokeNativeSetViaPolicy(const char *szPropertyName, + ...); +}; + +class PYXPCOM_EXPORT PyXPCOM_XPTStub : public PyG_Base, public nsXPTCStubBase +{ +friend class PyG_Base; +public: + NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) \ + {return PyG_Base::QueryInterface(aIID, aInstancePtr);} \ + NS_IMETHOD_(nsrefcnt) AddRef(void) {return PyG_Base::AddRef();} \ + NS_IMETHOD_(nsrefcnt) Release(void) {return PyG_Base::Release();} \ + + NS_IMETHOD GetInterfaceInfo(nsIInterfaceInfo** info); + // call this method and return result + NS_IMETHOD CallMethod(PRUint16 methodIndex, + const nsXPTMethodInfo* info, + nsXPTCMiniVariant* params); + + virtual void *ThisAsIID(const nsIID &iid); +protected: + PyXPCOM_XPTStub(PyObject *instance, const nsIID &iid) : PyG_Base(instance, iid) {;} +private: +}; + +// For the Gateways me manually implement. +#define PYGATEWAY_BASE_SUPPORT(INTERFACE, GATEWAY_BASE) \ + NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) \ + {return PyG_Base::QueryInterface(aIID, aInstancePtr);} \ + NS_IMETHOD_(nsrefcnt) AddRef(void) {return PyG_Base::AddRef();} \ + NS_IMETHOD_(nsrefcnt) Release(void) {return PyG_Base::Release();} \ + virtual void *ThisAsIID(const nsIID &iid) { \ + if (iid.Equals(NS_GET_IID(INTERFACE))) return (INTERFACE *)this; \ + return GATEWAY_BASE::ThisAsIID(iid); \ + } \ + + +// Weak Reference class. This is a true COM object, representing +// a weak reference to a Python object. For each Python XPCOM object, +// there is exactly zero or one corresponding weak reference instance. +// When both are alive, each holds a pointer to the other. When the main +// object dies due to XPCOM reference counting, it zaps the pointer +// in its corresponding weak reference object. Thus, the weak-reference +// can live beyond the object (possibly with a NULL pointer back to the +// "real" object, but as implemented, the weak reference will never be +// destroyed before the object +class PYXPCOM_EXPORT PyXPCOM_GatewayWeakReference : public nsIWeakReference { +public: + PyXPCOM_GatewayWeakReference(PyG_Base *base); + ~PyXPCOM_GatewayWeakReference(); + NS_DECL_ISUPPORTS + NS_DECL_NSIWEAKREFERENCE; + PyG_Base *m_pBase; // NO REF COUNT!!! +}; + + +// Helpers classes for our gateways. +class PYXPCOM_EXPORT PyXPCOM_GatewayVariantHelper +{ +public: + PyXPCOM_GatewayVariantHelper( PyG_Base *gateway, + int methodIndex, + const nsXPTMethodInfo *info, + nsXPTCMiniVariant* params ); + ~PyXPCOM_GatewayVariantHelper(); + PyObject *MakePyArgs(); + nsresult ProcessPythonResult(PyObject *ob); + PyG_Base *m_gateway; +private: + nsresult BackFillVariant( PyObject *ob, int index); + PyObject *MakeSingleParam(int index, PythonTypeDescriptor &td); + PRBool GetIIDForINTERFACE_ID(int index, const nsIID **ppret); + nsresult GetArrayType(PRUint8 index, PRUint8 *ret); + PRUint32 GetSizeIs( int var_index, PRBool is_arg1); + PRBool SetSizeIs( int var_index, PRBool is_arg1, PRUint32 new_size); + PRBool CanSetSizeIs( int var_index, PRBool is_arg1 ); + + + nsXPTCMiniVariant* m_params; + const nsXPTMethodInfo *m_info; + int m_method_index; + PythonTypeDescriptor *m_python_type_desc_array; + int m_num_type_descs; + +}; + +// Misc converters. +PyObject *PyObject_FromXPTTypeDescriptor( const XPTTypeDescriptor *d); +PyObject *PyObject_FromXPTParamDescriptor( const XPTParamDescriptor *d); +PyObject *PyObject_FromXPTMethodDescriptor( const XPTMethodDescriptor *d); +PyObject *PyObject_FromXPTConstant( const XPTConstDescriptor *d); + + +// DLL reference counting functions. +// Although we maintain the count, we never actually +// finalize Python when it hits zero! +void PyXPCOM_DLLAddRef(); +void PyXPCOM_DLLRelease(); + +/************************************************************************* +************************************************************************** + + LOCKING AND THREADING + +************************************************************************** +*************************************************************************/ + +// +// We have 2 discrete locks in use (when no free-threaded is used, anyway). +// The first type of lock is the global Python lock. This is the standard lock +// in use by Python, and must be used as documented by Python. Specifically, no +// 2 threads may _ever_ call _any_ Python code (including INCREF/DECREF) without +// first having this thread lock. +// +// The second type of lock is a "global framework lock", and used whenever 2 threads +// of C code need access to global data. This is different than the Python +// lock - this lock is used when no Python code can ever be called by the +// threads, but the C code still needs thread-safety. + +// We also supply helper classes which make the usage of these locks a one-liner. + +// The "framework" lock, implemented as a PRLock +PYXPCOM_EXPORT void PyXPCOM_AcquireGlobalLock(void); +PYXPCOM_EXPORT void PyXPCOM_ReleaseGlobalLock(void); + +// Helper class for the DLL global lock. +// +// This class magically waits for PyXPCOM framework global lock, and releases it +// when finished. +// NEVER new one of these objects - only use on the stack! +class CEnterLeaveXPCOMFramework { +public: + CEnterLeaveXPCOMFramework() {PyXPCOM_AcquireGlobalLock();} + ~CEnterLeaveXPCOMFramework() {PyXPCOM_ReleaseGlobalLock();} +}; + +// Python thread-lock stuff. Free-threading patches use different semantics, but +// these are abstracted away here... +//#include + +// Helper class for Enter/Leave Python +// +// This class magically waits for the Python global lock, and releases it +// when finished. + +// Nested invocations will deadlock, so be careful. + +// NEVER new one of these objects - only use on the stack! + +extern PYXPCOM_EXPORT PyInterpreterState *PyXPCOM_InterpreterState; +extern PYXPCOM_EXPORT PRBool PyXPCOM_ThreadState_Ensure(); +extern PYXPCOM_EXPORT void PyXPCOM_ThreadState_Free(); +extern PYXPCOM_EXPORT void PyXPCOM_ThreadState_Clear(); +extern PYXPCOM_EXPORT void PyXPCOM_InterpreterLock_Acquire(); +extern PYXPCOM_EXPORT void PyXPCOM_InterpreterLock_Release(); +extern PYXPCOM_EXPORT void PyXPCOM_MakePendingCalls(); + +extern PYXPCOM_EXPORT PRBool PyXPCOM_Globals_Ensure(); + +class CEnterLeavePython { +public: + CEnterLeavePython() { + created = PyXPCOM_ThreadState_Ensure(); + PyXPCOM_InterpreterLock_Acquire(); + if (created) { + // If pending python calls are waiting as we enter Python, + // it will generally mean an asynch signal handler, etc. + // We can either call it here, or wait for Python to call it + // as part of its "even 'n' opcodes" check. If we wait for + // Python to check it and the pending call raises an exception, + // then it is _our_ code that will fail - this is unfair, + // as the signal was raised before we were entered - indeed, + // we may be directly responding to the signal! + // Thus, we flush all the pending calls here, and report any + // exceptions via our normal exception reporting mechanism. + // We can then execute our code in the knowledge that only + // signals raised _while_ we are executing will cause exceptions. + PyXPCOM_MakePendingCalls(); + } + } + ~CEnterLeavePython() { + // The interpreter state must be cleared + // _before_ we release the lock, as some of + // the sys. attributes cleared (eg, the current exception) + // may need the lock to invoke their destructors - + // specifically, when exc_value is a class instance, and + // the exception holds the last reference! + if ( created ) + PyXPCOM_ThreadState_Clear(); + PyXPCOM_InterpreterLock_Release(); + if ( created ) + PyXPCOM_ThreadState_Free(); + } +private: + PRBool created; +}; + +#endif // __PYXPCOM_H__ diff --git a/mozilla/extensions/python/xpcom/src/PyXPCOM_std.h b/mozilla/extensions/python/xpcom/src/PyXPCOM_std.h new file mode 100644 index 00000000000..dc063eb10d8 --- /dev/null +++ b/mozilla/extensions/python/xpcom/src/PyXPCOM_std.h @@ -0,0 +1,49 @@ +/* Copyright (c) 2000-2001 ActiveState Tool Corporation. + See the file LICENSE.txt for licensing information. */ + +// standard include - sets up all the defines used by +// the mozilla make process - too lazy to work out how to integrate +// with their make, so this will do! + +// +// This code is part of the XPCOM extensions for Python. +// +// Written May 2000 by Mark Hammond. +// +// Based heavily on the Python COM support, which is +// (c) Mark Hammond and Greg Stein. +// +// (c) 2000, ActiveState corp. + +// Main Mozilla cross-platform declarations. +#include "xp_core.h" + +#ifdef _DEBUG +# ifndef DEBUG +# define DEBUG +# endif + +# define DEVELOPER_DEBUG +# define NS_DEBUG +# define DEBUG_markh +#endif // DEBUG + +#include +#include +#include +#include +#include +#include + +// This header is considered internal - hence +// we can use it to trigger "exports" +#define BUILD_PYXPCOM + +#ifdef HAVE_LONG_LONG + // Mozilla also defines this - we undefine it to + // prevent a compiler warning. +# undef HAVE_LONG_LONG +#endif // HAVE_LONG_LONG + +#include "Python.h" +#include "PyXPCOM.h" diff --git a/mozilla/extensions/python/xpcom/src/Pyxpt_info.cpp b/mozilla/extensions/python/xpcom/src/Pyxpt_info.cpp new file mode 100644 index 00000000000..bf6509c0a58 --- /dev/null +++ b/mozilla/extensions/python/xpcom/src/Pyxpt_info.cpp @@ -0,0 +1,136 @@ +/* Copyright (c) 2000-2001 ActiveState Tool Corporation. + See the file LICENSE.txt for licensing information. */ + +// Pyxpt_info.cpp - wrappers for the xpt_info objects. +// +// This code is part of the XPCOM extensions for Python. +// +// Written May 2000 by Mark Hammond. +// +// Based heavily on the Python COM support, which is +// (c) Mark Hammond and Greg Stein. +// +// (c) 2000, ActiveState corp. +#include "PyXPCOM_std.h" + +PyObject *PyObject_FromXPTTypeDescriptor( const XPTTypeDescriptor *d) +{ + if (d==nsnull) { + Py_INCREF(Py_None); + return Py_None; + } + return Py_BuildValue("bbbh", + d->prefix.flags, + d->argnum, + d->argnum2, + d->type.iface // this is actually a union! + ); +} + +PyObject *PyObject_FromXPTParamDescriptor( const XPTParamDescriptor *d) +{ + if (d==nsnull) { + Py_INCREF(Py_None); + return Py_None; + } + PyObject *ob = PyObject_FromXPTTypeDescriptor(&d->type); + PyObject *ret = Py_BuildValue("bO", d->flags, ob); + Py_DECREF(ob); + return ret; +} + +PyObject *PyObject_FromXPTMethodDescriptor( const XPTMethodDescriptor *d) +{ + if (d==nsnull) { + Py_INCREF(Py_None); + return Py_None; + } + PyObject *ob_params = PyTuple_New(d->num_args); + if (ob_params==NULL) + return NULL; + for (int i=0;inum_args;i++) + PyTuple_SET_ITEM(ob_params, i, PyObject_FromXPTParamDescriptor(d->params+i)); + PyObject *ob_ret = PyObject_FromXPTParamDescriptor(d->result); + PyObject *ret = Py_BuildValue("bsOO", d->flags, d->name, ob_params, ob_ret); + Py_XDECREF(ob_ret); + Py_XDECREF(ob_params); + return ret; +} + +PyObject *PyObject_FromXPTConstant( const XPTConstDescriptor *c) +{ + if (c==nsnull) { + Py_INCREF(Py_None); + return Py_None; + } + PyObject *ob_type = PyObject_FromXPTTypeDescriptor(&c->type); + if (ob_type==NULL) + return NULL; + PyObject *v = NULL; + switch (c->type.prefix.flags) { + case TD_INT8: + v = PyInt_FromLong( c->value.i8 ); + break; + case TD_INT16: + v = PyInt_FromLong( c->value.i16 ); + break; + case TD_INT32: + v = PyInt_FromLong( c->value.i32 ); + break; + case TD_INT64: + v = PyLong_FromLongLong(c->value.i64); + break; + case TD_UINT8: + v = PyInt_FromLong( c->value.ui8 ); + break; + case TD_UINT16: + v = PyInt_FromLong( c->value.ui16 ); + break; + case TD_UINT32: + v = PyInt_FromLong( c->value.ui8 ); + break; + case TD_UINT64: + v = PyLong_FromUnsignedLongLong(c->value.ui64); + break; + case TD_FLOAT: + v = PyFloat_FromDouble(c->value.flt); + break; + case TD_DOUBLE: + v = PyFloat_FromDouble(c->value.dbl); + break; + case TD_BOOL: + v = c->value.bul ? Py_True : Py_False; + Py_INCREF(v); + break; + case TD_CHAR: + v = PyString_FromStringAndSize(&c->value.ch, 1); + break; + case TD_WCHAR: + v = PyUnicode_FromUnicode(&c->value.wch, 1); + break; + // TD_VOID = 13, + case TD_PNSIID: + v = Py_nsIID::PyObjectFromIID(*c->value.iid); + break; + // TD_PBSTR = 15, + case TD_PSTRING: + v = PyString_FromString(c->value.str); + break; + case TD_PWSTRING: + v = PyUnicode_FromUnicode(c->value.wstr, nsCRT::strlen(c->value.wstr)); + break; + // TD_INTERFACE_TYPE = 18, + // TD_INTERFACE_IS_TYPE = 19, + // TD_ARRAY = 20, + // TD_PSTRING_SIZE_IS = 21, + // TD_PWSTRING_SIZE_IS = 22 + default: + v = PyString_FromString("Unknown type code!!"); + break; + + } + PyObject *ret = Py_BuildValue("sbO", c->name, ob_type, v); + Py_DECREF(ob_type); + Py_DECREF(v); + return ret; +} diff --git a/mozilla/extensions/python/xpcom/src/TypeObject.cpp b/mozilla/extensions/python/xpcom/src/TypeObject.cpp new file mode 100644 index 00000000000..bf8ced9bcdf --- /dev/null +++ b/mozilla/extensions/python/xpcom/src/TypeObject.cpp @@ -0,0 +1,194 @@ +/* Copyright (c) 2000-2001 ActiveState Tool Corporation. + See the file LICENSE.txt for licensing information. */ + +// +// This code is part of the XPCOM extensions for Python. +// +// Written May 2000 by Mark Hammond. +// +// Based heavily on the Python COM support, which is +// (c) Mark Hammond and Greg Stein. +// +// (c) 2000, ActiveState corp. + +#include "PyXPCOM_std.h" +#include +#include + + +static PyTypeObject PyInterfaceType_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /* Number of items for varobject */ + "interface-type", /* Name of this type */ + sizeof(PyTypeObject), /* Basic object size */ + 0, /* Item size for varobject */ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + PyType_Type.tp_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + PyType_Type.tp_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_xxx1*/ + 0, /*tp_xxx2*/ + 0, /*tp_xxx3*/ + 0, /*tp_xxx4*/ + "Define the behavior of a PythonCOM Interface type.", +}; + +/*static*/ PRBool +PyXPCOM_TypeObject::IsType(PyTypeObject *t) +{ + return t->ob_type == &PyInterfaceType_Type; +} + +//////////////////////////////////////////////////////////////////// +// +// The type methods +// +/*static*/PyObject * +PyXPCOM_TypeObject::Py_getattr(PyObject *self, char *name) +{ + if (strcmp(name, "IID")==0) + return Py_nsIID::PyObjectFromIID( ((Py_nsISupports *)self)->m_iid ); + + PyXPCOM_TypeObject *this_type = (PyXPCOM_TypeObject *)self->ob_type; + return Py_FindMethodInChain(&this_type->chain, self, name); +} + +/*static*/int +PyXPCOM_TypeObject::Py_setattr(PyObject *op, char *name, PyObject *v) +{ + char buf[128]; + sprintf(buf, "%s has read-only attributes", op->ob_type->tp_name ); + PyErr_SetString(PyExc_TypeError, buf); + return -1; +} + +// @pymethod int|Py_nsISupports|__cmp__|Implements XPCOM rules for object identity. +/*static*/int +PyXPCOM_TypeObject::Py_cmp(PyObject *self, PyObject *other) +{ + // @comm NOTE: Copied from COM - have not confirmed these rules are true for XPCOM + // @comm As per the XPCOM rules for object identity, both objects are queried for nsISupports, and these values compared. + // The only meaningful test is for equality - the result of other comparisons is undefined + // (ie, determined by the object's relative addresses in memory. + nsISupports *pUnkOther; + nsISupports *pUnkThis; + if (!Py_nsISupports::InterfaceFromPyObject(self, NS_GET_IID(nsISupports), &pUnkThis, PR_FALSE)) + return -1; + if (!Py_nsISupports::InterfaceFromPyObject(other, NS_GET_IID(nsISupports), &pUnkOther, PR_FALSE)) { + pUnkThis->Release(); + return -1; + } + int rc = pUnkThis==pUnkOther ? 0 : + (pUnkThis < pUnkOther ? -1 : 1); + pUnkThis->Release(); + pUnkOther->Release(); + return rc; +} + +// @pymethod int|Py_nsISupports|__hash__|Implement a hash-code for the XPCOM object using XPCOM identity rules. +/*static*/long PyXPCOM_TypeObject::Py_hash(PyObject *self) +{ + // We always return the value of the nsISupports *. + nsISupports *pUnkThis; + if (!Py_nsISupports::InterfaceFromPyObject(self, NS_GET_IID(nsISupports), &pUnkThis, PR_FALSE)) + return -1; + long ret = _Py_HashPointer(pUnkThis); + pUnkThis->Release(); + return ret; +} + +// @method string|Py_nsISupports|__repr__|Called to create a representation of a Py_nsISupports object +/*static */PyObject * +PyXPCOM_TypeObject::Py_repr(PyObject *self) +{ + // @comm The repr of this object displays both the object's address, and its attached nsISupports's address + Py_nsISupports *pis = (Py_nsISupports *)self; + // Try and get the IID name. + char *iid_repr; + nsCOMPtr iim = XPTI_GetInterfaceInfoManager(); + if (iim!=nsnull) + iim->GetNameForIID(&pis->m_iid, &iid_repr); + if (iid_repr==nsnull) + // no IIM available, or it doesnt know the name. + iid_repr = pis->m_iid.ToString(); + // XXX - need some sort of buffer overflow. + char buf[512]; + sprintf(buf, "", iid_repr, self, pis->m_obj); + nsAllocator::Free(iid_repr); + return PyString_FromString(buf); +} + +/*static */PyObject * +PyXPCOM_TypeObject::Py_str(PyObject *self) +{ + Py_nsISupports *pis = (Py_nsISupports *)self; + nsresult rv; + char *val = NULL; + Py_BEGIN_ALLOW_THREADS; + { // scope to kill pointer while thread-lock released. + nsCOMPtr ss( do_QueryInterface(pis->m_obj, &rv )); + if (NS_SUCCEEDED(rv)) + rv = ss->ToString(&val); + } // end-scope + Py_END_ALLOW_THREADS; + PyObject *ret; + if (NS_FAILED(rv)) + ret = Py_repr(self); + else + ret = PyString_FromString(val); + if (val) nsAllocator::Free(val); + return ret; +} + +/* static */void +PyXPCOM_TypeObject::Py_dealloc(PyObject *self) +{ + delete (Py_nsISupports *)self; +} + +PyXPCOM_TypeObject::PyXPCOM_TypeObject( const char *name, PyXPCOM_TypeObject *pBase, int typeSize, struct PyMethodDef* methodList, PyXPCOM_I_CTOR thector) +{ + static const PyTypeObject type_template = { + PyObject_HEAD_INIT(&PyInterfaceType_Type) + 0, /*ob_size*/ + "XPCOMTypeTemplate", /*tp_name*/ + sizeof(Py_nsISupports), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + Py_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + Py_getattr, /* tp_getattr */ + Py_setattr, /* tp_setattr */ + Py_cmp, /* tp_compare */ + Py_repr, /* tp_repr */ + 0, /* tp_as_number*/ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + Py_hash, /* tp_hash */ + 0, /* tp_call */ + Py_str, /* tp_str */ + }; + + *((PyTypeObject *)this) = type_template; + + chain.methods = methodList; + chain.link = pBase ? &pBase->chain : NULL; + + baseType = pBase; + ctor = thector; + + // cast away const, as Python doesnt use it. + tp_name = (char *)name; + tp_basicsize = typeSize; +} + +PyXPCOM_TypeObject::~PyXPCOM_TypeObject() +{ +} diff --git a/mozilla/extensions/python/xpcom/src/VariantUtils.cpp b/mozilla/extensions/python/xpcom/src/VariantUtils.cpp new file mode 100644 index 00000000000..8e6f34df541 --- /dev/null +++ b/mozilla/extensions/python/xpcom/src/VariantUtils.cpp @@ -0,0 +1,2092 @@ +/* Copyright (c) 2000-2001 ActiveState Tool Corporation. + See the file LICENSE.txt for licensing information. */ + +// +// This code is part of the XPCOM extensions for Python. +// +// Written May 2000 by Mark Hammond. +// +// Based heavily on the Python COM support, which is +// (c) Mark Hammond and Greg Stein. +// +// (c) 2000, ActiveState corp. + +#include "PyXPCOM_std.h" +#include +#include +#include +#include +#include + +#define BREAK_FALSE {rc=PR_FALSE;break;} + +class PythonTypeDescriptor { +public: + PythonTypeDescriptor() { + param_flags = type_flags = argnum = argnum2 = 0; + extra = NULL; + is_auto_out = PR_FALSE; + is_auto_in = PR_FALSE; + have_set_auto = PR_FALSE; + } + ~PythonTypeDescriptor() { + Py_XDECREF(extra); + } + PRUint8 param_flags; + PRUint8 type_flags; + PRUint8 argnum; /* used for iid_is and size_is */ + PRUint8 argnum2; /* used for length_is */ + PyObject *extra; // The IID object, or the type of the array. + // Extra items to help our processing. + // Is this auto-filled by some other "in" param? + PRBool is_auto_in; + // Is this auto-filled by some other "out" param? + PRBool is_auto_out; + // If is_auto_out, have I already filled it? Used when multiple + // params share a size_is fields - first time sets it, subsequent + // time check it. + PRBool have_set_auto; +}; + +static int ProcessPythonTypeDescriptors(PythonTypeDescriptor *pdescs, int num) +{ + // Loop over the array, checking all the params marked as having an arg. + // If these args nominate another arg as the size_is param, then + // we reset the size_is param to _not_ requiring an arg. + int i; + for (i=0;iRelease(); + Py_END_ALLOW_THREADS; + } + break; + default: + break; // nothing to do! + } +} + +#define FILL_SIMPLE_POINTER( type, val ) *((type *)pthis) = (type)(val); + +PRBool FillSingleArray(void *array_ptr, PyObject *sequence_ob, PRUint32 sequence_size, PRUint32 array_element_size, PRUint8 array_type) +{ + PRUint8 *pthis = (PRUint8 *)array_ptr; + NS_ABORT_IF_FALSE(pthis, "Don't have a valid array to fill!"); + PRBool rc = PR_TRUE; + // We handle T_U8 specially as a string. + // If it is NOT a string, we just fall through and allow the standard + // sequence unpack code process it (just slower!) + if ( (array_type & XPT_TDP_TAGMASK) == nsXPTType::T_U8 && PyString_Check(sequence_ob)) { + nsCRT::memcpy(pthis, PyString_AS_STRING(sequence_ob), sequence_size); + return PR_TRUE; + } + + for (PRUint32 i=0; rc && iRelease(); + Py_END_ALLOW_THREADS; + } + *pp = pnew; // ref-count added by InterfaceFromPyObject + break; + } + default: + // try and limp along in this case. + // leave rc TRUE + PyXPCOM_LogWarning("Converting Python object for an array element - The object type (0x%x) is unknown - leaving param alone!\n", array_type); + break; + } + Py_XDECREF(val_use); + Py_DECREF(val); + } + return rc; +} + +PyObject *UnpackSingleArray(void *array_ptr, PRUint32 sequence_size, PRUint32 array_element_size, PRUint8 array_type) +{ + if (array_ptr==NULL) { + Py_INCREF(Py_None); + return Py_None; + } + if ((array_type & XPT_TDP_TAGMASK) == nsXPTType::T_U8) + return PyString_FromStringAndSize( (char *)array_ptr, sequence_size ); + + PyObject *list_ret = PyList_New(sequence_size); + PRUint8 *pthis = (PRUint8 *)array_ptr; + for (PRUint32 i=0; iRelease(); + Py_END_ALLOW_THREADS; + } + } + if (ns_v.IsValDOMString() && ns_v.val.p) { + PythonTypeDescriptor &ptd = m_python_type_desc_array[i]; + if (XPT_PD_IS_OUT(ptd.param_flags) || XPT_PD_IS_DIPPER(ptd.param_flags)) + delete (nsAReadableString *)ns_v.val.p; + } + if (ns_v.IsValArray()) { + nsXPTCVariant &ns_v = m_var_array[i]; + if (ns_v.val.p) { + PRUint8 array_type = (PRUint8)PyInt_AsLong(m_python_type_desc_array[i].extra); + PRUint32 seq_size = GetSizeIs(i, PR_FALSE); + FreeSingleArray(ns_v.val.p, seq_size, array_type); + } + } + // IsOwned must be the last check of the loop, as + // this frees the underlying data used above (eg, by the array free process) + if (ns_v.IsValAllocated() && !ns_v.IsValInterface() && !ns_v.IsValDOMString()) { + NS_ABORT_IF_FALSE(ns_v.IsPtrData(), "expecting a pointer to free"); + nsAllocator::Free(ns_v.val.p); + } + } + if (m_buffer_array && m_buffer_array[i]) + nsAllocator::Free(m_buffer_array[i]); + } + delete [] m_python_type_desc_array; + delete [] m_buffer_array; + delete [] m_var_array; +} + +PRBool PyXPCOM_InterfaceVariantHelper::Init(PyObject *obParams) +{ + PRBool ok = PR_FALSE; + int i; + int total_params_needed = 0; + if (!PySequence_Check(obParams) || PySequence_Length(obParams)!=2) { + PyErr_Format(PyExc_TypeError, "Param descriptors must be a sequence of exactly length 2"); + return PR_FALSE; + } + PyObject *typedescs = PySequence_GetItem(obParams, 0); + if (typedescs==NULL) + return PR_FALSE; + // NOTE: The length of the typedescs may be different than the + // args actually passed. The typedescs always include all + // hidden params (such as "size_is"), while the actual + // args never include this. + m_num_array = PySequence_Length(typedescs); + if (PyErr_Occurred()) goto done; + + m_pyparams = PySequence_GetItem(obParams, 1); + if (m_pyparams==NULL) goto done; + + m_python_type_desc_array = new PythonTypeDescriptor[m_num_array]; + if (!m_python_type_desc_array) goto done; + + // Pull apart the type descs and stash them. + for (i=0;iLength() ); + CopyUnicodeTo(*rs, 0, PyUnicode_AsUnicode(ret), rs->Length()); + } + break; + } + case nsXPTType::T_CHAR_STR: + if (*((char **)ns_v.ptr) == NULL) { + ret = Py_None; + Py_INCREF(Py_None); + } else + ret = PyString_FromString( *((char **)ns_v.ptr) ); + break; + + case nsXPTType::T_WCHAR_STR: { + PRUnichar *us = *((PRUnichar **)ns_v.ptr); + if (us == NULL) { + ret = Py_None; + Py_INCREF(Py_None); + } else + ret = PyUnicode_FromUnicode( us, nsCRT::strlen(us)); + break; + } + case nsXPTType::T_INTERFACE: { + nsIID iid; + if (!Py_nsIID::IIDFromPyObject(td.extra, &iid)) + break; + nsISupports *iret = *((nsISupports **)ns_v.ptr); + // We _do_ add a reference here, as our cleanup code will + // remove this reference should we own it. + ret = Py_nsISupports::PyObjectFromInterface(iret, iid, PR_TRUE); + break; + } + case nsXPTType::T_INTERFACE_IS: { + nsIID iid; + nsXPTCVariant &ns_viid = m_var_array[td.argnum]; + NS_WARN_IF_FALSE(XPT_TDP_TAG(ns_viid.type)==nsXPTType::T_IID, "The INTERFACE_IS iid describer isnt an IID!"); + if (XPT_TDP_TAG(ns_viid.type)==nsXPTType::T_IID) { + nsIID *piid = (nsIID *)ns_viid.val.p; + if (piid==NULL) + // Also serious, but like below, not our fault! + iid = NS_GET_IID(nsISupports); + else + iid = *piid; + } else + // This is a pretty serious problem, but not Python's fault! + // Just return an nsISupports and hope the caller does whatever + // QI they need before using it. + iid = NS_GET_IID(nsISupports); + nsISupports *iret = *((nsISupports **)ns_v.ptr); + // We _do_ add a reference here, as our cleanup code will + // remove this reference should we own it. + ret = Py_nsISupports::PyObjectFromInterface(iret, iid, PR_TRUE); + break; + } + case nsXPTType::T_ARRAY: { + if ( (* ((void **)ns_v.ptr)) == NULL) { + ret = Py_None; + Py_INCREF(Py_None); + } + if (!PyInt_Check(td.extra)) { + PyErr_SetString(PyExc_TypeError, "The array info is not valid"); + break; + } + PRUint8 array_type = (PRUint8)PyInt_AsLong(td.extra); + PRUint32 element_size = GetArrayElementSize(array_type); + PRUint32 seq_size = GetSizeIs(index, PR_FALSE); + ret = UnpackSingleArray(* ((void **)ns_v.ptr), seq_size, element_size, array_type); + break; + } + + case nsXPTType::T_PSTRING_SIZE_IS: + if (*((char **)ns_v.ptr) == NULL) { + ret = Py_None; + Py_INCREF(Py_None); + } else { + PRUint32 string_size = GetSizeIs(index, PR_TRUE); + ret = PyString_FromStringAndSize( *((char **)ns_v.ptr), string_size ); + } + break; + + case nsXPTType::T_PWSTRING_SIZE_IS: + if (*((PRUnichar **)ns_v.ptr) == NULL) { + ret = Py_None; + Py_INCREF(Py_None); + } else { + PRUint32 string_size = GetSizeIs(index, PR_TRUE); + ret = PyUnicode_FromUnicode( *((PRUnichar **)ns_v.ptr), string_size ); + } + break; + default: + PyErr_Format(PyExc_ValueError, "Unknown XPCOM type code (0x%x)", XPT_TDP_TAG(ns_v.type)); + /* ret remains nsnull */ + break; + } + return ret; +} + + +PyObject *PyXPCOM_InterfaceVariantHelper::MakePythonResult() +{ + // First we count the results. + int i = 0; + int n_results = 0; + PyObject *ret = NULL; + PRBool have_retval = PR_FALSE; + for (i=0;i 1) { + ret = PyTuple_New(n_results); + if (ret==NULL) + return NULL; + } + int ret_index = 0; + int max_index = m_num_array; + // Stick the retval at the front if we have have + if (have_retval && n_results > 1) { + PyObject *val = MakeSinglePythonResult(m_num_array-1); + if (val==NULL) { + Py_DECREF(ret); + return NULL; + } + PyTuple_SET_ITEM(ret, 0, val); + max_index--; + ret_index++; + + } + for (i=0;ret_index < n_results && i < max_index;i++) { + if (!m_python_type_desc_array[i].is_auto_out) { + if (XPT_PD_IS_OUT(m_python_type_desc_array[i].param_flags) || XPT_PD_IS_DIPPER(m_python_type_desc_array[i].param_flags)) { + PyObject *val = MakeSinglePythonResult(i); + if (val==NULL) { + Py_XDECREF(ret); + return NULL; + } + if (n_results > 1) { + PyTuple_SET_ITEM(ret, ret_index, val); + ret_index++; + } else { + NS_ABORT_IF_FALSE(ret==NULL, "shouldnt already have a ret!"); + ret = val; + } + } + } + } + + } + return ret; +} + +/************************************************************************* +************************************************************************** + + Helpers when IMPLEMENTING interfaces. + +************************************************************************** +*************************************************************************/ + +PyXPCOM_GatewayVariantHelper::PyXPCOM_GatewayVariantHelper( PyG_Base *gw, int method_index, const nsXPTMethodInfo *info, nsXPTCMiniVariant* params ) +{ + m_params = params; + m_info = info; + // no references added - this class is only alive for + // a single gateway invocation + m_gateway = gw; + m_method_index = method_index; + m_python_type_desc_array = NULL; + m_num_type_descs = 0; +} + +PyXPCOM_GatewayVariantHelper::~PyXPCOM_GatewayVariantHelper() +{ + delete [] m_python_type_desc_array; +} + +PyObject *PyXPCOM_GatewayVariantHelper::MakePyArgs() +{ + NS_PRECONDITION(sizeof(XPTParamDescriptor) == sizeof(nsXPTParamInfo), "We depend on nsXPTParamInfo being a wrapper over the XPTParamDescriptor struct"); + // Setup our array of Python typedescs, and determine the number of objects we + // pass to Python. + m_num_type_descs = m_info->num_args; + m_python_type_desc_array = new PythonTypeDescriptor[m_num_type_descs]; + if (m_python_type_desc_array==nsnull) + return PyErr_NoMemory(); + + // First loop to count the number of objects + // we pass to Python + int i; + for (i=0;inum_args;i++) { + nsXPTParamInfo *pi = (nsXPTParamInfo *)m_info->params+i; + PythonTypeDescriptor &td = m_python_type_desc_array[i]; + td.param_flags = pi->flags; + td.type_flags = pi->type.prefix.flags; + td.argnum = pi->type.argnum; + td.argnum2 = pi->type.argnum2; + } + int num_args = ProcessPythonTypeDescriptors(m_python_type_desc_array, m_num_type_descs); + PyObject *ret = PyTuple_New(num_args); + if (ret==NULL) + return NULL; + int this_arg = 0; + for (i=0;i=0 && this_arg= m_num_type_descs) { + PyErr_SetString(PyExc_ValueError, "dont have a valid size_is indicator for this param"); + return PR_FALSE; + } + PRBool is_out = XPT_PD_IS_OUT(m_python_type_desc_array[argnum].param_flags); + nsXPTCMiniVariant &ns_v = m_params[argnum]; + NS_ABORT_IF_FALSE( (m_python_type_desc_array[argnum].type_flags & XPT_TDP_TAGMASK) == nsXPTType::T_U32, "size param must be Uint32"); + return is_out ? *((PRUint32 *)ns_v.val.p) : ns_v.val.u32; +} + +#undef DEREF_IN_OR_OUT +#define DEREF_IN_OR_OUT( element, ret_type ) (ret_type)(is_out ? *((ret_type *)ns_v.val.p) : (ret_type)(element)) + +PyObject *PyXPCOM_GatewayVariantHelper::MakeSingleParam(int index, PythonTypeDescriptor &td) +{ + NS_PRECONDITION(XPT_PD_IS_IN(td.param_flags), "Must be an [in] param!"); + nsXPTCMiniVariant &ns_v = m_params[index]; + PyObject *ret = NULL; + PRBool is_out = XPT_PD_IS_OUT(td.param_flags); + + switch (td.type_flags & XPT_TDP_TAGMASK) { + case nsXPTType::T_I8: + ret = PyInt_FromLong( DEREF_IN_OR_OUT(ns_v.val.i8, PRInt8 ) ); + break; + case nsXPTType::T_I16: + ret = PyInt_FromLong( DEREF_IN_OR_OUT(ns_v.val.i16, PRInt16) ); + break; + case nsXPTType::T_I32: + ret = PyInt_FromLong( DEREF_IN_OR_OUT(ns_v.val.i32, PRInt32) ); + break; + case nsXPTType::T_I64: + ret = PyLong_FromLongLong( DEREF_IN_OR_OUT(ns_v.val.i64, PRInt64) ); + break; + case nsXPTType::T_U8: + ret = PyInt_FromLong( DEREF_IN_OR_OUT(ns_v.val.u8, PRUint8) ); + break; + case nsXPTType::T_U16: + ret = PyInt_FromLong( DEREF_IN_OR_OUT(ns_v.val.u16, PRUint16) ); + break; + case nsXPTType::T_U32: + ret = PyInt_FromLong( DEREF_IN_OR_OUT(ns_v.val.u32, PRUint32) ); + break; + case nsXPTType::T_U64: + ret = PyLong_FromUnsignedLongLong( DEREF_IN_OR_OUT(ns_v.val.u64, PRUint64) ); + break; + case nsXPTType::T_FLOAT: + ret = PyFloat_FromDouble( DEREF_IN_OR_OUT(ns_v.val.f, float) ); + break; + case nsXPTType::T_DOUBLE: + ret = PyFloat_FromDouble( DEREF_IN_OR_OUT(ns_v.val.d, double) ); + break; + case nsXPTType::T_BOOL: { + PRBool temp = DEREF_IN_OR_OUT(ns_v.val.b, PRBool); + ret = temp ? Py_True : Py_False; + Py_INCREF(ret); + break; + } + case nsXPTType::T_CHAR: { + char temp = DEREF_IN_OR_OUT(ns_v.val.c, char); + ret = PyString_FromStringAndSize(&temp, 1); + break; + } + case nsXPTType::T_WCHAR: { + wchar_t temp = (wchar_t)DEREF_IN_OR_OUT(ns_v.val.wc, PRUint16); + ret = PyUnicode_FromWideChar(&temp, 1); + break; + } +// case nsXPTType::T_VOID: + case nsXPTType::T_IID: { + ret = Py_nsIID::PyObjectFromIID( * DEREF_IN_OR_OUT(ns_v.val.p, const nsIID *) ); + break; + } + case nsXPTType::T_DOMSTRING: { + NS_ABORT_IF_FALSE(is_out || !XPT_PD_IS_DIPPER(td.param_flags), "DOMStrings can't be inout"); + nsAReadableString *rs = (nsAReadableString *)ns_v.val.p; + if (rs==NULL) { + ret = Py_None; + Py_INCREF(Py_None); + } else { + ret = PyUnicode_FromUnicode( NULL, rs->Length() ); + CopyUnicodeTo(*rs, 0, PyUnicode_AsUnicode(ret), rs->Length()); + } + break; + } + case nsXPTType::T_CHAR_STR: { + char *t = DEREF_IN_OR_OUT(ns_v.val.p, char *); + if (t==NULL) { + ret = Py_None; + Py_INCREF(Py_None); + } else + ret = PyString_FromString(t); + break; + } + + case nsXPTType::T_WCHAR_STR: { + PRUnichar *us = DEREF_IN_OR_OUT(ns_v.val.p, PRUnichar *); + if (us==NULL) { + ret = Py_None; + Py_INCREF(Py_None); + } else + ret = PyUnicode_FromUnicode( us, nsCRT::strlen(us)); + break; + } + case nsXPTType::T_INTERFACE_IS: // our Python code does it :-) + case nsXPTType::T_INTERFACE: { + nsISupports *iret = DEREF_IN_OR_OUT(ns_v.val.p, nsISupports *); + nsXPTParamInfo *pi = (nsXPTParamInfo *)m_info->params+index; + ret = m_gateway->MakeInterfaceParam(iret, NULL, m_method_index, pi, index); + break; + } +/*** + nsISupports *iret = DEREF_IN_OR_OUT(ns_v.val.p, nsISupports *); + nsXPTParamInfo *pi = (nsXPTParamInfo *)m_info->params+index; + nsXPTCMiniVariant &ns_viid = m_params[td.argnum]; + NS_ABORT_IF_FALSE((m_python_type_desc_array[td.argnum].type_flags & XPT_TDP_TAGMASK) == nsXPTType::T_IID, "The INTERFACE_IS iid describer isnt an IID!"); + const nsIID * iid = NULL; + if (XPT_PD_IS_IN(m_python_type_desc_array[td.argnum].param_flags)) + // may still be inout! + iid = DEREF_IN_OR_OUT(ns_v.val.p, const nsIID *); + + ret = m_gateway->MakeInterfaceParam(iret, iid, m_method_index, pi, index); + break; + } +****/ + case nsXPTType::T_ARRAY: { + void *t = DEREF_IN_OR_OUT(ns_v.val.p, void *); + PRUint32 seq_size = GetSizeIs(index, PR_FALSE); + if (t==NULL) { + ret = Py_None; + Py_INCREF(Py_None); + } else { + PRUint8 array_type; + nsresult ns = GetArrayType(index, &array_type); + if (NS_FAILED(ns)) { + PyXPCOM_BuildPyException(ns); + break; + } + PRUint32 element_size = GetArrayElementSize(array_type); + PRUint32 seq_size = GetSizeIs(index, PR_FALSE); + ret = UnpackSingleArray(t, seq_size, element_size, array_type); + } + break; + } + case nsXPTType::T_PSTRING_SIZE_IS: { + char *t = DEREF_IN_OR_OUT(ns_v.val.p, char *); + PRUint32 string_size = GetSizeIs(index, PR_TRUE); + if (t==NULL) { + ret = Py_None; + Py_INCREF(Py_None); + } else + ret = PyString_FromStringAndSize(t, string_size); + break; + } + case nsXPTType::T_PWSTRING_SIZE_IS: { + PRUnichar *t = DEREF_IN_OR_OUT(ns_v.val.p, PRUnichar *); + PRUint32 string_size = GetSizeIs(index, PR_TRUE); + if (t==NULL) { + ret = Py_None; + Py_INCREF(Py_None); + } else + ret = PyUnicode_FromUnicode(t, string_size); + break; + } + default: + // As this is called by external components, + // we return _something_ rather than failing before any user code has run! + { + char buf[128]; + sprintf(buf, "Unknown XPCOM type flags (0x%x)", td.type_flags); + PyXPCOM_LogWarning("%s - returning a string object with this message!\n", buf); + ret = PyString_FromString(buf); + break; + } + } + return ret; +} + +nsresult PyXPCOM_GatewayVariantHelper::GetArrayType(PRUint8 index, PRUint8 *ret) +{ + nsCOMPtr iim = XPTI_GetInterfaceInfoManager(); + NS_ABORT_IF_FALSE(iim != nsnull, "Cant get interface from IIM!"); + if (iim==nsnull) + return NS_ERROR_FAILURE; + + nsCOMPtr ii; + nsresult rc = iim->GetInfoForIID( &m_gateway->m_iid, getter_AddRefs(ii)); + if (NS_FAILED(rc)) + return rc; + nsXPTType datumType; + const nsXPTParamInfo& param_info = m_info->GetParam((PRUint8)index); + rc = ii->GetTypeForParam(m_method_index, ¶m_info, 1, &datumType); + if (NS_FAILED(rc)) + return rc; + *ret = datumType.flags; + return NS_OK; +} + +PRBool PyXPCOM_GatewayVariantHelper::GetIIDForINTERFACE_ID(int index, const nsIID **ppret) +{ + // Not sure if the IID pointed at by by this is allows to be + // in or out, so we will allow it. + nsXPTParamInfo *pi = (nsXPTParamInfo *)m_info->params+index; + nsXPTType typ = pi->GetType(); + NS_WARN_IF_FALSE(XPT_TDP_TAG(typ) == nsXPTType::T_IID, "INTERFACE_IS IID param isnt an IID!"); + NS_ABORT_IF_FALSE(typ.IsPointer(), "Expecting to re-fill a pointer value."); + if (XPT_TDP_TAG(typ) != nsXPTType::T_IID) + *ppret = &NS_GET_IID(nsISupports); + else { + nsXPTCMiniVariant &ns_v = m_params[index]; + if (pi->IsOut()) { + nsIID **pp = (nsIID **)ns_v.val.p; + if (pp && *pp) + *ppret = *pp; + else + *ppret = &NS_GET_IID(nsISupports); + } else if (pi->IsIn()) { + nsIID *p = (nsIID *)ns_v.val.p; + if (p) + *ppret = p; + else + *ppret = &NS_GET_IID(nsISupports); + } else { + NS_ERROR("Param is not in or out!"); + *ppret = &NS_GET_IID(nsISupports); + } + } + return PR_TRUE; +} + +#undef FILL_SIMPLE_POINTER +#define FILL_SIMPLE_POINTER( type, ob ) *((type *)ns_v.val.p) = (type)(ob); + +nsresult PyXPCOM_GatewayVariantHelper::BackFillVariant( PyObject *val, int index) +{ + nsXPTParamInfo *pi = (nsXPTParamInfo *)m_info->params+index; + NS_ABORT_IF_FALSE(pi->IsOut() || pi->IsDipper(), "The value must be marked as [out] (or a dipper) to be back-filled!"); + NS_ABORT_IF_FALSE(!pi->IsShared(), "Dont know how to back-fill a shared out param"); + nsXPTCMiniVariant &ns_v = m_params[index]; + PyObject *ret = NULL; + + nsXPTType typ = pi->GetType(); + PyObject* val_use = NULL; + + NS_ABORT_IF_FALSE(pi->IsDipper() || ns_v.val.p, "No space for result!"); + if (!pi->IsDipper() && !ns_v.val.p) return NS_ERROR_INVALID_POINTER; + NS_ABORT_IF_FALSE(((pi->IsDipper()==0) ^ (XPT_TDP_TAG(typ)==nsXPTType::T_DOMSTRING==0)) == 0, "Only handle DOMString dippers!"); + + PRBool rc = PR_TRUE; + switch (XPT_TDP_TAG(typ)) { + case nsXPTType::T_I8: + if ((val_use=PyNumber_Int(val))==NULL) BREAK_FALSE; + FILL_SIMPLE_POINTER( PRInt8, PyInt_AsLong(val_use) ); + break; + case nsXPTType::T_I16: + if ((val_use=PyNumber_Int(val))==NULL) BREAK_FALSE; + FILL_SIMPLE_POINTER( PRInt16, PyInt_AsLong(val_use) ); + break; + case nsXPTType::T_I32: + if ((val_use=PyNumber_Int(val))==NULL) BREAK_FALSE; + FILL_SIMPLE_POINTER( PRInt32, PyInt_AsLong(val_use) ); + break; + case nsXPTType::T_I64: + if ((val_use=PyNumber_Long(val))==NULL) BREAK_FALSE; + FILL_SIMPLE_POINTER( PRInt64, PyLong_AsLongLong(val_use) ); + break; + case nsXPTType::T_U8: + if ((val_use=PyNumber_Int(val))==NULL) BREAK_FALSE; + FILL_SIMPLE_POINTER( PRUint8, PyInt_AsLong(val_use) ); + break; + case nsXPTType::T_U16: + if ((val_use=PyNumber_Int(val))==NULL) BREAK_FALSE; + FILL_SIMPLE_POINTER( PRUint16, PyInt_AsLong(val_use) ); + break; + case nsXPTType::T_U32: + if ((val_use=PyNumber_Int(val))==NULL) BREAK_FALSE; + FILL_SIMPLE_POINTER( PRUint32, PyInt_AsLong(val_use) ); + break; + case nsXPTType::T_U64: + if ((val_use=PyNumber_Long(val))==NULL) BREAK_FALSE; + FILL_SIMPLE_POINTER( PRUint64, PyLong_AsUnsignedLongLong(val_use) ); + break; + case nsXPTType::T_FLOAT: + if ((val_use=PyNumber_Float(val))==NULL) BREAK_FALSE + FILL_SIMPLE_POINTER( float, PyFloat_AsDouble(val_use) ); + break; + case nsXPTType::T_DOUBLE: + if ((val_use=PyNumber_Float(val))==NULL) BREAK_FALSE + FILL_SIMPLE_POINTER( double, PyFloat_AsDouble(val_use) ); + break; + case nsXPTType::T_BOOL: + if ((val_use=PyNumber_Int(val))==NULL) BREAK_FALSE + FILL_SIMPLE_POINTER( PRBool, PyInt_AsLong(val_use) ); + break; + case nsXPTType::T_CHAR: + if (!PyString_Check(val) && !PyUnicode_Check(val)) { + PyErr_SetString(PyExc_TypeError, "This parameter must be a string or Unicode object"); + BREAK_FALSE; + } + if ((val_use = PyObject_Str(val))==NULL) + BREAK_FALSE; + // Sanity check should PyObject_Str() ever loosen its semantics wrt Unicode! + NS_ABORT_IF_FALSE(PyString_Check(val_use), "PyObject_Str didnt return a string object!"); + FILL_SIMPLE_POINTER( char, *PyString_AS_STRING(val_use) ); + break; + + case nsXPTType::T_WCHAR: + if (!PyString_Check(val) && !PyUnicode_Check(val)) { + PyErr_SetString(PyExc_TypeError, "This parameter must be a string or Unicode object"); + BREAK_FALSE; + } + if ((val_use = PyUnicode_FromObject(val))==NULL) + BREAK_FALSE; + NS_ABORT_IF_FALSE(PyUnicode_Check(val_use), "PyUnicode_FromObject didnt return a Unicode object!"); + FILL_SIMPLE_POINTER( PRUnichar, *PyUnicode_AS_UNICODE(val_use) ); + break; + +// case nsXPTType::T_VOID: + case nsXPTType::T_IID: { + nsIID iid; + if (!Py_nsIID::IIDFromPyObject(val, &iid)) + BREAK_FALSE; + nsIID **pp = (nsIID **)ns_v.val.p; + // If there is an existing IID, free it. + if (*pp) + nsAllocator::Free(*pp); + *pp = (nsIID *)nsAllocator::Alloc(sizeof(nsIID)); + if (*pp==NULL) { + PyErr_NoMemory(); + BREAK_FALSE; + } + nsCRT::memcpy(*pp, &iid, sizeof(iid)); + break; + } + + case nsXPTType::T_DOMSTRING: { + nsAWritableString *ws = (nsAWritableString *)ns_v.val.p; + NS_ABORT_IF_FALSE(ws->Length() == 0, "Why does this writable string already have chars??"); + if (val == Py_None) { + PyErr_SetString(PyExc_ValueError, "Dont know how to handle NULL DOMStrings yet!"); + BREAK_FALSE; + } + if (!PyString_Check(val) && !PyUnicode_Check(val)) { + PyErr_SetString(PyExc_TypeError, "This parameter must be a string or Unicode object"); + BREAK_FALSE; + } + val_use = PyUnicode_FromObject(val); + NS_ABORT_IF_FALSE(PyUnicode_Check(val_use), "PyUnicode_FromObject didnt return a Unicode object!"); + const PRUnichar *sz = PyUnicode_AS_UNICODE(val_use); + ws->Assign(sz); + break; + } + + case nsXPTType::T_CHAR_STR: { + // If it is an existing string, free it. + char **pp = (char **)ns_v.val.p; + if (*pp) + nsAllocator::Free(*pp); + *pp = nsnull; + + if (val == Py_None) + break; // Remains NULL. + if (!PyString_Check(val) && !PyUnicode_Check(val)) { + PyErr_SetString(PyExc_TypeError, "This parameter must be a string or Unicode object"); + BREAK_FALSE; + } + if ((val_use = PyObject_Str(val))==NULL) + BREAK_FALSE; + // Sanity check should PyObject_Str() ever loosen its semantics wrt Unicode! + NS_ABORT_IF_FALSE(PyString_Check(val_use), "PyObject_Str didnt return a string object!"); + + const char *sz = PyString_AS_STRING(val_use); + int nch = PyString_GET_SIZE(val_use); + + *pp = (char *)nsAllocator::Alloc(nch+1); + if (*pp==NULL) { + PyErr_NoMemory(); + BREAK_FALSE; + } + strncpy(*pp, sz, nch+1); + break; + } + case nsXPTType::T_WCHAR_STR: { + // If it is an existing string, free it. + PRUnichar **pp = (PRUnichar **)ns_v.val.p; + if (*pp) + nsAllocator::Free(*pp); + *pp = nsnull; + if (val == Py_None) + break; // Remains NULL. + if (!PyString_Check(val) && !PyUnicode_Check(val)) { + PyErr_SetString(PyExc_TypeError, "This parameter must be a string or Unicode object"); + BREAK_FALSE; + } + val_use = PyUnicode_FromObject(val); + NS_ABORT_IF_FALSE(PyUnicode_Check(val_use), "PyUnicode_FromObject didnt return a Unicode object!"); + const PRUnichar *sz = PyUnicode_AS_UNICODE(val_use); + int nch = PyUnicode_GET_SIZE(val_use); + + *pp = (PRUnichar *)nsAllocator::Alloc(sizeof(PRUnichar) * (nch+1)); + if (*pp==NULL) { + PyErr_NoMemory(); + BREAK_FALSE; + } + nsCRT::memcpy(*pp, sz, sizeof(PRUnichar) * (nch + 1)); + break; + } + case nsXPTType::T_INTERFACE: { + nsISupports *pnew = nsnull; + // Get it the "standard" way. + // We do allow NULL here, even tho doing so will no-doubt crash some objects. + // (but there will certainly be objects out there that will allow NULL :-( + if (!Py_nsISupports::InterfaceFromPyObject(val, NS_GET_IID(nsISupports), &pnew, PR_TRUE)) + BREAK_FALSE; + nsISupports **pp = (nsISupports **)ns_v.val.p; + if (*pp) { + Py_BEGIN_ALLOW_THREADS; // MUST release thread-lock, incase a Python COM object that re-acquires. + (*pp)->Release(); + Py_END_ALLOW_THREADS; + } + + *pp = pnew; // ref-count added by InterfaceFromPyObject + break; + } + case nsXPTType::T_INTERFACE_IS: { + // We do allow NULL here, even tho doing so will no-doubt crash some objects. + // (but there will certainly be objects out there that will allow NULL :-( + const nsIID *piid; + if (!GetIIDForINTERFACE_ID(pi->type.argnum, &piid)) + BREAK_FALSE; + + nsISupports *pnew = nsnull; + // Get it the "standard" way. + // We do allow NULL here, even tho doing so will no-doubt crash some objects. + // (but there will certainly be objects out there that will allow NULL :-( + if (!Py_nsISupports::InterfaceFromPyObject(val, *piid, &pnew, PR_TRUE)) + BREAK_FALSE; + nsISupports **pp = (nsISupports **)ns_v.val.p; + if (*pp) { + Py_BEGIN_ALLOW_THREADS; // MUST release thread-lock, incase a Python COM object that re-acquires. + (*pp)->Release(); + Py_END_ALLOW_THREADS; + } + + *pp = pnew; // ref-count added by InterfaceFromPyObject + break; + } + + case nsXPTType::T_PSTRING_SIZE_IS: { + const char *sz = nsnull; + PRUint32 nch = 0; + if (val != Py_None) { + if (!PyString_Check(val) && !PyUnicode_Check(val)) { + PyErr_SetString(PyExc_TypeError, "This parameter must be a string or Unicode object"); + BREAK_FALSE; + } + if ((val_use = PyObject_Str(val))==NULL) + BREAK_FALSE; + // Sanity check should PyObject_Str() ever loosen its semantics wrt Unicode! + NS_ABORT_IF_FALSE(PyString_Check(val_use), "PyObject_Str didnt return a string object!"); + + sz = PyString_AS_STRING(val_use); + nch = PyString_GET_SIZE(val_use); + } + PRBool bBackFill = PR_FALSE; + PRBool bCanSetSizeIs = CanSetSizeIs(index, PR_TRUE); + // If we can not change the size, check our sequence is correct. + if (!bCanSetSizeIs) { + PRUint32 existing_size = GetSizeIs(index, PR_TRUE); + if (nch != existing_size) { + PyErr_Format(PyExc_ValueError, "This function is expecting a string of exactly length %d - %d characters were passed", existing_size, nch); + BREAK_FALSE; + } + // It we have an "inout" param, but an "in" count, then + // it is probably a buffer the caller expects us to + // fill in-place! + bBackFill = pi->IsIn(); + } + if (bBackFill) { + nsCRT::memcpy(*(char **)ns_v.val.p, sz, nch); + } else { + // If we have an existing string, free it! + char **pp = (char **)ns_v.val.p; + if (*pp) + nsAllocator::Free(*pp); + *pp = nsnull; + if (sz==nsnull) // None specified. + break; // Remains NULL. + *pp = (char *)nsAllocator::Alloc(nch); + if (*pp==NULL) { + PyErr_NoMemory(); + BREAK_FALSE; + } + nsCRT::memcpy(*pp, sz, nch); + if (bCanSetSizeIs) + rc = SetSizeIs(index, PR_TRUE, nch); + else { + NS_ABORT_IF_FALSE(GetSizeIs(index, PR_TRUE) == nch, "Can't set sizeis, but string isnt correct size"); + } + } + break; + } + + case nsXPTType::T_PWSTRING_SIZE_IS: { + const PRUnichar *sz = nsnull; + PRUint32 nch = 0; + PRUint32 nbytes = 0; + + if (val != Py_None) { + if (!PyString_Check(val) && !PyUnicode_Check(val)) { + PyErr_SetString(PyExc_TypeError, "This parameter must be a string or Unicode object"); + BREAK_FALSE; + } + val_use = PyUnicode_FromObject(val); + NS_ABORT_IF_FALSE(PyUnicode_Check(val_use), "PyUnicode_FromObject didnt return a Unicode object!"); + sz = PyUnicode_AS_UNICODE(val_use); + nch = PyUnicode_GET_SIZE(val_use); + nbytes = sizeof(PRUnichar) * nch; + } + PRBool bBackFill = PR_FALSE; + PRBool bCanSetSizeIs = CanSetSizeIs(index, PR_TRUE); + // If we can not change the size, check our sequence is correct. + if (!bCanSetSizeIs) { + // It is a buffer the caller prolly wants us to fill in-place! + PRUint32 existing_size = GetSizeIs(index, PR_TRUE); + if (nch != existing_size) { + PyErr_Format(PyExc_ValueError, "This function is expecting a string of exactly length %d - %d characters were passed", existing_size, nch); + BREAK_FALSE; + } + // It we have an "inout" param, but an "in" count, then + // it is probably a buffer the caller expects us to + // fill in-place! + bBackFill = pi->IsIn(); + } + if (bBackFill) { + nsCRT::memcpy(*(PRUnichar **)ns_v.val.p, sz, nbytes); + } else { + // If it is an existing string, free it. + PRUnichar **pp = (PRUnichar **)ns_v.val.p; + if (*pp) + nsAllocator::Free(*pp); + *pp = nsnull; + + if (val == Py_None) + break; // Remains NULL. + *pp = (PRUnichar *)nsAllocator::Alloc(nbytes); + if (*pp==NULL) { + PyErr_NoMemory(); + BREAK_FALSE; + } + nsCRT::memcpy(*pp, sz, nbytes); + if (bCanSetSizeIs) + rc = SetSizeIs(index, PR_TRUE, nch); + else { + NS_ABORT_IF_FALSE(GetSizeIs(index, PR_TRUE) == nch, "Can't set sizeis, but string isnt correct size"); + } + } + break; + } + case nsXPTType::T_ARRAY: { + // If it is an existing array of the correct size, keep it. + PRUint32 sequence_size = 0; + PRUint8 array_type; + nsresult ns = GetArrayType(index, &array_type); + if (NS_FAILED(ns)) + return ns; + PRUint32 element_size = GetArrayElementSize(array_type); + if (val != Py_None) { + if (!PySequence_Check(val)) { + PyErr_Format(PyExc_TypeError, "Object for xpcom array must be a sequence, not type '%s'", val->ob_type->tp_name); + BREAK_FALSE; + } + sequence_size = PySequence_Length(val); + } + PRUint32 existing_size = GetSizeIs(index, PR_FALSE); + PRBool bBackFill = PR_FALSE; + PRBool bCanSetSizeIs = CanSetSizeIs(index, PR_FALSE); + // If we can not change the size, check our sequence is correct. + if (!bCanSetSizeIs) { + // It is a buffer the caller prolly wants us to fill in-place! + if (sequence_size != existing_size) { + PyErr_Format(PyExc_ValueError, "This function is expecting a sequence of exactly length %d - %d items were passed", existing_size, sequence_size); + BREAK_FALSE; + } + // It we have an "inout" param, but an "in" count, then + // it is probably a buffer the caller expects us to + // fill in-place! + bBackFill = pi->IsIn(); + } + if (bBackFill) + rc = FillSingleArray(*(void **)ns_v.val.p, val, sequence_size, element_size, array_type); + else { + // If it is an existing array, free it. + void **pp = (void **)ns_v.val.p; + if (*pp) { + FreeSingleArray(*pp, existing_size, array_type); + nsAllocator::Free(*pp); + } + *pp = nsnull; + if (val == Py_None) + break; // Remains NULL. + size_t nbytes = sequence_size * element_size; + *pp = (void *)nsAllocator::Alloc(nbytes); + memset(*pp, 0, nbytes); + rc = FillSingleArray(*pp, val, sequence_size, element_size, array_type); + if (!rc) break; + if (bCanSetSizeIs) + rc = SetSizeIs(index, PR_FALSE, sequence_size); + else { + NS_ABORT_IF_FALSE(GetSizeIs(index, PR_FALSE) == sequence_size, "Can't set sizeis, but string isnt correct size"); + } + } + break; + } + default: + // try and limp along in this case. + // leave rc TRUE + PyXPCOM_LogWarning("Converting Python object for an [out] param - The object type (0x%x) is unknown - leaving param alone!\n", XPT_TDP_TAG(typ)); + break; + } + Py_XDECREF(val_use); + if (!rc) + return NS_ERROR_FAILURE; + return NS_OK; +} + +nsresult PyXPCOM_GatewayVariantHelper::ProcessPythonResult(PyObject *ret_ob) +{ + // NOTE - although we return an nresult, if we leave a Python + // exception set, then our caller may take additional action + // (ie, translating our nsresult to a more appropriate nsresult + // for the Python exception.) + NS_PRECONDITION(!PyErr_Occurred(), "Expecting no Python exception to be pending when processing the return result"); + + nsresult rc = NS_OK; + // If we dont get a tuple back, then the result is only + // an int nresult for the underlying function. + // (ie, the policy is expected to return (NS_OK, user_retval), + // but can also return (say), NS_ERROR_FAILURE + if (PyInt_Check(ret_ob)) + return PyInt_AsLong(ret_ob); + // Now it must be the tuple. + if (!PyTuple_Check(ret_ob) || + PyTuple_Size(ret_ob)!=2 || + !PyInt_Check(PyTuple_GET_ITEM(ret_ob, 0))) { + PyErr_SetString(PyExc_TypeError, "The Python result must be a single integer or a tuple of length==2 and first item an int."); + return NS_ERROR_FAILURE; + } + PyObject *user_result = PyTuple_GET_ITEM(ret_ob, 1); + // Count up how many results our function needs. + int i; + int num_results = 0; + int last_result = -1; // optimization if we only have one - this is it! + int index_retval = -1; + for (i=0;iparams+i; + if (!m_python_type_desc_array[i].is_auto_out) { + if (pi->IsOut() || pi->IsDipper()) { + num_results++; + last_result = i; + } + if (pi->IsRetval()) + index_retval = i; + } + } + + if (num_results==0) { + ; // do nothing + } else if (num_results==1) { + // May or may not be the nominated retval - who cares! + NS_ABORT_IF_FALSE(last_result >=0 && last_result < m_num_type_descs, "Have one result, but dont know its index!"); + rc = BackFillVariant( user_result, last_result ); + } else { + // Loop over each one, filling as we go. + // We allow arbitary sequences here, but _not_ strings + // or Unicode! + // NOTE - We ALWAYS do the nominated retval first. + // The Python pattern is always: + // return retval [, byref1 [, byref2 ...] ] + // But the retval is often the last param described in the info. + if (!PySequence_Check(user_result) || + PyString_Check(user_result) || + PyUnicode_Check(user_result)) { + PyErr_SetString(PyExc_TypeError, "This function has multiple results, but a sequence was not given to fill them"); + return NS_ERROR_FAILURE; + } + int num_user_results = PySequence_Length(user_result); + // If they havent given enough, we dont really care. + // although a warning is probably appropriate. + if (num_user_results != num_results) { + const char *method_name = m_info->GetName(); + PyXPCOM_LogWarning("The method '%s' has %d out params, but %d were supplied by the Python code\n", + method_name, + num_results, + num_user_results); + } + int this_py_index = 0; + if (index_retval != -1) { + // We always return the nominated result first! + PyObject *sub = PySequence_GetItem(user_result, 0); + if (sub==NULL) + return NS_ERROR_FAILURE; + rc = BackFillVariant(sub, index_retval); + Py_DECREF(sub); + this_py_index = 1; + } + for (i=0;NS_SUCCEEDED(rc) && iGetParamCount();i++) { + // If weve already done it, or dont need to do it! + if (i==index_retval || m_python_type_desc_array[i].is_auto_out) + continue; + nsXPTParamInfo *pi = (nsXPTParamInfo *)m_info->params+i; + if (pi->IsOut()) { + PyObject *sub = PySequence_GetItem(user_result, this_py_index); + if (sub==NULL) + return NS_ERROR_FAILURE; + rc = BackFillVariant(sub, i); + Py_DECREF(sub); + this_py_index++; + } + } + } + return rc; +} diff --git a/mozilla/extensions/python/xpcom/src/dllmain.cpp b/mozilla/extensions/python/xpcom/src/dllmain.cpp new file mode 100644 index 00000000000..147b01b0e93 --- /dev/null +++ b/mozilla/extensions/python/xpcom/src/dllmain.cpp @@ -0,0 +1,217 @@ +/* Copyright (c) 2000-2001 ActiveState Tool Corporation. + See the file LICENSE.txt for licensing information. */ + +// +// This code is part of the XPCOM extensions for Python. +// +// Written May 2000 by Mark Hammond. +// +// Based heavily on the Python COM support, which is +// (c) Mark Hammond and Greg Stein. +// +// (c) 2000, ActiveState corp. + +#include "PyXPCOM_std.h" +#include + +#ifdef XP_WIN +#define WIN32_LEAN_AND_MEAN +#include "windows.h" +#endif + +static PRInt32 g_cLockCount = 0; +static PRBool bDidInitPython = PR_FALSE; +static PyThreadState *ptsGlobal = nsnull; +PyInterpreterState *PyXPCOM_InterpreterState; +static PRLock *g_lockMain = nsnull; + +PRUintn tlsIndex = 0; + + +//////////////////////////////////////////////////////////// +// Thread-state helpers/global functions. +// + +// This function must be called at some time when the interpreter lock and state is valid. +// Called by init{module} functions and also COM factory entry point. +void PyXPCOM_InterpreterState_Ensure() +{ + if (PyXPCOM_InterpreterState==NULL) { + PyThreadState *threadStateSave = PyThreadState_Swap(NULL); + if (threadStateSave==NULL) + Py_FatalError("Can not setup interpreter state, as current state is invalid"); + + PyXPCOM_InterpreterState = threadStateSave->interp; + PyThreadState_Swap(threadStateSave); + } +} + +void PyXPCOM_InterpreterState_Free() +{ + PyXPCOM_ThreadState_Free(); + PyXPCOM_InterpreterState = NULL; // Eek - should I be freeing something? +} + +// This structure is stored in the TLS slot. At this stage only a Python thread state +// is kept, but this may change in the future... +struct ThreadData{ + PyThreadState *ts; +}; + +// Ensure that we have a Python thread state available to use. +// If this is called for the first time on a thread, it will allocate +// the thread state. This does NOT change the state of the Python lock. +// Returns TRUE if a new thread state was created, or FALSE if a +// thread state already existed. +PRBool PyXPCOM_ThreadState_Ensure() +{ + ThreadData *pData = (ThreadData *)PR_GetThreadPrivate(tlsIndex); + if (pData==NULL) { /* First request on this thread */ + /* Check we have an interpreter state */ + if (PyXPCOM_InterpreterState==NULL) { + Py_FatalError("Can not setup thread state, as have no interpreter state"); + } + pData = (ThreadData *)nsAllocator::Alloc(sizeof(ThreadData)); + if (!pData) + Py_FatalError("Out of memory allocating thread state."); + memset(pData, 0, sizeof(*pData)); + if (NS_FAILED( PR_SetThreadPrivate( tlsIndex, pData ) ) ) { + NS_ABORT_IF_FALSE(0, "Could not create thread data for this thread!"); + Py_FatalError("Could not thread private thread data!"); + } + pData->ts = PyThreadState_New(PyXPCOM_InterpreterState); + return PR_TRUE; // Did create a thread state state + } + return PR_FALSE; // Thread state was previously created +} + +// Asuming we have a valid thread state, acquire the Python lock. +void PyXPCOM_InterpreterLock_Acquire() +{ + ThreadData *pData = (ThreadData *)PR_GetThreadPrivate(tlsIndex); + NS_ABORT_IF_FALSE(pData, "Have no thread data for this thread!"); + PyThreadState *thisThreadState = pData->ts; + PyEval_AcquireThread(thisThreadState); +} + +// Asuming we have a valid thread state, release the Python lock. +void PyXPCOM_InterpreterLock_Release() +{ + ThreadData *pData = (ThreadData *)PR_GetThreadPrivate(tlsIndex); + NS_ABORT_IF_FALSE(pData, "Have no thread data for this thread!"); + PyThreadState *thisThreadState = pData->ts; + PyEval_ReleaseThread(thisThreadState); +} + +// Free the thread state for the current thread +// (Presumably previously create with a call to +// PyXPCOM_ThreadState_Ensure) +void PyXPCOM_ThreadState_Free() +{ + ThreadData *pData = (ThreadData *)PR_GetThreadPrivate(tlsIndex); + if (!pData) return; + PyThreadState *thisThreadState = pData->ts; + PyThreadState_Delete(thisThreadState); + PR_SetThreadPrivate(tlsIndex, NULL); + nsAllocator::Free(pData); +} + +void PyXPCOM_ThreadState_Clear() +{ + ThreadData *pData = (ThreadData *)PR_GetThreadPrivate(tlsIndex); + PyThreadState *thisThreadState = pData->ts; + PyThreadState_Clear(thisThreadState); +} + +//////////////////////////////////////////////////////////// +// Lock/exclusion global functions. +// + +void PyXPCOM_AcquireGlobalLock(void) +{ + NS_PRECONDITION(g_lockMain != nsnull, "Cant acquire a NULL lock!"); + PR_Lock(g_lockMain); +} +void PyXPCOM_ReleaseGlobalLock(void) +{ + NS_PRECONDITION(g_lockMain != nsnull, "Cant release a NULL lock!"); + PR_Unlock(g_lockMain); +} + +void PyXPCOM_DLLAddRef(void) +{ + // Must be thread-safe, although cant have the Python lock! + CEnterLeaveXPCOMFramework _celf; + PRInt32 cnt = PR_AtomicIncrement(&g_cLockCount); + if (cnt==1) { // First call + if (!Py_IsInitialized()) { + Py_Initialize(); + // Make sure our Windows framework is all setup. + PyXPCOM_Globals_Ensure(); + // Make sure we have _something_ as sys.argv. + if (PySys_GetObject("argv")==NULL) { + PyObject *path = PyList_New(0); + PyObject *str = PyString_FromString(""); + PyList_Append(path, str); + PySys_SetObject("argv", path); + Py_XDECREF(path); + Py_XDECREF(str); + } + + // Must force Python to start using thread locks, as + // we are free-threaded (maybe, I think, sometimes :-) + PyEval_InitThreads(); + // Release Python lock, as first thing we do is re-get it. + ptsGlobal = PyEval_SaveThread(); + // NOTE: We never finalize Python!! + } + } +} +void PyXPCOM_DLLRelease(void) +{ + PR_AtomicDecrement(&g_cLockCount); +} + +extern "C" PRBool _init(void) +{ + PRStatus status; + g_lockMain = PR_NewLock(); + status = PR_NewThreadPrivateIndex( &tlsIndex, NULL ); + NS_WARN_IF_FALSE(status==0, "Could not allocate TLS storage"); + if (NS_FAILED(status)) { + PR_DestroyLock(g_lockMain); + return PR_FALSE; + } + return PR_TRUE; +} + +extern "C" void _fini(void) +{ + PR_DestroyLock(g_lockMain); + // I can't locate a way to kill this - + // should I pass a dtor to PR_NewThreadPrivateIndex?? + // TlsFree(tlsIndex); +} + +#ifdef XP_WIN + +extern "C" __declspec(dllexport) +BOOL WINAPI DllMain(HANDLE hInstance, DWORD dwReason, LPVOID lpReserved) +{ + switch (dwReason) { + case DLL_PROCESS_ATTACH: { + if (!_init()) + return FALSE; + break; + } + case DLL_PROCESS_DETACH: + { + _fini(); + break; + } + default: + break; + } + return TRUE; // ok +} +#endif // XP_WIN diff --git a/mozilla/extensions/python/xpcom/src/loader/pyloader.cpp b/mozilla/extensions/python/xpcom/src/loader/pyloader.cpp new file mode 100644 index 00000000000..e8477f2cd15 --- /dev/null +++ b/mozilla/extensions/python/xpcom/src/loader/pyloader.cpp @@ -0,0 +1,392 @@ +/* Copyright (c) 2000-2001 ActiveState Tool Corporation. + See the file LICENSE.txt for licensing information. */ + +// pyloader +// +// Not part of the main Python _xpcom package, but a seperate, thin DLL. +// +// The main loader and registrar for Python. A thin DLL that is designed to live in +// the xpcom "components" directory. Simply locates and loads the standard +// _xpcom support module and transfers control to that. + +#include "xp_core.h" +#include "nsIComponentLoader.h" +#include "nsIRegistry.h" +#include "nsISupports.h" +#include "nsIModule.h" + +#include // For console logging. + +#ifdef HAVE_LONG_LONG +#undef HAVE_LONG_LONG +#endif + + +#ifdef XP_WIN +// Can only assume dynamic loading on Windows. +#define LOADER_LINKS_WITH_PYTHON +#endif + + +#ifdef LOADER_LINKS_WITH_PYTHON +#include "Python.h" + +static PyThreadState *ptsGlobal = nsnull; +static char *PyTraceback_AsString(PyObject *exc_tb); + +#else // LOADER_LINKS_WITH_PYTHON + +static PRBool find_xpcom_module(char *buf, size_t bufsize); + +#endif // LOADER_LINKS_WITH_PYTHON + + +#ifdef XP_WIN +#define WIN32_LEAN_AND_MEAN +#include "windows.h" +#endif + +#ifdef XP_UNIX +#include +#include + +#endif + + +typedef nsresult (*pfnPyXPCOM_NSGetModule)(nsIComponentManager *servMgr, + nsIFile* location, + nsIModule** result); + + +pfnPyXPCOM_NSGetModule pfnEntryPoint = nsnull; + + +static void LogError(const char *fmt, ...); + +extern "C" NS_EXPORT nsresult NSGetModule(nsIComponentManager *servMgr, + nsIFile* location, + nsIModule** result) +{ + // What to do for other platforms here? + // I tried using their nsDll class, but it wont allow + // a LoadLibrary() - it insists on a full path it can load. + // So if Im going to the trouble of locating the DLL on Windows, + // I may as well just do the whole thing myself. +#ifdef LOADER_LINKS_WITH_PYTHON + PRBool bDidInitPython = !Py_IsInitialized(); // well, I will next line, anyway :-) + if (bDidInitPython) { + // If Python was already initialized, we almost certainly + // do not have the thread-lock, so can not attempt to import anything + // We simply must assume/hope that Python already has our module loaded. + Py_Initialize(); + if (!Py_IsInitialized()) { + LogError("Python initialization failed!\n"); + return NS_ERROR_FAILURE; + } + PyObject *mod = PyImport_ImportModule("xpcom._xpcom"); + if (mod==NULL) { + LogError("Could not import the Python XPCOM extension\n"); + return NS_ERROR_FAILURE; + } + } +#endif // LOADER_LINKS_WITH_PYTHON + if (pfnEntryPoint == nsnull) { + +#ifdef XP_WIN + +#ifdef DEBUG + const char *mod_name = "_xpcom_d.pyd"; +#else + const char *mod_name = "_xpcom.pyd"; +#endif + HMODULE hmod = GetModuleHandle(mod_name); + if (hmod==NULL) { + LogError("Could not get a handle to the Python XPCOM extension\n"); + return NS_ERROR_FAILURE; + } + pfnEntryPoint = (pfnPyXPCOM_NSGetModule)GetProcAddress(hmod, "PyXPCOM_NSGetModule"); +#endif // XP_WIN + +#ifdef XP_UNIX + static char module_path[1024]; + if (!find_xpcom_module(module_path, sizeof(module_path))) + return NS_ERROR_FAILURE; + + void *handle = dlopen(module_path, RTLD_GLOBAL | RTLD_LAZY); + if (handle==NULL) { + LogError("Could not open the Python XPCOM extension at '%s' - '%s'\n", module_path, dlerror()); + return NS_ERROR_FAILURE; + } + pfnEntryPoint = (pfnPyXPCOM_NSGetModule)dlsym(handle, "PyXPCOM_NSGetModule"); +#endif // XP_UNIX + } + if (pfnEntryPoint==NULL) { + LogError("Could not load main Python entry point\n"); + return NS_ERROR_FAILURE; + } + +#ifdef LOADER_LINKS_WITH_PYTHON + // We abandon the thread-lock, as the first thing Python does + // is re-establish the lock (the Python thread-state story SUCKS!!! + + if (bDidInitPython) + ptsGlobal = PyEval_SaveThread(); + // Note this is never restored, and Python is never finalized! +#endif // LOADER_LINKS_WITH_PYTHON + return (*pfnEntryPoint)(servMgr, location, result); +} + +// The internal helper that actually moves the +// formatted string to the target! + +void LogMessage(const char *prefix, const char *pszMessageText) +{ + nsOutputConsoleStream console; + console << prefix << pszMessageText; +} + +// A helper for the various logging routines. +static void VLogF(const char *prefix, const char *fmt, va_list argptr) +{ + char buff[512]; + + vsprintf(buff, fmt, argptr); + + LogMessage(prefix, buff); +} + +static void LogError(const char *fmt, ...) +{ + va_list marker; + va_start(marker, fmt); + VLogF("PyXPCOM Loader Error: ", fmt, marker); +#ifdef LOADER_LINKS_WITH_PYTHON + // If we have a Python exception, also log that: + PyObject *exc_typ = NULL, *exc_val = NULL, *exc_tb = NULL; + PyErr_Fetch( &exc_typ, &exc_val, &exc_tb); + if (exc_typ) { + char *string1 = nsnull; + nsOutputStringStream streamout(string1); + + if (exc_tb) { + const char *szTraceback = PyTraceback_AsString(exc_tb); + if (szTraceback == NULL) + streamout << "Can't get the traceback info!"; + else { + streamout << "Traceback (most recent call last):\n"; + streamout << szTraceback; + PyMem_Free((ANY *)szTraceback); + } + } + PyObject *temp = PyObject_Str(exc_typ); + if (temp) { + streamout << PyString_AsString(temp); + Py_DECREF(temp); + } else + streamout << "Can convert exception to a string!"; + streamout << ": "; + if (exc_val != NULL) { + temp = PyObject_Str(exc_val); + if (temp) { + streamout << PyString_AsString(temp); + Py_DECREF(temp); + } else + streamout << "Can convert exception value to a string!"; + } + streamout << "\n"; + LogMessage("PyXPCOM Exception:", string1); + } + PyErr_Restore(exc_typ, exc_val, exc_tb); +#endif // LOADER_LINKS_WITH_PYTHON +} + +static void LogWarning(const char *fmt, ...) +{ + va_list marker; + va_start(marker, fmt); + VLogF("PyXPCOM Loader Warning: ", fmt, marker); +} + +#ifdef DEBUG +void LogDebug(const char *fmt, ...) +{ + va_list marker; + va_start(marker, fmt); + VLogF("PyXPCOM Loader Debug: ", fmt, marker); +} +#else +#define LogDebug() +#endif + +#ifdef LOADER_LINKS_WITH_PYTHON + +/* Obtains a string from a Python traceback. + This is the exact same string as "traceback.print_exc" would return. + + Pass in a Python traceback object (probably obtained from PyErr_Fetch()) + Result is a string which must be free'd using PyMem_Free() +*/ +#define TRACEBACK_FETCH_ERROR(what) {errMsg = what; goto done;} + +char *PyTraceback_AsString(PyObject *exc_tb) +{ + char *errMsg = NULL; /* a static that hold a local error message */ + char *result = NULL; /* a valid, allocated result. */ + PyObject *modStringIO = NULL; + PyObject *modTB = NULL; + PyObject *obFuncStringIO = NULL; + PyObject *obStringIO = NULL; + PyObject *obFuncTB = NULL; + PyObject *argsTB = NULL; + PyObject *obResult = NULL; + + /* Import the modules we need - cStringIO and traceback */ + modStringIO = PyImport_ImportModule("cStringIO"); + if (modStringIO==NULL) + TRACEBACK_FETCH_ERROR("cant import cStringIO\n"); + + modTB = PyImport_ImportModule("traceback"); + if (modTB==NULL) + TRACEBACK_FETCH_ERROR("cant import traceback\n"); + /* Construct a cStringIO object */ + obFuncStringIO = PyObject_GetAttrString(modStringIO, "StringIO"); + if (obFuncStringIO==NULL) + TRACEBACK_FETCH_ERROR("cant find cStringIO.StringIO\n"); + obStringIO = PyObject_CallObject(obFuncStringIO, NULL); + if (obStringIO==NULL) + TRACEBACK_FETCH_ERROR("cStringIO.StringIO() failed\n"); + /* Get the traceback.print_exception function, and call it. */ + obFuncTB = PyObject_GetAttrString(modTB, "print_tb"); + if (obFuncTB==NULL) + TRACEBACK_FETCH_ERROR("cant find traceback.print_tb\n"); + + argsTB = Py_BuildValue("OOO", + exc_tb ? exc_tb : Py_None, + Py_None, + obStringIO); + if (argsTB==NULL) + TRACEBACK_FETCH_ERROR("cant make print_tb arguments\n"); + + obResult = PyObject_CallObject(obFuncTB, argsTB); + if (obResult==NULL) + TRACEBACK_FETCH_ERROR("traceback.print_tb() failed\n"); + /* Now call the getvalue() method in the StringIO instance */ + Py_DECREF(obFuncStringIO); + obFuncStringIO = PyObject_GetAttrString(obStringIO, "getvalue"); + if (obFuncStringIO==NULL) + TRACEBACK_FETCH_ERROR("cant find getvalue function\n"); + Py_DECREF(obResult); + obResult = PyObject_CallObject(obFuncStringIO, NULL); + if (obResult==NULL) + TRACEBACK_FETCH_ERROR("getvalue() failed.\n"); + + /* And it should be a string all ready to go - duplicate it. */ + if (!PyString_Check(obResult)) + TRACEBACK_FETCH_ERROR("getvalue() did not return a string\n"); + + { // a temp scope so I can use temp locals. + char *tempResult = PyString_AsString(obResult); + result = (char *)PyMem_Malloc(strlen(tempResult)+1); + if (result==NULL) + TRACEBACK_FETCH_ERROR("memory error duplicating the traceback string"); + + strcpy(result, tempResult); + } // end of temp scope. +done: + /* All finished - first see if we encountered an error */ + if (result==NULL && errMsg != NULL) { + result = (char *)PyMem_Malloc(strlen(errMsg)+1); + if (result != NULL) + /* if it does, not much we can do! */ + strcpy(result, errMsg); + } + Py_XDECREF(modStringIO); + Py_XDECREF(modTB); + Py_XDECREF(obFuncStringIO); + Py_XDECREF(obStringIO); + Py_XDECREF(obFuncTB); + Py_XDECREF(argsTB); + Py_XDECREF(obResult); + return result; +} + +#else // LOADER_LINKS_WITH_PYTHON + +#ifdef XP_UNIX + +// From Python getpath.c +#ifndef S_ISREG +#define S_ISREG(x) (((x) & S_IFMT) == S_IFREG) +#endif + +#ifndef S_ISDIR +#define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR) +#endif + +static int +isfile(char *filename) /* Is file, not directory */ +{ + struct stat buf; + if (stat(filename, &buf) != 0) + return 0; + if (!S_ISREG(buf.st_mode)) + return 0; + return 1; +} + + +static int +isxfile(char *filename) /* Is executable file */ +{ + struct stat buf; + if (stat(filename, &buf) != 0) + return 0; + if (!S_ISREG(buf.st_mode)) + return 0; + if ((buf.st_mode & 0111) == 0) + return 0; + return 1; +} + + +static int +isdir(char *filename) /* Is directory */ +{ + struct stat buf; + if (stat(filename, &buf) != 0) + return 0; + if (!S_ISDIR(buf.st_mode)) + return 0; + return 1; +} + + +PRBool find_xpcom_module(char *buf, size_t bufsize) +{ + char *pypath = getenv("PYTHONPATH"); + char *searchPath = pypath ? strdup(pypath) : NULL; + char *tok = searchPath ? strtok(searchPath, ":") : NULL; + while (tok != NULL) { + int thissize = bufsize; + int baselen = strlen(tok); + strncpy(buf, tok, thissize); + thissize-=baselen; + if (thissize > 1 && buf[baselen-1] != '/') { + buf[baselen++]='/'; + } + strncpy(buf+baselen, "xpcom/_xpcommodule.so", thissize); +// LogDebug("Python _xpcom module at '%s'?\n", buf); + if (isfile(buf)) { +// LogDebug("Found python _xpcom module at '%s'\n", buf); + return PR_TRUE; + } + tok = strtok(NULL, ":"); + } + LogError("Failed to find a Python _xpcom module\n"); + return PR_FALSE; +} + +#endif // XP_UNIX + +#endif // LOADER_LINKS_WITH_PYTHON + diff --git a/mozilla/extensions/python/xpcom/src/readme.html b/mozilla/extensions/python/xpcom/src/readme.html new file mode 100644 index 00000000000..fab3bf875ee --- /dev/null +++ b/mozilla/extensions/python/xpcom/src/readme.html @@ -0,0 +1,66 @@ + + + + + + + + + +Building the Python XPCOM package + + + + +

Building the Python XPCOM package.

+ +

This file describes how to build the Python XPCOM C++ sources.

+

There are the following steps

+ +

Testing etc is described in the main readme.

+

Configuring environment variables

+

MOZ_SRC 

+

Windows: Run the standard MOZENV.BAT used to build Mozilla.  This +sets MOZ_SRC

+

Unix: Set MOZ_SRC to point to the base source directory - assumes +"mozilla" sub-directory with mozilla directory tree under that. eg: assuming +/home/user/src/mozilla/dist/..."

+
export MOZ_SRC=/home/user/src
+

PYTHON_SRC

+

Windows: Set PYTHON_SRC to point to the base Python source directory.  +eg: assuming c:\src\python\PCBuild\...

set PYTHON_SRC=c:\src\python
+

Unix: Set PYTHON_SRC to point to the base of an "installed" Python +tree. eg:

export PYTHON_SRC=/usr/local/ActivePython-1.6
+

Building the sources

+

You must ensure some environment variables are setup.  The section on configuring +environment variables explains how.

+

There are 2 build processes to run All C++ sources are in the xpcom\src + directory.:

+

Windows

+
    +
  • Execute "compile.py" in this directory. This will take Setup.in, create an MSDev project, and build + ..\_xpcom.pyd and ..\_xpcom_d.pyd"
  • +
  • Change to the loader directory.
  • +
  • Run nmake -f makefile.win. This will create pyloader.dll, and + automatically copy it to the Mozilla build directory.
  • + +
+

Finally, run the tests, + where we also test everything imports correctly.

+

Linux

+

NOTE: Do not attempt to use "Setup.in" to create a Makefile 

+
    +
  • Run "make" in this directory.  This will create ../_xpcommodule.so
  • +
  • Run "make" in the loader directory. This will create libpyloader.so, + and copy it to the Mozilla directory.
  • + +
+

Finally, running the tests, + where we also test everything imports correctly.

+ + + + diff --git a/mozilla/extensions/python/xpcom/src/xpcom.cpp b/mozilla/extensions/python/xpcom/src/xpcom.cpp new file mode 100644 index 00000000000..70629e4a510 --- /dev/null +++ b/mozilla/extensions/python/xpcom/src/xpcom.cpp @@ -0,0 +1,532 @@ +/* Copyright (c) 2000-2001 ActiveState Tool Corporation. + See the file LICENSE.txt for licensing information. */ + +// +// This code is part of the XPCOM extensions for Python. +// +// Written May 2000 by Mark Hammond. +// +// Based heavily on the Python COM support, which is +// (c) Mark Hammond and Greg Stein. +// +// (c) 2000, ActiveState corp. + +#include "PyXPCOM_std.h" +#include +#include +#include +#include +#include +#include +#include + +#ifdef XP_WIN +#define WIN32_LEAN_AND_MEAN +#include "windows.h" +#endif + +#include +#include + +PYXPCOM_EXPORT PyObject *PyXPCOM_Error = NULL; +extern void PyXPCOM_InterpreterState_Ensure(); +extern PRInt32 _PyXPCOM_GetGatewayCount(void); +extern PRInt32 _PyXPCOM_GetInterfaceCount(void); + +extern void AddDefaultGateway(PyObject *instance, nsISupports *gateway); + +// Hrm - So we can't have templates, eh?? +// preprocessor to the rescue, I guess. +#define PyXPCOM_INTERFACE_DEFINE(ClassName, InterfaceName, Methods ) \ + \ +extern struct PyMethodDef Methods[]; \ + \ +class ClassName : public Py_nsISupports \ +{ \ +public: \ + static PyXPCOM_TypeObject *type; \ + static Py_nsISupports *Constructor(nsISupports *pInitObj, const nsIID &iid) { \ + return new ClassName(pInitObj, iid); \ + } \ + static void InitType(PyObject *iidNameDict) { \ + type = new PyXPCOM_TypeObject( \ + #InterfaceName, \ + Py_nsISupports::type, \ + sizeof(ClassName), \ + Methods, \ + Constructor); \ + const nsIID &iid = NS_GET_IID(InterfaceName); \ + RegisterInterface(iid, type); \ + PyObject *iid_ob = Py_nsIID::PyObjectFromIID(iid); \ + PyDict_SetItemString(iidNameDict, "IID_"#InterfaceName, iid_ob); \ + Py_DECREF(iid_ob); \ + } \ +protected: \ + ClassName(nsISupports *p, const nsIID &iid) : \ + Py_nsISupports(p, iid, type) { \ + /* The IID _must_ be the IID of the interface we are wrapping! */ \ + NS_ABORT_IF_FALSE(iid.Equals(NS_GET_IID(InterfaceName)), "Bad IID"); \ + } \ +}; \ + \ +PyXPCOM_TypeObject *ClassName::type = NULL; \ + \ +// End of PyXPCOM_INTERFACE_DEFINE macro + +// And the classes +PyXPCOM_INTERFACE_DEFINE(Py_nsIComponentManager, nsIComponentManager, PyMethods_IComponentManager) +PyXPCOM_INTERFACE_DEFINE(Py_nsIInterfaceInfoManager, nsIInterfaceInfoManager, PyMethods_IInterfaceInfoManager) +PyXPCOM_INTERFACE_DEFINE(Py_nsIEnumerator, nsIEnumerator, PyMethods_IEnumerator) +PyXPCOM_INTERFACE_DEFINE(Py_nsISimpleEnumerator, nsISimpleEnumerator, PyMethods_ISimpleEnumerator) +PyXPCOM_INTERFACE_DEFINE(Py_nsIInterfaceInfo, nsIInterfaceInfo, PyMethods_IInterfaceInfo) +PyXPCOM_INTERFACE_DEFINE(Py_nsIServiceManager, nsIServiceManager, PyMethods_IServiceManager) +PyXPCOM_INTERFACE_DEFINE(Py_nsIInputStream, nsIInputStream, PyMethods_IInputStream) + +// "boot-strap" methods - interfaces we need to get the base +// interface support! + +static PyObject * +PyXPCOMMethod_NS_LocateSpecialSystemDirectory(PyObject *self, PyObject *args) +{ + int typ; + if (!PyArg_ParseTuple(args, "i", &typ)) + return NULL; + nsIFileSpec *spec = NULL; + nsSpecialSystemDirectory systemDir((nsSpecialSystemDirectory::SystemDirectories)typ); + return PyString_FromString(systemDir.GetNativePathCString()); +} + +static PyObject * +PyXPCOMMethod_NS_NewFileSpec(PyObject *self, PyObject *args) +{ + char *szspec = NULL; + if (!PyArg_ParseTuple(args, "|s", &szspec)) + return NULL; + nsIFileSpec *spec = NULL; + nsresult nr; + Py_BEGIN_ALLOW_THREADS; + nr = NS_NewFileSpec(&spec); + if (NS_SUCCEEDED(nr) && spec && szspec) + nr = spec->SetNativePath(szspec); + Py_END_ALLOW_THREADS; + if (NS_FAILED(nr) || spec==nsnull) + return PyXPCOM_BuildPyException(nr); + return Py_nsISupports::PyObjectFromInterface(spec, NS_GET_IID(nsIFileSpec), PR_TRUE); +} + +static PyObject * +PyXPCOMMethod_NS_GetGlobalComponentManager(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) + return NULL; + nsIComponentManager* cm; + nsresult rv; + Py_BEGIN_ALLOW_THREADS; + rv = NS_GetGlobalComponentManager(&cm); + Py_END_ALLOW_THREADS; + if ( NS_FAILED(rv) ) + return PyXPCOM_BuildPyException(rv); + // NOTE - NS_GetGlobalComponentManager DOES NOT ADD A REFCOUNT + // (naughty, naughty) - we we explicitly ask our converter to + // add one, even tho this is not the common pattern. + + // Return a type based on the IID + // Can not auto-wrap the interface info manager as it is critical to + // building the support we need for autowrap. + return Py_nsISupports::PyObjectFromInterface(cm, NS_GET_IID(nsIComponentManager), PR_TRUE, PR_FALSE); +} + +static PyObject * +PyXPCOMMethod_GetGlobalServiceManager(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) + return NULL; + nsIServiceManager* sm; + nsresult rv; + Py_BEGIN_ALLOW_THREADS; + rv = nsServiceManager::GetGlobalServiceManager(&sm); + Py_END_ALLOW_THREADS; + if ( NS_FAILED(rv) ) + return PyXPCOM_BuildPyException(rv); + // NOTE - GetGlobalServiceManager DOES NOT ADD A REFCOUNT + // (naughty, naughty) - we we explicitly ask our converter to + // add one, even tho this is not the common pattern. + + // Return a type based on the IID + // Can not auto-wrap the interface info manager as it is critical to + // building the support we need for autowrap. + return Py_nsISupports::PyObjectFromInterface(sm, NS_GET_IID(nsIServiceManager), PR_TRUE, PR_FALSE); +} + + + +static PyObject * +PyXPCOMMethod_XPTI_GetInterfaceInfoManager(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) + return NULL; + nsIInterfaceInfoManager* im; + Py_BEGIN_ALLOW_THREADS; + im = XPTI_GetInterfaceInfoManager(); + Py_END_ALLOW_THREADS; + if ( im == nsnull ) + return PyXPCOM_BuildPyException(NS_ERROR_FAILURE); + + /* Return a type based on the IID (with no extra ref) */ + // Can not auto-wrap the interface info manager as it is critical to + // building the support we need for autowrap. + return Py_nsISupports::PyObjectFromInterface(im, NS_GET_IID(nsIInterfaceInfoManager), PR_FALSE, PR_FALSE); +} + +static PyObject * +PyXPCOMMethod_XPTC_InvokeByIndex(PyObject *self, PyObject *args) +{ + PyObject *obIS, *obParams; + nsCOMPtr pis; + int index; + + // We no longer rely on PyErr_Occurred() for our error state, + // but keeping this assertion can't hurt - it should still always be true! + NS_WARN_IF_FALSE(!PyErr_Occurred(), "Should be no pending Python error!"); + + if (!PyArg_ParseTuple(args, "OiO", &obIS, &index, &obParams)) + return NULL; + + // Ack! We must ask for the "native" interface supported by + // the object, not specifically nsISupports, else we may not + // back the same pointer (eg, Python, following identity rules, + // will return the "original" gateway when QI'd for nsISupports) + if (!Py_nsISupports::InterfaceFromPyObject( + obIS, + Py_nsIID_NULL, + getter_AddRefs(pis), + PR_FALSE)) + return NULL; + + PyXPCOM_InterfaceVariantHelper arg_helper; + if (!arg_helper.Init(obParams)) + return NULL; + + if (!arg_helper.FillArray()) + return NULL; + + nsresult r; + Py_BEGIN_ALLOW_THREADS; + r = XPTC_InvokeByIndex(pis, index, arg_helper.m_num_array, arg_helper.m_var_array); + Py_END_ALLOW_THREADS; + if ( NS_FAILED(r) ) + return PyXPCOM_BuildPyException(r); + + return arg_helper.MakePythonResult(); +} + +static PyObject * +PyXPCOMMethod_WrapObject(PyObject *self, PyObject *args) +{ + PyObject *ob, *obIID; + if (!PyArg_ParseTuple(args, "OO", &ob, &obIID)) + return NULL; + + nsIID iid; + if (!Py_nsIID::IIDFromPyObject(obIID, &iid)) + return NULL; + + nsISupports *ret = NULL; + nsresult r = PyXPCOM_XPTStub::CreateNew(ob, iid, (void **)&ret); + if ( NS_FAILED(r) ) + return PyXPCOM_BuildPyException(r); + + // _ALL_ wrapped objects are associated with a weak-ref + // to their "main" instance. + AddDefaultGateway(ob, ret); // inject a weak reference to myself into the instance. + + // Now wrap it in an interface. + return Py_nsISupports::PyObjectFromInterface(ret, iid, PR_FALSE); +} + +// @pymethod int|pythoncom|_GetInterfaceCount|Retrieves the number of interface objects currently in existance +static PyObject * +PyXPCOMMethod_GetInterfaceCount(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":_GetInterfaceCount")) + return NULL; + return PyInt_FromLong(_PyXPCOM_GetInterfaceCount()); + // @comm If is occasionally a good idea to call this function before your Python program + // terminates. If this function returns non-zero, then you still have PythonCOM objects + // alive in your program (possibly in global variables). +} + +// @pymethod int|pythoncom|_GetGatewayCount|Retrieves the number of gateway objects currently in existance +static PyObject * +PyXPCOMMethod_GetGatewayCount(PyObject *self, PyObject *args) +{ + // @comm This is the number of Python object that implement COM servers which + // are still alive (ie, serving a client). The only way to reduce this count + // is to have the process which uses these PythonCOM servers release its references. + if (!PyArg_ParseTuple(args, ":_GetGatewayCount")) + return NULL; + return PyInt_FromLong(_PyXPCOM_GetGatewayCount()); +} + +static PyObject * +PyXPCOMMethod_NS_ShutdownXPCOM(PyObject *self, PyObject *args) +{ + // @comm This is the number of Python object that implement COM servers which + // are still alive (ie, serving a client). The only way to reduce this count + // is to have the process which uses these PythonCOM servers release its references. + if (!PyArg_ParseTuple(args, ":NS_ShutdownXPCOM")) + return NULL; + nsresult nr; + Py_BEGIN_ALLOW_THREADS; + nr = NS_ShutdownXPCOM(nsnull); + Py_END_ALLOW_THREADS; + + // Dont raise an exception - as we are probably shutting down + // and dont really case - just return the status + return PyInt_FromLong(nr); +} + +static NS_DEFINE_CID(kProxyObjectManagerCID, NS_PROXYEVENT_MANAGER_CID); + +// A hack to work around their magic constants! +static PyObject * +PyXPCOMMethod_GetProxyForObject(PyObject *self, PyObject *args) +{ + PyObject *obQueue, *obIID, *obOb; + int flags; + if (!PyArg_ParseTuple(args, "OOOi", &obQueue, &obIID, &obOb, &flags)) + return NULL; + nsIID iid; + if (!Py_nsIID::IIDFromPyObject(obIID, &iid)) + return NULL; + nsCOMPtr pob; + if (!Py_nsISupports::InterfaceFromPyObject(obOb, iid, getter_AddRefs(pob), PR_FALSE)) + return NULL; + nsIEventQueue *pQueue = NULL; + nsIEventQueue *pQueueRelease = NULL; + + if (PyInt_Check(obQueue)) { + pQueue = (nsIEventQueue *)PyInt_AsLong(obQueue); + } else { + if (!Py_nsISupports::InterfaceFromPyObject(obQueue, NS_GET_IID(nsIEventQueue), (nsISupports **)&pQueue, PR_TRUE)) + return NULL; + pQueueRelease = pQueue; + } + + nsresult rv_proxy; + nsISupports *presult = nsnull; + Py_BEGIN_ALLOW_THREADS; + NS_WITH_SERVICE(nsIProxyObjectManager, + proxyMgr, + kProxyObjectManagerCID, + &rv_proxy); + + if ( NS_SUCCEEDED(rv_proxy) ) { + rv_proxy = proxyMgr->GetProxyForObject(pQueue, + iid, + pob, + flags, + (void **)&presult); + } + if (pQueueRelease) + pQueueRelease->Release(); + Py_END_ALLOW_THREADS; + + PyObject *result; + if (NS_SUCCEEDED(rv_proxy) ) { + result = Py_nsISupports::PyObjectFromInterface(presult, iid, PR_FALSE); + } else { + result = PyXPCOM_BuildPyException(rv_proxy); + } + return result; +} + +PyObject *AllocateBuffer(PyObject *self, PyObject *args) +{ + int bufSize; + if (!PyArg_ParseTuple(args, "i", &bufSize)) + return NULL; + return PyBuffer_New(bufSize); +} + +PyObject *LogWarning(PyObject *self, PyObject *args) +{ + char *msg; + if (!PyArg_ParseTuple(args, "s", &msg)) + return NULL; + PyXPCOM_LogWarning("%s", msg); + Py_INCREF(Py_None); + return Py_None; +} + +PyObject *LogError(PyObject *self, PyObject *args) +{ + char *msg; + if (!PyArg_ParseTuple(args, "s", &msg)) + return NULL; + PyXPCOM_LogError("%s", msg); + Py_INCREF(Py_None); + return Py_None; +} + +extern PyObject *PyXPCOMMethod_IID(PyObject *self, PyObject *args); + +static struct PyMethodDef xpcom_methods[]= +{ + {"NS_LocateSpecialSystemDirectory", PyXPCOMMethod_NS_LocateSpecialSystemDirectory, 1}, + {"NS_GetGlobalComponentManager", PyXPCOMMethod_NS_GetGlobalComponentManager, 1}, + {"NS_NewFileSpec", PyXPCOMMethod_NS_NewFileSpec, 1}, + {"XPTI_GetInterfaceInfoManager", PyXPCOMMethod_XPTI_GetInterfaceInfoManager, 1}, + {"XPTC_InvokeByIndex", PyXPCOMMethod_XPTC_InvokeByIndex, 1}, + {"GetGlobalServiceManager", PyXPCOMMethod_GetGlobalServiceManager, 1}, + {"IID", PyXPCOMMethod_IID, 1}, // IID is wrong - deprecated - not just IID, but CID, etc. + {"ID", PyXPCOMMethod_IID, 1}, // This is the official name. + {"NS_ShutdownXPCOM", PyXPCOMMethod_NS_ShutdownXPCOM, 1}, + {"WrapObject", PyXPCOMMethod_WrapObject, 1}, + {"_GetInterfaceCount", PyXPCOMMethod_GetInterfaceCount, 1}, + {"_GetGatewayCount", PyXPCOMMethod_GetGatewayCount, 1}, + {"getProxyForObject", PyXPCOMMethod_GetProxyForObject, 1}, + {"GetProxyForObject", PyXPCOMMethod_GetProxyForObject, 1}, + {"AllocateBuffer", AllocateBuffer, 1}, + {"LogWarning", LogWarning, 1}, + {"LogError", LogError, 1}, + { NULL } +}; + +//////////////////////////////////////////////////////////// +// Other helpers/global functions. +// +PRBool PyXPCOM_Globals_Ensure() +{ + PRBool rc = PR_TRUE; + + PyXPCOM_InterpreterState_Ensure(); + + // The exception object - we load it from .py code! + if (PyXPCOM_Error == NULL) { + rc = PR_FALSE; + PyObject *mod = NULL; + + mod = PyImport_ImportModule("xpcom"); + if (mod!=NULL) { + PyXPCOM_Error = PyObject_GetAttrString(mod, "Exception"); + Py_DECREF(mod); + } + rc = (PyXPCOM_Error != NULL); + } + if (!rc) + return rc; + + static PRBool bHaveInitXPCOM = PR_FALSE; + if (!bHaveInitXPCOM) { + nsCOMPtr thread_check; + // xpcom appears to assert if already initialized + // Is there an official way to determine this? + if (NS_FAILED(nsIThread::GetMainThread(getter_AddRefs(thread_check)))) { + // not already initialized. + + // We need to locate the Mozilla bin directory. +#ifdef XP_WIN + // On Windows this by using "xpcom.dll" + + char landmark[MAX_PATH+1]; + HMODULE hmod = GetModuleHandle("xpcom.dll"); + if (hmod==NULL) { + PyErr_SetString(PyExc_RuntimeError, "We dont appear to be linked against xpcom.dll!?!?"); + return PR_FALSE; + } + GetModuleFileName(hmod, landmark, sizeof(landmark)/sizeof(landmark[0])); + char *end = landmark + (strlen(landmark)-1); + while (end > landmark && *end != '\\') + end--; + if (end > landmark) *end = '\0'; + + nsCOMPtr ns_bin_dir; + NS_NewLocalFile(landmark, PR_FALSE, getter_AddRefs(ns_bin_dir)); + nsresult rv = NS_InitXPCOM(nsnull, ns_bin_dir); +#else + // Elsewhere, Mozilla can find it itself (we hope!) + nsresult rv = NS_InitXPCOM(nsnull, nsnull); +#endif // XP_WIN + if (NS_FAILED(rv)) { + PyErr_SetString(PyExc_RuntimeError, "The XPCOM subsystem could not be initialized"); + return PR_FALSE; + } + // Also set the "special directory" +#ifdef XP_WIN + nsFileSpec spec(landmark); + nsSpecialSystemDirectory::Set(nsSpecialSystemDirectory::OS_CurrentProcessDirectory, &spec); +#endif // XP_WIN + } + // Even if xpcom was already init, we want to flag it as init! + bHaveInitXPCOM = PR_TRUE; + } + return rc; +} + + +#define REGISTER_IID(t) { \ + PyObject *iid_ob = Py_nsIID::PyObjectFromIID(NS_GET_IID(t)); \ + PyDict_SetItemString(dict, "IID_"#t, iid_ob); \ + Py_DECREF(iid_ob); \ + } + +#define REGISTER_INT(val) { \ + PyObject *ob = PyInt_FromLong(val); \ + PyDict_SetItemString(dict, #val, ob); \ + Py_DECREF(ob); \ + } + + +//////////////////////////////////////////////////////////// +// The module init code. +// +extern "C" +#ifdef MS_WIN32 +__declspec(dllexport) +#endif +void +init_xpcom() { + PyObject *oModule; + + // ensure the framework has valid state to work with. + if (!PyXPCOM_Globals_Ensure()) + return; + + // Must force Python to start using thread locks + PyEval_InitThreads(); + + // Create the module and add the functions + oModule = Py_InitModule("_xpcom", xpcom_methods); + + PyObject *dict = PyModule_GetDict(oModule); + PyObject *pycom_Error = PyXPCOM_Error; + if (pycom_Error == NULL || PyDict_SetItemString(dict, "error", pycom_Error) != 0) + { + PyErr_SetString(PyExc_MemoryError, "can't define error"); + return; + } + PyDict_SetItemString(dict, "IIDType", (PyObject *)&Py_nsIID::type); + + REGISTER_IID(nsISupports); + REGISTER_IID(nsISupportsString); + REGISTER_IID(nsIModule); + REGISTER_IID(nsIFactory); + REGISTER_IID(nsIWeakReference); + REGISTER_IID(nsISupportsWeakReference); + // Register our custom interfaces. + + Py_nsISupports::InitType(); + Py_nsIComponentManager::InitType(dict); + Py_nsIInterfaceInfoManager::InitType(dict); + Py_nsIEnumerator::InitType(dict); + Py_nsISimpleEnumerator::InitType(dict); + Py_nsIInterfaceInfo::InitType(dict); + Py_nsIServiceManager::InitType(dict); + Py_nsIInputStream::InitType(dict); + + // We have special support for proxies - may as well add their constants! + REGISTER_INT(PROXY_SYNC); + REGISTER_INT(PROXY_ASYNC); + REGISTER_INT(PROXY_ALWAYS); +} diff --git a/mozilla/extensions/python/xpcom/test/output/test_com_exceptions b/mozilla/extensions/python/xpcom/test/output/test_com_exceptions new file mode 100644 index 00000000000..16e4bda15cf --- /dev/null +++ b/mozilla/extensions/python/xpcom/test/output/test_com_exceptions @@ -0,0 +1,8 @@ +test_com_exceptions +** Unhandled exception calling 'int8 do_short(in int16, inout int16, out int16, out retval int16);' +** Returning nsresult of NS_ERROR_FAILURE +** Unhandled exception calling 'int8 do_unsigned_short(in uint16, inout uint16, out uint16, out retval uint16);' +** Returning nsresult of NS_ERROR_FAILURE +** Unhandled exception calling 'int8 do_unsigned_long_long(in uint64, inout uint64, out uint64, out retval uint64);' +** Returning nsresult of NS_ERROR_FAILURE +The xpcom exception tests passed diff --git a/mozilla/extensions/python/xpcom/test/output/test_comfile b/mozilla/extensions/python/xpcom/test/output/test_comfile new file mode 100644 index 00000000000..8de43addd96 --- /dev/null +++ b/mozilla/extensions/python/xpcom/test/output/test_comfile @@ -0,0 +1,7 @@ +test_comfile +Open as string test worked. +Open as URL test worked. +File test using buffers worked. +Local file read test worked. +Read the correct data. +Chunks read the correct data. diff --git a/mozilla/extensions/python/xpcom/test/output/test_components b/mozilla/extensions/python/xpcom/test/output/test_components new file mode 100644 index 00000000000..4a6386e75d8 --- /dev/null +++ b/mozilla/extensions/python/xpcom/test/output/test_components @@ -0,0 +1,4 @@ +test_components +The interfaces object appeared to work! +The classes object appeared to work! +The ID function appeared to work! diff --git a/mozilla/extensions/python/xpcom/test/output/test_isupports_primitives b/mozilla/extensions/python/xpcom/test/output/test_isupports_primitives new file mode 100644 index 00000000000..7765ac217f9 --- /dev/null +++ b/mozilla/extensions/python/xpcom/test/output/test_isupports_primitives @@ -0,0 +1,2 @@ +test_isupports_primitives +The nsISupports primitive interface tests appeared to work diff --git a/mozilla/extensions/python/xpcom/test/output/test_misc b/mozilla/extensions/python/xpcom/test/output/test_misc new file mode 100644 index 00000000000..94e971f5805 --- /dev/null +++ b/mozilla/extensions/python/xpcom/test/output/test_misc @@ -0,0 +1,9 @@ +test_misc +Running all tests - use '-h' to see command-line options... +The netscape sample worked! +Enumerated all the ContractIDs +xpcom object hashing tests seemed to work +Dumping every interface I can find - please wait +(verbosity is turned off, so Im not actually going to print them) +Finished dumping all the interfaces. +The IID tests seemed to work diff --git a/mozilla/extensions/python/xpcom/test/output/test_streams b/mozilla/extensions/python/xpcom/test/output/test_streams new file mode 100644 index 00000000000..e81ef151a69 --- /dev/null +++ b/mozilla/extensions/python/xpcom/test/output/test_streams @@ -0,0 +1 @@ +test_streams diff --git a/mozilla/extensions/python/xpcom/test/output/test_test_component b/mozilla/extensions/python/xpcom/test/output/test_test_component new file mode 100644 index 00000000000..c57c1325637 --- /dev/null +++ b/mozilla/extensions/python/xpcom/test/output/test_test_component @@ -0,0 +1,4 @@ +test_test_component +Testing the Python.TestComponent component +The Python test component worked! +Javascript could successfully use the Python test component. diff --git a/mozilla/extensions/python/xpcom/test/output/test_weakreferences b/mozilla/extensions/python/xpcom/test/output/test_weakreferences new file mode 100644 index 00000000000..b337d26a5e9 --- /dev/null +++ b/mozilla/extensions/python/xpcom/test/output/test_weakreferences @@ -0,0 +1,2 @@ +test_weakreferences +Weak-reference tests appear to have worked! diff --git a/mozilla/extensions/python/xpcom/test/regrtest.py b/mozilla/extensions/python/xpcom/test/regrtest.py new file mode 100644 index 00000000000..a16a900ebfe --- /dev/null +++ b/mozilla/extensions/python/xpcom/test/regrtest.py @@ -0,0 +1,18 @@ +# Copyright (c) 2000-2001 ActiveState Tool Corporation. +# See the file LICENSE.txt for licensing information. + +# regrtest.py +# +# The Regression Tests for the xpcom package. +import os +import sys + +import test.regrtest # The standard Python test suite. + +path = os.path.abspath(os.path.split(sys.argv[0])[0]) +tests = [] +for arg in sys.argv[1:]: + if arg[0] not in "-/": + tests.append(arg) +tests = tests or test.regrtest.findtests(path, []) +test.regrtest.main(tests, path) \ No newline at end of file diff --git a/mozilla/extensions/python/xpcom/test/test_com_exceptions.py b/mozilla/extensions/python/xpcom/test/test_com_exceptions.py new file mode 100644 index 00000000000..dc0d2c52a96 --- /dev/null +++ b/mozilla/extensions/python/xpcom/test/test_com_exceptions.py @@ -0,0 +1,70 @@ +# Copyright (c) 2000-2001 ActiveState Tool Corporation. +# See the file LICENSE.txt for licensing information. + +# Test pyxpcom exception. + +from xpcom import components, nsError, ServerException, COMException +from xpcom.server import WrapObject + +class PythonFailingComponent: + # Re-use the test interface for this test. + _com_interfaces_ = components.interfaces.nsIPythonTestInterfaceExtra + + def do_boolean(self, p1, p2): + # This should cause the caller to see a "silent" NS_ERROR_FAILURE exception. + raise ServerException() + + def do_octet(self, p1, p2): + # This should cause the caller to see a "silent" NS_ERROR_NOT_IMPLEMENTED exception. + raise ServerException(nsError.NS_ERROR_NOT_IMPLEMENTED) + + def do_short(self, p1, p2): + # This should cause the caller to see a "debug" NS_ERROR_FAILURE exception. + raise COMException(nsError.NS_ERROR_NOT_IMPLEMENTED) + + def do_unsigned_short(self, p1, p2): + # This should cause the caller to see a "debug" NS_ERROR_FAILURE exception. + raise "Foo" + + def do_long(self, p1, p2): + # This should cause the caller to see a "silent" NS_ERROR_FAILURE exception. + raise ServerException + + def do_unsigned_long(self, p1, p2): + # This should cause the caller to see a "silent" NS_ERROR_NOT_IMPLEMENTED exception. + raise ServerException, nsError.NS_ERROR_NOT_IMPLEMENTED + + def do_long_long(self, p1, p2): + # This should cause the caller to see a "silent" NS_ERROR_NOT_IMPLEMENTED exception. + raise ServerException, (nsError.NS_ERROR_NOT_IMPLEMENTED, "testing") + + def do_unsigned_long_long(self, p1, p2): + # Report of a crash in this case - test it! + raise ServerException, "A bad exception param" + +def _testit(expected_errno, func, *args): + try: + apply(func, args) + except COMException, what: + if what.errno != expected_errno: + raise + +def test(): + # For the benefit of the test suite, we print some reassuring messages. + import sys + sys.__stderr__.write("***** NOTE: Three tracebacks below this is normal\n") + ob = WrapObject( PythonFailingComponent(), components.interfaces.nsIPythonTestInterfaceExtra) + _testit(nsError.NS_ERROR_FAILURE, ob.do_boolean, 0, 0) + _testit(nsError.NS_ERROR_NOT_IMPLEMENTED, ob.do_octet, 0, 0) + _testit(nsError.NS_ERROR_FAILURE, ob.do_short, 0, 0) + _testit(nsError.NS_ERROR_FAILURE, ob.do_unsigned_short, 0, 0) + _testit(nsError.NS_ERROR_FAILURE, ob.do_long, 0, 0) + _testit(nsError.NS_ERROR_NOT_IMPLEMENTED, ob.do_unsigned_long, 0, 0) + _testit(nsError.NS_ERROR_NOT_IMPLEMENTED, ob.do_long_long, 0, 0) + _testit(nsError.NS_ERROR_FAILURE, ob.do_unsigned_long_long, 0, 0) + print "The xpcom exception tests passed" + # For the benefit of the test suite, some more reassuring messages. + sys.__stderr__.write("***** NOTE: Three tracebacks printed above this is normal\n") + sys.__stderr__.write("***** It is testing the Python XPCOM Exception semantics\n") + +test() \ No newline at end of file diff --git a/mozilla/extensions/python/xpcom/test/test_comfile.py b/mozilla/extensions/python/xpcom/test/test_comfile.py new file mode 100644 index 00000000000..e78c33d6bbd --- /dev/null +++ b/mozilla/extensions/python/xpcom/test/test_comfile.py @@ -0,0 +1,7 @@ +# Copyright (c) 2000-2001 ActiveState Tool Corporation. +# See the file LICENSE.txt for licensing information. + +"""Test the xpcom.file module.""" +# Called "test_comfile" as Python has a standard test called test_file :-( +import xpcom.file +xpcom.file._TestAll() \ No newline at end of file diff --git a/mozilla/extensions/python/xpcom/test/test_component/py_test_component.html b/mozilla/extensions/python/xpcom/test/test_component/py_test_component.html new file mode 100644 index 00000000000..dd6f8bdb44b --- /dev/null +++ b/mozilla/extensions/python/xpcom/test/test_component/py_test_component.html @@ -0,0 +1,149 @@ + + + +
Python Component Sample + +

+
+Last modified + +

+ +

XPConnect allows JavaScript +to transparantly access and manipulate XPCOM objects; + +

Big Deal, I hear you say! But it also works for Python!!! + +

+This sample demonstrates accessing a XPCOM object through XPConnect. +The JavaScript executed when this page loads creates an instance +of the Python object by +using the Components object, then accesses it through +the nsISample interface by calling QueryInterface: +
+

+netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+var sample = Components.classes["component://mozilla/sample/sample-world"].createInstance();
+sample = sample.QueryInterface(Components.interfaces.nsISample);
+
+ +

+The buttons on the form are connected to JavaScript event handlers which +call the methods defined in Python + + +

Compiling the idl + +

The XPIDL compiler (xpidl on Unix, xpidl.exe on Windows, and a CodeWarrior plugin on Mac) +is compiled at build time (except on Mac) thus +you will have to build mozilla in order to test this out. If you +have already built mozilla then the compiler will be located at mozilla\dist\WIN32_D.OBJ\bin\xpidl.exe. + +

Once you have the XPIDL compiler enter the following command at your +prompt: +
D:\whereever\xpcom\test\test_component>d:\mozilla\dist\WIN32_D.OBJ\bin\xpidl -I +d:\mozilla\dist\idl -m typelib py_test_component.idl. You must then copy the generated .xpt file +to the mozilla component directory. + +

The -I d:\mozilla\dist\idl points the compiler to the folder +containing the other idl files, needed because nsISample.idl inherits from +nsISupports.idl. The -m typelib instruction tells the compiler +to build the .XPT typelib file.. + +

+For more information on compilation see the xpidl +compiler page. + +

Running the sample +

NOTE: This doesnt work for me - I get an access denied error using XPConnect! +

Using Mozilla, load this file. Pay attention +to the console when clicking "write". + + + + +

+

+ + + + + + + +
+ +

+JavaScript and form source: + + +

+<script>
+netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+var sample = Components.classes["component://Python.TestComponent"].createInstance();
+sample = sample.QueryInterface(Components.interfaces.nsIPythonTestInterface);
+dump("sample = " + sample + "\n");
+
+function get()
+{
+  var field = document.getElementById('Value');
+  field.value = sample.str_value;
+}
+
+function set()
+{
+  var field = document.getElementById('Value');
+  sample.str_value = field.value;
+}
+
+function poke()
+{
+  var field = document.getElementById('Value');
+  sample.poke(field.value);
+}
+
+function write()
+{
+  sample.writeValue("here is what I'm writing: ");
+}
+</script>
+
+<form name="form">
+<input type="button" value="Get" onclick="get();">
+<input type="button" value="Set" onclick="set();">
+<input type="button" value="Poke" onclick="poke();">
+<input type="text" id="Value">
+<input type="button" value="Write" onclick="write();">
+<form>
+
+
diff --git a/mozilla/extensions/python/xpcom/test/test_component/py_test_component.idl b/mozilla/extensions/python/xpcom/test/test_component/py_test_component.idl new file mode 100644 index 00000000000..9597b730044 --- /dev/null +++ b/mozilla/extensions/python/xpcom/test/test_component/py_test_component.idl @@ -0,0 +1,183 @@ +/* Copyright (c) 2000-2001 ActiveState Tool Corporation. + See the file LICENSE.txt for licensing information. */ + +// NOTE: This is a TEST interface, not a DEMO interface :-) +// We try to get as many data-types etc exposed, meaning this +// doesnt really make a good demo of a "simple component" +#include "nsISupports.idl" + +[scriptable, uuid(1ECAED4F-E4D5-4ee7-ABF0-7D72AE1441D7)] +interface nsIPythonTestInterface : nsISupports +{ + // Some constants for us to test - one for every type supported by xpidl + const short One = 1; + const long Two = 2; + const long MinusOne = -1; + const long BigLong = 0x7FFFFFFF; + const long BigULong = 0xFFFFFFFF; + + // Declare every type supported as an attribute. + attribute boolean boolean_value; // PRBool + attribute octet octet_value; // PRUint8 + attribute short short_value; // PRInt16 + attribute unsigned short ushort_value; // PRUint16 + attribute long long_value; // PRInt32 + attribute unsigned long ulong_value; // PRUint32 + attribute long long long_long_value; // PRInt64 + attribute unsigned long long ulong_long_value; // PRUint64 + attribute float float_value; // float + attribute double double_value; // double + attribute char char_value; // char + attribute wchar wchar_value; // PRUnichar + attribute string string_value; // char * + attribute wstring wstring_value; // PRUnichar* + attribute nsIIDRef iid_value; // an IID + attribute nsIPythonTestInterface interface_value; // A specific interface + attribute nsISupports isupports_value; // A generic interface + + // Declare every type supported as a method with an "in", "in/out" and "out" params + boolean do_boolean(in boolean p1, inout boolean p2, out boolean p3); + octet do_octet(in octet p1, inout octet p2, out octet p3); + short do_short(in short p1, inout short p2, out short p3); + unsigned short do_unsigned_short(in unsigned short p1, inout unsigned short p2, out unsigned short p3); + long do_long(in long p1, inout long p2, out long p3); + unsigned long do_unsigned_long(in unsigned long p1, inout unsigned long p2, out unsigned long p3); + long long do_long_long(in long long p1, inout long long p2, out long long p3); + unsigned long long do_unsigned_long_long(in unsigned long long p1, inout unsigned long long p2, out unsigned long long p3); + float do_float(in float p1, inout float p2, out float p3); + double do_double(in double p1, inout double p2, out double p3); + char do_char(in char p1, inout char p2, out char p3); + wchar do_wchar(in wchar p1, inout wchar p2, out wchar p3); + string do_string(in string p1, inout string p2, out string p3); + wstring do_wstring(in wstring p1, inout wstring p2, out wstring p3); + nsIIDRef do_nsIIDRef(in nsIIDRef p1, inout nsIIDRef p2, out nsIIDRef p3); + nsIPythonTestInterface do_nsIPythonTestInterface(in nsIPythonTestInterface p1, inout nsIPythonTestInterface p2, out nsIPythonTestInterface p3); + nsISupports do_nsISupports(in nsISupports p1, inout nsISupports p2, out nsISupports p3); + void do_nsISupportsIs(in nsIIDRef iid, [iid_is(iid),retval] out nsQIResult result); +// Do I really need these?? +// void do_nsISupportsIs2(inout nsIIDRef iid, [iid_is(iid)] inout nsQIResult result); +// void do_nsISupportsIs3(out nsIIDRef iid, [iid_is(iid)] inout nsQIResult result); +// void do_nsISupportsIs4(out nsIIDRef iid, [iid_is(iid)] out nsQIResult result); +}; + +// Another interface - we use another interface purely for testing purposes - +// We ensure that the entire interface hierarcy is available correctly. +[scriptable, uuid(B38D1538-FE92-42c3-831F-285242EDEEA4)] +interface nsIPythonTestInterfaceExtra : nsIPythonTestInterface +{ + // These were copied from the XPCOM test 'xpctest.idl' + // (and a few extras added) + void MultiplyEachItemInIntegerArray( + in PRInt32 val, + in PRUint32 count, + [array, size_is(count)] inout PRInt32 valueArray); + void MultiplyEachItemInIntegerArrayAndAppend( + in PRInt32 val, + inout PRUint32 count, + [array, size_is(count)] inout PRInt32 valueArray); + + // Note that this method shares a single "size_is" between 2 params! + void CompareStringArrays([array, size_is(count)] in string arr1, + [array, size_is(count)] in string arr2, + in unsigned long count, + [retval] out short result); + + void DoubleStringArray(inout PRUint32 count, + [array, size_is(count)] inout string valueArray); + void ReverseStringArray(in PRUint32 count, + [array, size_is(count)] inout string valueArray); + + // One count, one inout array. + void DoubleString(inout PRUint32 count, + [size_is(count)] inout string str); + // One in count and in array, plus out count and out array + void DoubleString2(in PRUint32 in_count, [size_is(in_count)] in string in_str, + out PRUint32 out_count, [size_is(out_count)] out string out_str); + // As per DoubleString2, but out string also marked retval + void DoubleString3(in PRUint32 in_count, [size_is(in_count)] in string in_str, + out PRUint32 out_count, [size_is(out_count), retval] out string out_str); + // One in array, one out array, one share inout count. + void DoubleString4([size_is(count)] in string in_str, inout PRUint32 count, [size_is(count)] out string out_str); + // UpString defines the count as only "in" - meaning the result must be the same size + void UpString(in PRUint32 count, + [size_is(count)] inout string str); + // UpString2 defines count as only "in", and a string as only "out" + void UpString2(in PRUint32 count, + [size_is(count)] in string in_str, + [size_is(count)]out string out_str); + // Test we can get an "out" array with an "in" size (and the size is not used anywhere as a size for an in!) + void GetFixedString(in PRUint32 count, [size_is(count)]out string out_str); + + void DoubleWideString(inout PRUint32 count, + [size_is(count)] inout wstring str); + void DoubleWideString2(in PRUint32 in_count, [size_is(in_count)] in wstring in_str, + out PRUint32 out_count, [size_is(out_count)] out wstring out_str); + void DoubleWideString3(in PRUint32 in_count, [size_is(in_count)] in wstring in_str, + out PRUint32 out_count, [size_is(out_count), retval] out wstring out_str); + void DoubleWideString4([size_is(count)] in wstring in_str, inout PRUint32 count, [size_is(count)] out wstring out_str); + // UpWideString defines the count as only "in" - meaning the result must be the same size + void UpWideString(in PRUint32 count, + [size_is(count)] inout wstring str); + // UpWideString2 defines count as only "in", and a string as only "out" + void UpWideString2(in PRUint32 count, + [size_is(count)] in wstring in_str, + [size_is(count)]out wstring out_str); + // Test we can get an "out" array with an "in" size (and the size is not used anywhere as a size for an in!) + void GetFixedWideString(in PRUint32 count, [size_is(count)]out string out_str); + + void GetStrings(out PRUint32 count, + [retval, array, size_is(count)] out string str); + + void UpOctetArray(inout PRUint32 count, + [array, size_is(count)] inout PRUint8 data); + + void UpOctetArray2(inout PRUint32 count, + [array, size_is(count)] inout PRUint8 data); + + // Arrays of interfaces + void CheckInterfaceArray(in PRUint32 count, + [array, size_is(count)] in nsISupports data, + [retval] out PRBool all_non_null); + void GetInterfaceArray(out PRUint32 count, + [array, size_is(count)] out nsISupports data); + void ExtendInterfaceArray(inout PRUint32 count, + [array, size_is(count)] inout nsISupports data); + + // Arrays of IIDs + void CheckIIDArray(in PRUint32 count, + [array, size_is(count)] in nsIIDRef data, + [retval] out PRBool all_mine); + void GetIIDArray(out PRUint32 count, + [array, size_is(count)] out nsIIDRef data); + void ExtendIIDArray(inout PRUint32 count, + [array, size_is(count)] inout nsIIDRef data); + + // More specific tests. + // Test our count param can be shared as an "in" param. + void SumArrays(in PRUint32 count, [array, size_is(count)]in PRInt32 array1, [array, size_is(count)]in PRInt32 array2, [retval]out PRInt32 result); + // Test our count param can be shared as an "out" param. + void GetArrays(out PRUint32 count, [array, size_is(count)]out PRInt32 array1, [array, size_is(count)]out PRInt32 array2); + // Test we can get an "out" array with an "in" size (and the size is not used anywhere as a size for an in!) + void GetFixedArray(in PRUint32 count, [array, size_is(count)]out PRInt32 array1); + // Test our "in" count param can be shared as one "in", plus one "out" param. + void CopyArray(in PRUint32 count, [array, size_is(count)]in PRInt32 array1, [array, size_is(count)]out PRInt32 array2); + // Test our "in-out" count param can be shared as one "in", plus one "out" param. + void CopyAndDoubleArray(inout PRUint32 count, [array, size_is(count)]in PRInt32 array1, [array, size_is(count)]out PRInt32 array2); + // Test our "in-out" count param can be shared as one "in", plus one "in-out" param. + void AppendArray(inout PRUint32 count, [array, size_is(count)]in PRInt32 array1, [array, size_is(count)]inout PRInt32 array2); +}; + +// DOM String support is a "recent" (01/2001) addition to XPCOM. These test +// have their own interface for no real good reason ;-) +[scriptable, uuid(657ae651-a973-4818-8c06-f4b948b3d758)] +interface nsIPythonTestInterfaceDOMStrings : nsIPythonTestInterfaceExtra +{ + DOMString GetDOMStringResult(); + void GetDOMStringOut([retval] out DOMString s); + PRUint32 GetDOMStringLength(in DOMString s); + PRUint32 GetDOMStringRefLength(in DOMStringRef s); + PRUint32 GetDOMStringPtrLength(in DOMStringPtr s); + void ConcatDOMStrings(in DOMString s1, in DOMString s2, out DOMString ret); + attribute DOMString domstring_value; + readonly attribute DOMString domstring_value_ro; +}; diff --git a/mozilla/extensions/python/xpcom/test/test_component/py_test_component.py b/mozilla/extensions/python/xpcom/test/test_component/py_test_component.py new file mode 100644 index 00000000000..288fdcdca38 --- /dev/null +++ b/mozilla/extensions/python/xpcom/test/test_component/py_test_component.py @@ -0,0 +1,344 @@ +# Copyright (c) 2000-2001 ActiveState Tool Corporation. +# See the file LICENSE.txt for licensing information. + +# NOTE: This is a TEST interface, not a DEMO interface :-) +# We try to get as many data-types etc exposed, meaning this +# doesnt really make a good demo of a "simple component" + + +from xpcom import components, verbose + +class PythonTestComponent: + # Note we only list the "child" interface, not our intermediate interfaces + # (which we must, by definition, also support) + _com_interfaces_ = components.interfaces.nsIPythonTestInterfaceDOMStrings + _reg_clsid_ = "{7EE4BDC6-CB53-42c1-A9E4-616B8E012ABA}" + _reg_contractid_ = "Python.TestComponent" + def __init__(self): + self.boolean_value = 1 + self.octet_value = 2 + self.short_value = 3 + self.ushort_value = 4 + self.long_value = 5 + self.ulong_value = 6 + self.long_long_value = 7 + self.ulong_long_value = 8 + self.float_value = 9.0 + self.double_value = 10.0 + self.char_value = "a" + self.wchar_value = "b" + self.string_value = "cee" + self.wstring_value = "dee" + self.iid_value = self._reg_clsid_ + self.interface_value = None + self.isupports_value = None + self.domstring_value = "dom" + + def __del__(self): + if verbose: + print "Python.TestComponent: __del__ method called - object is destructing" + + def do_boolean(self, p1, p2): + # boolean do_boolean(in boolean p1, inout boolean p2, out boolean p3); + ret = p1 ^ p2 + return ret, not ret, ret + + def do_octet(self, p1, p2): + # octet do_octet(in octet p1, inout octet p2, out octet p3); + return p1+p2, p1-p2, p1*p2 + + def do_short(self, p1, p2): + # short do_short(in short p1, inout short p2, out short p3); + return p1+p2, p1-p2, p1*p2 + + def do_unsigned_short(self, p1, p2): + # unsigned short do_unsigned_short(in unsigned short p1, inout unsigned short p2, out unsigned short p3); + return p1+p2, p1-p2, p1*p2 + def do_long(self, p1, p2): + # long do_long(in long p1, inout long p2, out long p3); + return p1+p2, p1-p2, p1*p2 + + def do_unsigned_long(self, p1, p2): + # unsigned long do_unsigned_long(in unsigned long p1, inout unsigned long p2, out unsigned long p3); + return p1+p2, p1-p2, p1*p2 + def do_long_long(self, p1, p2): + # long long do_long_long(in long long p1, inout long long p2, out long long p3); + return p1+p2, p1-p2, p1*p2 + def do_unsigned_long_long(self, p1, p2): + # unsigned long long do_unsigned_long_long(in unsigned long long p1, inout unsigned long long p2, out unsigned long long p3); + return p1+p2, p1-p2, p1*p2 + def do_float(self, p1, p2): + # float do_float(in float p1, inout float p2, out float p3); + return p1+p2, p1-p2, p1*p2 + def do_double(self, p1, p2): + # double do_double(in double p1, inout double p2, out double p3); + return p1+p2, p1-p2, p1*p2 + def do_char(self, p1, p2): + # char do_char(in char p1, inout char p2, out char p3); + return chr(ord(p1)+ord(p2)), p2, p1 + def do_wchar(self, p1, p2): + # wchar do_wchar(in wchar p1, inout wchar p2, out wchar p3); + return chr(ord(p1)+ord(p2)), p2, p1 + def do_string(self, p1, p2): + # string do_string(in string p1, inout string p2, out string p3); + ret = "" + if p1 is not None: ret = ret + p1 + if p2 is not None: ret = ret + p2 + return ret, p1, p2 + def do_wstring(self, p1, p2): + # wstring do_wstring(in wstring p1, inout wstring p2, out wstring p3); + ret = u"" + if p1 is not None: ret = ret + p1 + if p2 is not None: ret = ret + p2 + return ret, p1, p2 + def do_nsIIDRef(self, p1, p2): + # nsIIDRef do_nsIIDRef(in nsIIDRef p1, inout nsIIDRef p2, out nsIIDRef p3); + return p1, self._reg_clsid_, p2 + def do_nsIPythonTestInterface(self, p1, p2): + # nsIPythonTestInterface do_nsIPythonTestInterface(in nsIPythonTestInterface p1, inout nsIPythonTestInterface p2, out nsIPythonTestInterface p3); + return p2, p1, self + def do_nsISupports(self, p1, p2): + # nsISupports do_nsISupports(in nsISupports p1, inout nsISupports p2, out nsISupports p3); + return self, p1, p2 + def do_nsISupportsIs(self, iid): + # void do_nsISupportsIs(in nsIIDRef iid, [iid_is(iid),retval] out nsQIResult result) + # Note the framework does the QI etc on us, so there is no real point me doing it. + # (However, user code _should_ do the QI - otherwise any errors are deemed "internal" (as they + # are raised by the C++ framework), and therefore logged to the console, etc. + # A user QI allows the user to fail gracefully, whatever gracefully means for them! + return self +# Do I really need these?? +## def do_nsISupportsIs2(self, iid, interface): +## # void do_nsISupportsIs2(inout nsIIDRef iid, [iid_is(iid),retval] inout nsQIResult result); +## return iid, interface +## def do_nsISupportsIs3(self, interface): +## # void do_nsISupportsIs3(out nsIIDRef iid, [iid_is(iid)] inout nsQIResult result); +## return self._com_interfaces_, interface +## def do_nsISupportsIs4(self): +## # void do_nsISupportsIs4(out nsIIDRef iid, [iid_is(iid)] out nsQIResult result); +## return self._com_interfaces_, self + + # Methods from the nsIPythonTestInterfaceExtra interface + # + def MultiplyEachItemInIntegerArray(self, val, valueArray): + # void MultiplyEachItemInIntegerArray( + # in PRInt32 val, + # in PRUint32 count, + # [array, size_is(count)] inout PRInt32 valueArray); + # NOTE - the "sizeis" params are never passed to or returned from Python! + results = [] + for item in valueArray: + results.append(item * val) + return results + def MultiplyEachItemInIntegerArrayAndAppend(self, val, valueArray): + #void MultiplyEachItemInIntegerArrayAndAppend( + # in PRInt32 val, + # inout PRUint32 count, + # [array, size_is(count)] inout PRInt32 valueArray); + results = valueArray[:] + for item in valueArray: + results.append(item * val) + return results + def DoubleStringArray(self, valueArray): + # void DoubleStringArray(inout PRUint32 count, + # [array, size_is(count)] inout string valueArray); + results = [] + for item in valueArray: + results.append(item * 2) + return results + + def ReverseStringArray(self, valueArray): + # void ReverseStringArray(in PRUint32 count, + # [array, size_is(count)] inout string valueArray); + valueArray.reverse() + return valueArray + + # Note that this method shares a single "size_is" between 2 params! + def CompareStringArrays(self, ar1, ar2): + # void CompareStringArrays([array, size_is(count)] in string arr1, + # [array, size_is(count)] in string arr2, + # in unsigned long count, + # [retval] out short result); + return cmp(ar1, ar2) + + def DoubleString(self, val): + # void DoubleString(inout PRUint32 count, + # [size_is(count)] inout string str); + return val * 2 + def DoubleString2(self, val): + # void DoubleString2(in PRUint32 in_count, [size_is(in_count)] in string in_str, + # out PRUint32 out_count, [size_is(out_count)] out string out_str); + return val * 2 + def DoubleString3(self, val): + # void DoubleString3(in PRUint32 in_count, [size_is(in_count)] in string in_str, + # out PRUint32 out_count, [size_is(out_count), retval] string out_str); + return val * 2 + def DoubleString4(self, val): + # void DoubleString4([size_is(count)] in string in_str, inout PRUint32 count, [size_is(count)] out string out_str); + return val * 2 + def UpString(self, val): + # // UpString defines the count as only "in" - meaning the result must be the same size + # void UpString(in PRUint32 count, + # [size_is(count)] inout string str); + return val.upper() + UpString2 = UpString + # // UpString2 defines count as only "in", and a string as only "out" + # void UpString2(in PRUint32 count, + # [size_is(count)] inout string in_str, + # [size_is(count)]out string out_str); + + def GetFixedString(self, count): + # void GetFixedString(in PRUint32 count, [size_is(count)out string out_str); + return "A" * count + + # DoubleWideString functions are identical to DoubleString, except use wide chars! + def DoubleWideString(self, val): + return val * 2 + def DoubleWideString2(self, val): + return val * 2 + def DoubleWideString3(self, val): + return val * 2 + def DoubleWideString4(self, val): + return val * 2 + def UpWideString(self, val): + return val.upper() + UpWideString2 = UpWideString + + # Test we can get an "out" array with an "in" size (and the size is not used anywhere as a size for an in!) + def GetFixedWideString(self, count): + # void GetFixedWideString(in PRUint32 count, [size_is(count)out string out_str); + return u"A" * count + + def GetStrings(self): + # void GetStrings(out PRUint32 count, + # [retval, array, size_is(count)] out string str); + return "Hello from the Python test component".split() + # Some tests for our special "PRUint8" support. + def UpOctetArray( self, data ): + # void UpOctetArray(inout PRUint32 count, + # [array, size_is(count)] inout PRUint8 data); + return data.upper() + + def UpOctetArray2( self, data ): + # void UpOctetArray2(inout PRUint32 count, + # [array, size_is(count)] inout PRUint8 data); + data = data.upper() + # This time we return a list of integers. + return map( ord, data ) + + # Arrays of interfaces + def CheckInterfaceArray(self, interfaces): + # void CheckInterfaceArray(in PRUint32 count, + # [array, size_is(count)] in nsISupports data, + # [retval] out PRBool all_non_null); + ret = 1 + for i in interfaces: + if i is None: + ret = 0 + break + return ret + def GetInterfaceArray(self): + # void GetInterfaceArray(out PRUint32 count, + # [array, size_is(count)] out nsISupports data); + return self, self, self, None + def ExtendInterfaceArray(self, data): + # void ExtendInterfaceArray(inout PRUint32 count, + # [array, size_is(count)] inout nsISupports data); + return data * 2 + + # Arrays of IIDs + def CheckIIDArray(self, data): + # void CheckIIDArray(in PRUint32 count, + # [array, size_is(count)] in nsIIDRef data, + # [retval] out PRBool all_mine); + ret = 1 + for i in data: + if i!= self._com_interfaces_ and i != self._reg_clsid_: + ret = 0 + break + return ret + def GetIIDArray(self): + # void GetIIDArray(out PRUint32 count, + # [array, size_is(count)] out nsIIDRef data); + return self._com_interfaces_, self._reg_clsid_ + def ExtendIIDArray(self, data): + # void ExtendIIDArray(inout PRUint32 count, + # [array, size_is(count)] inout nsIIDRef data); + return data * 2 + + # Test our count param can be shared as an "in" param. + def SumArrays(self, array1, array2): + # void SumArrays(in PRUint32 count, [array, size_is(count)]in array1, [array, size_is(count)]in array2, [retval]result); + if len(array1)!=len(array2): + print "SumArrays - not expecting different lengths!" + result = 0 + for i in array1: + result = result + i + for i in array2: + result = result+i + return result + + # Test our count param can be shared as an "out" param. + def GetArrays(self): + # void GetArrays(out PRUint32 count, [array, size_is(count)]out array1, [array, size_is(count)]out array2); + return (1,2,3), (4,5,6) + # Test we can get an "out" array with an "in" size + def GetFixedArray(self, size): + # void GetFixedArray(in PRUint32 count, [array, size_is(count)]out PRInt32 array1]); + return 0 * size + + # Test our "in" count param can be shared as one "in", plus one "out" param. + def CopyArray(self, array1): + # void CopyArray(in PRUint32 count, [array, size_is(count)]in array1, [array, size_is(count)]out array2); + return array1 + # Test our "in-out" count param can be shared as one "in", plus one "out" param. + def CopyAndDoubleArray(self, array): + # void CopyAndDoubleArray(inout PRUint32 count, [array, size_is(count)]in array1, [array, size_is(count)]out array2); + return array + array + # Test our "in-out" count param can be shared as one "in", plus one "in-out" param. + def AppendArray(self, array1, array2): + # void AppendArray(inout PRUint32 count, [array, size_is(count)]in array1, [array, size_is(count)]inout array2); + rc = array1 + if array2 is not None: + rc.extend(array2) + return rc + + # Some tests for the "new" (Feb-2001) DOMString type. + def GetDOMStringResult( self ): + # Result: DOMString & + return "A DOM String" + def GetDOMStringOut( self ): + # Result: DOMString & + return "Another DOM String" + def GetDOMStringLength( self, param0 ): + # Result: uint32 + # In: param0: DOMString & + return len(param0) + + def GetDOMStringRefLength( self, param0 ): + # Result: uint32 + # In: param0: DOMString & + return len(param0) + + def GetDOMStringPtrLength( self, param0 ): + # Result: uint32 + # In: param0: DOMString * + return len(param0) + + def ConcatDOMStrings( self, param0, param1 ): + # Result: void - None + # In: param0: DOMString & + # In: param1: DOMString & + # Out: DOMString & + return param0 + param1 + def get_domstring_value( self ): + # Result: DOMString & + return self.domstring_value + def set_domstring_value( self, param0 ): + # Result: void - None + # In: param0: DOMString & + self.domstring_value = param0 + + def get_domstring_value_ro( self ): + # Result: DOMString & + return self.domstring_value \ No newline at end of file diff --git a/mozilla/extensions/python/xpcom/test/test_components.py b/mozilla/extensions/python/xpcom/test/test_components.py new file mode 100644 index 00000000000..af95b5e451b --- /dev/null +++ b/mozilla/extensions/python/xpcom/test/test_components.py @@ -0,0 +1,78 @@ +# Copyright (c) 2000-2001 ActiveState Tool Corporation. +# See the file LICENSE.txt for licensing information. + +"""Tests the "xpcom.components" object. +""" + +import xpcom.components + +if not __debug__: + raise RuntimeError, "This test uses assert, so must be run in debug mode" + +def test_interfaces(): + "Test the xpcom.components.interfaces object" + + iid = xpcom.components.interfaces.nsISupports + assert iid == xpcom._xpcom.IID_nsISupports, "Got the wrong IID!" + iid = xpcom.components.interfaces['nsISupports'] + assert iid == xpcom._xpcom.IID_nsISupports, "Got the wrong IID!" + + # Test dictionary semantics + num_fetched = num_nsisupports = 0 + for name, iid in xpcom.components.interfaces.items(): + num_fetched = num_fetched + 1 + if name == "nsISupports": + num_nsisupports = num_nsisupports + 1 + assert iid == xpcom._xpcom.IID_nsISupports, "Got the wrong IID!" + assert xpcom.components.interfaces[name] == iid + # Check all the lengths match. + assert len(xpcom.components.interfaces.keys()) == len(xpcom.components.interfaces.values()) == \ + len(xpcom.components.interfaces.items()) == len(xpcom.components.interfaces) == \ + num_fetched, "The collection lengths were wrong" + if num_nsisupports != 1: + print "Didnt find exactly 1 nsiSupports!" + print "The interfaces object appeared to work!" + +def test_classes(): + # Need a well-known contractID here? + prog_id = "@mozilla.org/filelocator;1" + clsid = xpcom.components.ID("{78043e01-e603-11d2-915f-f08a208628fc}") + + # Check we can create the instance (dont check we can do anything with it tho!) + klass = xpcom.components.classes[prog_id] + instance = klass.createInstance() + + # Test dictionary semantics + num_fetched = num_mine = 0 + for name, klass in xpcom.components.classes.items(): + num_fetched = num_fetched + 1 + if name == prog_id: + if klass.clsid != clsid: + print "Eeek - didn't get the correct IID - got", klass.clsid + num_mine = num_mine + 1 + +# xpcom appears to add charset info to the contractid!? +# assert xpcom.components.classes[name].contractid == prog_id, "Expected '%s', got '%s'" % (prog_id, xpcom.components.classes[name].contractid) + # Check all the lengths match. + if len(xpcom.components.classes.keys()) == len(xpcom.components.classes.values()) == \ + len(xpcom.components.classes.items()) == len(xpcom.components.classes) == \ + num_fetched: + pass + else: + raise RuntimeError, "The collection lengths were wrong" + if num_fetched <= 0: + raise RuntimeError, "Didnt get any classes!!!" + if num_mine != 1: + raise RuntimeError, "Didnt find exactly 1 of my contractid! (%d)" % (num_mine,) + print "The classes object appeared to work!" + +def test_id(): + id = xpcom.components.ID(str(xpcom._xpcom.IID_nsISupports)) + assert id == xpcom._xpcom.IID_nsISupports + print "The ID function appeared to work!" + + +# regrtest doesnt like if __name__=='__main__' blocks - it fails when running as a test! +test_interfaces() +test_classes() +test_id() diff --git a/mozilla/extensions/python/xpcom/test/test_isupports_primitives.py b/mozilla/extensions/python/xpcom/test/test_isupports_primitives.py new file mode 100644 index 00000000000..92a005346b1 --- /dev/null +++ b/mozilla/extensions/python/xpcom/test/test_isupports_primitives.py @@ -0,0 +1,116 @@ +# Copyright (c) 2000-2001 ActiveState Tool Corporation. +# See the file LICENSE.txt for licensing information. + +# Test our support for the interfaces defined in nsISupportsPrimitives.idl +# +# The framework supports nsISupportsString and nsISupportsWString, but +# only if our class doesnt provide explicit support. + +from xpcom import components + +class NoSupportsString: + _com_interfaces_ = [components.interfaces.nsISupports] + pass + +class ImplicitSupportsString: + _com_interfaces_ = [components.interfaces.nsISupports] + def __str__(self): + return "" + +class ExplicitSupportsString: + _com_interfaces_ = [components.interfaces.nsISupports, components.interfaces.nsISupportsString] + # __str__ will be ignored by XPCOM, as we have _explicit_ support. + def __str__(self): + return "" + # This is the one that will be used. + def toString(self): + return "" + +class ImplicitSupportsInt: + _com_interfaces_ = [components.interfaces.nsISupports] + def __int__(self): + return 99 + +class ExplicitSupportsInt: + _com_interfaces_ = [components.interfaces.nsISupportsPRInt32] + def get_data(self): + return 99 + +class ImplicitSupportsLong: + _com_interfaces_ = [components.interfaces.nsISupports] + def __long__(self): + return 99L + +class ExplicitSupportsLong: + _com_interfaces_ = [components.interfaces.nsISupportsPRInt64] + def get_data(self): + return 99 + +class ExplicitSupportsFloat: + _com_interfaces_ = [components.interfaces.nsISupportsDouble] + def get_data(self): + return 99.99 + +class ImplicitSupportsFloat: + _com_interfaces_ = [components.interfaces.nsISupports] + def __float__(self): + return 99.99 + +def test(): + import xpcom.server, xpcom.client + ob = xpcom.server.WrapObject( NoSupportsString(), components.interfaces.nsISupports) + if not str(ob).startswith("\0<", "a null >") # strings are NULL terminated!! + test_attribute(c, "string_value", "cee", "") + test_attribute(c, "string_value", "cee", u"dee") + test_attribute(c, "string_value", "cee", u"a null >\0<", "a null >") # strings are NULL terminated!! + test_attribute(c, "string_value", "cee", u"") + + test_attribute(c, "wstring_value", "dee", "cee") + test_attribute(c, "wstring_value", "dee", "a null >\0<", "a null >") # strings are NULL terminated!! + test_attribute(c, "wstring_value", "dee", "") + test_attribute(c, "wstring_value", "dee", really_big_string) + test_attribute(c, "wstring_value", "dee", u"cee") + test_attribute(c, "wstring_value", "dee", u"a null >\0<", "a null >") # strings are NULL terminated!! + test_attribute(c, "wstring_value", "dee", u"") + test_attribute(c, "wstring_value", "dee", really_big_wstring) + + test_attribute(c, "iid_value", component_iid, new_iid) + test_attribute(c, "iid_value", component_iid, str(new_iid), new_iid) + test_attribute(c, "iid_value", component_iid, xpcom._xpcom.IID(new_iid)) + + test_attribute_failure(c, "no_attribute", "boo", AttributeError) + + test_attribute(c, "interface_value", None, c) + test_attribute_failure(c, "interface_value", 2, TypeError) + + test_attribute(c, "isupports_value", None, c) + + # The methods + test_method(c.do_boolean, (0,1), (1,0,1)) + test_method(c.do_boolean, (1,0), (1,0,1)) + test_method(c.do_boolean, (1,1), (0,1,0)) + + test_int_method(c.do_octet) + test_int_method(c.do_short) + + test_int_method(c.do_unsigned_short) + test_int_method(c.do_long) + test_int_method(c.do_unsigned_long) + test_int_method(c.do_long_long) + test_int_method(c.do_unsigned_long) + test_int_method(c.do_float) + test_int_method(c.do_double) + + test_method(c.do_char, ("A", " "), (chr(ord("A")+ord(" ")), " ","A") ) + test_method(c.do_char, ("A", "\0"), ("A", "\0","A") ) + test_method(c.do_wchar, ("A", " "), (chr(ord("A")+ord(" ")), " ","A") ) + test_method(c.do_wchar, ("A", "\0"), ("A", "\0","A") ) + + test_method(c.do_string, ("Hello from ", "Python"), ("Hello from Python", "Hello from ", "Python") ) + test_method(c.do_string, (u"Hello from ", u"Python"), ("Hello from Python", "Hello from ", "Python") ) + test_method(c.do_string, (None, u"Python"), ("Python", None, "Python") ) + test_method(c.do_string, (None, really_big_string), (really_big_string, None, really_big_string) ) + test_method(c.do_string, (None, really_big_wstring), (really_big_string, None, really_big_string) ) + test_method(c.do_wstring, ("Hello from ", "Python"), ("Hello from Python", "Hello from ", "Python") ) + test_method(c.do_wstring, (u"Hello from ", u"Python"), ("Hello from Python", "Hello from ", "Python") ) + test_method(c.do_string, (None, really_big_wstring), (really_big_wstring, None, really_big_wstring) ) + test_method(c.do_string, (None, really_big_string), (really_big_wstring, None, really_big_wstring) ) + test_method(c.do_nsIIDRef, (component_iid, new_iid), (component_iid, component_iid, new_iid)) + test_method(c.do_nsIIDRef, (new_iid, component_iid), (new_iid, component_iid, component_iid)) + test_method(c.do_nsIPythonTestInterface, (None, None), (None, None, c)) + test_method(c.do_nsIPythonTestInterface, (c, c), (c, c, c)) + test_method(c.do_nsISupports, (None, None), (c, None, None)) + test_method(c.do_nsISupports, (c,c), (c, c, c)) + test_method(c.do_nsISupportsIs, (xpcom._xpcom.IID_nsISupports,), c) + test_method(c.do_nsISupportsIs, (xpcom.components.interfaces.nsIPythonTestInterface,), c) +## test_method(c.do_nsISupportsIs2, (xpcom.components.interfaces.nsIPythonTestInterface,c), (xpcom.components.interfaces.nsIPythonTestInterface,c)) +## test_method(c.do_nsISupportsIs3, (c,), (xpcom.components.interfaces.nsIPythonTestInterface,c)) +## test_method(c.do_nsISupportsIs4, (), (xpcom.components.interfaces.nsIPythonTestInterface,c)) + # Test the constants. + test_constant(c, "One", 1) + test_constant(c, "Two", 2) + test_constant(c, "MinusOne", -1) + test_constant(c, "BigLong", 0x7FFFFFFF) + test_constant(c, "BigULong", 0xFFFFFFFF) + # Test the components.Interfaces semantics + i = xpcom.components.interfaces.nsIPythonTestInterface + test_constant(i, "One", 1) + test_constant(i, "Two", 2) + test_constant(i, "MinusOne", -1) + test_constant(i, "BigLong", 0x7FFFFFFF) + test_constant(i, "BigULong", 0xFFFFFFFF) + +def test_derived_interface(c): + val = "Hello\0there" + expected = val * 2 + + test_method(c.DoubleString, (val,), expected) + test_method(c.DoubleString2, (val,), expected) + test_method(c.DoubleString3, (val,), expected) + test_method(c.DoubleString4, (val,), expected) + test_method(c.UpString, (val,), val.upper()) + test_method(c.UpString2, (val,), val.upper()) + test_method(c.GetFixedString, (20,), "A"*20) + val = u"Hello\0there" + expected = val * 2 + test_method(c.DoubleWideString, (val,), expected) + test_method(c.DoubleWideString2, (val,), expected) + test_method(c.DoubleWideString3, (val,), expected) + test_method(c.DoubleWideString4, (val,), expected) + test_method(c.UpWideString, (val,), val.upper()) + test_method(c.UpWideString2, (val,), val.upper()) + test_method(c.GetFixedWideString, (20,), u"A"*20) + items = [1,2,3,4,5] + test_method(c.MultiplyEachItemInIntegerArray, (3, items,), map(lambda i:i*3, items)) + + test_method(c.MultiplyEachItemInIntegerArrayAndAppend, (3, items), items + map(lambda i:i*3, items)) + items = "Hello from Python".split() + expected = map( lambda x: x*2, items) + test_method(c.DoubleStringArray, (items,), expected) + + test_method(c.CompareStringArrays, (items, items), cmp(items, items)) + # Can we pass lists and tuples correctly? + test_method(c.CompareStringArrays, (items, tuple(items)), cmp(items, items)) + items2 = ["Not", "the", "same"] + test_method(c.CompareStringArrays, (items, items2), cmp(items, items2)) + + expected = items[:] + expected.reverse() + test_method(c.ReverseStringArray, (items,), expected) + + expected = "Hello from the Python test component".split() + test_method(c.GetStrings, (), expected) + + val = "Hello\0there" + test_method(c.UpOctetArray, (val,), val.upper()) + test_method(c.UpOctetArray2, (val,), val.upper()) + + test_method(c.CheckInterfaceArray, ((c, c),), 1) + test_method(c.CheckInterfaceArray, ((c, None),), 0) + test_method(c.CheckInterfaceArray, ((),), 1) + + test_method(c.GetInterfaceArray, (), [c,c,c, None]) + test_method(c.ExtendInterfaceArray, ((c,c,c, None),), [c,c,c,None,c,c,c,None] ) + + expected = [xpcom.components.interfaces.nsIPythonTestInterfaceDOMStrings, xpcom.components.classes[contractid].clsid] + test_method(c.GetIIDArray, (), expected) + + val = [xpcom.components.interfaces.nsIPythonTestInterfaceExtra, xpcom.components.classes[contractid].clsid] + expected = val * 2 + test_method(c.ExtendIIDArray, (val,), expected) + + test_method(c.GetArrays, (), ( [1,2,3], [4,5,6] ) ) + test_method(c.CopyArray, ([1,2,3],), [1,2,3] ) + test_method(c.CopyAndDoubleArray, ([1,2,3],), [1,2,3,1,2,3] ) + test_method(c.AppendArray, ([1,2,3],), [1,2,3]) + test_method(c.AppendArray, ([1,2,3],[4,5,6]), [1,2,3,4,5,6]) + + c = c.queryInterface(xpcom.components.interfaces.nsIPythonTestInterfaceDOMStrings) + test_method(c.GetDOMStringResult, (), "A DOM String") + test_method(c.GetDOMStringOut, (), "Another DOM String") + val = "Hello there" + test_method(c.GetDOMStringLength, (val,), len(val)) + test_method(c.GetDOMStringRefLength, (val,), len(val)) + test_method(c.GetDOMStringPtrLength, (val,), len(val)) + test_method(c.ConcatDOMStrings, (val,val), val+val) + test_attribute(c, "domstring_value", "dom", "new dom") + if c.domstring_value_ro != "dom": + print "Read-only DOMString not currect - got", c.domstring_ro + try: + c.dom_string_ro = "new dom" + print "Managed to set a readonly attribute - eek!" + except AttributeError: + pass + except: + print "Unexpected exception when setting readonly attribute: %s: %s" % (sys.exc_info()[0], sys.exc_info()[1]) + if c.domstring_value_ro != "dom": + print "Read-only DOMString not correct after failed set attempt - got", c.domstring_ro + +def do_test_failures(): + c = xpcom.client.Component(contractid, xpcom.components.interfaces.nsIPythonTestInterfaceExtra) + try: + ret = c.do_nsISupportsIs( xpcom._xpcom.IID_nsIInterfaceInfoManager ) + print "*** got", ret, "***" + raise RuntimeError, "We worked when using an IID we dont support!?!" + except xpcom.Exception, details: + if details.errno != xpcom.nsError.NS_ERROR_NO_INTERFACE: + raise RuntimeError, "Wrong COM exception type: %r" % (details,) + +def test_failures(): + # This extra stack-frame ensures Python cleans up sys.last_traceback etc + do_test_failures() + +def test_all(): + c = xpcom.client.Component(contractid, xpcom.components.interfaces.nsIPythonTestInterface) + test_base_interface(c) + # Now create an instance using the derived IID, and test that. + c = xpcom.client.Component(contractid, xpcom.components.interfaces.nsIPythonTestInterfaceExtra) + test_base_interface(c) + test_derived_interface(c) + test_failures() + +try: + from sys import gettotalrefcount +except ImportError: + # Not a Debug build - assume no references (can't be leaks then :-) + def gettotalrefcount(): + return 0 + +def test_from_js(): + # Ensure we can find the js test script - same dir as this! + # Assume the path of sys.argv[0] is where we can find the js test code. + # (Running under the regression test is a little painful) + script_dir = os.path.split(sys.argv[0])[0] + fname = os.path.join( script_dir, "test_test_component.js") + if not os.path.isfile(fname): + raise RuntimeError, "Can not find '%s'" % (fname,) + # Note we _dont_ pump the test output out, as debug "xpcshell" spews + # extra debug info that will cause our output comparison to fail. + try: + data = os.popen('xpcshell "' + fname + '"').readlines() + good = 0 + for line in data: + if line.strip() == "javascript successfully tested the Python test component.": + good = 1 + if good: + print "Javascript could successfully use the Python test component." + else: + print "** The javascript test appeared to fail! Test output follows **" + print "".join(data) + print "** End of javascript test output **" + + except os.error, why: + print "Error executing the javascript test program:", why + + +def doit(num_loops = -1): + if "-v" in sys.argv: # Hack the verbose flag for the server + xpcom.verbose = 1 + # Do the test lots of times - can help shake-out ref-count bugs. + print "Testing the Python.TestComponent component" + if num_loops == -1: num_loops = 10 + for i in xrange(num_loops): + test_all() + + if i==0: + # First loop is likely to "leak" as we cache things. + # Leaking after that is a problem. + num_refs = gettotalrefcount() + + if num_errors: + break + + lost = gettotalrefcount() - num_refs + # Sometimes we get spurious counts off by 1 or 2. + # This can't indicate a real leak, as we have looped + # more than twice! + if abs(lost)>2: + print "*** Lost %d references" % (lost,) + + if num_errors: + print "There were", num_errors, "errors testing the Python component :-(" + else: + print "The Python test component worked!" + +# regrtest doesnt like if __name__=='__main__' blocks - it fails when running as a test! +num_iters = -1 +if __name__=='__main__' and len(sys.argv) > 1: + num_iters = int(sys.argv[1]) + +doit(num_iters) +test_from_js() + +if __name__=='__main__': + # But we can only do this if _not_ testing - otherwise we + # screw up any tests that want to run later. + xpcom._xpcom.NS_ShutdownXPCOM() + ni = xpcom._xpcom._GetInterfaceCount() + ng = xpcom._xpcom._GetGatewayCount() + if ni or ng: + print "********* WARNING - Leaving with %d/%d objects alive" % (ni,ng) + \ No newline at end of file diff --git a/mozilla/extensions/python/xpcom/test/test_weakreferences.py b/mozilla/extensions/python/xpcom/test/test_weakreferences.py new file mode 100644 index 00000000000..9a65034e618 --- /dev/null +++ b/mozilla/extensions/python/xpcom/test/test_weakreferences.py @@ -0,0 +1,52 @@ +# Copyright (c) 2000-2001 ActiveState Tool Corporation. +# See the file LICENSE.txt for licensing information. + +# test_weakreferences.py - Test our weak reference implementation. +from xpcom import components, _xpcom +import xpcom.server, xpcom.client + +num_alive = 0 + +class koTestSimple: + _com_interfaces_ = [components.interfaces.nsIInputStream] + def __init__(self): + global num_alive + num_alive += 1 + def __del__(self): + global num_alive + num_alive -= 1 + def close( self ): + pass + +def test(): + ob = xpcom.server.WrapObject( koTestSimple(), components.interfaces.nsIInputStream) + + if num_alive != 1: raise RuntimeError, "Eeek - there are %d objects alive" % (num_alive,) + + # Check we can create a weak reference to our object. + wr = xpcom.client.WeakReference(ob) + if num_alive != 1: raise RuntimeError, "Eeek - there are %d objects alive" % (num_alive,) + + # Check we can call methods via the weak reference. + if wr() is None: raise RuntimeError, "Our weak-reference is returning None before it should!" + wr().close() + + ob = None # This should kill the object. + if num_alive != 0: raise RuntimeError, "Eeek - there are %d objects alive" % (num_alive,) + if wr() is not None: raise RuntimeError, "Our weak-reference is not returning None when it should!" + + # Now a test that we can get a _new_ interface from the weak reference - ie, + # an IID the real object has never previously been queried for + # (this behaviour previously caused a bug - never again ;-) + ob = xpcom.server.WrapObject( koTestSimple(), components.interfaces.nsISupports) + if num_alive != 1: raise RuntimeError, "Eeek - there are %d objects alive" % (num_alive,) + wr = xpcom.client.WeakReference(ob, components.interfaces.nsIInputStream) + if num_alive != 1: raise RuntimeError, "Eeek - there are %d objects alive" % (num_alive,) + wr() # This would die once upon a time ;-) + ob = None # This should kill the object. + if num_alive != 0: raise RuntimeError, "Eeek - there are %d objects alive" % (num_alive,) + if wr() is not None: raise RuntimeError, "Our weak-reference is not returning None when it should!" + + +test() +print "Weak-reference tests appear to have worked!" diff --git a/mozilla/extensions/python/xpcom/tools/regxpcom.py b/mozilla/extensions/python/xpcom/tools/regxpcom.py new file mode 100644 index 00000000000..e27bd07400c --- /dev/null +++ b/mozilla/extensions/python/xpcom/tools/regxpcom.py @@ -0,0 +1,33 @@ +# Copyright (c) 2000-2001 ActiveState Tool Corporation. +# See the file LICENSE.txt for licensing information. + +# regxpcom.py - basically the standard regxpcom.cpp ported to Python. + +from xpcom import components, _xpcom +import sys +import os + +def ProcessArgs(args): + unregister = 0 + for arg in args: + if arg == "-u": + unregister = 1 + else: + spec = components.classes['@mozilla.org/file/local;1'].createInstance() + spec = spec.QueryInterface(components.interfaces.nsILocalFile) + spec.initWithPath(os.path.abspath(arg)) + if unregister: + components.manager.autoUnregisterComponent( components.manager.NS_Startup, spec) + print "Successfully unregistered", spec.path + else: + components.manager.autoRegisterComponent( components.manager.NS_Startup, spec) + print "Successfully registered", spec.path + unregister = 0 + +import xpcom +if len(sys.argv) < 2: + components.manager.autoRegister( components.manager.NS_Startup, None) +else: + ProcessArgs(sys.argv[1:]) + +_xpcom.NS_ShutdownXPCOM() \ No newline at end of file diff --git a/mozilla/extensions/python/xpcom/tools/tracer_demo.py b/mozilla/extensions/python/xpcom/tools/tracer_demo.py new file mode 100644 index 00000000000..9e1eb574404 --- /dev/null +++ b/mozilla/extensions/python/xpcom/tools/tracer_demo.py @@ -0,0 +1,79 @@ +# Copyright (c) 2000-2001 ActiveState Tool Corporation. +# See the file LICENSE.txt for licensing information. + +# This is a demo is how to use the xpcom.server "tracer" facility. +# +# This demo installs a tracer that uses the Python profiler. It then +# creates the Python test component, and references some methods +# and properties. It then dumps the profile statistics. + +# This same technique could also be used for debugging, for example. + +import profile + +p = profile.Profile() +getters = {} +setters = {} + +# A wrapper around a function - looks like a function, +# but actually profiles the delegate. +class TracerDelegate: + def __init__(self, callme): + self.callme = callme + def __call__(self, *args): + return p.runcall(self.callme, *args) + +# A wrapper around each of our XPCOM objects. All PyXPCOM calls +# in are made on this object, which creates a TracerDelagate around +# every function. As the function is called, it collects profile info. +class Tracer: + def __init__(self, ob): + self.__dict__['_ob'] = ob + def __repr__(self): + return "" % (self._ob,) + def __str__(self): + return "" % (self._ob,) + def __getattr__(self, attr): + ret = getattr(self._ob, attr) # Attribute error just goes up + if callable(ret): + return TracerDelegate(ret) + else: + if not attr.startswith("_com_") and not attr.startswith("_reg_"): + getters[attr] = getters.setdefault(attr,0) + 1 + return ret + def __setattr__(self, attr, val): + if self.__dict__.has_key(attr): + self.__dict__[attr] = val + return + setters[attr] = setters.setdefault(attr,0) + 1 + setattr(self._ob, attr, val) + +# Installed as a global XPCOM function that if exists, will be called +# to wrap each XPCOM object created. +def MakeTracer(ob): + # In some cases we may be asked to wrap ourself, so handle that. + if isinstance(ob, Tracer): + return ob + return Tracer(ob) + +def test(): + import xpcom.server, xpcom.components + xpcom.server.tracer = MakeTracer + contractid = "Python.TestComponent" + for i in range(100): + c = xpcom.components.classes[contractid].createInstance().queryInterface(xpcom.components.interfaces.nsIPythonTestInterface) + c.boolean_value = 0 + a = c.boolean_value + c.do_boolean(0,1) + print "Finshed" + p.print_stats() + print "%-30s%s" % ("Attribute Gets", "Number") + print "-" * 36 + for name, num in getters.items(): + print "%-30s%d" % (name, num) + print "%-30s%s" % ("Attribute Sets", "Number") + print "-" * 36 + for name, num in setters.items(): + print "%-30s%d" % (name, num) + +test() \ No newline at end of file diff --git a/mozilla/extensions/python/xpcom/xpcom_consts.py b/mozilla/extensions/python/xpcom/xpcom_consts.py new file mode 100644 index 00000000000..75a075ed652 --- /dev/null +++ b/mozilla/extensions/python/xpcom/xpcom_consts.py @@ -0,0 +1,202 @@ +# Copyright (c) 2000-2001 ActiveState Tool Corporation. +# See the file LICENSE.txt for licensing information. + +# Could maybe later have a process that extracted these enums should they change. +# from nsFileLocations.h +App_DirectoryBase = 0x00010000 +App_PrefsDirectory30 = App_DirectoryBase + 1 +App_PrefsDirectory40 = App_DirectoryBase + 2 +App_PrefsDirectory50 = App_DirectoryBase + 3 +App_ResDirectory = App_DirectoryBase + 5 +App_UserProfileDirectory30 = App_DirectoryBase + 10 +App_UserProfileDirectory40 = App_DirectoryBase + 11 +App_UserProfileDirectory50 = App_DirectoryBase + 12 +App_DefaultUserProfileRoot30 = App_DirectoryBase + 13 +App_DefaultUserProfileRoot40 = App_DirectoryBase + 14 +App_DefaultUserProfileRoot50 = App_DirectoryBase + 15 +App_ProfileDefaultsFolder30 = App_DirectoryBase + 16 +App_ProfileDefaultsFolder40 = App_DirectoryBase + 17 +App_ProfileDefaultsFolder50 = App_DirectoryBase + 18 +App_PrefDefaultsFolder50 = App_DirectoryBase + 19 +App_DefaultsFolder50 = App_DirectoryBase + 25 +App_ComponentsDirectory = App_DirectoryBase + 30 +App_ChromeDirectory = App_DirectoryBase + 31 +App_PluginsDirectory = App_DirectoryBase + 32 +App_UserChromeDirectory = App_DirectoryBase + 40 +App_FileBase = App_DirectoryBase + 1000 +App_PreferencesFile30 = App_FileBase + 1 +App_PreferencesFile40 = App_FileBase + 2 +App_PreferencesFile50 = App_FileBase + 3 +App_BookmarksFile30 = App_FileBase + 10 +App_BookmarksFile40 = App_FileBase + 11 +App_BookmarksFile50 = App_FileBase + 12 +App_Registry40 = App_FileBase + 20 +App_Registry50 = App_FileBase + 21 +App_LocalStore50 = App_FileBase + 30 +App_History50 = App_FileBase + 40 +App_MailDirectory50 = App_FileBase + 50 +App_ImapMailDirectory50 = App_FileBase + 60 +App_NewsDirectory50 = App_FileBase + 70 +App_MessengerFolderCache50 = App_FileBase + 80 +App_UsersPanels50 = App_FileBase + 90 +App_SearchFile50 = App_FileBase + 100 +App_SearchDirectory50 = App_FileBase + 101 + +# From nsSpecialSystemDirectory.h +OS_DriveDirectory = 1 +OS_TemporaryDirectory = 2 +OS_CurrentProcessDirectory= 3 +OS_CurrentWorkingDirectory= 4 + +XPCOM_CurrentProcessComponentDirectory= 5 +XPCOM_CurrentProcessComponentRegistry= 6 + +Moz_BinDirectory = 10 + +Mac_SystemDirectory = 101 +Mac_DesktopDirectory = 102 +Mac_TrashDirectory = 103 +Mac_StartupDirectory = 104 +Mac_ShutdownDirectory = 105 +Mac_AppleMenuDirectory = 106 +Mac_ControlPanelDirectory = 107 +Mac_ExtensionDirectory = 108 +Mac_FontsDirectory = 109 +Mac_PreferencesDirectory = 110 +Mac_DocumentsDirectory = 111 +Mac_InternetSearchDirectory = 112 + +Win_SystemDirectory = 201 +Win_WindowsDirectory = 202 + +Win_HomeDirectory = 203 +Win_Desktop = 204 +Win_Programs = 205 +Win_Controls = 206 +Win_Printers = 207 +Win_Personal = 208 +Win_Favorites = 209 +Win_Startup = 210 +Win_Recent = 211 +Win_Sendto = 212 +Win_Bitbucket = 213 +Win_Startmenu = 214 +Win_Desktopdirectory = 215 +Win_Drives = 216 +Win_Network = 217 +Win_Nethood = 218 +Win_Fonts = 219 +Win_Templates = 220 +Win_Common_Startmenu = 221 +Win_Common_Programs = 222 +Win_Common_Startup = 223 +Win_Common_Desktopdirectory = 224 +Win_Appdata = 225 +Win_Printhood = 226 + +Unix_LocalDirectory = 301 +Unix_LibDirectory = 302 +Unix_HomeDirectory = 303 + +BeOS_SettingsDirectory = 401 +BeOS_HomeDirectory = 402 +BeOS_DesktopDirectory = 403 +BeOS_SystemDirectory = 404 + +OS2_SystemDirectory = 501 + +# Type/Variant related constants. +TD_INT8 = 0 +TD_INT16 = 1 +TD_INT32 = 2 +TD_INT64 = 3 +TD_UINT8 = 4 +TD_UINT16 = 5 +TD_UINT32 = 6 +TD_UINT64 = 7 +TD_FLOAT = 8 +TD_DOUBLE = 9 +TD_BOOL = 10 +TD_CHAR = 11 +TD_WCHAR = 12 +TD_VOID = 13 +TD_PNSIID = 14 +TD_DOMSTRING = 15 +TD_PSTRING = 16 +TD_PWSTRING = 17 +TD_INTERFACE_TYPE = 18 +TD_INTERFACE_IS_TYPE = 19 +TD_ARRAY = 20 +TD_PSTRING_SIZE_IS = 21 +TD_PWSTRING_SIZE_IS = 22 + +# From xpt_struct.h +XPT_TDP_POINTER = 0x80 +XPT_TDP_UNIQUE_POINTER = 0x40 +XPT_TDP_REFERENCE = 0x20 +XPT_TDP_FLAGMASK = 0xe0 +XPT_TDP_TAGMASK = (~XPT_TDP_FLAGMASK) +def XPT_TDP_TAG(tdp): return (tdp & XPT_TDP_TAGMASK) + +def XPT_TDP_IS_POINTER(flags): return (flags & XPT_TDP_POINTER) +def XPT_TDP_IS_UNIQUE_POINTER(flags): return (flags & XPT_TDP_UNIQUE_POINTER) +def XPT_TDP_IS_REFERENCE(flags): return (flags & XPT_TDP_REFERENCE) + +XPT_ID_SCRIPTABLE = 0x80 +XPT_ID_FLAGMASK = 0x80 +XPT_ID_TAGMASK = ~XPT_ID_FLAGMASK +def XPT_ID_TAG(id): return id & XPT_ID_TAGMASK + +def XPT_ID_IS_SCRIPTABLE(flags): return flags & XPT_ID_SCRIPTABLE + +XPT_PD_IN = 0x80 +XPT_PD_OUT = 0x40 +XPT_PD_RETVAL = 0x20 +XPT_PD_SHARED = 0x10 +XPT_PD_DIPPER = 0x08 +XPT_PD_FLAGMASK = 0xf0 + +def XPT_PD_IS_IN(flags): return (flags & XPT_PD_IN) +def XPT_PD_IS_OUT(flags): return (flags & XPT_PD_OUT) +def XPT_PD_IS_RETVAL(flags): return (flags & XPT_PD_RETVAL) +def XPT_PD_IS_SHARED(flags): return (flags & XPT_PD_SHARED) +def XPT_PD_IS_DIPPER(flags): return (flags & XPT_PD_DIPPER) + +XPT_MD_GETTER = 0x80 +XPT_MD_SETTER = 0x40 +XPT_MD_NOTXPCOM = 0x20 +XPT_MD_CTOR = 0x10 +XPT_MD_HIDDEN = 0x08 +XPT_MD_FLAGMASK = 0xf8 + +def XPT_MD_IS_GETTER(flags): return (flags & XPT_MD_GETTER) +def XPT_MD_IS_SETTER(flags): return (flags & XPT_MD_SETTER) +def XPT_MD_IS_NOTXPCOM(flags): return (flags & XPT_MD_NOTXPCOM) +def XPT_MD_IS_CTOR(flags): return (flags & XPT_MD_CTOR) +def XPT_MD_IS_HIDDEN(flags): return (flags & XPT_MD_HIDDEN) + +# From xptinfo.h + +T_I8 = TD_INT8 +T_I16 = TD_INT16 +T_I32 = TD_INT32 +T_I64 = TD_INT64 +T_U8 = TD_UINT8 +T_U16 = TD_UINT16 +T_U32 = TD_UINT32 +T_U64 = TD_UINT64 +T_FLOAT = TD_FLOAT +T_DOUBLE = TD_DOUBLE +T_BOOL = TD_BOOL +T_CHAR = TD_CHAR +T_WCHAR = TD_WCHAR +T_VOID = TD_VOID +T_IID = TD_PNSIID +T_DOMSTRING = TD_DOMSTRING +T_CHAR_STR = TD_PSTRING +T_WCHAR_STR = TD_PWSTRING +T_INTERFACE = TD_INTERFACE_TYPE +T_INTERFACE_IS = TD_INTERFACE_IS_TYPE +T_ARRAY = TD_ARRAY +T_PSTRING_SIZE_IS = TD_PSTRING_SIZE_IS +T_PWSTRING_SIZE_IS = TD_PWSTRING_SIZE_IS diff --git a/mozilla/extensions/python/xpcom/xpt.py b/mozilla/extensions/python/xpcom/xpt.py new file mode 100644 index 00000000000..bc5f0bbc68c --- /dev/null +++ b/mozilla/extensions/python/xpcom/xpt.py @@ -0,0 +1,435 @@ +# Copyright (c) 2000-2001 ActiveState Tool Corporation. +# See the file LICENSE.txt for licensing information. + +""" +Program: xpt.py + +Task: describe interfaces etc using XPCOM reflection. + +Subtasks: + output (nearly) exactly the same stuff as xpt_dump, for verification + output Python source code that can be used as a template for an interface + +Status: Works pretty well if you ask me :-) + +Author: + David Ascher did an original version that parsed XPT files + directly. Mark Hammond changed it to use the reflection interfaces, + but kept most of the printing logic. + + +Revision: + + 0.1: March 6, 2000 + 0.2: April 2000 - Mark removed lots of Davids lovely parsing code in favour + of the new xpcom interfaces that provide this info. + + May 2000 - Moved into Perforce - track the log there! + +Todo: + Fill out this todo list. + +""" + +import string, sys +import xpcom +import xpcom._xpcom + +from xpcom_consts import * + +class Interface: + def __init__(self, iid): + iim = xpcom._xpcom.XPTI_GetInterfaceInfoManager() + if hasattr(iid, "upper"): # Is it a stringy thing. + item = iim.GetInfoForName(iid) + else: + item = iim.GetInfoForIID(iid) + self.interface_info = item + self.namespace = "" # where does this come from? + self.methods = Methods(item) + self.constants = Constants(item) + + # delegate attributes to the real interface + def __getattr__(self, attr): + return getattr(self.interface_info, attr) + + def GetParent(self): + try: + raw_parent = self.interface_info.GetParent() + if raw_parent is None: + return None + return Interface(raw_parent.GetIID()) + except xpcom.Exception: + # Parent interface is probably not scriptable - assume nsISupports. + if xpcom.verbose: + # The user may be confused as to why this is happening! + print "The parent interface of IID '%s' can not be located - assuming nsISupports" + return Interface(xpcom._xpcom.IID_nsISupports) + + def Describe_Python(self): + method_reprs = [] + methods = filter(lambda m: not m.IsNotXPCOM(), self.methods) + for m in methods: + method_reprs.append(m.Describe_Python()) + method_joiner = "\n" + methods_repr = method_joiner.join(method_reprs) + return \ +"""class %s: + _com_interfaces_ = xpcom.components.interfaces.%s + # If this object needs to be registered, the following 2 are also needed. + # _reg_clsid_ = "{a new clsid generated for this object}" + # _reg_contractid_ = "The.Object.Name"\n%s""" % (self.GetName(), self.GetIID().name, methods_repr) + + def Describe(self): + # Make the IID look like xtp_dump - "(" instead of "{" + iid_use = "(" + str(self.GetIID())[1:-1] + ")" + s = ' - '+self.namespace+'::'+ self.GetName() + ' ' + iid_use + ':\n' + + parent = self.GetParent() + if parent is not None: + s = s + ' Parent: ' + parent.namespace + '::' + parent.GetName() + '\n' + s = s + ' Flags:\n' + if self.IsScriptable(): word = 'TRUE' + else: word = 'FALSE' + s = s + ' Scriptable: ' + word + '\n' + s = s + ' Methods:\n' + methods = filter(lambda m: not m.IsNotXPCOM(), self.methods) + if len(methods): + for m in methods: + s = s + ' ' + m.Describe() + '\n' + else: + s = s + ' No Methods\n' + s = s + ' Constants:\n' + if self.constants: + for c in self.constants: + s = s + ' ' + c.Describe() + '\n' + else: + s = s + ' No Constants\n' + + return s + +# A class that allows caching and iterating of methods. +class Methods: + def __init__(self, interface_info): + self.interface_info = interface_info + try: + self.items = [None] * interface_info.GetMethodCount() + except xpcom.Exception: + if xpcom.verbose: + print "** GetMethodCount failed?? - assuming no methods" + self.items = [] + def __len__(self): + return len(self.items) + def __getitem__(self, index): + ret = self.items[index] + if ret is None: + mi = self.interface_info.GetMethodInfo(index) + ret = self.items[index] = Method(mi, index, self.interface_info) + return ret + +class Method: + + def __init__(self, method_info, method_index, interface_info = None): + self.interface_info = interface_info + self.method_index = method_index + self.flags, self.name, param_descs, self.result_desc = method_info + # Build the params. + self.params = [] + pi=0 + for pd in param_descs: + self.params.append( Parameter(pd, pi, method_index, interface_info) ) + pi = pi + 1 + # Run over the params setting the "sizeof" params to hidden. + for p in self.params: + td = p.type_desc + tag = XPT_TDP_TAG(td[0]) + if tag==T_ARRAY and p.IsIn(): + self.params[td[1]].hidden_indicator = 2 + elif tag in [T_PSTRING_SIZE_IS, T_PWSTRING_SIZE_IS] and p.IsIn(): + self.params[td[1]].hidden_indicator = 1 + + def IsGetter(self): + return (self.flags & XPT_MD_GETTER) + def IsSetter(self): + return (self.flags & XPT_MD_SETTER) + def IsNotXPCOM(self): + return (self.flags & XPT_MD_NOTXPCOM) + def IsConstructor(self): + return (self.flags & XPT_MD_CTOR) + def IsHidden(self): + return (self.flags & XPT_MD_HIDDEN) + + def Describe_Python(self): + if self.method_index < 3: # Ignore QI etc + return "" + base_name = self.name + if self.IsGetter(): + name = "get_%s" % (base_name,) + elif self.IsSetter(): + name = "set_%s" % (base_name,) + else: + name = base_name + param_decls = ["self"] + in_comments = [] + out_descs = [] + result_comment = "Result: void - None" + for p in self.params: + in_desc, in_desc_comments, out_desc, this_result_comment = p.Describe_Python() + if in_desc is not None: + param_decls.append(in_desc) + if in_desc_comments is not None: + in_comments.append(in_desc_comments) + if out_desc is not None: + out_descs.append(out_desc) + if this_result_comment is not None: + result_comment = this_result_comment + joiner = "\n # " + in_comment = out_desc = "" + if in_comments: in_comment = joiner + joiner.join(in_comments) + if out_descs: out_desc = joiner + joiner.join(out_descs) + + return """ def %s( %s ): + # %s%s%s + pass""" % (name, ", ".join(param_decls), result_comment, in_comment, out_desc) + + def Describe(self): + s = '' + if self.IsGetter(): + G = 'G' + else: + G = ' ' + if self.IsSetter(): + S = 'S' + else: S = ' ' + if self.IsHidden(): + H = 'H' + else: + H = ' ' + if self.IsNotXPCOM(): + N = 'N' + else: + N = ' ' + if self.IsConstructor(): + C = 'C' + else: + C = ' ' + + def desc(a): return a.Describe() + method_desc = string.join(map(desc, self.params), ', ') + result_type = TypeDescriber(self.result_desc[0], None) + return_desc = result_type.Describe() + i = string.find(return_desc, 'retval ') + if i != -1: + return_desc = return_desc[:i] + return_desc[i+len('retval '):] + return G+S+H+N+C+' '+return_desc+' '+self.name + '('+ method_desc + ');' + +class Parameter: + def __init__(self, param_desc, param_index, method_index, interface_info = None): + self.param_flags, self.type_desc = param_desc + self.hidden_indicator = 0 # Is this a special "size" type param that will be hidden from Python? + self.param_index = param_index + self.method_index= method_index + self.interface_info = interface_info + def __repr__(self): + return "" % self.__dict__ + def IsIn(self): + return XPT_PD_IS_IN(self.param_flags) + def IsOut(self): + return XPT_PD_IS_OUT(self.param_flags) + def IsInOut(self): + return self.IsIn() and self.IsOut() + def IsRetval(self): + return XPT_PD_IS_RETVAL(self.param_flags) + def IsShared(self): + return XPT_PD_IS_SHARED(self.param_flags) + def IsDipper(self): + return XPT_PD_IS_DIPPER(self.param_flags) + + def Describe_Python(self): + name = "param%d" % (self.param_index,) + if self.hidden_indicator: + # Could remove the comment - Im trying to tell the user where that param has + # gone from the signature! + return None, "%s is a hidden parameter" % (name,), None, None + t = TypeDescriber(self.type_desc[0], self) + decl = in_comment = out_comment = result_comment = None + type_desc = t.Describe() + if self.IsIn() and not self.IsDipper(): + decl = name + extra="" + if self.IsOut(): + extra = "Out" + in_comment = "In%s: %s: %s" % (extra, name, type_desc) + elif self.IsOut() or self.IsDipper(): + if self.IsRetval(): + result_comment = "Result: %s" % (type_desc,) + else: + out_comment = "Out: %s" % (type_desc,) + return decl, in_comment, out_comment, result_comment + + def Describe(self): + parts = [] + if self.IsInOut(): + parts.append('inout') + elif self.IsIn(): + parts.append('in') + elif self.IsOut(): + parts.append('out') + + if self.IsDipper(): parts.append("dipper") + if self.IsRetval(): parts.append('retval') + if self.IsShared(): parts.append('shared') + t = TypeDescriber(self.type_desc[0], self) + type_str = t.Describe() + parts.append(type_str) + return string.join(parts) + +# A class that allows caching and iterating of constants. +class Constants: + def __init__(self, interface_info): + self.interface_info = interface_info + try: + self.items = [None] * interface_info.GetConstantCount() + except xpcom.Exception: + if xpcom.verbose: + print "** GetConstantCount failed?? - assuming no constants" + self.items = [] + def __len__(self): + return len(self.items) + def __getitem__(self, index): + ret = self.items[index] + if ret is None: + ci = self.interface_info.GetConstant(index) + ret = self.items[index] = Constant(ci) + return ret + +class Constant: + def __init__(self, ci): + self.name, self.type, self.value = ci + + def Describe(self): + return TypeDescriber(self.type, None).Describe() + ' ' +self.name+' = '+str(self.value)+';' + + __str__ = Describe + +def MakeReprForInvoke(param): + tag = param.type_desc[0] & XPT_TDP_TAGMASK + if tag == T_INTERFACE: + i_info = param.interface_info + try: + iid = i_info.GetIIDForParam(param.method_index, param.param_index) + except xpcom.Exception: + # IID not available (probably not scriptable) - just use nsISupports. + iid = xpcom._xpcom.IID_nsISupports + return param.type_desc[0], 0, 0, str(iid) + elif tag == T_ARRAY: + i_info = param.interface_info + array_desc = i_info.GetTypeForParam(param.method_index, param.param_index, 1) + return param.type_desc[:-1] + array_desc[:1] + return param.type_desc + + +class TypeDescriber: + def __init__(self, type_flags, param): + self.type_flags = type_flags + self.tag = XPT_TDP_TAG(self.type_flags) + self.param = param + def IsPointer(self): + return XPT_TDP_IS_POINTER(self.type_flags) + def IsUniquePointer(self): + return XPT_TDP_IS_UNIQUE_POINTER(self.type_flags) + def IsReference(self): + return XPT_TDP_IS_REFERENCE(self.type_flags) + def repr_for_invoke(self): + return (self.type_flags,) + def GetName(self): + is_ptr = self.IsPointer() + data = type_info_map.get(self.tag) + if data is None: + data = ("unknown",) + if self.IsReference(): + if len(data) > 2: + return data[2] + return data[0] + " &" + if self.IsPointer(): + if len(data)>1: + return data[1] + return data[0] + " *" + return data[0] + + def Describe(self): + if self.tag == T_ARRAY: + # NOTE - Adding a type specifier to the array is different from xpt_dump.exe + if self.param is None or self.param.interface_info is None: + type_desc = "" # Dont have explicit info about the array type :-( + else: + i_info = self.param.interface_info + type_code = i_info.GetTypeForParam(self.param.method_index, self.param.param_index, 1) + type_desc = TypeDescriber( type_code[0], None).Describe() + return self.GetName() + "[" + type_desc + "]" + elif self.tag == T_INTERFACE: + if self.param is None or self.param.interface_info is None: + return "nsISomething" # Dont have explicit info about the IID :-( + i_info = self.param.interface_info + m_index = self.param.method_index + p_index = self.param.param_index + try: + iid = i_info.GetIIDForParam(m_index, p_index) + return iid.name + except xpcom.Exception: + return "nsISomething" + return self.GetName() + +# These are just for output purposes, so should be +# the same as xpt_dump uses +type_info_map = { + T_I8 : ("int8",), + T_I16 : ("int16",), + T_I32 : ("int32",), + T_I64 : ("int64",), + T_U8 : ("uint8",), + T_U16 : ("uint16",), + T_U32 : ("uint32",), + T_U64 : ("uint64",), + T_FLOAT : ("float",), + T_DOUBLE : ("double",), + T_BOOL : ("boolean",), + T_CHAR : ("char",), + T_WCHAR : ("wchar_t", "wstring"), + T_VOID : ("void",), + T_IID : ("reserved", "nsIID *", "nsIID &"), + T_DOMSTRING : ("DOMString",), + T_CHAR_STR : ("reserved", "string"), + T_WCHAR_STR : ("reserved", "wstring"), + T_INTERFACE : ("reserved", "Interface"), + T_INTERFACE_IS : ("reserved", "InterfaceIs *"), + T_ARRAY : ("reserved", "Array"), + T_PSTRING_SIZE_IS : ("reserved", "string_s"), + T_PWSTRING_SIZE_IS : ("reserved", "wstring_s"), +} + +def dump_interface(iid, mode): + interface = Interface(iid) + describer_name = "Describe" + if mode == "xptinfo": mode = None + if mode is not None: + describer_name = describer_name + "_" + mode.capitalize() + describer = getattr(interface, describer_name) + print describer() + +if __name__=='__main__': + if len(sys.argv) == 1: + print "Usage: xpt.py [-xptinfo] interface_name, ..." + print " -info: Dump in a style similar to the xptdump tool" + print "Dumping nsISupports and nsIInterfaceInfo" + sys.argv.append('nsIInterfaceInfo') + sys.argv.append('-xptinfo') + sys.argv.append('nsISupports') + sys.argv.append('nsIInterfaceInfo') + + mode = "Python" + for i in sys.argv[1:]: + if i[0] == "-": + mode = i[1:] + else: + dump_interface(i, mode)