pax_global_header00006660000000000000000000000064135637513230014522gustar00rootroot0000000000000052 comment=fe3b1ec434700f45bf75615285a18decfa3aca1a cjs-4.4.0/000077500000000000000000000000001356375132300123065ustar00rootroot00000000000000cjs-4.4.0/.circleci/000077500000000000000000000000001356375132300141415ustar00rootroot00000000000000cjs-4.4.0/.circleci/config.yml000066400000000000000000000030461356375132300161340ustar00rootroot00000000000000version: 2.0 shared: &shared steps: - checkout - run: name: Prepare environment command: apt-get update - run: name: Build project command: mint-build -i - run: name: Prepare packages command: | if [ -z $CI_PULL_REQUEST ]; then mkdir /packages mv /root/*.deb /packages/ git log > /packages/git.log cd / tar zcvf packages.tar.gz packages fi - run: name: Deploy packages to Github command: | if [ -z $CI_PULL_REQUEST ]; then wget https://github.com/tcnksm/ghr/releases/download/v0.5.4/ghr_v0.5.4_linux_amd64.zip apt-get install --yes unzip unzip ghr_v0.5.4_linux_amd64.zip TAG="master".$CIRCLE_JOB ./ghr -t $GITHUB_TOKEN -u $CIRCLE_PROJECT_USERNAME -r $CIRCLE_PROJECT_REPONAME -replace $TAG /packages.tar.gz ./ghr -t $GITHUB_TOKEN -u $CIRCLE_PROJECT_USERNAME -r $CIRCLE_PROJECT_REPONAME -recreate -b "Latest unstable packages" $TAG /packages.tar.gz fi jobs: "mint19": <<: *shared docker: - image: linuxmintd/mint19-amd64 "lmde3": <<: *shared docker: - image: linuxmintd/lmde3-amd64 workflows: version: 2 build: jobs: - "mint19" - "lmde3" cjs-4.4.0/.travis.yml000066400000000000000000000027741356375132300144310ustar00rootroot00000000000000language: c dist: trusty cache: directories: - ~/docker matrix: include: - env: BASE=ubuntu IMAGE=ubuntu:16.04 CCO=gcc - env: BASE=ubuntu IMAGE=ubuntu:16.04 CCO=clang - env: BASE=ubuntu IMAGE=ubuntu:16.10 CCO=gcc - env: BASE=fedora IMAGE=fedora:25 CCO=gcc before_install: - cd .. - git clone --depth 1 https://github.com/GNOME/jhbuild.git before_script: - export NAME=$(echo $IMAGE | sed "s/:/_/g") # Check if there is a saved base image - if [[ -f ~/docker/$NAME.tar.gz ]]; then cat ~/docker/$NAME.tar.gz | docker import - local/$NAME; fi # Build the base image, if necessary - 'if [[ ! -f ~/docker/$NAME.tar.gz ]]; then docker run -v $(pwd):/cwd -v $(pwd)/gjs/test/travis-ci.sh:/travis-ci.sh -e "BASE=$BASE" -e "OS=$IMAGE" -e "CC=gcc" "$IMAGE" bash -e -c "/travis-ci.sh BUILD_MOZ"; docker run --name $NAME -v $(pwd):/cwd -v $(pwd)/gjs/test/travis-ci.sh:/travis-ci.sh -e "BASE=$BASE" -e "OS=$IMAGE" -e "CC=$CCO" "$IMAGE" bash -e -c "/travis-ci.sh GET_FILES"; docker commit $NAME local/$NAME; mkdir -p ~/docker; docker export $NAME | gzip > ~/docker/$NAME.tar.gz; fi' script: - docker run -v $(pwd):/cwd -v $(pwd)/gjs/test/travis-ci.sh:/travis-ci.sh -e BASE=$BASE -e OS=$IMAGE -e CC=$CCO local/$NAME bash -e -c "/travis-ci.sh GJS" after_failure: - echo '-- FAILURE --' - 'if [[ -f $(pwd)/.cache/jhbuild/build/gjs/test-suite.log ]]; then cat $(pwd)/.cache/jhbuild/build/gjs/test-suite.log; else echo "-- NO LOG FILE FOUND --"; fi' cjs-4.4.0/AUTHORS000066400000000000000000000000001356375132300133440ustar00rootroot00000000000000cjs-4.4.0/COPYING000066400000000000000000000023711356375132300133440ustar00rootroot00000000000000Copyright (c) 2008 litl, LLC This project is dual-licensed as MIT and LGPLv2+. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. The following files contain code from Mozilla which is triple licensed under MPL1.1/LGPLv2+/GPLv2+: The console module (modules/console.c) Stack printer (gjs/stack.c) cjs-4.4.0/COPYING.LGPL000066400000000000000000000613141356375132300141030ustar00rootroot00000000000000 GNU LIBRARY GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the library GPL. It is numbered 2 because it goes with version 2 of the ordinary GPL.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Library General Public License, applies to some specially designated Free Software Foundation software, and to any other libraries whose authors decide to use it. You can use it for your libraries, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library, or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link a program with the library, you must provide complete object files to the recipients so that they can relink them with the library, after making changes to the library and recompiling it. And you must show them these terms so they know their rights. Our method of protecting your rights has two steps: (1) copyright the library, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the library. Also, for each distributor's protection, we want to make certain that everyone understands that there is no warranty for this free library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that companies distributing free software will individually obtain patent licenses, thus in effect transforming the program into proprietary software. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License, which was designed for utility programs. This license, the GNU Library General Public License, applies to certain designated libraries. This license is quite different from the ordinary one; be sure to read it in full, and don't assume that anything in it is the same as in the ordinary license. The reason we have a separate public license for some libraries is that they blur the distinction we usually make between modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in some sense simply using the library, and is analogous to running a utility program or application program. However, in a textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary General Public License treats it as such. Because of this blurred distinction, using the ordinary General Public License for libraries did not effectively promote software sharing, because most developers did not use the libraries. We concluded that weaker conditions might promote sharing better. However, unrestricted linking of non-free programs would deprive the users of those programs of all benefit from the free status of the libraries themselves. This Library General Public License is intended to permit developers of non-free programs to use free libraries, while preserving your freedom as a user of such programs to change the free libraries that are incorporated in them. (We have not seen how to achieve this as regards changes in header files, but we have achieved it as regards changes in the actual functions of the Library.) The hope is that this will lead to faster development of free libraries. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, while the latter only works together with the library. Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one. GNU LIBRARY GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also compile or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. c) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. d) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Library General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! cjs-4.4.0/ChangeLog000066400000000000000000000000001356375132300140460ustar00rootroot00000000000000cjs-4.4.0/Makefile-examples.am000066400000000000000000000003611356375132300161560ustar00rootroot00000000000000EXTRA_DIST += \ examples/clutter.js \ examples/gio-cat.js \ examples/gtk.js \ examples/http-server.js \ examples/test.jpg cjs-4.4.0/Makefile-insttest.am000066400000000000000000000024701356375132300162200ustar00rootroot00000000000000EXTRA_DIST += \ installed-tests/minijasmine.test.in \ installed-tests/script.test.in \ installed-tests/js/jsunit.gresources.xml \ $(NULL) gjsinsttestdir = $(pkglibexecdir)/installed-tests installedtestmetadir = $(datadir)/installed-tests/cjs jstestsdir = $(gjsinsttestdir)/js jsscripttestsdir = $(gjsinsttestdir)/scripts gjsinsttest_PROGRAMS = gjsinsttest_DATA = installedtestmeta_DATA = jstests_DATA = jsscripttests_DATA = pkglib_LTLIBRARIES = if BUILDOPT_INSTALL_TESTS gjsinsttest_PROGRAMS += minijasmine gjsinsttest_DATA += $(TEST_INTROSPECTION_TYPELIBS) installedtestmeta_DATA += \ $(jasmine_tests:.js=.test) \ $(simple_tests:%=%.test) \ $(NULL) jstests_DATA += $(jasmine_tests) jsscripttests_DATA += $(simple_tests) pkglib_LTLIBRARIES += libregress.la libwarnlib.la libgimarshallingtests.la %.test: %.js installed-tests/minijasmine.test.in Makefile $(AM_V_GEN)$(MKDIR_P) $(@D) && \ $(SED) -e s,@pkglibexecdir\@,$(pkglibexecdir),g \ -e s,@name\@,$(notdir $<), \ < $(srcdir)/installed-tests/minijasmine.test.in > $@.tmp && \ mv $@.tmp $@ %.test: % installed-tests/script.test.in Makefile $(AM_V_GEN)$(MKDIR_P) $(@D) && \ $(SED) -e s,@pkglibexecdir\@,$(pkglibexecdir), \ -e s,@name\@,$(notdir $<), \ < $(srcdir)/installed-tests/script.test.in > $@.tmp && \ mv $@.tmp $@ endif BUILDOPT_INSTALL_TESTS cjs-4.4.0/Makefile-modules.am000066400000000000000000000035461356375132300160200ustar00rootroot00000000000000 NATIVE_MODULES = libconsole.la libsystem.la libmodules_resources.la if ENABLE_CAIRO NATIVE_MODULES += libcairoNative.la endif noinst_LTLIBRARIES += $(NATIVE_MODULES) libcjs_la_LIBADD += $(NATIVE_MODULES) JS_NATIVE_MODULE_CPPFLAGS = \ $(AM_CPPFLAGS) \ -DGJS_COMPILATION \ $(GJS_CFLAGS) \ $(GJS_PRIVATE_CFLAGS) JS_NATIVE_MODULE_LIBADD = \ $(GJS_LIBS) \ $(GJS_PRIVATE_LIBS) modules_resource_files := $(shell glib-compile-resources --sourcedir=$(srcdir) --generate-dependencies $(srcdir)/modules/modules.gresource.xml) modules-resources.h: $(srcdir)/modules/modules.gresource.xml $(modules_resource_files) $(AM_V_GEN) glib-compile-resources --target=$@ --sourcedir=$(srcdir) --sourcedir=$(builddir) --generate --c-name modules_resources $< modules-resources.c: $(srcdir)/modules/modules.gresource.xml $(modules_resource_files) $(AM_V_GEN) glib-compile-resources --target=$@ --sourcedir=$(srcdir) --sourcedir=$(builddir) --generate --c-name modules_resources $< EXTRA_DIST += $(modules_resource_files) $(srcdir)/modules/modules.gresource.xml include gjs-modules-srcs.mk nodist_libmodules_resources_la_SOURCES = $(module_resource_srcs) CLEANFILES += $(nodist_libmodules_resources_la_SOURCES) libmodules_resources_la_CPPFLAGS = $(JS_NATIVE_MODULE_CPPFLAGS) libmodules_resources_la_LIBADD = $(JS_NATIVE_MODULE_LIBADD) libcairoNative_la_CPPFLAGS = $(JS_NATIVE_MODULE_CPPFLAGS) $(GJS_CAIRO_CFLAGS) $(GJS_CAIRO_XLIB_CFLAGS) libcairoNative_la_LIBADD = $(JS_NATIVE_MODULE_LIBADD) $(GJS_CAIRO_LIBS) $(GJS_CAIRO_XLIB_LIBS) libcairoNative_la_SOURCES = $(module_cairo_srcs) libsystem_la_CPPFLAGS = $(JS_NATIVE_MODULE_CPPFLAGS) libsystem_la_LIBADD = $(JS_NATIVE_MODULE_LIBADD) libsystem_la_SOURCES = $(module_system_srcs) libconsole_la_CPPFLAGS = $(JS_NATIVE_MODULE_CPPFLAGS) libconsole_la_LIBADD = $(JS_NATIVE_MODULE_LIBADD) $(READLINE_LIBS) libconsole_la_SOURCES = $(module_console_srcs) cjs-4.4.0/Makefile-test.am000066400000000000000000000264361356375132300153320ustar00rootroot00000000000000EXTRA_DIST += \ test/test-bus.conf \ test/run-test \ $(NULL) if XVFB_TESTS XVFB_INVOCATION = $(XVFB) -ac -noreset -screen 0 1024x768x16 XIDS = 101 102 103 104 105 106 107 197 199 211 223 227 293 307 308 309 310 311 \ 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 \ 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 \ 1008 1009 4703 4721 4723 4729 4733 4751 9973 9974 9975 9976 9977 9978 9979 \ 9980 9981 9982 9983 9984 9985 9986 9987 9988 9989 9990 9991 9992 9993 9994 \ 9995 9996 9997 9998 9999 XVFB_START = \ ${XVFB_INVOCATION} -help 2>/dev/null 1>&2 \ && XID=`for id in $(XIDS) ; do test -e /tmp/.X$$id-lock || { echo $$id; exit 0; }; done; exit 1` \ && { ${XVFB} :$$XID -nolisten tcp -auth /dev/null >/dev/null 2>&1 & \ trap "kill -15 $$! " 0 HUP INT QUIT TRAP USR1 PIPE TERM ; } \ || { echo "Gtk+Tests:ERROR: Failed to start Xvfb environment for X11 target tests."; exit 1; } \ && export DISPLAY=:$$XID; else XVFB_START = endif ### TEST RESOURCES ##################################################### mock_js_resources_files := $(shell glib-compile-resources --sourcedir=$(srcdir) --generate-dependencies $(srcdir)/test/mock-js-resources.gresource.xml) mock-js-resources.h: $(srcdir)/test/mock-js-resources.gresource.xml $(modules_resource_files) $(AM_V_GEN) glib-compile-resources --target=$@ --sourcedir=$(srcdir) --sourcedir=$(builddir) --generate --c-name mock_js_resources $< mock-js-resources.c: $(srcdir)/test/mock-js-resources.gresource.xml $(modules_resource_files) $(AM_V_GEN) glib-compile-resources --target=$@ --sourcedir=$(srcdir) --sourcedir=$(builddir) --generate --c-name mock_js_resources $< jsunit_resources_files := $(shell glib-compile-resources --sourcedir=$(srcdir)/installed-tests/js --generate-dependencies $(srcdir)/installed-tests/js/jsunit.gresources.xml) jsunit-resources.h: $(srcdir)/installed-tests/js/jsunit.gresources.xml $(jsunit_resources_files) $(AM_V_GEN) glib-compile-resources --target=$@ --sourcedir=$(srcdir)/installed-tests/js --sourcedir=$(builddir) --generate --c-name jsunit_resources $< jsunit-resources.c: $(srcdir)/installed-tests/js/jsunit.gresources.xml $(jsunit_resources_files) $(AM_V_GEN) glib-compile-resources --target=$@ --sourcedir=$(srcdir)/installed-tests/js --sourcedir=$(builddir) --generate --c-name jsunit_resources $< BUILT_SOURCES += mock-js-resources.c jsunit-resources.h jsunit-resources.c EXTRA_DIST += \ $(mock_js_resources_files) \ $(srcdir)/test/mock-js-resources.gresource.xml \ $(jsunit_resources_files) \ $(NULL) CLEANFILES += \ mock-js-resources.c \ jsunit-resources.c \ jsunit-resources.h \ $(NULL) ### TEST PROGRAMS ###################################################### # gjs-tests.gtester checks private APIs and is run only uninstalled, # on "make check". # # jsunit checks public APIs using JS test scripts, and is run on "make check", # as well as installed if --enable-installed-tests is given at configure time. # See Makefile-insttest.am for the build rules installing the tests. check_PROGRAMS += gjs-tests.gtester minijasmine gjs_tests_gtester_CPPFLAGS = \ $(AM_CPPFLAGS) \ -DGJS_COMPILATION \ $(GJSTESTS_CFLAGS) \ $(gjs_directory_defines) \ -I$(top_srcdir)/test gjs_tests_gtester_LDADD = \ libcjs.la \ $(GJSTESTS_LIBS) gjs_tests_gtester_SOURCES = \ test/gjs-tests.cpp \ test/gjs-test-utils.cpp \ test/gjs-test-utils.h \ test/gjs-test-call-args.cpp \ test/gjs-test-coverage.cpp \ test/gjs-test-rooting.cpp \ mock-js-resources.c \ $(NULL) minijasmine_SOURCES = \ installed-tests/minijasmine.cpp \ jsunit-resources.c \ jsunit-resources.h \ $(NULL) minijasmine_CPPFLAGS = \ $(AM_CPPFLAGS) \ $(GJS_CFLAGS) \ $(GJS_PRIVATE_CFLAGS) \ -I$(top_srcdir) \ -DINSTTESTDIR=\"$(gjsinsttestdir)\" \ -DPKGLIBDIR=\"$(pkglibdir)\" \ $(NULL) minijasmine_LDADD = $(GJS_LIBS) $(GJS_PRIVATE_LIBS) libcjs.la ### TEST GIRS ########################################################## TEST_INTROSPECTION_GIRS = common_test_ldflags = $(AM_LDFLAGS) -avoid-version common_test_libadd = $(GJS_LIBS) $(GJS_PRIVATE_LIBS) if !BUILDOPT_INSTALL_TESTS # This rpath /nowhere thing is the libtool upstream recommended way to # force generation of shared libraries, which we need in order for the # tests to work uninstalled. common_test_ldflags += -rpath /nowhere # In the installed tests case, these libraries are built for pkglibdir; sadly we # can only have one destination at a time check_LTLIBRARIES += libregress.la libwarnlib.la libgimarshallingtests.la endif # These sources are installed as part of gobject-introspection, and symlinked # into the build directory at configure time. They should not be distributed # with GJS. nodist_libregress_la_SOURCES = \ installed-tests/regress.c \ installed-tests/regress.h \ $(NULL) libregress_la_CPPFLAGS = $(AM_CPPFLAGS) $(GJS_CFLAGS) $(GJS_PRIVATE_CFLAGS) $(GJS_GDBUS_CFLAGS) libregress_la_LDFLAGS = $(common_test_ldflags) $(GJS_GDBUS_LIBS) libregress_la_LIBADD = $(common_test_libadd) libregress_scannerflags_includes = --include=Gio-2.0 if ENABLE_CAIRO libregress_la_CPPFLAGS += $(GJS_CAIRO_CFLAGS) libregress_la_LDFLAGS += $(GJS_CAIRO_LIBS) libregress_scannerflags_includes += --include=cairo-1.0 else libregress_la_CPPFLAGS += -D_GI_DISABLE_CAIRO endif Regress-1.0.gir: libregress.la Regress_1_0_gir_LIBS = libregress.la Regress_1_0_gir_FILES = $(nodist_libregress_la_SOURCES) Regress_1_0_gir_SCANNERFLAGS = \ --warn-all --warn-error \ $(libregress_scannerflags_includes) \ $(WARN_SCANNERFLAGS) \ $(NULL) if !ENABLE_CAIRO Regress_1_0_gir_CFLAGS = -D_GI_DISABLE_CAIRO endif TEST_INTROSPECTION_GIRS += Regress-1.0.gir nodist_libwarnlib_la_SOURCES = \ installed-tests/warnlib.c \ installed-tests/warnlib.h \ $(NULL) libwarnlib_la_CPPFLAGS = $(AM_CPPFLAGS) $(GJS_CFLAGS) $(GJS_PRIVATE_CFLAGS) libwarnlib_la_LDFLAGS = $(common_test_ldflags) libwarnlib_la_LIBADD = $(common_test_libadd) WarnLib-1.0.gir: libwarnlib.la WarnLib_1_0_gir_LIBS = libwarnlib.la WarnLib_1_0_gir_INCLUDES = Gio-2.0 WarnLib_1_0_gir_FILES = $(nodist_libwarnlib_la_SOURCES) WarnLib_1_0_gir_SCANNERFLAGS = \ --c-include="warnlib.h" \ --symbol-prefix=warnlib_ \ $(NULL) TEST_INTROSPECTION_GIRS += WarnLib-1.0.gir nodist_libgimarshallingtests_la_SOURCES = \ installed-tests/gimarshallingtests.c \ installed-tests/gimarshallingtests.h \ $(NULL) libgimarshallingtests_la_CPPFLAGS = $(AM_CPPFLAGS) $(GJS_CFLAGS) $(GJS_PRIVATE_CFLAGS) libgimarshallingtests_la_LDFLAGS = $(common_test_ldflags) libgimarshallingtests_la_LIBADD = $(common_test_libadd) GIMarshallingTests-1.0.gir: libgimarshallingtests.la GIMarshallingTests_1_0_gir_LIBS = libgimarshallingtests.la GIMarshallingTests_1_0_gir_INCLUDES = Gio-2.0 GIMarshallingTests_1_0_gir_FILES = $(nodist_libgimarshallingtests_la_SOURCES) GIMarshallingTests_1_0_gir_SCANNERFLAGS = \ --symbol-prefix=gi_marshalling_tests \ --warn-all --warn-error \ $(WARN_SCANNERFLAGS) \ $(NULL) TEST_INTROSPECTION_GIRS += GIMarshallingTests-1.0.gir $(foreach gir,$(TEST_INTROSPECTION_GIRS),$(eval $(call introspection-scanner,$(gir)))) TEST_INTROSPECTION_TYPELIBS = $(TEST_INTROSPECTION_GIRS:.gir=.typelib) noinst_DATA += $(TEST_INTROSPECTION_TYPELIBS) CLEANFILES += $(TEST_INTROSPECTION_GIRS) $(TEST_INTROSPECTION_TYPELIBS) ### JAVASCRIPT TESTS ################################################### common_jstests_files = \ installed-tests/js/testself.js \ installed-tests/js/testByteArray.js \ installed-tests/js/testExceptions.js \ installed-tests/js/testEverythingBasic.js \ installed-tests/js/testEverythingEncapsulated.js \ installed-tests/js/testFormat.js \ installed-tests/js/testFundamental.js \ installed-tests/js/testGettext.js \ installed-tests/js/testGIMarshalling.js \ installed-tests/js/testGLib.js \ installed-tests/js/testGObjectClass.js \ installed-tests/js/testGObjectInterface.js \ installed-tests/js/testGTypeClass.js \ installed-tests/js/testGio.js \ installed-tests/js/testImporter.js \ installed-tests/js/testLang.js \ installed-tests/js/testLegacyClass.js \ installed-tests/js/testLegacyGObject.js \ installed-tests/js/testLocale.js \ installed-tests/js/testMainloop.js \ installed-tests/js/testNamespace.js \ installed-tests/js/testPackage.js \ installed-tests/js/testParamSpec.js \ installed-tests/js/testSignals.js \ installed-tests/js/testSystem.js \ installed-tests/js/testTweener.js \ $(NULL) jasmine_tests = $(common_jstests_files) if DBUS_TESTS jasmine_tests += installed-tests/js/testGDBus.js endif if ENABLE_GTK jasmine_tests += \ installed-tests/js/testGtk.js \ installed-tests/js/testGObjectDestructionAccess.js \ installed-tests/js/testLegacyGtk.js \ $(NULL) endif if ENABLE_CAIRO jasmine_tests += installed-tests/js/testCairo.js endif EXTRA_DIST += \ $(common_jstests_files) \ installed-tests/js/testCairo.js \ installed-tests/js/testGtk.js \ installed-tests/js/testGDBus.js \ installed-tests/js/testGObjectDestructionAccess.js \ installed-tests/js/testLegacyGtk.js \ installed-tests/extra/gjs.supp \ installed-tests/extra/lsan.supp \ $(NULL) ### TEST EXECUTION ##################################################### #@VALGRIND_CHECK_RULES@ VALGRIND_SUPPRESSIONS_FILES = \ $(datadir)/glib-2.0/valgrind/glib.supp \ $(top_srcdir)/installed-tests/extra/gjs.supp \ $(NULL) if DBUS_TESTS DBUS_SESSION_COMMAND = $(DBUS_RUN_SESSION) --config-file=$(srcdir)/test/test-bus.conf -- else DBUS_SESSION_COMMAND = endif if CODE_COVERAGE_ENABLED COVERAGE_TESTS_ENVIRONMENT = \ export GJS_UNIT_COVERAGE_OUTPUT=lcov; \ export GJS_UNIT_COVERAGE_PREFIX=resource:///org/gnome/gjs/; \ $(NULL) else COVERAGE_TESTS_ENVIRONMENT = endif # GJS_PATH is empty here since we want to force the use of our own # resources. G_FILENAME_ENCODING ensures filenames are not UTF-8. AM_TESTS_ENVIRONMENT = \ export TOP_BUILDDIR="$(abs_top_builddir)"; \ export GJS_USE_UNINSTALLED_FILES=1; \ export GJS_PATH=; \ export GI_TYPELIB_PATH="$(builddir):$${GI_TYPELIB_PATH:+:$$GI_TYPELIB_PATH}"; \ export LD_LIBRARY_PATH="$(builddir)/.libs:$${LD_LIBRARY_PATH:+:$$LD_LIBRARY_PATH}"; \ export G_FILENAME_ENCODING=latin1; \ export LSAN_OPTIONS="suppressions=$(abs_top_srcdir)/installed-tests/extra/lsan.supp"; \ export NO_AT_BRIDGE=1; \ export LC_ALL=C.UTF-8; \ $(COVERAGE_TESTS_ENVIRONMENT) \ $(XVFB_START) \ $(DBUS_SESSION_COMMAND) \ $(NULL) simple_tests = \ installed-tests/scripts/testCommandLine.sh \ installed-tests/scripts/testWarnings.sh \ $(NULL) EXTRA_DIST += $(simple_tests) TESTS = \ gjs-tests.gtester \ $(simple_tests) \ $(jasmine_tests) \ $(NULL) TEST_EXTENSIONS = .gtester .sh .js LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) $(top_srcdir)/tap-driver.sh GTESTER_LOG_DRIVER = $(LOG_DRIVER) GTESTER_LOG_COMPILER = $(top_srcdir)/test/run-test SH_LOG_DRIVER = $(LOG_DRIVER) JS_LOG_DRIVER = $(LOG_DRIVER) JS_LOG_COMPILER = $$LOG_COMPILER $$LOG_FLAGS $(top_builddir)/minijasmine CODE_COVERAGE_IGNORE_PATTERN = */{include,mfbt,gjs/test}/* CODE_COVERAGE_GENHTML_OPTIONS = \ $(CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT) \ lcov/coverage.lcov \ --prefix $(abs_top_builddir)/lcov/org/gnome/gjs \ --prefix $(abs_top_builddir) \ $(NULL) @CODE_COVERAGE_RULES@ cjs-4.4.0/Makefile.am000066400000000000000000000137061356375132300143510ustar00rootroot00000000000000# http://people.gnome.org/~walters/docs/build-api.txt .buildapi-allow-builddir: -include $(INTROSPECTION_MAKEFILE) bin_PROGRAMS = lib_LTLIBRARIES = noinst_HEADERS = noinst_LTLIBRARIES = noinst_DATA = dist_gjsjs_DATA = BUILT_SOURCES = CLEANFILES = EXTRA_DIST = check_PROGRAMS = check_LTLIBRARIES = INTROSPECTION_GIRS = ## ACLOCAL_AMFLAGS can be removed for Automake 1.13 ACLOCAL_AMFLAGS = -I m4 AM_CFLAGS = $(WARN_CFLAGS) $(CODE_COVERAGE_CFLAGS) AM_CXXFLAGS = $(WARN_CXXFLAGS) $(CODE_COVERAGE_CXXFLAGS) AM_CPPFLAGS = -DG_LOG_DOMAIN=\"Cjs\" $(CODE_COVERAGE_CPPFLAGS) AM_LDFLAGS = $(WARN_LDFLAGS) $(CODE_COVERAGE_LIBS) MAINTAINERCLEANFILES = \ $(GITIGNORE_MAINTAINERCLEANFILES_TOPLEVEL) \ $(GITIGNORE_MAINTAINERCLEANFILES_MAKEFILE_IN) \ $(GITIGNORE_MAINTAINERCLEANFILES_M4_LIBTOOL) \ tap-driver.sh \ $(NULL) GITIGNOREFILES = INSTALL m4 gjsjsdir = @gjsjsdir@ gjsoverridedir = $(gjsjsdir)/overrides gjs_public_includedir = $(includedir)/cjs-1.0 ######################################################################## include gjs-srcs.mk ######################################################################## nobase_gjs_public_include_HEADERS = $(gjs_public_headers) ######################################################################## pkgconfig_DATA = cjs-1.0.pc EXTRA_DIST += cjs-1.0.pc.in ######################################################################## gjs_directory_defines = \ -DGJS_TOP_SRCDIR=\"$(top_srcdir)\" \ -DGJS_TOP_BUILDDIR=\"$(top_builddir)\" \ -DGJS_JS_DIR=\"$(gjsjsdir)\" \ -DPKGLIBDIR=\"$(pkglibdir)\" ######################################################################## lib_LTLIBRARIES += libcjs.la libcjs_la_CPPFLAGS = \ $(AM_CPPFLAGS) \ $(GJS_CFLAGS) \ $(GJS_PRIVATE_CFLAGS) \ $(gjs_directory_defines)\ -I$(top_srcdir)/gi \ -DGJS_COMPILATION libcjs_la_LDFLAGS = \ $(AM_LDFLAGS) \ -export-symbols-regex "^[^_]" \ -version-info 0:0:0 \ $(NO_UNDEFINED_FLAG) \ $(NULL) libcjs_la_LIBADD = \ $(GJS_LIBS) \ $(GJS_PRIVATE_LIBS) if ENABLE_GTK libcjs_la_CPPFLAGS += $(GJS_GTK_CFLAGS) libcjs_la_LIBADD += $(GJS_GTK_LIBS) endif # Please see gjs-srcs.mk for brief expanations # of the layout of the sources due to historical # reasons libcjs_la_SOURCES = $(gjs_srcs) if ENABLE_PROFILER libcjs_la_SOURCES += $(gjs_sysprof_srcs) libcjs_la_LIBADD += $(LIB_TIMER_TIME) endif # Also, these files used to be a separate library libgjs_private_source_files = $(gjs_private_srcs) if ENABLE_GTK libgjs_private_source_files += $(gjs_gtk_private_srcs) endif libcjs_la_SOURCES += $(libgjs_private_source_files) CjsPrivate-1.0.gir: libcjs.la CjsPrivate_1_0_gir_LIBS = libcjs.la CjsPrivate_1_0_gir_INCLUDES = GObject-2.0 Gio-2.0 CjsPrivate_1_0_gir_CFLAGS = -I$(top_srcdir) CjsPrivate_1_0_gir_FILES = $(libgjs_private_source_files) CjsPrivate_1_0_gir_SCANNERFLAGS = \ --identifier-prefix=Gjs \ --symbol-prefix=gjs_ \ --warn-all \ $(WARN_SCANNERFLAGS) \ $(NULL) if ENABLE_GTK CjsPrivate_1_0_gir_INCLUDES += Gtk-3.0 endif INTROSPECTION_GIRS += CjsPrivate-1.0.gir if ENABLE_DTRACE gjs_gi_probes.h: gi/gjs_gi_probes.d $(DTRACE) -C -h -s $< -o $@ gjs_gi_probes.o: gi/gjs_gi_probes.d $(DTRACE) -G -s $< -o $@ BUILT_SOURCES += gjs_gi_probes.h gjs_gi_probes.o libcjs_la_LIBADD += gjs_gi_probes.o endif EXTRA_DIST += gi/gjs_gi_probes.d tapset_in_files = cjs/cjs.stp.in EXTRA_DIST += $(tapset_in_files) if ENABLE_SYSTEMTAP cjs/cjs.stp: cjs/cjs.stp.in Makefile $(AM_V_GEN)$(MKDIR_P) $(@D) && \ $(SED) -e s,@EXPANDED_LIBDIR@,$(libdir), < $< > $@.tmp && mv $@.tmp $@ tapsetdir = $(datadir)/systemtap/tapset tapset_DATA = $(tapset_in_files:.stp.in=.stp) endif include Makefile-modules.am include Makefile-examples.am typelibdir = $(pkglibdir)/girepository-1.0 typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib) CLEANFILES += $(INTROSPECTION_GIRS) $(typelib_DATA) ######################################################################## bin_PROGRAMS += cjs-console cjs_console_CPPFLAGS = \ $(AM_CPPFLAGS) \ $(GJS_CONSOLE_CFLAGS) \ $(NULL) cjs_console_LDADD = \ $(GJS_CONSOLE_LIBS) \ libcjs.la cjs_console_LDFLAGS = $(AM_LDFLAGS) -rdynamic cjs_console_SOURCES = $(gjs_console_srcs) install-exec-hook: (cd $(DESTDIR)$(bindir) && $(LN_S) -f cjs-console$(EXEEXT) cjs$(EXEEXT)) include Makefile-test.am include Makefile-insttest.am EXTRA_DIST += \ autogen.sh \ COPYING.LGPL \ doc/ByteArray.md \ doc/cairo.md \ doc/Hacking.md \ doc/SpiderMonkey_Memory.md \ doc/Style_Guide.md \ win32/build-rules-msvc.mak \ win32/config-msvc.mak \ win32/config.h.win32 \ win32/create-lists-msvc.mak \ win32/create-lists.bat \ win32/detectenv-msvc.mak \ win32/generate-msvc.mak \ win32/gjs-introspection-msvc.mak \ win32/info-msvc.mak \ win32/install.mak \ win32/introspection-msvc.mak \ win32/Makefile.vc \ win32/README.txt \ $(NULL) # Colin's handy Makefile bits for: # 1) stuffing tarballs with pre-generated scripts from your workstation # 2) bumping configure.ac version post-release # 3) tagging correctly in git # 4) uploading to gnome.org # To use: # $ make check # $ make dist # $ make prepare-minor-release # Customize to taste TAG_PREFIX=GJS_ COMPRESSION=.bz2 PACKAGE=@PACKAGE@ VERSION=@VERSION@ DISTNAME=$(PACKAGE)-$(VERSION).tar$(COMPRESSION) TAG_VERSION := $(shell echo $(VERSION) | $(SED) s/\\\./_/g) prepare-release-tag: Makefile git tag -m "Tag $(TAG_VERSION)" -a $(TAG_PREFIX)$(TAG_VERSION) prepare-minor-release: $(DISTNAME) prepare-release-tag Makefile env top_srcdir=$(top_srcdir) python $(top_srcdir)/verbump.py upload-release: $(DISTNAME) Makefile git log origin/master..master @echo -n "Ok to push? [y/N] "; read ans; test x$$ans == xy || exit 1 git push --tags origin master:master scp $(DISTNAME) master.gnome.org: ssh master.gnome.org install-module $(DISTNAME) CPPCHECK=cppcheck ### cppcheck static code analysis # cppcheck: $(CPPCHECK) --inline-suppr \ --enable=warning,performance,portability,information,missingInclude \ --force -q $(top_srcdir) -I $(top_builddir) -include $(top_srcdir)/git.mk cjs-4.4.0/NEWS000066400000000000000000001420331356375132300130100ustar00rootroot00000000000000Version 1.52.1 -------------- - This version has more changes than would normally be expected from a stable version. The intention of 1.52.1 is to deliver a version that runs cleaner under performance tools, in time for the upcoming GNOME Shell performance hackfest. We also wanted to deliver a stable CI pipeline before branching GNOME 3.28 off of master. - Claudio André's work on the CI pipeline deserves a spotlight. We now have test jobs that run linters, sanitizers, Valgrind, and more; the tests are run on up-to-date Docker images; and the reliability errors that were plaguing the test runs are solved. - In addition to System.dumpHeap(), you can now dump a heap from a running Javascript program by starting it with the environment variable GJS_DEBUG_HEAP_OUTPUT=some_name, and sending it SIGUSR1. - heapgraph.py is a tool in the repository (not installed in distributions) for analyzing and graphing heap dumps, to aid with tracking down memory leaks. - The linter CI jobs will compare your branch against GNOME/gjs@master, and fail if your branch added any new linter errors. There may be false positives, and the rules configuration is not perfect. If that's the case on your merge request, you can skip the appropriate linter job by adding the text "[skip (linter)]" in your commit message: e.g., "[skip cpplint]". - We welcomed first merge requests from several new contributors for this release. - Closed bugs and merge requests: * Crash when resolving promises if exception is pending [#18, !95, Philip Chimento] * gjs_byte_array_get_proto(JSContext*): assertion failed: (((void) "gjs_" "byte_array" "_define_proto() must be called before " "gjs_" "byte_array" "_get_proto()", !v_proto.isUndefined())) [#39, !92, Philip Chimento] * Tools for examining heap graph [#116, !61, !118, Andy Holmes, Tommi Komulainen, Philip Chimento] * Run analysis tools to prepare for release [#120, !88, Philip Chimento] * Add support for passing flags to Gio.DBusProxy in makeProxyWrapper [#122, !81, Florian Müllner] * Cannot instantiate Cairo.Context [#126, !91, Philip Chimento] * GISCAN GjsPrivate-1.0.gir fails [#128, !90, Philip Chimento] * Invalid read of g_object_finalized flag [#129, !117, Philip Chimento] * Fix race condition in coverage file test [#130, !99, Philip Chimento] * Linter jobs should only fail if new lint errors were added [#133, !94, Philip Chimento] * Disable all tests that depends on X if there is no XServer [#135, !109, Claudio André] * Pick a different C++ linter [#137, !102, Philip Chimento] * Create a CI test that builds using autotools only [!74, Claudio André] * CI: enable ASAN [!89, Claudio André] * CI: disable static analysis jobs using the commit message [!93, Claudio André] * profiler: Don't assume layout of struct sigaction [!96, James Cowgill] * Valgrind [!98, Claudio André] * Robustness of CI [!103, Claudio André] * CI: make a separate job for installed tests [!106, Claudio André] * Corrected Markdown format and added links to JHBuild in setup guide for GJS [!111, Avi Zajac] * Update tweener.js -- 48 eslint errors fixed [!112, Karen Medina] * Various maintenance [!100, !104, !105, !107, !110, !113, !116, Claudio André, Philip Chimento] Version 1.52.0 -------------- - No changes from 1.51.92 except for the continuous integration configuration. - Closed bugs and merge requests: * Various CI improvements [!84, !85, !86, !87, Claudio André] Version 1.51.92 --------------- - Closed bugs and merge requests: * abort if we are called back in a non-main thread [#75, !72, Philip Chimento] * 3.27.91 build failure on debian/Ubuntu [#122, !73, Tim Lunn] * Analyze project code quality with Code Climate inside CI [#10, !77, Claudio André] * Various CI improvements [!75, !76, !79, !80, !82, !83, Claudio André] Version 1.51.91 --------------- - Promises now resolve with a higher priority, so asynchronous code should be faster. [Jason Hicks] - Closed bugs and merge requests: * New build 'warnings' [#117, !62, !63, Claudio André, Philip Chimento] * Various CI maintenance [!64, !65, !66, Claudio André, Philip Chimento] * profiler: Don't include alloca.h when disabled [!69, Ting-Wei Lan] * GNOME crash with fatal error "Finalizing proxy for an object that's scheduled to be unrooted: Gio.Subprocess" in gjs [#26, !70, Philip Chimento] Version 1.51.90 --------------- - Note that all the old Bugzilla bug reports have been migrated over to GitLab. - GJS now, once again, includes a profiler, which outputs files that can be read with sysprof. To use it, simply run your program with the environment variable GJS_ENABLE_PROFILER=1 set. If your program is a JS script that is executed with the interpreter, you can also pass --profile to the interpreter. See "gjs --help" for more info. - New API: For programs that want more control over when to start and stop profiling, there is new API for GjsContext. When you create your GjsContext there are two construct-only properties available, "profiler-enabled" and "profiler-sigusr2". If you set profiler-sigusr2 to TRUE, then the profiler can be started and stopped while the program is running by sending SIGUSR2 to the process. You can also use gjs_context_get_profiler(), gjs_profiler_set_filename(), gjs_profiler_start(), and gjs_profiler_stop() for more explicit control. - New API: GObject.signal_connect(), GObject.signal_disconnect(), and GObject.signal_emit_by_name() are now available in case a GObject-derived class has conflicting connect(), disconnect() or emit() methods. - Closed bugs and merge requests: * Handle 0-valued GType gracefully [#11, !10, Philip Chimento] * Profiler [#31, !37, Christian Hergert, Philip Chimento] * Various maintenance [!40, !59, Philip Chimento, Giovanni Campagna] * Rename GObject.Object.connect/disconnect? [#65, !47, Giovanni Campagna] * Better debugging output for uncatchable exceptions [!39, Simon McVittie] * Update Docker images and various CI maintenance [!54, !56, !57, !58, Claudio André] * Install GJS suppression file for Valgrind [#2, !55, Philip Chimento] Version 1.50.4 -------------- - Closed bugs and merge requests: * Gnome Shell crash with places-status extension when you plug an USB device [#33, !38, Philip Chimento] Version 1.50.3 -------------- - GJS will now log a warning when a GObject is accessed in Javascript code after the underlying object has been freed in C. (This used to work most of the time, but crash unpredictably.) We now prevent this situation which, is usually caused by a memory management bug in the underlying C library. - Closed bugs and merge requests: * Add checks for GObjects that have been finalized [#21, #23, !25, !28, !33, Marco Trevisan] * Test "Cairo context has methods when created from a C function" fails [#27, !35, Valentín Barros] * Various fixes from the master branch for rare crashes [Philip Chimento] Version 1.51.4 -------------- - We welcomed code and documentation from several new contributors in this release! - GJS will now log a warning when a GObject is accessed in Javascript code after the underlying object has been freed in C. (This used to work most of the time, but crash unpredictably.) We now prevent this situation which, is usually caused by a memory management bug in the underlying C library. - APIs exposed through GObject Introspection that use the GdkAtom type are now usable from Javascript. Previously these did not work. On the Javascript side, a GdkAtom translates to a string, so there is no Gdk.Atom type that you can access. The special atom GDK_NONE translates to null in Javascript, and there is also no Gdk.NONE constant. - The GitLab CI tasks have continued to gradually become more and more sophisticated. - Closed bugs and merge requests: * Add checks for GObjects that have been finalized [#21, #23, !22, !27, Marco Trevisan] * Fail static analyzer if new warnings are found [!24, Claudio André] * Run code coverage on GitLab [!20, Claudio André] * Amend gtk.js and add gtk-application.js with suggestion [!32, Andy Holmes] * Improve GdkAtom support that is blocking clipboard APIs [#14, !29, makepost] * Test "Cairo context has methods when created from a C function" fails [#27, !35, Valentín Barros] * Various CI improvements [#6, !26, !34, Claudio André] * Various maintenance [!23, !36, Philip Chimento] Version 1.51.3 -------------- - This release was made from an earlier state of master, before a breaking change was merged, while we decide whether to revert that change or not. - Closed bugs and merge requests: * CI improvements on GitLab [!14, !15, !19, Claudio André] * Fix CI build on Ubuntu [#16, !18, !21, Claudio André, Philip Chimento] Version 1.51.2 -------------- - Version 1.51.1 was skipped. - The home of GJS is now at GNOME's GitLab instance: https://gitlab.gnome.org/GNOME/gjs From now on we'll be taking GitLab merge requests instead of Bugzilla patches. If you want to report a bug, please report it at GitLab. - Closed bugs and merge requests: * Allow throwing GErrors from JS virtual functions [#682701, Giovanni Campagna] * [RFC] bootstrap system [#777724, Jasper St. Pierre, Philip Chimento] * Fix code coverage (and refactor it to take advantage of mozjs52 features) [#788166, !1, !3, Philip Chimento] * Various maintenance [!2, Philip Chimento] * Get GitLab CI working and various improvements [#6, !7, !9, !11, !13, Claudio André] * Add build status badge to README [!8, Claudio André] * Use Docker images for CI [!12, Claudio André] - Some changes in progress to improve garbage collection when signals are disconnected. See bug #679688 for more information [Giovanni Campagna] Version 1.50.2 -------------- - Closed bugs and merge requests: * tweener: Fix a couple of warnings [!5, Florian Müllner] * legacy: Allow ES6 classes to inherit from abstract Lang.Class class [!6, Florian Müllner] - Minor bugfixes [Philip Chimento] Version 1.50.1 -------------- - As a debugging aid, gjs_dumpstack() now works even during garbage collection. - Code coverage tools did not work so well in the last few 1.49 releases. The worst problems are now fixed, although even more improvements will be released in the next unstable version. Fixes include: * Specifing prefixes for code coverage files now works again * Code coverage now works on lines inside ES6 class definitions * The detection of which lines are executable has been improved a bit Version 1.50.0 -------------- - Closed bugs: * Relicense coverage.cpp and coverage.h to the same license as the rest of GJS [#787263, Philip Chimento; thanks to Dominique Leuenberger for pointing out the mistake] Version 1.49.92 --------------- - It's now possible to build GJS with sanitizers (ASan and UBSan) enabled; add "--enable-asan" and "--enable-ubsan" to your configure flags. This has already caught some memory leaks. - There's also a "make check-valgrind" target which will run GJS's test suite under Valgrind to catch memory leaks and threading races. - Many of the crashes in GNOME 3.24 were caused by GJS's closure invalidation code which had to change from the known-working state in 1.46 because of changes to SpiderMonkey's garbage collector. This code has been refactored to be less complicated, which will hopefully improve stability and debuggability. - Closed bugs: * Clean up the idle closure invalidation mess [#786668, Philip Chimento] * Add ASan and UBSan to GJS [#783220, Claudio André] * Run analysis tools on GJS to prepare for release [#786995, Philip Chimento] * Fix testLegacyGObject importing the GTK overrides [#787113, Philip Chimento] - Docs tweak [Philip Chimento] 1.49.91 ------- - Deprecation: The private "__name__" property on Lang.Class instances is now discouraged. Code should not have been using this anyway, but if it did then it should use the "name" property on the class (this.__name__ should become this.constructor.name), which is compatible with ES6 classes. - Closed bugs: * Use ES6 classes [#785652, Philip Chimento] * A few fixes for stack traces and error reporting [#786183, Philip Chimento] * /proc/self/stat is read for every frame if GC was not needed [#786017, Benjamin Berg] - Build fix [Philip Chimento] Version 1.49.90 --------------- - New API: GObject.registerClass(), intended for use with ES6 classes. When defining a GObject class using ES6 syntax, you must call GObject.registerClass() on the class object, with an optional metadata object as the first argument. (The metadata object works exactly like the meta properties from Lang.Class, except that Name and Extends are not present.) Old: var MyClass = new Lang.Class({ Name: 'MyClass', Extends: GObject.Object, Signals: { 'event': {} }, _init(props={}) { this._private = []; this.parent(props); }, }); New: var MyClass = GObject.registerClass({ Signals: { 'event': {} }, }, class MyClass extends GObject.Object { _init(props={}) { this._private = []; super._init(props); } }); It is forward compatible with the following syntax requiring decorators and class fields, which are not in the JS standard yet: @GObject.registerClass class MyClass extends GObject.Object { static [GObject.signals] = { 'event': {} } _init(props={}) { this._private = []; super._init(props); } } One limitation is that GObject ES6 classes can't have constructor() methods, they must do any setup in an _init() method. This may be able to be fixed in the future. - Closed bugs: * Misc 1.49 and mozjs52 enhancements [#785040, Philip Chimento] * Switch to native promises [#784713, Philip Chimento] * Can't call exports using top-level variable toString [#781623, Philip Chimento] * Properties no longer recognized when shadowed by a method [#785091, Philip Chimento, Rico Tzschichholz] * Patch: backport of changes required for use with mozjs-55 [#785424, Luke Jones] Version 1.48.6 -------------- - Closed bugs: * GJS crash in needsPostBarrier, possible access from wrong thread [#783935, Philip Chimento] (again) Version 1.49.4 -------------- - New JavaScript features! This version of GJS is based on SpiderMonkey 52, an upgrade from the previous ESR (Extended Support Release) of SpiderMonkey 38. GJS now uses the latest ESR in its engine and the plan is to upgrade again when SpiderMonkey 59 is released in March 2018, pending maintainer availability. Here are the highlights of the new JavaScript features. For more information, look them up on MDN or devdocs.io. * New language features + ES6 classes + Async functions and await operator + Reflect - built-in object with methods for interceptable operations * New syntax + Exponentiation operator: `**` + Variable-length Unicode code point escapes: `"\u{1f369}"` + Destructured default arguments: `function f([x, y]=[1, 2], {z: z}={z: 3})` + Destructured rest parameters: `function f(...[a, b, c])` + `new.target` allows a constructor access to the original constructor that was invoked + Unicode (u) flag for regular expressions, and corresponding RegExp.unicode property + Trailing comma in function parameter lists now allowed * New APIs + New Array, String, and TypedArray method: includes() + TypedArray sort(), toLocaleString(), and toString() methods, to correspond with regular arrays + New Object.getOwnPropertyDescriptors() and Object.values() methods + New Proxy traps: getPrototypeOf() and setPrototypeOf() + [Symbol.toPrimitive] property specifying how to convert an object to a primitive value + [Symbol.species] property allowing to override the default constructor for objects + [Symbol.match], [Symbol.replace], [Symbol.search], and [Symbol.split] properties allowing to customize matching behaviour in RegExp subclasses + [Symbol.hasInstance] property allowing to customize the behaviour of the instanceof operator for objects + [Symbol.toStringTag] property allowing to customize the message printed by Object.toString() without overriding it + [Symbol.isConcatSpreadable] property allowing to control the behaviour of an array subclass in an argument list to Array.concat() + [Symbol.unscopables] property allowing to control which object properties are lifted into the scope of a with statement + New Intl.getCanonicalLocales() method + Date.toString() and RegExp.toString() generic methods + Typed arrays can now be constructed from any iterable object + Array.toLocaleString() gained optional locales and options arguments, to correspond with other toLocaleString() methods * New behaviour + The "arguments" object is now iterable + Date.prototype, WeakMap.prototype, and WeakSet.prototype are now ordinary objects, not instances + Full ES6-compliant implementation of let keyword + RegExp.sticky ('y' flag) behaviour is ES6 standard, it used to be subject to a long-standing bug in Firefox + RegExp constructor with RegExp first argument and flags no longer throws an exception (`new RegExp(/ab+c/, 'i')` works now) + Generators are no longer constructible, as per ES6 (`function* f {}` followed by `new f` will not work) + It is now required to construct ArrayBuffer, TypedArray, Map, Set, and WeakMap with the new operator + Block-level functions (e.g. `{ function foo() {} }`) are now allowed in strict mode; they are scoped to their block + The options.timeZone argument to Date.toLocaleDateString(), Date.toLocaleString(), Date.toLocaleTimeString(), and the constructor of Intl.DateTimeFormat now understands IANA time zone names (such as "America/Vancouver") * Backwards-incompatible changes + Non-standard "let expressions" and "let blocks" (e.g., `let (x = 5) { use(x) }`) are not supported any longer + Non-standard flags argument to String.match(), String.replace(), and String.search() (e.g. `str.replace('foo', 'bar', 'g')`) is now ignored + Non-standard WeakSet.clear() method has been removed + Variables declared with let and const are now 'global lexical bindings', as per the ES6 standard, meaning that they will not be exported in modules. We are maintaining the old behaviour for the time being as a compatibility workaround, but please change "let" or "const" to "var" inside your module file. A warning will remind you of this. For more information, read: https://blog.mozilla.org/addons/2015/10/14/breaking-changes-let-const-firefox-nightly-44/ * Experimental features (may change in future versions) + String.padEnd(), String.padStart() methods (proposed in ES2017) + Intl.DateTimeFormat.formatToParts() method (proposed in ES2017) + Object.entries() method (proposed in ES2017) + Atomics, SharedArrayBuffer, and WebAssembly are disabled by default, but can be enabled if you compile mozjs yourself - Closed bugs: * Prepare for SpiderMonkey 45 and 52 [#781429, Philip Chimento] * Add a static analysis tool as a make target [#783214, Claudio André] * Fix the build with debug logs enabled [#784469, Tomas Popela] * Switch to SpiderMonkey 52 [#784196, Philip Chimento, Chun-wei Fan] * Test suite fails when run with JIT enabled [#616193, Philip Chimento] Version 1.48.5 -------------- - Closed bugs: * GJS crash in needsPostBarrier, possible access from wrong thread [#783935, Philip Chimento] - Fix format string, caught by static analysis [Claudio André] - Fixes for regression in 1.48.4 [Philip Chimento] Version 1.49.3 -------------- - This will be the last release using SpiderMonkey 38. - Fixes in preparation for SpiderMonkey 52 [Philip Chimento] - Use the Centricular fork of libffi to build on Windows [Chun-wei Fan] - Closed bugs: * [RFC] Use a C++ auto pointer instead of g_autofree [#777597, Chun-wei Fan, Daniel Boles, Philip Chimento] * Build failure in GNOME Continuous [#783031, Chun-wei Fan] Version 1.48.4 -------------- - Closed bugs: * gnome-shell 3.24.1 crash on wayland [#781799, Philip Chimento]; thanks to everyone who contributed clues Version 1.49.2 -------------- - New feature: When building an app with the Package module, using the Meson build system, you can now run the app with "ninja run" and all the paths will be set up correctly. - New feature: Gio.ListStore is now iterable. - New API: Package.requireSymbol(), a companion for the already existing Package.require(), that not only checks for a GIR library but also for a symbol defined in that library. - New API: Package.checkSymbol(), similar to Package.requireSymbol() but does not exit if the symbol was not found. Use this to support older versions of a GIR library with fallback functionality. - New API: System.dumpHeap(), for debugging only. Prints the state of the JS engine's heap to standard output. Takes an optional filename parameter which will dump to a file instead if given. - Closed bugs: * Make gjs build on Windows/Visual Studio [#775868, Chun-wei Fan] * Bring back fancy error reporter in gjs-console [#781882, Philip Chimento] * Add Meson running from source support to package.js [#781882, Patrick Griffis] * package: Fix initSubmodule() when running from source in Meson [#782065, Patrick Griffis] * package: Set GSETTINGS_SCHEMA_DIR when ran from source [#782069, Patrick Griffis] * Add imports.gi.has() to check for symbol availability [#779593, Florian Müllner] * overrides: Implement Gio.ListStore[Symbol.iterator] [#782310, Patrick Griffis] * tweener: Explicitly check for undefined properties [#781219, Debarshi Ray, Philip Chimento] * Add a way to dump the heap [#780106, Juan Pablo Ugarte] - Fixes in preparation for SpiderMonkey 52 [Philip Chimento] - Misc fixes [Philip Chimento] Version 1.48.3 -------------- - Closed bugs: * arg: don't crash when asked to convert a null strv to an array [#775679, Cosimo Cecchi, Sam Spilsbury] * gjs 1.48.0: does not compile on macOS with clang [#780350, Tom Schoonjans, Philip Chimento] * Modernize shell scripts [#781806, Claudio André] Version 1.49.1 -------------- - Closed bugs: * test GObject Class failure [#693676, Stef Walter] * Enable incremental GCs [#724797, Giovanni Campagna] * Don't silently accept extra arguments to C functions [#680215, Jasper St. Pierre, Philip Chimento] * Special case GValues in signals and properties [#688128, Giovanni Campagna, Philip Chimento] * [cairo] Instantiate wrappers properly [#614413, Philip Chimento, Johan Dahlin] * Warn if we're importing an unversioned namespace [#689654, Colin Walters, Philip Chimento] - Fixes in preparation for SpiderMonkey 45 [Philip Chimento] - Misc fixes [Philip Chimento, Chun-wei Fan, Dan Winship] Version 1.48.2 -------------- - Closed bugs: * Intermittent crash in gnome-shell, probably in weak pointer updating code [#781194, Georges Basile Stavracas Neto] * Add contributor's guide [#781297, Philip Chimento] - Misc fixes [Debarshi Ray, Philip Chimento] Version 1.48.1 -------------- - Closed bugs: * gjs crashed with SIGSEGV in gjs_object_from_g_object [#779918, Philip Chimento] - Misc bug fixes [Florian Müllner, Philip Chimento, Emmanuele Bassi] Version 1.48.0 -------------- - Closed bugs: * Memory leak in object_instance_resolve() [#780171, Philip Chimento]; thanks to Luke Jones and Hussam Al-Tayeb Version 1.47.92 --------------- - Closed bugs: * gjs 1.47.91 configure fails with Fedora's mozjs38 [#779412, Philip Chimento] * tests: Don't fail when Gtk+-4.0 is available [#779594, Florian Müllner] * gjs 1.47.91 test failures on non-amd64 [#779399, Philip Chimento] * gjs_eval_thread should always be set [#779693, Philip Chimento] * System.exit() should exit even across main loop iterations [#779692, Philip Chimento] * Fix a typo in testCommandLine.sh [#779772, Claudio André] * arg: Fix accidental fallthrough [#779838, Florian Müllner] * jsUnit: Explicitly check if tempTop.parent is defined [#779871, Iain Lane] - Misc bug fixes [Philip Chimento] Version 1.47.91 --------------- - Closed bugs: * overrides/Gio: Provide an empty array on error, rather than null [#677513, Jasper St. Pierre, Philip Chimento] * WithSignals parameter for Lang.Class [#664897, Philip Chimento] * add API to better support asynchronous code [#608450, Philip Chimento] * 1.47.90 tests are failing [#778780, Philip Chimento] * boxed: Plug a memory leak [#779036, Florian Müllner] * Don't crash when marshalling an unsafe integer from introspection [#778705, Philip Chimento] * Lang.Class should include symbol properties [#778718, Philip Chimento] * Console output of arrays should be UTF-8 aware [#778729, Philip Chimento] * Various fixes for 1.47.91 [#779293, Philip Chimento] - Progress towards a Visual Studio build of GJS on Windows [Chun-wei Fan] - Misc bug fixes [Chun-wei Fan, Philip Chimento] Version 1.47.90 --------------- - New JavaScript features! This version of GJS is based on SpiderMonkey 38, an upgrade from the previous ESR (Extended Support Release) of SpiderMonkey 31. Our plans are to continue upgrading to subsequent ESRs as maintainer availability allows. Here are the highlights of the new JavaScript features. For more information, look them up on MDN or devdocs.io. * New syntax + Shorthand syntax for method definitions: { foo() { return 5; } } + Shorthand syntax for object literals: let b = 42; let o = {b}; o.b === 42 + Computed property names for the above, as well as in getter and setter expressions and destructuring assignment: { ['b' + 'ar']() { return 6; } } + Spread operator in destructuring assignment: let [a, ...b] = [1, 2, 3]; + Template literals: `Hello, ${name}` with optional tags: tag`string` * New APIs + Symbol, a new fundamental type + WeakSet, a Set which does not prevent its members from being garbage-collected + [Symbol.iterator] properties for Array, Map, Set, String, TypedArray, and the arguments object + New Array and TypedArray functionality: Array.copyWithin(), Array.from() + New return() method for generators + New Number.isSafeInteger() method + New Object.assign() method which can replace Lang.copyProperties() in many cases + New Object.getOwnPropertySymbols() method + New RegExp flags, global, ignoreCase, multiline, sticky properties that give access to the flags that the regular expression was created with + String.raw, a tag for template strings similar to Python's r"" + New TypedArray methods for correspondence with Array: entries(), every(), fill(), filter(), find(), findIndex(), forEach(), indexOf(), join(), keys(), lastIndexOf(), map(), of(), reduce(), reduceRight(), reverse(), slice(), some(), values() * New behaviour + Temporal dead zone: print(x); let x = 5; no longer allowed + Full ES6-compliant implementation of const keyword + The Set, Map, and WeakMap constructors can now take null as their argument + The WeakMap constructor can now take an iterable as its argument + The Function.name and Function.length properties are configurable + When subclassing Map, WeakMap, and Set or using the constructors on generic objects, they will look for custom set() and add() methods. + RegExp.source and RegExp.toString() now deal with empty regexes, and escape their output. + Non-object arguments to Object.preventExtensions() now do not throw an exception, simply return the original object * Backwards-incompatible changes + It is now a syntax error to declare the same variable twice with "let" or "const" in the same scope. Existing code may need to be fixed, but the fix is trivial. + SpiderMonkey is now extra vocal about warning when you access an undefined property, and this causes some false positives. You can turn this warning off by setting GJS_DISABLE_EXTRA_WARNINGS=1. If it is overly annoying, let me know and I will consider making it disabled by default in a future release. + When enumerating the importer object (i.e., "for (let i in imports) {...}") you will now get the names of any built-in modules that have previously been imported. (But don't do this, anyway.) - Closed bugs: * SpiderMonkey 38 prep [#776966, Philip Chimento] * Misc fixes [#777205, Philip Chimento] * missing class name in error message [#642506, Philip Chimento] * Add continuous integration to GJS [#776549, Claudio André] * Switch to SpiderMonkey 38 [#777962, Philip Chimento] - Progress towards a build of GJS on Windows [Chun-wei Fan] - Progress towards increasing test coverage [Claudio André] - Misc bug fixes [Philip Chimento] Version 1.47.4 -------------- - New JavaScript feature: ES6 Promises. This release includes Lie [1], a small, self-contained Promise implementation, which is imported automatically to form the Promise global object [2]. In the future, Promises will be built into the SpiderMonkey engine and Lie will be removed, but any code using Promises will continue to work as before. [1] https://github.com/calvinmetcalf/lie [2] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise - News for GJS embedders such as gnome-shell: * New API: The GjsCoverage type and its methods are now exposed. Use this if you are embedding GJS and need to output code coverage statistics. - Closed bugs: * Add GjsCoverage to gjs-1.0 public API [#775776, Philip Chimento] * Should use uint32_t instead of u_int32_t in coverage.cpp [#776193, Shawn Walker, Alan Coopersmith] * Port tests to use an embedded copy of Jasmine [#775444, Philip Chimento] * support fields in GObject [#563391, Havoc Pennington, Philip Chimento] * Javascript errors in property getters and setter not always reported [#730101, Matt Watson, Philip Chimento] * Exception swallowed while importing Gom [#737607, Philip Chimento] * log a warning if addSignalMethods() replaces existing methods [#619710, Joe Shaw, Philip Chimento] * Provide a useful toString for importer and module objects [#636283, Jasper St. Pierre, Philip Chimento] * Fails to marshal out arrays [#697020, Paolo Borelli] * coverage: Don't warn about executing odd lines by default anymore [#751146, Sam Spilsbury, Philip Chimento] * coverage: Crash in EnterBaseline on SpiderMonkey when Ion is enabled during coverage mode. [#742852, Sam Spilsbury, Philip Chimento] * installed tests cannot load libregress.so [#776938, Philip Chimento] * Crash with subclassed fundamental with no introspection [#760057, Lionel Landwerlin] - Misc bug fixes [Philip Chimento, Claudio André] Version 1.47.3 -------------- - New JavaScript features! This version of GJS is based on SpiderMonkey 31, an upgrade from the previous ESR (Extended Support Release) of SpiderMonkey 24. Our plans are to continue upgrading to subsequent ESRs as maintainer availability allows. Here are the highlights of the new JavaScript features. For more information, look them up on MDN or devdocs.io. * New syntax + Spread operator in function calls: someFunction(arg1, arg2, ...iterableObj) + Generator functions: yield, function*, yield* + Binary and octal numeric literals: 0b10011100, 0o377 + Function arguments without defaults can now come after those with defaults: function f(x=1, y) {} * New standard library module + Intl - Locale-sensitive formatting and string comparison * New APIs + Iterator protocol - any object implementing certain methods is an "iterator" + New Array functionality: fill(), find(), findIndex(), of() + New String functionality for working with Unicode: codePointAt(), fromCodePoint(), normalize() + New Array methods for correspondence with Object: entries(), keys() + ES6 Generator methods to replace the old Firefox-specific generator API: next(), throw() + forEach() methods for Map and Set, for correspondence with Array + A bunch of new Math functions: acosh(), asinh(), atanh(), cbrt(), clz32(), cosh(), expm1(), fround(), hypot(), log10(), log1p(), log2(), sign(), sinh(), tanh(), trunc() + Some constants to tell information about float support on the platform: Number.EPSILON, Number.MAX_SAFE_INTEGER, Number.MIN_SAFE_INTEGER + New Number.parseInt() and Number.parseFloat() which are now preferred over those in the global namespace + New Object.setPrototypeOf() which now is preferred over setting obj.prototype.__proto__ + New locales and options extra arguments to all toLocaleString() and related methods + Misc new functionality: ArrayBuffer.isView(), Proxy.handler.isExtensible, Proxy.revocable() * New behaviour + -0 and +0 are now considered equal as Map keys and Set values + On typed arrays, numerical indexed properties ignore the prototype object: Int8Array.prototype[20] = 'foo'; (new Int8Array(32))[20] == 0 * New non-standard Mozilla extensions + Array comprehensions + Generator comprehensions; both were originally proposed for ES6 but removed - Backwards-incompatible change: we have changed the way certain JavaScript values are marshalled into GObject introspection 32 or 64-bit signed integer values, to match the ECMA standard. Here is the relevant section of the standard: http://www.ecma-international.org/ecma-262/7.0/index.html#sec-toint32 Notable differences between the old and new behaviour are: * Floating-point numbers ending in 0.5 are rounded differently when marshalled into 32 or 64-bit signed integers. Previously they were rounded to floor(x + 0.5), now they are rounded to signum(x) * floor(abs(x)) as per the ECMA standard: http://www.ecma-international.org/ecma-262/7.0/index.html#sec-toint32 Note that previously, GJS rounded according to the standard when converting to *unsigned* integers! * Objects whose number value is NaN (e.g, arrays of strings), would previously fail to convert, throwing a TypeError. Now they convert to 0 when marshalled into 32 or 64-bit signed integers. Note that the new behaviour is the behaviour you got all along when using pure JavaScript, without any GObject introspection: gjs> let a = new Int32Array(2); gjs> a[0] = 10.5; 10.5 gjs> a[1] = ['this', 'is', 'fine']; this,is,fine gjs> a[0] 10 gjs> a[1] 0 - News for GJS embedders such as gnome-shell: * New API: gjs_error_quark() is now exposed, and the error domain GJS_ERROR and codes GJS_ERROR_FAILED and GJS_ERROR_SYSTEM_EXIT. * Backwards-incompatible change: Calling System.exit() from JS code will now not abort the program immediately, but instead will return immediately from gjs_context_eval() so that you can unref your GjsContext before internal resources are released on exit. If gjs_context_eval() or gjs_context_eval_file() returns an error with code GJS_ERROR_SYSTEM_EXIT, it means that the JS code called System.exit(). The exit code will be found in the 'exit_status_p' out parameter to gjs_context_eval() or gjs_context_eval_file(). If you receive this error, you should do any cleanup needed and exit your program with the given exit code. - Closed bugs: * spidermonkey 31 prep [Philip Chimento, Tim Lunn, #742249] * Please use dbus-run-session to run dbus related tests [Philip Chimento, #771745] * don't abort gdb'd tests [Philip Chimento, Havoc Pennington, #605972] * Use Automake test suite runner [Philip Chimento, #775205] * please add doc/ directory to make dist tar file [Philip Chimento, #595439] * support new mozjs 31.5 [Philip Chimento, #751252] * SEGFAULT in js::NewObjectWithClassProtoCommon when instantiating a dynamic type defined in JS [Philip Chimento, Juan Pablo Ugarte, #770244] - Misc bug fixes [Philip Chimento, Alexander Larsson] Version 1.47.0 -------------- - New API: GLib.log_structured() is a convenience wrapper for the C function g_log_variant(), allowing you to do structured logging without creating GVariants by hand. For example: GLib.log_structured('test', GLib.LogLevelFlags.LEVEL_WARNING, { 'MESSAGE': 'Just a test', 'A_FIELD': 'A value', }); - Backwards-incompatible change: we have changed the way gjs-console interprets command-line arguments. Previously there was a heuristic to try to decide whether "--help" given on the command line was meant for GJS itself or for a script being launched. This did not work in some cases, for example: $ gjs -c 'if (ARGV.indexOf("--help") == -1) throw "ugh"' --help would print the GJS help. Now, all arguments meant for GJS itself must come _before_ the name of the script to execute or any script given with a "-c" argument. Any arguments _after_ the filename or script are passed on to the script. This is the way that Python handles its command line arguments as well. If you previously relied on any -I arguments after your script being added to the search path, then you should either reorder those arguments, use GJS_PATH, or handle -I inside your script, adding paths to imports.searchPath manually. In order to ease the pain of migration, GJS will continue to take those arguments into account for the time being, while still passing them on to the script. A warning will be logged if you are using the deprecated behaviour. - News for GJS embedders such as gnome-shell: * Removed API: The gjs-internals-1.0 API is now discontinued. Since gnome-shell was the only known user of this API, its pkg-config file has been removed completely and gnome-shell has been patched not to use it. - Closed bugs: * make check fails with --enable-debug / -DDEBUG spidermonkey [Philip Chimento, #573335] * Modernize autotools configuration [Philip Chimento, #772027] * overrides/GLib: Add log_structured() - wrapper for g_log_variant() [Jonh Wendell, #771598] * Remove gjs-internals-1.0 API [Philip Chimento, #772386] * Add support for boolean, gunichar, and 64-bit int arrays [Philip Chimento, #772790] * add a --version flag [Philip Chimento, #772033] * Switch to AX_COMPILER_FLAGS and compile warning-free [Philip Chimento, #773297] * Reinstate importer test [Philip Chimento, #773335] Version 1.46.0 -------------- - Be future proof against Format fixes in SpiderMonkey [Giovanni Campagna, #770111] Version 1.45.4 -------------- - Release out args before freeing caller-allocated structs [Florian Müllner, #768413] - Marshal variable array-typed signal arguments [Philip Chimento, Florian Müllner, #761659] - Marshal all structs in out arrays correctly [Philip Chimento, #761658] - Call setlocale() before processing arguments [Ting-Wei Lan, #760424] - Build fixes and improvements [Philip Chimento, #737702, #761072] [Hashem Nasarat, #761366] [Carlos Garnacho, #765905] [Simon McVittie, #767368] [Emmanuele Bassi] [Michael Catanzaro] [Matt Watson] Version 1.45.3 -------------- - Support external construction of gjs-defined GObjects [Florian Müllner, #681254] - Add new format.printf() API [Colin Walters, #689664] - Add new API to get the name of a repository [Jasper St. Pierre, #685413] - Add C to JS support for arrays of flat structures [Giovanni Campagna, #704842] - Add API to specify CSS node name [Florian Müllner, #758349] - Return value of default signal handler for "on_signal_name" methods [Philip Chimento, #729288] - Fix multiple emissions of onOverwrite in Tweener [Tommi Komulainen, #597927] - Misc bug fixes [Philip Chimento, #727370] [Owen Taylor, #623330] [Juan RP, #667908] [Ben Iofel, #757763] Version 1.44.0 -------------- - Add Lang.Interface and GObject.Interface [Philip Chimento, Roberto Goizueta, #751343, #752984] - Support callbacks with (transfer full) return types [Debarshi Ray, #750286] - Add binding for setlocale() [Philip Chimento, #753072] - Improve support to generate code coverage reports [Sam Spilsbury, #743009, #743007, #742362, #742535, #742797, #742466, #751732] - Report errors from JS property getters/setters [Matt Watson, #730101] - Fix crash when garbage collection triggers while inside an init function [Sam Spilsbury, #742517] - Port to CallReceiver/CallArgs [Tim Lunn, #742249] - Misc bug fixes [Ting-Wei Lan, #736979, #753072] [Iain Lane, #750688] [Jasper St. Pierre] [Philip Chimento] [Colin Walters] Version 1.43.3 -------------- - GTypeClass and GTypeInterface methods, such as g_object_class_list_properties(), are now available [#700347] - Added full automatic support for GTK widget templates [#700347, #737661] [Jonas Danielsson, #739739] - Added control of JS Date caches to system module [#739790] - Misc bug fixes and memory leak fixes [Owen Taylor, #738122] [Philip Chimento, #740696, #737701] Version 1.42.0 -------------- - Fix a regression caused by PPC fixes in 1.41.91 Version 1.41.91 --------------- - Added the ability to disable JS language warnings [Lionel Landwerlin, #734569] - Fixed crashes in PPC (and probably other arches) due to invalid callback signatures [Michel Danzer, #729554] - Fixed regressions with dbus 1.8.6 [Tim Lunn, #735358] - Readded file system paths to the default module search, to allow custom GI overrides for third party libraries [Jasper St. Pierre] Version 1.41.4 -------------- - Fixed memory management of GObject methods that unref their instance [#729545] - Added a package module implementing the https://wiki.gnome.org/Projects/Gjs/Package application conventions [#690136] - Misc bug fixes Version 1.41.3 -------------- - Fixed GObject and Gtk overrides [Mattias Bengtsson, #727781] [#727394] - Fixed crashes caused by reentrancy during finalization [#725024] - Added a wrapper type for cairo regions [Jasper St. Pierre, #682303] - Several cleanups to GC [#725024] - Thread-safe structures are now finalized in the background, for greater responsiveness [#725024] [#730030] - A full GC is now scheduled if after executing a piece of JS we see that the RSS has grown by over 150% [Cosimo Cecchi, #725099] [#728048] - ParamSpecs now support methods and static methods implemented by glib and exposed by gobject-introspection, in addition to the manually bound fields [#725282] - Protototypes no longer include static properties or constructors [#725282] - Misc cleanups and bugfixes [Mattias Bengtsson, #727786] [#725282] [Lionel Landwerlin, #728004] [Lionel Landwerlin, #727824] Version 1.40.0 -------------- - No changes Version 1.39.90 --------------- - Implemented fundamental object support [Lionel Landwerlin, #621716, #725061] - Fixed GIRepositoryGType prototype [#724925] - Moved GObject.prototype.disconnect() to a JS implementation [#698283] - Added support for enumeration methods [#725143] - Added pseudo-classes for fundamental types [#722554] - Build fixes [Ting-Wei Lan, #724853] Version 0.3 (03-Jul-2009) ------------------------- Changes: - DBus support At a high level there are three pieces. First, the "gjs-dbus" library is a C support library for DBus. Second, the modules/dbus*.[ch] implement parts of the JavaScript API in C. Third, the modules/dbus.js file fills out the rest of the JavaScript API and implementation. - Support simple fields for boxed types - Support "copy construction" of boxed types - Support simple structures not registered as boxed - Allow access to nested structures - Allow direct assignment to nested structure fields - Allow enum and flag structure fields - Allow creating boxed wrapper without copy - Support for non-default constructor (i.e. constructors like GdkPixbuf.Pixbuf.new_from_file(file)) - Add a Lang.bind function which binds the meaning of 'this' - Add an interactive console gjs-console - Allow code in directory modules (i.e. the code should reside in __init__.js files) - Fix handling of enum/flags return values - Handle non-gobject-registered flags - Add Tweener.registerSpecialProperty to tweener module - Add profiler for javascript code - Add gjs_context_get_all and gjs_dumpstack - useful to invoke from a debugger such as gdb - Support GHashTable - GHashTable return values/out parameters - Support GHashTable 'in' parameters - Convert JSON-style object to a GHashTable - Add support for UNIX shebang (i.e. #!/usr/bin/gjs-console) - Support new introspection short/ushort type tags - Support GI_TYPE_TAG_FILENAME - Improve support for machine-dependent integer types and arrays of integers - Fix several memory leaks Contributors: Colin Walters, C. Scott Ananian, Dan Winship, David Zeuthen, Havoc Pennington, Johan Bilien, Johan Dahlin, Lucas Rocha, Marco Pesenti Gritti, Marina Zhurakhinskaya, Owen Taylor, Tommi Komulainen Bugs fixed: Bug 560506 - linking problem Bug 560670 - Turn on compilation warnings Bug 560808 - Simple field supported for boxed types Bug 561514 - memory leak when skipping deprecated methods Bug 561516 - skipping deprecated methods shouldn't signal error case Bug 561849 - Alternate way of connecting signals. Bug 562892 - valgrind errors on get_obj_key Bug 564424 - Add an interactive console Bug 564664 - Expose JS_SetDebugErrorHook Bug 566185 - Allow code in directory modules Bug 567675 - Handle non-gobject-registered flags Bug 569178 - Add readline support to the console module Bug 570775 - array of parameters leaked on each new GObject Bug 570964 - Race when shutting down a context, r=hp Bug 580948 - Add DBus support Bug 584560 - Add support for UNIX shebang Bug 584850 - Clean up some __BIG definitions. Bug 584858 - Fix errors (and leftover debugging) from dbus merge. Bug 584858 - Move gjsdbus to gjs-dbus to match installed location. Bug 585386 - Add a flush() method to DBus bus object. Bug 585460 - Fix segfault when a non-existing node is introspected. Bug 586665 - Fix seg fault when attempting to free callback type. Bug 586760 - Support converting JavaScript doubles to DBus int64/uint64. Bug 561203 - Fix priv_from_js_with_typecheck() for dynamic types Bug 561573 - Add non-default constructors and upcoming static methods to "class" Bug 561585 - allow using self-built spidermonkey Bug 561664 - Closure support is broken Bug 561686 - Don't free prov->gboxed for "has_parent" Boxed Bug 561812 - Support time_t Bug 562575 - Set correct GC parameters and max memory usage Bug 565029 - Style guide - change recommendation for inheritance Bug 567078 - Don't keep an extra reference to closures Bug 569374 - Logging exceptions from tweener callbacks could be better Bug 572113 - add profiler Bug 572121 - Invalid read of size 1 Bug 572130 - memory leaks Bug 572258 - profiler hooks should be installed only when needed Bug 580865 - Call JS_SetLocaleCallbacks() Bug 580947 - Validate UTF-8 strings in gjs_string_to_utf8() Bug 580957 - Change the way we trigger a warning in testDebugger Bug 581277 - Call JS_SetScriptStackQuota on our contexts Bug 581384 - Propagate exceptions from load context Bug 581385 - Return false when gjs_g_arg_release_internal fails Bug 581389 - Fix arg.c to use 'interface' instead of 'symbol' Bug 582686 - Don't g_error() when failing to release an argument Bug 582704 - Don't depend on hash table order in test cases Bug 582707 - Fix problems with memory management for in parameters Bug 584849 - Kill warnings (uninitialized variables, strict aliasing) Bug 560808 - Structure support Version 0.2 (12-Nov-2008) ------------------------- Changes: - Compatible with gobject-introspection 0.6.0 - New modules: mainloop, signals, tweener - Support passing string arrays to gobject-introspection methods - Added jsUnit based unit testing framework - Improved error handling and error reporting Contributors: Colin Walters, Havoc Pennington, Johan Bilien, Johan Dahlin, Lucas Rocha, Owen Taylor, Tommi Komulainen Bugs fixed: Bug 557398 – Allow requiring namespace version Bug 557448 – Enum and Flags members should be uppercase Bug 557451 – Add search paths from environment variables Bug 557451 – Add search paths from environment variables Bug 557466 – Module name mangling considered harmful Bug 557579 – Remove use of GJS_USE_UNINSTALLED_FILES in favor of GI_TYPELIB_PATH Bug 557772 - gjs_invoke_c_function should work with union and boxed as well Bug 558114 – assertRaises should print return value Bug 558115 – Add test for basic types Bug 558148 – 'const char*' in arguments are leaked Bug 558227 – Memory leak if invoked function returns an error Bug 558741 – Mutual imports cause great confusion Bug 558882 – Bad error if you omit 'new' Bug 559075 – enumerating importer should skip hidden files Bug 559079 – generate code coverage report Bug 559194 - Fix wrong length buffer in get_obj_key() Bug 559612 - Ignore deprecated methods definitions. Bug 560244 - support for strv parameters Version 0.1 ----------- Initial version! Ha! cjs-4.4.0/README000066400000000000000000000020631356375132300131670ustar00rootroot00000000000000JavaScript bindings for Cinnamon ================================ Based on GJS: https://wiki.gnome.org/action/show/Projects/Gjs Please do the following when reporting CJS crashes: =================================================== If possible, provide a stack trace. Run dmesg and provide the line related to the crash, for instance: [ 4947.459104] cinnamon[2868]: segfault at 7f2611ffffe8 ip 00007f2667dda305 sp 00007fffb416b9d0 error 4 in libcjs.so.0.0.0[7f2667db1000+c1000] Launch the Calculator, choose Advanced Mode and set it to Hexadecimal. Then substract the loading address (first address in brackets: 7f2667db1000) from the ip (00007f2667dda305). In the example above: ip: 00007f2667dda305 loading address: 7f2667db1000 00007f2667dda305 - 7f2667db1000 = 29305 This gives us the offset. Use addr2line to see what's under it in our shared library: addr2line -e /usr/lib/libcjs.so.0.0.0 29305 -fCi gjs_typecheck_boxed When reporting the bug, along with the trace and the dmesg line, please report that function name (in this example gjs_typecheck_boxed). cjs-4.4.0/README.md000066400000000000000000000021071356375132300135650ustar00rootroot00000000000000JavaScript bindings for Cinnamon ================================ Based on GJS: https://wiki.gnome.org/action/show/Projects/Gjs Please do the following when reporting CJS crashes: =================================================== If possible, provide a stack trace. Run dmesg and provide the line related to the crash, for instance: `[ 4947.459104] cinnamon[2868]: segfault at 7f2611ffffe8 ip **00007f2667dda305** sp 00007fffb416b9d0 error 4 in libcjs.so.0.0.0[**7f2667db1000**+c1000]` Launch the Calculator, choose Advanced Mode and set it to Hexadecimal. Then substract the loading address (first address in brackets: 7f2667db1000) from the ip (00007f2667dda305). In the example above: ip: 00007f2667dda305 loading address: 7f2667db1000 `00007f2667dda305 - 7f2667db1000 = 29305` This gives us the offset. Use addr2line to see what's under it in our shared library: ``` addr2line -e /usr/lib/libcjs.so.0.0.0 29305 -fCi gjs_typecheck_boxed ``` When reporting the bug, along with the trace and the dmesg line, please report that function name (in this example gjs_typecheck_boxed). cjs-4.4.0/autogen.sh000077500000000000000000000015461356375132300143150ustar00rootroot00000000000000#!/bin/sh # Run this to generate all the initial makefiles, etc. test -n "$srcdir" || srcdir=`dirname "$0"` test -z "$srcdir" && srcdir=. olddir=`pwd` cd "$srcdir" (test -f configure.ac) || { echo -n "**Error**: Directory "\`$srcdir\'" does not look like the" echo " top-level directory" exit 1 } if [ "$#" = 0 -a "x$NOCONFIGURE" = "x" ]; then echo "*** WARNING: I am going to run 'configure' with no arguments." >&2 echo "*** If you wish to pass any to it, please specify them on the" >&2 echo "*** '$0' command line." >&2 echo "" >&2 fi mkdir -p m4 autoreconf --verbose --force --install || exit 1 cd "$olddir" if [ "$NOCONFIGURE" = "" ]; then "$srcdir/configure" "$@" || exit 1 if [ "$1" = "--help" ]; then exit 0 else echo "Now type 'make' to compile" || exit 1 fi else echo "Skipping configure process." fi cjs-4.4.0/cjs-1.0.pc.in000066400000000000000000000006151356375132300143140ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ bindir=@bindir@ includedir=@includedir@ datarootdir=@datarootdir@ datadir=@datadir@ cjs_console=${bindir}/cjs-console Cflags: -I${includedir}/cjs-1.0 Requires: @CJS_PACKAGE_REQUIRES@ Requires.private: @CJS_PACKAGE_REQUIRES_PRIVATE@ Libs: -L${libdir} -lcjs Name: cjs-1.0 Description: Cinnamon JS bindings for GObjects Version: @VERSION@ cjs-4.4.0/cjs/000077500000000000000000000000001356375132300130655ustar00rootroot00000000000000cjs-4.4.0/cjs/byteArray.cpp000066400000000000000000000570141356375132300155420ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2010 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include "byteArray.h" #include "gi/boxed.h" #include "jsapi-class.h" #include "jsapi-wrapper.h" #include "jsapi-util-args.h" #include #include typedef struct { GByteArray *array; GBytes *bytes; } ByteArrayInstance; extern struct JSClass gjs_byte_array_class; GJS_DEFINE_PRIV_FROM_JS(ByteArrayInstance, gjs_byte_array_class) static bool byte_array_get_prop (JSContext *context, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleValue value_p); static bool byte_array_set_prop (JSContext *context, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleValue value_p, JS::ObjectOpResult& result); GJS_NATIVE_CONSTRUCTOR_DECLARE(byte_array); static void byte_array_finalize (JSFreeOp *fop, JSObject *obj); static JSObject *gjs_byte_array_get_proto(JSContext *); static const struct JSClassOps gjs_byte_array_class_ops = { NULL, /* addProperty */ NULL, /* deleteProperty */ byte_array_get_prop, byte_array_set_prop, NULL, /* enumerate */ NULL, /* resolve */ nullptr, /* mayResolve */ byte_array_finalize }; struct JSClass gjs_byte_array_class = { "ByteArray", JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE, &gjs_byte_array_class_ops }; bool gjs_typecheck_bytearray(JSContext *context, JS::HandleObject object, bool throw_error) { return do_base_typecheck(context, object, throw_error); } static JS::Value gjs_value_from_gsize(gsize v) { if (v <= (gsize) JSVAL_INT_MAX) { return JS::Int32Value(v); } return JS::NumberValue(v); } static void byte_array_ensure_array (ByteArrayInstance *priv) { if (priv->bytes) { priv->array = g_bytes_unref_to_array(priv->bytes); priv->bytes = NULL; } else { g_assert(priv->array); } } static void byte_array_ensure_gbytes (ByteArrayInstance *priv) { if (priv->array) { priv->bytes = g_byte_array_free_to_bytes(priv->array); priv->array = NULL; } else { g_assert(priv->bytes); } } static bool gjs_value_to_gsize(JSContext *context, JS::HandleValue value, gsize *v_p) { guint32 val32; /* Just JS::ToUint32() would work. However, we special case ints for a nicer * error message on negative indices. */ if (value.isInt32()) { int i = value.toInt32(); if (i < 0) { gjs_throw(context, "Negative length or index %d is not allowed for ByteArray", i); return false; } *v_p = i; return true; } else { bool ret; /* This is pretty liberal (it converts about anything to * a number) but it's what we use elsewhere in gjs too. */ ret = JS::ToUint32(context, value, &val32); *v_p = val32; return ret; } } static bool gjs_value_to_byte(JSContext *context, JS::HandleValue value, guint8 *v_p) { gsize v; if (!gjs_value_to_gsize(context, value, &v)) return false; if (v >= 256) { gjs_throw(context, "Value %" G_GSIZE_FORMAT " is not a valid byte; must be in range [0,255]", v); return false; } *v_p = v; return true; } static bool byte_array_get_index(JSContext *context, JS::HandleObject obj, ByteArrayInstance *priv, gsize idx, JS::MutableHandleValue value_p) { gsize len; guint8 *data; gjs_byte_array_peek_data(context, obj, &data, &len); if (idx >= len) { gjs_throw(context, "Index %" G_GSIZE_FORMAT " is out of range for ByteArray length %lu", idx, (unsigned long)len); return false; } value_p.setInt32(data[idx]); return true; } /* a hook on getting a property; set value_p to override property's value. * Return value is false on OOM/exception. */ static bool byte_array_get_prop(JSContext *context, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleValue value_p) { ByteArrayInstance *priv; priv = priv_from_js(context, obj); if (!priv) return true; /* prototype, not an instance. */ JS::RootedValue id_value(context); if (!JS_IdToValue(context, id, &id_value)) return false; /* First handle array indexing */ if (id_value.isNumber()) { gsize idx; if (!gjs_value_to_gsize(context, id_value, &idx)) return false; return byte_array_get_index(context, obj, priv, idx, value_p); } /* We don't special-case anything else for now. Regular JS arrays * allow string versions of ints for the index, we don't bother. */ return true; } static bool byte_array_length_getter(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_PRIV(context, argc, vp, args, to, ByteArrayInstance, priv); gsize len = 0; if (!priv) return true; /* prototype, not an instance. */ if (priv->array != NULL) len = priv->array->len; else if (priv->bytes != NULL) len = g_bytes_get_size (priv->bytes); args.rval().set(gjs_value_from_gsize(len)); return true; } static bool byte_array_length_setter(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_PRIV(context, argc, vp, args, to, ByteArrayInstance, priv); gsize len = 0; if (!priv) return true; /* prototype, not instance */ byte_array_ensure_array(priv); if (!gjs_value_to_gsize(context, args[0], &len)) { gjs_throw(context, "Can't set ByteArray length to non-integer"); return false; } g_byte_array_set_size(priv->array, len); args.rval().setUndefined(); return true; } static bool byte_array_set_index(JSContext *context, JS::HandleObject obj, ByteArrayInstance *priv, gsize idx, JS::MutableHandleValue value_p, JS::ObjectOpResult& result) { guint8 v; if (!gjs_value_to_byte(context, value_p, &v)) { return false; } byte_array_ensure_array(priv); /* grow the array if necessary */ if (idx >= priv->array->len) { g_byte_array_set_size(priv->array, idx + 1); } g_array_index(priv->array, guint8, idx) = v; /* Stop JS from storing a copy of the value */ value_p.setUndefined(); return result.succeed(); } /* a hook on setting a property; set value_p to override property value to * be set. Return value is false on OOM/exception. */ static bool byte_array_set_prop(JSContext *context, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleValue value_p, JS::ObjectOpResult& result) { ByteArrayInstance *priv; priv = priv_from_js(context, obj); if (!priv) return result.succeed(); /* prototype, not an instance. */ JS::RootedValue id_value(context); if (!JS_IdToValue(context, id, &id_value)) return false; /* First handle array indexing */ if (id_value.isNumber()) { gsize idx; if (!gjs_value_to_gsize(context, id_value, &idx)) return false; return byte_array_set_index(context, obj, priv, idx, value_p, result); } /* We don't special-case anything else for now */ return result.succeed(); } static GByteArray * gjs_g_byte_array_new(int preallocated_length) { GByteArray *array; /* can't use g_byte_array_new() because we need to clear to zero. * We nul-terminate too for ease of toString() and for security * paranoia. */ array = (GByteArray*) g_array_sized_new (true, /* nul-terminated */ true, /* clear to zero */ 1, /* element size */ preallocated_length); if (preallocated_length > 0) { /* we want to not only allocate the size, but have it * already be the array's length. */ g_byte_array_set_size(array, preallocated_length); } return array; } GJS_NATIVE_CONSTRUCTOR_DECLARE(byte_array) { GJS_NATIVE_CONSTRUCTOR_VARIABLES(byte_array) ByteArrayInstance *priv; gsize preallocated_length; GJS_NATIVE_CONSTRUCTOR_PRELUDE(byte_array); preallocated_length = 0; if (argc >= 1) { if (!gjs_value_to_gsize(context, argv[0], &preallocated_length)) { gjs_throw(context, "Argument to ByteArray constructor should be a positive number for array length"); return false; } } priv = g_slice_new0(ByteArrayInstance); priv->array = gjs_g_byte_array_new(preallocated_length); g_assert(priv_from_js(context, object) == NULL); JS_SetPrivate(object, priv); GJS_NATIVE_CONSTRUCTOR_FINISH(byte_array); return true; } static void byte_array_finalize(JSFreeOp *fop, JSObject *obj) { ByteArrayInstance *priv; priv = (ByteArrayInstance*) JS_GetPrivate(obj); if (!priv) return; /* prototype, not instance */ if (priv->array) { g_byte_array_free(priv->array, true); priv->array = NULL; } else if (priv->bytes) { g_clear_pointer(&priv->bytes, g_bytes_unref); } g_slice_free(ByteArrayInstance, priv); } /* implement toString() with an optional encoding arg */ static bool to_string_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_PRIV(context, argc, vp, argv, to, ByteArrayInstance, priv); GjsAutoJSChar encoding; bool encoding_is_utf8; gchar *data; if (!priv) return true; /* prototype, not instance */ byte_array_ensure_array(priv); if (argc >= 1 && argv[0].isString()) { JS::RootedString str(context, argv[0].toString()); encoding = JS_EncodeStringToUTF8(context, str); if (!encoding) return false; /* maybe we should be smarter about utf8 synonyms here. * doesn't matter much though. encoding_is_utf8 is * just an optimization anyway. */ encoding_is_utf8 = (strcmp(encoding, "UTF-8") == 0); } else { encoding_is_utf8 = true; } if (priv->array->len == 0) /* the internal data pointer could be NULL in this case */ data = (gchar*)""; else data = (gchar*)priv->array->data; if (encoding_is_utf8) { /* optimization, avoids iconv overhead and runs * libmozjs hardwired utf8-to-utf16 */ return gjs_string_from_utf8_n(context, data, priv->array->len, argv.rval()); } else { bool ok = false; gsize bytes_written; GError *error; JSString *s; char *u16_str; char16_t *u16_out; error = NULL; u16_str = g_convert(data, priv->array->len, "UTF-16", encoding, NULL, /* bytes read */ &bytes_written, &error); if (u16_str == NULL) { /* frees the GError */ gjs_throw_g_error(context, error); return false; } /* bytes_written should be bytes in a UTF-16 string so * should be a multiple of 2 */ g_assert((bytes_written % 2) == 0); u16_out = g_new(char16_t, bytes_written / 2); memcpy(u16_out, u16_str, bytes_written); s = JS_NewUCStringCopyN(context, u16_out, bytes_written / 2); if (s != NULL) { ok = true; argv.rval().setString(s); } g_free(u16_str); g_free(u16_out); return ok; } } static bool to_gbytes_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_PRIV(context, argc, vp, rec, to, ByteArrayInstance, priv); JSObject *ret_bytes_obj; GIBaseInfo *gbytes_info; if (!priv) return true; /* prototype, not instance */ byte_array_ensure_gbytes(priv); gbytes_info = g_irepository_find_by_gtype(NULL, G_TYPE_BYTES); ret_bytes_obj = gjs_boxed_from_c_struct(context, (GIStructInfo*)gbytes_info, priv->bytes, GJS_BOXED_CREATION_NONE); rec.rval().setObjectOrNull(ret_bytes_obj); return true; } static JSObject* byte_array_new(JSContext *context) { ByteArrayInstance *priv; JS::RootedObject proto(context, gjs_byte_array_get_proto(context)); JS::RootedObject array(context, JS_NewObjectWithGivenProto(context, &gjs_byte_array_class, proto)); priv = g_slice_new0(ByteArrayInstance); g_assert(priv_from_js(context, array) == NULL); JS_SetPrivate(array, priv); return array; } /* fromString() function implementation */ static bool from_string_func(JSContext *context, unsigned argc, JS::Value *vp) { JS::CallArgs argv = JS::CallArgsFromVp (argc, vp); ByteArrayInstance *priv; GjsAutoJSChar encoding; bool encoding_is_utf8; JS::RootedObject obj(context, byte_array_new(context)); if (!obj) return false; priv = priv_from_js(context, obj); g_assert (priv != NULL); g_assert(argc > 0); /* because we specified min args 1 */ priv->array = gjs_g_byte_array_new(0); if (!argv[0].isString()) { gjs_throw(context, "byteArray.fromString() called with non-string as first arg"); return false; } if (argc > 1 && argv[1].isString()) { JS::RootedString str(context, argv[1].toString()); encoding = JS_EncodeStringToUTF8(context, str); if (!encoding) return false; /* maybe we should be smarter about utf8 synonyms here. * doesn't matter much though. encoding_is_utf8 is * just an optimization anyway. */ encoding_is_utf8 = (strcmp(encoding, "UTF-8") == 0); } else { encoding_is_utf8 = true; } if (encoding_is_utf8) { /* optimization? avoids iconv overhead and runs * libmozjs hardwired utf16-to-utf8. */ JS::RootedString str(context, argv[0].toString()); GjsAutoJSChar utf8 = JS_EncodeStringToUTF8(context, str); if (!utf8) return false; g_byte_array_set_size(priv->array, 0); g_byte_array_append(priv->array, reinterpret_cast(utf8.get()), strlen(utf8)); } else { JSString *str = argv[0].toString(); /* Rooted by argv */ GError *error = NULL; char *encoded = NULL; gsize bytes_written; /* Scope for AutoCheckCannotGC, will crash if a GC is triggered * while we are using the string's chars */ { JS::AutoCheckCannotGC nogc; size_t len; if (JS_StringHasLatin1Chars(str)) { const JS::Latin1Char *chars = JS_GetLatin1StringCharsAndLength(context, nogc, str, &len); if (chars == NULL) return false; encoded = g_convert((char *) chars, len, encoding, /* to_encoding */ "LATIN1", /* from_encoding */ NULL, /* bytes read */ &bytes_written, &error); } else { const char16_t *chars = JS_GetTwoByteStringCharsAndLength(context, nogc, str, &len); if (chars == NULL) return false; encoded = g_convert((char *) chars, len * 2, encoding, /* to_encoding */ "UTF-16", /* from_encoding */ NULL, /* bytes read */ &bytes_written, &error); } } if (encoded == NULL) { /* frees the GError */ gjs_throw_g_error(context, error); return false; } g_byte_array_set_size(priv->array, 0); g_byte_array_append(priv->array, (guint8*) encoded, bytes_written); g_free(encoded); } argv.rval().setObject(*obj); return true; } /* fromArray() function implementation */ static bool from_array_func(JSContext *context, unsigned argc, JS::Value *vp) { JS::CallArgs argv = JS::CallArgsFromVp (argc, vp); ByteArrayInstance *priv; guint32 len; guint32 i; bool is_array; JS::RootedObject obj(context, byte_array_new(context)); if (!obj) return false; priv = priv_from_js(context, obj); g_assert (priv != NULL); g_assert(argc > 0); /* because we specified min args 1 */ priv->array = gjs_g_byte_array_new(0); JS::RootedObject array_obj(context, &argv[0].toObject()); if (!JS_IsArrayObject(context, array_obj, &is_array)) return false; if (!is_array) { gjs_throw(context, "byteArray.fromArray() called with non-array as first arg"); return false; } if (!JS_GetArrayLength(context, array_obj, &len)) { gjs_throw(context, "byteArray.fromArray() can't get length of first array arg"); return false; } g_byte_array_set_size(priv->array, len); JS::RootedValue elem(context); for (i = 0; i < len; ++i) { guint8 b; elem = JS::UndefinedValue(); if (!JS_GetElement(context, array_obj, i, &elem)) { /* this means there was an exception, while elem.isUndefined() * means no element found */ return false; } if (elem.isUndefined()) continue; if (!gjs_value_to_byte(context, elem, &b)) return false; g_array_index(priv->array, guint8, i) = b; } argv.rval().setObject(*obj); return true; } static bool from_gbytes_func(JSContext *context, unsigned argc, JS::Value *vp) { JS::CallArgs argv = JS::CallArgsFromVp (argc, vp); JS::RootedObject bytes_obj(context); GBytes *gbytes; ByteArrayInstance *priv; if (!gjs_parse_call_args(context, "overrides_gbytes_to_array", argv, "o", "bytes", &bytes_obj)) return false; if (!gjs_typecheck_boxed(context, bytes_obj, NULL, G_TYPE_BYTES, true)) return false; gbytes = (GBytes*) gjs_c_struct_from_boxed(context, bytes_obj); JS::RootedObject obj(context, byte_array_new(context)); if (!obj) return false; priv = priv_from_js(context, obj); g_assert (priv != NULL); priv->bytes = g_bytes_ref(gbytes); argv.rval().setObject(*obj); return true; } JSObject * gjs_byte_array_from_byte_array (JSContext *context, GByteArray *array) { ByteArrayInstance *priv; g_return_val_if_fail(context != NULL, NULL); g_return_val_if_fail(array != NULL, NULL); JS::RootedObject proto(context, gjs_byte_array_get_proto(context)); JS::RootedObject object(context, JS_NewObjectWithGivenProto(context, &gjs_byte_array_class, proto)); if (!object) { gjs_throw(context, "failed to create byte array"); return NULL; } priv = g_slice_new0(ByteArrayInstance); g_assert(priv_from_js(context, object) == NULL); JS_SetPrivate(object, priv); priv->array = g_byte_array_new(); priv->array->data = (guint8*) g_memdup(array->data, array->len); priv->array->len = array->len; return object; } GBytes * gjs_byte_array_get_bytes (JSContext *context, JS::HandleObject object) { ByteArrayInstance *priv; priv = priv_from_js(context, object); g_assert(priv != NULL); byte_array_ensure_gbytes(priv); return g_bytes_ref (priv->bytes); } GByteArray * gjs_byte_array_get_byte_array (JSContext *context, JS::HandleObject obj) { ByteArrayInstance *priv; priv = priv_from_js(context, obj); g_assert(priv != NULL); byte_array_ensure_array(priv); return g_byte_array_ref (priv->array); } void gjs_byte_array_peek_data (JSContext *context, JS::HandleObject obj, guint8 **out_data, gsize *out_len) { ByteArrayInstance *priv; priv = priv_from_js(context, obj); g_assert(priv != NULL); if (priv->array != NULL) { *out_data = (guint8*)priv->array->data; *out_len = (gsize)priv->array->len; } else if (priv->bytes != NULL) { *out_data = (guint8*)g_bytes_get_data(priv->bytes, out_len); } else { g_assert_not_reached(); } } static JSPropertySpec gjs_byte_array_proto_props[] = { JS_PSGS("length", byte_array_length_getter, byte_array_length_setter, JSPROP_PERMANENT), JS_PS_END }; static JSFunctionSpec gjs_byte_array_proto_funcs[] = { JS_FS("toString", to_string_func, 0, 0), JS_FS("toGBytes", to_gbytes_func, 0, 0), JS_FS_END }; static JSFunctionSpec *gjs_byte_array_static_funcs = nullptr; static JSFunctionSpec gjs_byte_array_module_funcs[] = { JS_FS("fromString", from_string_func, 1, 0), JS_FS("fromArray", from_array_func, 1, 0), JS_FS("fromGBytes", from_gbytes_func, 1, 0), JS_FS_END }; GJS_DEFINE_PROTO_FUNCS(byte_array) bool gjs_define_byte_array_stuff(JSContext *cx, JS::MutableHandleObject module) { module.set(JS_NewPlainObject(cx)); JS::RootedObject proto(cx); return gjs_byte_array_define_proto(cx, module, &proto) && JS_DefineFunctions(cx, module, gjs_byte_array_module_funcs); } cjs-4.4.0/cjs/byteArray.h000066400000000000000000000044521356375132300152050ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2010 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_BYTE_ARRAY_H__ #define __GJS_BYTE_ARRAY_H__ #include #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS bool gjs_typecheck_bytearray(JSContext *context, JS::HandleObject obj, bool throw_error); bool gjs_define_byte_array_stuff(JSContext *context, JS::MutableHandleObject module); JSObject * gjs_byte_array_from_byte_array (JSContext *context, GByteArray *array); GByteArray *gjs_byte_array_get_byte_array(JSContext *context, JS::HandleObject object); GBytes *gjs_byte_array_get_bytes(JSContext *context, JS::HandleObject object); void gjs_byte_array_peek_data(JSContext *context, JS::HandleObject object, guint8 **out_data, gsize *out_len); G_END_DECLS #endif /* __GJS_BYTE_ARRAY_H__ */ cjs-4.4.0/cjs/cjs.stp.in000066400000000000000000000012331356375132300150000ustar00rootroot00000000000000 probe gjs.object_proxy_new = process("@EXPANDED_LIBDIR@/libgjs-gi.so.0.0.0").mark("object__proxy__new") { proxy_address = $arg1; gobject_address = $arg2; gi_namespace = user_string($arg3); gi_name = user_string($arg4); probestr = sprintf("gjs.object_proxy_new(%p, %s, %s)", proxy_address, gi_namespace, gi_name); } probe gjs.object_proxy_finalize = process("@EXPANDED_LIBDIR@/libgjs-gi.so.0.0.0").mark("object__proxy__finalize") { proxy_address = $arg1; gobject_address = $arg2; gi_namespace = user_string($arg3); gi_name = user_string($arg4); probestr = sprintf("gjs.object_proxy_finalize(%p, %s, %s)", proxy_address, gi_namespace, gi_name); } cjs-4.4.0/cjs/console.cpp000066400000000000000000000330361356375132300152400ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include #include static char **include_path = NULL; static char **coverage_prefixes = NULL; static char *coverage_output_path = NULL; static char *profile_output_path = nullptr; static char *command = NULL; static gboolean print_version = false; static gboolean print_js_version = false; static bool enable_profiler = false; static gboolean parse_profile_arg(const char *, const char *, void *, GError **); /* Keep in sync with entries in check_script_args_for_stray_gjs_args() */ static GOptionEntry entries[] = { { "version", 0, 0, G_OPTION_ARG_NONE, &print_version, "Print GJS version and exit" }, { "jsversion", 0, 0, G_OPTION_ARG_NONE, &print_js_version, "Print version of the JS engine and exit" }, { "command", 'c', 0, G_OPTION_ARG_STRING, &command, "Program passed in as a string", "COMMAND" }, { "coverage-prefix", 'C', 0, G_OPTION_ARG_STRING_ARRAY, &coverage_prefixes, "Add the prefix PREFIX to the list of files to generate coverage info for", "PREFIX" }, { "coverage-output", 0, 0, G_OPTION_ARG_STRING, &coverage_output_path, "Write coverage output to a directory DIR. This option is mandatory when using --coverage-path", "DIR", }, { "include-path", 'I', 0, G_OPTION_ARG_STRING_ARRAY, &include_path, "Add the directory DIR to the list of directories to search for js files.", "DIR" }, { "profile", 0, G_OPTION_FLAG_OPTIONAL_ARG | G_OPTION_FLAG_FILENAME, G_OPTION_ARG_CALLBACK, reinterpret_cast(&parse_profile_arg), "Enable the profiler and write output to FILE (default: gjs-$PID.syscap)", "FILE" }, { NULL } }; static char ** strndupv(int n, char * const *strv) { int ix; if (n == 0) return NULL; char **retval = g_new(char *, n + 1); for (ix = 0; ix < n; ix++) retval[ix] = g_strdup(strv[ix]); retval[n] = NULL; return retval; } static char ** strcatv(char **strv1, char **strv2) { if (strv1 == NULL && strv2 == NULL) return NULL; if (strv1 == NULL) return g_strdupv(strv2); if (strv2 == NULL) return g_strdupv(strv1); unsigned len1 = g_strv_length(strv1); unsigned len2 = g_strv_length(strv2); char **retval = g_new(char *, len1 + len2 + 1); unsigned ix; for (ix = 0; ix < len1; ix++) retval[ix] = g_strdup(strv1[ix]); for (ix = 0; ix < len2; ix++) retval[len1 + ix] = g_strdup(strv2[ix]); retval[len1 + len2] = NULL; return retval; } static gboolean parse_profile_arg(const char *option_name, const char *value, void *data, GError **error_out) { enable_profiler = true; g_free(profile_output_path); if (value) profile_output_path = g_strdup(value); return true; } static gboolean check_stray_profile_arg(const char *option_name, const char *value, void *data, GError **error_out) { g_warning("You used the --profile option after the script on the GJS " "command line. Support for this will be removed in a future " "version. Place the option before the script or use the " "GJS_ENABLE_PROFILER environment variable."); return parse_profile_arg(option_name, value, data, error_out); } static void check_script_args_for_stray_gjs_args(int argc, char * const *argv) { GError *error = NULL; char **new_coverage_prefixes = NULL; char *new_coverage_output_path = NULL; char **new_include_paths = NULL; /* Keep in sync with entries[] at the top */ static GOptionEntry script_check_entries[] = { { "coverage-prefix", 'C', 0, G_OPTION_ARG_STRING_ARRAY, &new_coverage_prefixes }, { "coverage-output", 0, 0, G_OPTION_ARG_STRING, &new_coverage_output_path }, { "include-path", 'I', 0, G_OPTION_ARG_STRING_ARRAY, &new_include_paths }, { "profile", 0, G_OPTION_FLAG_OPTIONAL_ARG | G_OPTION_FLAG_FILENAME, G_OPTION_ARG_CALLBACK, reinterpret_cast(&check_stray_profile_arg) }, { NULL } }; char **argv_copy = g_new(char *, argc + 2); int ix; argv_copy[0] = g_strdup("dummy"); /* Fake argv[0] for GOptionContext */ for (ix = 0; ix < argc; ix++) argv_copy[ix + 1] = g_strdup(argv[ix]); argv_copy[argc + 1] = NULL; GOptionContext *script_options = g_option_context_new(NULL); g_option_context_set_ignore_unknown_options(script_options, true); g_option_context_set_help_enabled(script_options, false); g_option_context_add_main_entries(script_options, script_check_entries, NULL); if (!g_option_context_parse_strv(script_options, &argv_copy, &error)) { g_warning("Scanning script arguments failed: %s", error->message); g_error_free(error); g_strfreev(argv_copy); return; } if (new_coverage_prefixes != NULL) { g_warning("You used the --coverage-prefix option after the script on " "the GJS command line. Support for this will be removed in a " "future version. Place the option before the script or use " "the GJS_COVERAGE_PREFIXES environment variable."); char **old_coverage_prefixes = coverage_prefixes; coverage_prefixes = strcatv(old_coverage_prefixes, new_coverage_prefixes); g_strfreev(old_coverage_prefixes); } if (new_include_paths != NULL) { g_warning("You used the --include-path option after the script on the " "GJS command line. Support for this will be removed in a " "future version. Place the option before the script or use " "the GJS_PATH environment variable."); char **old_include_paths = include_path; include_path = strcatv(old_include_paths, new_include_paths); g_strfreev(old_include_paths); } if (new_coverage_output_path != NULL) { g_warning("You used the --coverage-output option after the script on " "the GJS command line. Support for this will be removed in a " "future version. Place the option before the script or use " "the GJS_COVERAGE_OUTPUT environment variable."); g_free(coverage_output_path); coverage_output_path = new_coverage_output_path; } g_option_context_free(script_options); g_strfreev(argv_copy); } int main(int argc, char **argv) { GOptionContext *context; GError *error = NULL; GjsContext *js_context; GjsCoverage *coverage = NULL; char *script; const char *filename; const char *program_name; gsize len; int code, gjs_argc = argc, script_argc, ix; char **argv_copy = g_strdupv(argv), **argv_copy_addr = argv_copy; char **gjs_argv, **gjs_argv_addr; char * const *script_argv; const char *env_coverage_output_path; const char *env_coverage_prefixes; bool interactive_mode = false; setlocale(LC_ALL, ""); context = g_option_context_new(NULL); g_option_context_set_ignore_unknown_options(context, true); g_option_context_set_help_enabled(context, false); g_option_context_add_main_entries(context, entries, NULL); if (!g_option_context_parse_strv(context, &argv_copy, &error)) g_error("option parsing failed: %s", error->message); /* Split options so we pass unknown ones through to the JS script */ int argc_copy = g_strv_length(argv_copy); for (ix = 1; ix < argc; ix++) { /* Check if a file was given and split after it */ if (argc_copy >= 2 && strcmp(argv[ix], argv_copy[1]) == 0) { /* Filename given; split after this argument */ gjs_argc = ix + 1; break; } /* Check if -c or --command was given and split after following arg */ if (command != NULL && (strcmp(argv[ix], "-c") == 0 || strcmp(argv[ix], "--command") == 0)) { gjs_argc = ix + 2; break; } } gjs_argv_addr = gjs_argv = strndupv(gjs_argc, argv); script_argc = argc - gjs_argc; script_argv = argv + gjs_argc; g_strfreev(argv_copy_addr); /* Parse again, only the GJS options this time */ include_path = NULL; coverage_prefixes = NULL; coverage_output_path = NULL; command = NULL; print_version = false; print_js_version = false; g_option_context_set_ignore_unknown_options(context, false); g_option_context_set_help_enabled(context, true); if (!g_option_context_parse_strv(context, &gjs_argv, &error)) g_error("option parsing failed: %s", error->message); g_option_context_free (context); if (print_version) { g_print("%s\n", PACKAGE_STRING); exit(0); } if (print_js_version) { g_print("%s\n", gjs_get_js_version()); exit(0); } gjs_argc = g_strv_length(gjs_argv); if (command != NULL) { script = command; len = strlen(script); filename = ""; program_name = gjs_argv[0]; } else if (gjs_argc == 1) { script = g_strdup("const Console = imports.console; Console.interact();"); len = strlen(script); filename = ""; program_name = gjs_argv[0]; interactive_mode = true; } else { /* All unprocessed options should be in script_argv */ g_assert(gjs_argc == 2); error = NULL; if (!g_file_get_contents(gjs_argv[1], &script, &len, &error)) { g_printerr("%s\n", error->message); exit(1); } filename = gjs_argv[1]; program_name = gjs_argv[1]; } /* This should be removed after a suitable time has passed */ check_script_args_for_stray_gjs_args(script_argc, script_argv); if (interactive_mode && enable_profiler) { g_message("Profiler disabled in interactive mode."); enable_profiler = false; g_unsetenv("GJS_ENABLE_PROFILER"); /* ignore env var in eval() */ } js_context = (GjsContext*) g_object_new(GJS_TYPE_CONTEXT, "search-path", include_path, "program-name", program_name, "profiler-enabled", enable_profiler, NULL); env_coverage_output_path = g_getenv("GJS_COVERAGE_OUTPUT"); if (env_coverage_output_path != NULL) { g_free(coverage_output_path); coverage_output_path = g_strdup(env_coverage_output_path); } env_coverage_prefixes = g_getenv("GJS_COVERAGE_PREFIXES"); if (env_coverage_prefixes != NULL) { if (coverage_prefixes != NULL) g_strfreev(coverage_prefixes); coverage_prefixes = g_strsplit(env_coverage_prefixes, ":", -1); } if (coverage_prefixes) { if (!coverage_output_path) g_error("--coverage-output is required when taking coverage statistics"); GFile *output = g_file_new_for_commandline_arg(coverage_output_path); coverage = gjs_coverage_new(coverage_prefixes, js_context, output); g_object_unref(output); } if (enable_profiler && profile_output_path) { GjsProfiler *profiler = gjs_context_get_profiler(js_context); gjs_profiler_set_filename(profiler, profile_output_path); } /* prepare command line arguments */ if (!gjs_context_define_string_array(js_context, "ARGV", script_argc, (const char **) script_argv, &error)) { code = 1; g_printerr("Failed to defined ARGV: %s", error->message); g_clear_error(&error); goto out; } /* evaluate the script */ if (!gjs_context_eval(js_context, script, len, filename, &code, &error)) { if (!g_error_matches(error, GJS_ERROR, GJS_ERROR_SYSTEM_EXIT)) g_printerr("%s\n", error->message); g_clear_error(&error); goto out; } out: g_strfreev(gjs_argv_addr); /* Probably doesn't make sense to write statistics on failure */ if (coverage && code == 0) gjs_coverage_write_statistics(coverage); g_free(coverage_output_path); g_free(profile_output_path); g_strfreev(coverage_prefixes); if (coverage) g_object_unref(coverage); g_object_unref(js_context); g_free(script); exit(code); } cjs-4.4.0/cjs/context-private.h000066400000000000000000000051431356375132300163750ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2014 Colin Walters * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_CONTEXT_PRIVATE_H__ #define __GJS_CONTEXT_PRIVATE_H__ #include #include "context.h" #include "jsapi-util.h" #include "jsapi-wrapper.h" G_BEGIN_DECLS bool _gjs_context_destroying (GjsContext *js_context); void _gjs_context_schedule_gc_if_needed (GjsContext *js_context); void _gjs_context_schedule_gc(GjsContext *js_context); void _gjs_context_exit(GjsContext *js_context, uint8_t exit_code); bool _gjs_context_get_is_owner_thread(GjsContext *js_context); bool _gjs_context_should_exit(GjsContext *js_context, uint8_t *exit_code_p); void _gjs_context_set_sweeping(GjsContext *js_context, bool sweeping); bool _gjs_context_is_sweeping(JSContext *cx); bool _gjs_context_enqueue_job(GjsContext *gjs_context, JS::HandleObject job); bool _gjs_context_run_jobs(GjsContext *gjs_context); void _gjs_context_unregister_unhandled_promise_rejection(GjsContext *gjs_context, uint64_t promise_id); G_END_DECLS void _gjs_context_register_unhandled_promise_rejection(GjsContext *gjs_context, uint64_t promise_id, GjsAutoChar&& stack); #endif /* __GJS_CONTEXT_PRIVATE_H__ */ cjs-4.4.0/cjs/context.cpp000066400000000000000000001034651356375132300152660ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include #include #include #include "context-private.h" #include "engine.h" #include "global.h" #include "importer.h" #include "jsapi-util.h" #include "jsapi-wrapper.h" #include "mem.h" #include "native.h" #include "profiler-private.h" #include "byteArray.h" #include "gi/object.h" #include "gi/repo.h" #include #include #include #include #ifdef G_OS_WIN32 #define WIN32_LEAN_AND_MEAN #include #endif #include static void gjs_context_dispose (GObject *object); static void gjs_context_finalize (GObject *object); static void gjs_context_constructed (GObject *object); static void gjs_context_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); static void gjs_context_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); using JobQueue = JS::GCVector; struct _GjsContext { GObject parent; JSContext *context; JS::Heap global; GThread *owner_thread; char *program_name; char **search_path; bool destroying; bool in_gc_sweep; bool should_exit; uint8_t exit_code; guint auto_gc_id; bool force_gc; std::array const_strings; JS::PersistentRooted *job_queue; unsigned idle_drain_handler; bool draining_job_queue; std::unordered_map unhandled_rejection_stacks; GjsProfiler *profiler; bool should_profile : 1; bool should_listen_sigusr2 : 1; }; /* Keep this consistent with GjsConstString */ static const char *const_strings[] = { "constructor", "prototype", "length", "imports", "__parentModule__", "__init__", "searchPath", "__gjsKeepAlive", "__gjsPrivateNS", "gi", "versions", "overrides", "_init", "_instance_init", "_new_internal", "new", "message", "code", "stack", "fileName", "lineNumber", "columnNumber", "name", "x", "y", "width", "height", "__modulePath__" }; G_STATIC_ASSERT(G_N_ELEMENTS(const_strings) == GJS_STRING_LAST); struct _GjsContextClass { GObjectClass parent; }; /* Temporary workaround for https://bugzilla.gnome.org/show_bug.cgi?id=793175 */ #if __GNUC__ >= 8 _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wcast-function-type\"") #endif G_DEFINE_TYPE(GjsContext, gjs_context, G_TYPE_OBJECT); #if __GNUC__ >= 8 _Pragma("GCC diagnostic pop") #endif enum { PROP_0, PROP_SEARCH_PATH, PROP_PROGRAM_NAME, PROP_PROFILER_ENABLED, PROP_PROFILER_SIGUSR2, }; static GMutex contexts_lock; static GList *all_contexts = NULL; static GjsAutoChar dump_heap_output; static unsigned dump_heap_idle_id = 0; static void gjs_context_dump_heaps(void) { static unsigned counter = 0; gjs_memory_report("signal handler", false); /* dump to sequential files to allow easier comparisons */ GjsAutoChar filename = g_strdup_printf("%s.%jd.%u", dump_heap_output.get(), intmax_t(getpid()), counter); ++counter; FILE *fp = fopen(filename, "w"); if (!fp) return; for (GList *l = all_contexts; l; l = g_list_next(l)) { auto js_context = static_cast(l->data); js::DumpHeap(js_context->context, fp, js::IgnoreNurseryObjects); } fclose(fp); } static gboolean dump_heap_idle(gpointer user_data) { dump_heap_idle_id = 0; gjs_context_dump_heaps(); return false; } static void dump_heap_signal_handler(int signum) { if (dump_heap_idle_id == 0) dump_heap_idle_id = g_idle_add_full(G_PRIORITY_HIGH_IDLE, dump_heap_idle, nullptr, nullptr); } static void setup_dump_heap(void) { static bool dump_heap_initialized = false; if (!dump_heap_initialized) { dump_heap_initialized = true; /* install signal handler only if environment variable is set */ const char *heap_output = g_getenv("GJS_DEBUG_HEAP_OUTPUT"); if (heap_output) { struct sigaction sa; dump_heap_output = g_strdup(heap_output); memset(&sa, 0, sizeof(sa)); sa.sa_handler = dump_heap_signal_handler; sigaction(SIGUSR1, &sa, nullptr); } } } static void gjs_context_init(GjsContext *js_context) { gjs_context_make_current(js_context); } static void gjs_context_class_init(GjsContextClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GParamSpec *pspec; object_class->dispose = gjs_context_dispose; object_class->finalize = gjs_context_finalize; object_class->constructed = gjs_context_constructed; object_class->get_property = gjs_context_get_property; object_class->set_property = gjs_context_set_property; pspec = g_param_spec_boxed("search-path", "Search path", "Path where modules to import should reside", G_TYPE_STRV, (GParamFlags) (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property(object_class, PROP_SEARCH_PATH, pspec); g_param_spec_unref(pspec); pspec = g_param_spec_string("program-name", "Program Name", "The filename of the launched JS program", "", (GParamFlags) (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property(object_class, PROP_PROGRAM_NAME, pspec); g_param_spec_unref(pspec); /** * GjsContext:profiler-enabled: * * Set this property to profile any JS code run by this context. By * default, the profiler is started and stopped when you call * gjs_context_eval(). * * The value of this property is superseded by the GJS_ENABLE_PROFILER * environment variable. * * You may only have one context with the profiler enabled at a time. */ pspec = g_param_spec_boolean("profiler-enabled", "Profiler enabled", "Whether to profile JS code run by this context", FALSE, GParamFlags(G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property(object_class, PROP_PROFILER_ENABLED, pspec); g_param_spec_unref(pspec); /** * GjsContext:profiler-sigusr2: * * Set this property to install a SIGUSR2 signal handler that starts and * stops the profiler. This property also implies that * #GjsContext:profiler-enabled is set. */ pspec = g_param_spec_boolean("profiler-sigusr2", "Profiler SIGUSR2", "Whether to activate the profiler on SIGUSR2", FALSE, GParamFlags(G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property(object_class, PROP_PROFILER_SIGUSR2, pspec); g_param_spec_unref(pspec); /* For GjsPrivate */ { #ifdef G_OS_WIN32 extern HMODULE gjs_dll; char *basedir = g_win32_get_package_installation_directory_of_module (gjs_dll); char *priv_typelib_dir = g_build_filename (basedir, "lib", "girepository-1.0", NULL); g_free (basedir); #else char *priv_typelib_dir = g_build_filename (PKGLIBDIR, "girepository-1.0", NULL); #endif g_irepository_prepend_search_path(priv_typelib_dir); g_free (priv_typelib_dir); } gjs_register_native_module("byteArray", gjs_define_byte_array_stuff); gjs_register_native_module("_gi", gjs_define_private_gi_stuff); gjs_register_native_module("gi", gjs_define_repo); gjs_register_static_modules(); } static void gjs_context_tracer(JSTracer *trc, void *data) { GjsContext *gjs_context = reinterpret_cast(data); JS::TraceEdge(trc, &gjs_context->global, "GJS global object"); } static void warn_about_unhandled_promise_rejections(GjsContext *gjs_context) { for (auto& kv : gjs_context->unhandled_rejection_stacks) { const char *stack = kv.second; g_warning("Unhandled promise rejection. To suppress this warning, add " "an error handler to your promise chain with .catch() or a " "try-catch block around your await expression. %s%s", stack ? "Stack trace of the failed promise:\n" : "Unfortunately there is no stack trace of the failed promise.", stack ? stack : ""); } gjs_context->unhandled_rejection_stacks.clear(); } static void gjs_context_dispose(GObject *object) { gjs_debug(GJS_DEBUG_CONTEXT, "JS shutdown sequence"); GjsContext *js_context; js_context = GJS_CONTEXT(object); /* Profiler must be stopped and freed before context is shut down */ gjs_debug(GJS_DEBUG_CONTEXT, "Stopping profiler"); if (js_context->profiler) g_clear_pointer(&js_context->profiler, _gjs_profiler_free); /* Stop accepting entries in the toggle queue before running dispose * notifications, which causes all GjsMaybeOwned instances to unroot. * We don't want any objects to toggle down after that. */ gjs_debug(GJS_DEBUG_CONTEXT, "Shutting down toggle queue"); gjs_object_clear_toggles(); gjs_object_shutdown_toggle_queue(); /* Run dispose notifications next, so that anything releasing * references in response to this can still get garbage collected */ gjs_debug(GJS_DEBUG_CONTEXT, "Notifying reference holders of GjsContext dispose"); G_OBJECT_CLASS(gjs_context_parent_class)->dispose(object); if (js_context->context != NULL) { gjs_debug(GJS_DEBUG_CONTEXT, "Checking unhandled promise rejections"); warn_about_unhandled_promise_rejections(js_context); JS_BeginRequest(js_context->context); /* Do a full GC here before tearing down, since once we do * that we may not have the JS_GetPrivate() to access the * context */ gjs_debug(GJS_DEBUG_CONTEXT, "Final triggered GC"); JS_GC(js_context->context); JS_EndRequest(js_context->context); gjs_debug(GJS_DEBUG_CONTEXT, "Destroying JS context"); js_context->destroying = true; /* Now, release all native objects, to avoid recursion between * the JS teardown and the C teardown. The JSObject proxies * still exist, but point to NULL. */ gjs_debug(GJS_DEBUG_CONTEXT, "Releasing all native objects"); gjs_object_prepare_shutdown(); gjs_debug(GJS_DEBUG_CONTEXT, "Disabling auto GC"); if (js_context->auto_gc_id > 0) { g_source_remove (js_context->auto_gc_id); js_context->auto_gc_id = 0; } gjs_debug(GJS_DEBUG_CONTEXT, "Ending trace on global object"); JS_RemoveExtraGCRootsTracer(js_context->context, gjs_context_tracer, js_context); js_context->global = NULL; gjs_debug(GJS_DEBUG_CONTEXT, "Unrooting atoms"); for (auto& root : js_context->const_strings) delete root; gjs_debug(GJS_DEBUG_CONTEXT, "Freeing allocated resources"); delete js_context->job_queue; /* Tear down JS */ JS_DestroyContext(js_context->context); js_context->context = NULL; gjs_debug(GJS_DEBUG_CONTEXT, "JS context destroyed"); } } static void gjs_context_finalize(GObject *object) { GjsContext *js_context; js_context = GJS_CONTEXT(object); if (js_context->search_path != NULL) { g_strfreev(js_context->search_path); js_context->search_path = NULL; } if (js_context->program_name != NULL) { g_free(js_context->program_name); js_context->program_name = NULL; } if (gjs_context_get_current() == (GjsContext*)object) gjs_context_make_current(NULL); g_mutex_lock(&contexts_lock); all_contexts = g_list_remove(all_contexts, object); g_mutex_unlock(&contexts_lock); js_context->global.~Heap(); js_context->const_strings.~array(); js_context->unhandled_rejection_stacks.~unordered_map(); G_OBJECT_CLASS(gjs_context_parent_class)->finalize(object); } static void gjs_context_constructed(GObject *object) { GjsContext *js_context = GJS_CONTEXT(object); int i; G_OBJECT_CLASS(gjs_context_parent_class)->constructed(object); js_context->owner_thread = g_thread_self(); JSContext *cx = gjs_create_js_context(js_context); if (!cx) g_error("Failed to create javascript context"); js_context->context = cx; const char *env_profiler = g_getenv("GJS_ENABLE_PROFILER"); if (env_profiler || js_context->should_listen_sigusr2) js_context->should_profile = true; if (js_context->should_profile) { js_context->profiler = _gjs_profiler_new(js_context); if (!js_context->profiler) { js_context->should_profile = false; } else { if (js_context->should_listen_sigusr2) _gjs_profiler_setup_signals(js_context->profiler, js_context); } } new (&js_context->unhandled_rejection_stacks) std::unordered_map; new (&js_context->const_strings) std::array; for (i = 0; i < GJS_STRING_LAST; i++) { js_context->const_strings[i] = new JS::PersistentRootedId(cx, gjs_intern_string_to_id(cx, const_strings[i])); } js_context->job_queue = new JS::PersistentRooted(cx); if (!js_context->job_queue) g_error("Failed to initialize promise job queue"); JS_BeginRequest(cx); JS::RootedObject global(cx, gjs_create_global_object(cx)); if (!global) { gjs_log_exception(js_context->context); g_error("Failed to initialize global object"); } JSAutoCompartment ac(cx, global); new (&js_context->global) JS::Heap(global); JS_AddExtraGCRootsTracer(cx, gjs_context_tracer, js_context); JS::RootedObject importer(cx, gjs_create_root_importer(cx, js_context->search_path ? js_context->search_path : nullptr)); if (!importer) g_error("Failed to create root importer"); JS::Value v_importer = gjs_get_global_slot(cx, GJS_GLOBAL_SLOT_IMPORTS); g_assert(((void) "Someone else already created root importer", v_importer.isUndefined())); gjs_set_global_slot(cx, GJS_GLOBAL_SLOT_IMPORTS, JS::ObjectValue(*importer)); if (!gjs_define_global_properties(cx, global, "default")) { gjs_log_exception(cx); g_error("Failed to define properties on global object"); } /* Pre-import the byteArray module. We depend on this module for some of * our GObject introspection marshalling, so the ByteArray prototype * defined in it needs to be always available. */ gjs_import_native_module(cx, importer, "byteArray"); JS_EndRequest(cx); g_mutex_lock (&contexts_lock); all_contexts = g_list_prepend(all_contexts, object); g_mutex_unlock (&contexts_lock); setup_dump_heap(); g_object_weak_ref(object, gjs_object_context_dispose_notify, nullptr); } static void gjs_context_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GjsContext *js_context; js_context = GJS_CONTEXT (object); switch (prop_id) { case PROP_PROGRAM_NAME: g_value_set_string(value, js_context->program_name); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gjs_context_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GjsContext *js_context; js_context = GJS_CONTEXT (object); switch (prop_id) { case PROP_SEARCH_PATH: js_context->search_path = (char**) g_value_dup_boxed(value); break; case PROP_PROGRAM_NAME: js_context->program_name = g_value_dup_string(value); break; case PROP_PROFILER_ENABLED: js_context->should_profile = g_value_get_boolean(value); break; case PROP_PROFILER_SIGUSR2: js_context->should_listen_sigusr2 = g_value_get_boolean(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } GjsContext* gjs_context_new(void) { return (GjsContext*) g_object_new (GJS_TYPE_CONTEXT, NULL); } GjsContext* gjs_context_new_with_search_path(char** search_path) { return (GjsContext*) g_object_new (GJS_TYPE_CONTEXT, "search-path", search_path, NULL); } bool _gjs_context_destroying (GjsContext *context) { return context->destroying; } static gboolean trigger_gc_if_needed (gpointer user_data) { GjsContext *js_context = GJS_CONTEXT(user_data); js_context->auto_gc_id = 0; if (js_context->force_gc) JS_GC(js_context->context); else gjs_gc_if_needed(js_context->context); js_context->force_gc = false; return G_SOURCE_REMOVE; } static void _gjs_context_schedule_gc_internal(GjsContext *js_context, bool force_gc) { js_context->force_gc |= force_gc; if (js_context->auto_gc_id > 0) return; js_context->auto_gc_id = g_idle_add_full(G_PRIORITY_LOW, trigger_gc_if_needed, js_context, NULL); } void _gjs_context_schedule_gc(GjsContext *js_context) { _gjs_context_schedule_gc_internal(js_context, true); } void _gjs_context_schedule_gc_if_needed(GjsContext *js_context) { _gjs_context_schedule_gc_internal(js_context, false); } void _gjs_context_exit(GjsContext *js_context, uint8_t exit_code) { g_assert(!js_context->should_exit); js_context->should_exit = true; js_context->exit_code = exit_code; } bool _gjs_context_should_exit(GjsContext *js_context, uint8_t *exit_code_p) { if (exit_code_p != NULL) *exit_code_p = js_context->exit_code; return js_context->should_exit; } static void context_reset_exit(GjsContext *js_context) { js_context->should_exit = false; js_context->exit_code = 0; } bool _gjs_context_get_is_owner_thread(GjsContext *js_context) { return js_context->owner_thread == g_thread_self(); } void _gjs_context_set_sweeping(GjsContext *js_context, bool sweeping) { js_context->in_gc_sweep = sweeping; } bool _gjs_context_is_sweeping(JSContext *cx) { auto js_context = static_cast(JS_GetContextPrivate(cx)); return js_context->in_gc_sweep; } static gboolean drain_job_queue_idle_handler(void *data) { auto gjs_context = static_cast(data); _gjs_context_run_jobs(gjs_context); /* Uncatchable exceptions are swallowed here - no way to get a handle on * the main loop to exit it from this idle handler */ g_assert(((void) "_gjs_context_run_jobs() should have emptied queue", gjs_context->idle_drain_handler == 0)); return G_SOURCE_REMOVE; } /* See engine.cpp and JS::SetEnqueuePromiseJobCallback(). */ bool _gjs_context_enqueue_job(GjsContext *gjs_context, JS::HandleObject job) { if (gjs_context->idle_drain_handler) g_assert(gjs_context->job_queue->length() > 0); else g_assert(gjs_context->job_queue->length() == 0); if (!gjs_context->job_queue->append(job)) return false; if (!gjs_context->idle_drain_handler) gjs_context->idle_drain_handler = g_idle_add_full(G_PRIORITY_DEFAULT, drain_job_queue_idle_handler, gjs_context, nullptr); return true; } /** * _gjs_context_run_jobs: * @gjs_context: The #GjsContext instance * * Drains the queue of promise callbacks that the JS engine has reported * finished, calling each one and logging any exceptions that it throws. * * Adapted from js::RunJobs() in SpiderMonkey's default job queue * implementation. * * Returns: false if one of the jobs threw an uncatchable exception; * otherwise true. */ bool _gjs_context_run_jobs(GjsContext *gjs_context) { bool retval = true; g_assert(gjs_context->job_queue); if (gjs_context->draining_job_queue || gjs_context->should_exit) return true; auto cx = static_cast(gjs_context_get_native_context(gjs_context)); JSAutoRequest ar(cx); gjs_context->draining_job_queue = true; /* Ignore reentrant calls */ JS::RootedObject job(cx); JS::HandleValueArray args(JS::HandleValueArray::empty()); JS::RootedValue rval(cx); /* Execute jobs in a loop until we've reached the end of the queue. * Since executing a job can trigger enqueueing of additional jobs, * it's crucial to recheck the queue length during each iteration. */ for (size_t ix = 0; ix < gjs_context->job_queue->length(); ix++) { /* A previous job might have set this flag. e.g., System.exit(). */ if (gjs_context->should_exit) break; job = gjs_context->job_queue->get()[ix]; /* It's possible that job draining was interrupted prematurely, * leaving the queue partly processed. In that case, slots for * already-executed entries will contain nullptrs, which we should * just skip. */ if (!job) continue; gjs_context->job_queue->get()[ix] = nullptr; { JSAutoCompartment ac(cx, job); if (!JS::Call(cx, JS::UndefinedHandleValue, job, args, &rval)) { /* Uncatchable exception - return false so that * System.exit() works in the interactive shell and when * exiting the interpreter. */ if (!JS_IsExceptionPending(cx)) { /* System.exit() is an uncatchable exception, but does not * indicate a bug. Log everything else. */ if (!_gjs_context_should_exit(gjs_context, nullptr)) g_critical("Promise callback terminated with uncatchable exception"); retval = false; continue; } /* There's nowhere for the exception to go at this point */ gjs_log_exception(cx); } } } gjs_context->draining_job_queue = false; gjs_context->job_queue->clear(); if (gjs_context->idle_drain_handler) { g_source_remove(gjs_context->idle_drain_handler); gjs_context->idle_drain_handler = 0; } return retval; } void _gjs_context_register_unhandled_promise_rejection(GjsContext *gjs_context, uint64_t id, GjsAutoChar&& stack) { gjs_context->unhandled_rejection_stacks[id] = std::move(stack); } void _gjs_context_unregister_unhandled_promise_rejection(GjsContext *gjs_context, uint64_t id) { size_t erased = gjs_context->unhandled_rejection_stacks.erase(id); g_assert(((void)"Handler attached to rejected promise that wasn't " "previously marked as unhandled", erased == 1)); } /** * gjs_context_maybe_gc: * @context: a #GjsContext * * Similar to the Spidermonkey JS_MaybeGC() call which * heuristically looks at JS runtime memory usage and * may initiate a garbage collection. * * This function always unconditionally invokes JS_MaybeGC(), but * additionally looks at memory usage from the system malloc() * when available, and if the delta has grown since the last run * significantly, also initiates a full JavaScript garbage * collection. The idea is that since GJS is a bridge between * JavaScript and system libraries, and JS objects act as proxies * for these system memory objects, GJS consumers need a way to * hint to the runtime that it may be a good idea to try a * collection. * * A good time to call this function is when your application * transitions to an idle state. */ void gjs_context_maybe_gc (GjsContext *context) { gjs_maybe_gc(context->context); } /** * gjs_context_gc: * @context: a #GjsContext * * Initiate a full GC; may or may not block until complete. This * function just calls Spidermonkey JS_GC(). */ void gjs_context_gc (GjsContext *context) { JS_GC(context->context); } /** * gjs_context_get_all: * * Returns a newly-allocated list containing all known instances of #GjsContext. * This is useful for operating on the contexts from a process-global situation * such as a debugger. * * Return value: (element-type GjsContext) (transfer full): Known #GjsContext instances */ GList* gjs_context_get_all(void) { GList *result; GList *iter; g_mutex_lock (&contexts_lock); result = g_list_copy(all_contexts); for (iter = result; iter; iter = iter->next) g_object_ref((GObject*)iter->data); g_mutex_unlock (&contexts_lock); return result; } /** * gjs_context_get_native_context: * * Returns a pointer to the underlying native context. For SpiderMonkey, this * is a JSContext * */ void* gjs_context_get_native_context (GjsContext *js_context) { g_return_val_if_fail(GJS_IS_CONTEXT(js_context), NULL); return js_context->context; } bool gjs_context_eval(GjsContext *js_context, const char *script, gssize script_len, const char *filename, int *exit_status_p, GError **error) { bool ret = false; bool auto_profile = js_context->should_profile; if (auto_profile && (_gjs_profiler_is_running(js_context->profiler) || js_context->should_listen_sigusr2)) auto_profile = false; JSAutoCompartment ac(js_context->context, js_context->global); JSAutoRequest ar(js_context->context); g_object_ref(G_OBJECT(js_context)); if (auto_profile) gjs_profiler_start(js_context->profiler); JS::RootedValue retval(js_context->context); bool ok = gjs_eval_with_scope(js_context->context, nullptr, script, script_len, filename, &retval); /* The promise job queue should be drained even on error, to finish * outstanding async tasks before the context is torn down. Drain after * uncaught exceptions have been reported since draining runs callbacks. */ { JS::AutoSaveExceptionState saved_exc(js_context->context); ok = _gjs_context_run_jobs(js_context) && ok; } if (auto_profile) gjs_profiler_stop(js_context->profiler); if (!ok) { uint8_t code; if (_gjs_context_should_exit(js_context, &code)) { /* exit_status_p is public API so can't be changed, but should be * uint8_t, not int */ *exit_status_p = code; g_set_error(error, GJS_ERROR, GJS_ERROR_SYSTEM_EXIT, "Exit with code %d", code); goto out; /* Don't log anything */ } if (!JS_IsExceptionPending(js_context->context)) { g_critical("Script %s terminated with an uncatchable exception", filename); g_set_error(error, GJS_ERROR, GJS_ERROR_FAILED, "Script %s terminated with an uncatchable exception", filename); } else { g_set_error(error, GJS_ERROR, GJS_ERROR_FAILED, "Script %s threw an exception", filename); } gjs_log_exception(js_context->context); /* No exit code from script, but we don't want to exit(0) */ *exit_status_p = 1; goto out; } if (exit_status_p) { if (retval.isInt32()) { int code = retval.toInt32(); gjs_debug(GJS_DEBUG_CONTEXT, "Script returned integer code %d", code); *exit_status_p = code; } else { /* Assume success if no integer was returned */ *exit_status_p = 0; } } ret = true; out: g_object_unref(G_OBJECT(js_context)); context_reset_exit(js_context); return ret; } bool gjs_context_eval_file(GjsContext *js_context, const char *filename, int *exit_status_p, GError **error) { char *script; size_t script_len; GjsAutoUnref file = g_file_new_for_commandline_arg(filename); if (!g_file_load_contents(file, nullptr, &script, &script_len, nullptr, error)) return false; GjsAutoChar script_ref = script; return gjs_context_eval(js_context, script, script_len, filename, exit_status_p, error); } bool gjs_context_define_string_array(GjsContext *js_context, const char *array_name, gssize array_length, const char **array_values, GError **error) { JSAutoCompartment ac(js_context->context, js_context->global); JSAutoRequest ar(js_context->context); JS::RootedObject global_root(js_context->context, js_context->global); if (!gjs_define_string_array(js_context->context, global_root, array_name, array_length, array_values, JSPROP_READONLY | JSPROP_PERMANENT)) { gjs_log_exception(js_context->context); g_set_error(error, GJS_ERROR, GJS_ERROR_FAILED, "gjs_define_string_array() failed"); return false; } return true; } static GjsContext *current_context; GjsContext * gjs_context_get_current (void) { return current_context; } void gjs_context_make_current (GjsContext *context) { g_assert (context == NULL || current_context == NULL); current_context = context; } /* It's OK to return JS::HandleId here, to avoid an extra root, with the * caveat that you should not use this value after the GjsContext has * been destroyed. */ JS::HandleId gjs_context_get_const_string(JSContext *context, GjsConstString name) { GjsContext *gjs_context = (GjsContext *) JS_GetContextPrivate(context); return *gjs_context->const_strings[name]; } /** * gjs_get_import_global: * @context: a #JSContext * * Gets the "import global" for the context's runtime. The import * global object is the global object for the context. It is used * as the root object for the scope of modules loaded by GJS in this * runtime, and should also be used as the globals 'obj' argument passed * to JS_InitClass() and the parent argument passed to JS_ConstructObject() * when creating a native classes that are shared between all contexts using * the runtime. (The standard JS classes are not shared, but we share * classes such as GObject proxy classes since objects of these classes can * easily migrate between contexts and having different classes depending * on the context where they were first accessed would be confusing.) * * Return value: the "import global" for the context's * runtime. Will never return %NULL while GJS has an active context * for the runtime. */ JSObject* gjs_get_import_global(JSContext *context) { GjsContext *gjs_context = (GjsContext *) JS_GetContextPrivate(context); return gjs_context->global; } /** * gjs_context_get_profiler: * @self: the #GjsContext * * Returns the profiler's internal instance of #GjsProfiler for you to * customize, or %NULL if profiling is not enabled on this #GjsContext. * * Returns: (transfer none) (nullable): a #GjsProfiler */ GjsProfiler * gjs_context_get_profiler(GjsContext *self) { return self->profiler; } /** * gjs_get_js_version: * * Returns the underlying version of the JS engine. * * Returns: a string */ const char * gjs_get_js_version(void) { return JS_GetImplementationVersion(); }cjs-4.4.0/cjs/context.h000066400000000000000000000105661356375132300147320ustar00rootroot00000000000000/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_CONTEXT_H__ #define __GJS_CONTEXT_H__ #if !defined (__GJS_GJS_H__) && !defined (GJS_COMPILATION) #error "Only can be included directly." #endif #include #include #include #include G_BEGIN_DECLS typedef struct _GjsContext GjsContext; typedef struct _GjsContextClass GjsContextClass; #define GJS_TYPE_CONTEXT (gjs_context_get_type ()) #define GJS_CONTEXT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GJS_TYPE_CONTEXT, GjsContext)) #define GJS_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GJS_TYPE_CONTEXT, GjsContextClass)) #define GJS_IS_CONTEXT(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GJS_TYPE_CONTEXT)) #define GJS_IS_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GJS_TYPE_CONTEXT)) #define GJS_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GJS_TYPE_CONTEXT, GjsContextClass)) GJS_EXPORT GType gjs_context_get_type (void) G_GNUC_CONST; GJS_EXPORT GjsContext* gjs_context_new (void); GJS_EXPORT GjsContext* gjs_context_new_with_search_path (char **search_path); GJS_EXPORT bool gjs_context_eval_file (GjsContext *js_context, const char *filename, int *exit_status_p, GError **error); GJS_EXPORT bool gjs_context_eval (GjsContext *js_context, const char *script, gssize script_len, const char *filename, int *exit_status_p, GError **error); GJS_EXPORT bool gjs_context_define_string_array (GjsContext *js_context, const char *array_name, gssize array_length, const char **array_values, GError **error); GJS_EXPORT GList* gjs_context_get_all (void); GJS_EXPORT GjsContext *gjs_context_get_current (void); GJS_EXPORT void gjs_context_make_current (GjsContext *js_context); GJS_EXPORT void* gjs_context_get_native_context (GjsContext *js_context); GJS_EXPORT void gjs_context_print_stack_stderr (GjsContext *js_context); GJS_EXPORT void gjs_context_maybe_gc (GjsContext *context); GJS_EXPORT void gjs_context_gc (GjsContext *context); GJS_EXPORT GjsProfiler *gjs_context_get_profiler(GjsContext *self); GJS_EXPORT bool gjs_profiler_chain_signal(GjsContext *context, siginfo_t *info); GJS_EXPORT void gjs_dumpstack (void); GJS_EXPORT const char *gjs_get_js_version(void); G_END_DECLS #endif /* __GJS_CONTEXT_H__ */cjs-4.4.0/cjs/coverage.cpp000066400000000000000000000423171356375132300153730ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright © 2014 Endless Mobile, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. * * Authored By: Sam Spilsbury */ #include #include #include #include "coverage.h" #include "global.h" #include "importer.h" #include "jsapi-util-args.h" #include "util/error.h" struct _GjsCoverage { GObject parent; }; typedef struct { gchar **prefixes; GjsContext *context; JS::Heap compartment; GFile *output_dir; } GjsCoveragePrivate; #if __GNUC__ >= 8 _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wcast-function-type\"") #endif G_DEFINE_TYPE_WITH_PRIVATE(GjsCoverage, gjs_coverage, G_TYPE_OBJECT) #if __GNUC__ >= 8 _Pragma("GCC diagnostic pop") #endif enum { PROP_0, PROP_PREFIXES, PROP_CONTEXT, PROP_CACHE, PROP_OUTPUT_DIRECTORY, PROP_N }; static GParamSpec *properties[PROP_N] = { NULL, }; static char * get_file_identifier(GFile *source_file) { char *path = g_file_get_path(source_file); if (!path) path = g_file_get_uri(source_file); return path; } static bool write_source_file_header(GOutputStream *stream, GFile *source_file, GError **error) { GjsAutoChar path = get_file_identifier(source_file); return g_output_stream_printf(stream, NULL, NULL, error, "SF:%s\n", path.get()); } static bool copy_source_file_to_coverage_output(GFile *source_file, GFile *destination_file, GError **error) { /* We need to recursively make the directory we * want to copy to, as g_file_copy doesn't do that */ GjsAutoUnref destination_dir = g_file_get_parent(destination_file); if (!g_file_make_directory_with_parents(destination_dir, NULL, error)) { if (!g_error_matches(*error, G_IO_ERROR, G_IO_ERROR_EXISTS)) return false; g_clear_error(error); } return g_file_copy(source_file, destination_file, G_FILE_COPY_OVERWRITE, nullptr, nullptr, nullptr, error); } /* This function will strip a URI scheme and return * the string with the URI scheme stripped or NULL * if the path was not a valid URI */ static char * strip_uri_scheme(const char *potential_uri) { char *uri_header = g_uri_parse_scheme(potential_uri); if (uri_header) { gsize offset = strlen(uri_header); g_free(uri_header); /* g_uri_parse_scheme only parses the name * of the scheme, we also need to strip the * characters ':///' */ return g_strdup(potential_uri + offset + 4); } return NULL; } /* This function will return a string of pathname * components from the first directory indicating * where two directories diverge. For instance: * * child: /a/b/c/d/e * parent: /a/b/d/ * * Will return: c/d/e * * If the directories are not at all similar then * the full dirname of the child_path effectively * be returned. * * As a special case, child paths that are a URI * automatically return the full URI path with * the URI scheme and leading slash stripped out. */ static char * find_diverging_child_components(GFile *child, GFile *parent) { g_object_ref(parent); GFile *ancestor = parent; while (ancestor != NULL) { char *relpath = g_file_get_relative_path(ancestor, child); if (relpath) { g_object_unref(ancestor); return relpath; } GFile *new_ancestor = g_file_get_parent(ancestor); g_object_unref(ancestor); ancestor = new_ancestor; } /* This is a special case of getting the URI below. The difference is that * this gives you a regular path name; getting it through the URI would * give a URI-encoded path (%20 for spaces, etc.) */ GFile *root = g_file_new_for_path("/"); char *child_path = g_file_get_relative_path(root, child); g_object_unref(root); if (child_path) return child_path; char *child_uri = g_file_get_uri(child); char *stripped_uri = strip_uri_scheme(child_uri); g_free(child_uri); return stripped_uri; } static bool filename_has_coverage_prefixes(GjsCoverage *self, const char *filename) { auto priv = static_cast(gjs_coverage_get_instance_private(self)); for (const char * const *prefix = priv->prefixes; *prefix; prefix++) { if (g_str_has_prefix(filename, *prefix)) return true; } return false; } static inline bool write_line(GOutputStream *out, const char *line, GError **error) { return g_output_stream_printf(out, nullptr, nullptr, error, "%s\n", line); } static GjsAutoUnref write_statistics_internal(GjsCoverage *coverage, JSContext *cx, GError **error) { using AutoCChar = std::unique_ptr; using AutoStrv = std::unique_ptr; GjsCoveragePrivate *priv = (GjsCoveragePrivate *) gjs_coverage_get_instance_private(coverage); /* Create output directory if it doesn't exist */ if (!g_file_make_directory_with_parents(priv->output_dir, nullptr, error)) { if (!g_error_matches(*error, G_IO_ERROR, G_IO_ERROR_EXISTS)) return nullptr; g_clear_error(error); } GFile *output_file = g_file_get_child(priv->output_dir, "coverage.lcov"); size_t lcov_length; AutoCChar lcov(js::GetCodeCoverageSummary(cx, &lcov_length), free); GjsAutoUnref ostream = G_OUTPUT_STREAM(g_file_append_to(output_file, G_FILE_CREATE_NONE, NULL, error)); if (!ostream) return nullptr; AutoStrv lcov_lines(g_strsplit(lcov.get(), "\n", -1), g_strfreev); GjsAutoChar test_name; bool ignoring_file = false; for (const char * const *iter = lcov_lines.get(); *iter; iter++) { if (ignoring_file) { if (strcmp(*iter, "end_of_record") == 0) ignoring_file = false; continue; } if (g_str_has_prefix(*iter, "TN:")) { /* Don't write the test name if the next line shows we are * ignoring the source file */ test_name = *iter; continue; } else if (g_str_has_prefix(*iter, "SF:")) { const char *filename = *iter + 3; if (!filename_has_coverage_prefixes(coverage, filename)) { ignoring_file = true; continue; } /* Now we can write the test name before writing the source file */ if (!write_line(ostream, test_name.get(), error)) return nullptr; /* The source file could be a resource, so we must use * g_file_new_for_commandline_arg() to disambiguate between URIs and * filesystem paths. */ GjsAutoUnref source_file = g_file_new_for_commandline_arg(filename); GjsAutoChar diverged_paths = find_diverging_child_components(source_file, priv->output_dir); GjsAutoUnref destination_file = g_file_resolve_relative_path(priv->output_dir, diverged_paths); if (!copy_source_file_to_coverage_output(source_file, destination_file, error)) return nullptr; /* Rewrite the source file path to be relative to the output * dir so that genhtml will find it */ if (!write_source_file_header(ostream, destination_file, error)) return nullptr; continue; } if (!write_line(ostream, *iter, error)) return nullptr; } return output_file; } /** * gjs_coverage_write_statistics: * @coverage: A #GjsCoverage * @output_directory: A directory to write coverage information to. * * Scripts which were provided as part of the #GjsCoverage:prefixes * construction property will be written out to @output_directory, in the same * directory structure relative to the source dir where the tests were run. * * This function takes all available statistics and writes them out to either * the file provided or to files of the pattern (filename).info in the same * directory as the scanned files. It will provide coverage data for all files * ending with ".js" in the coverage directories. */ void gjs_coverage_write_statistics(GjsCoverage *coverage) { auto priv = static_cast(gjs_coverage_get_instance_private(coverage)); GError *error = nullptr; auto cx = static_cast(gjs_context_get_native_context(priv->context)); JSAutoCompartment ac(cx, gjs_get_import_global(cx)); JSAutoRequest ar(cx); GjsAutoUnref output_file = write_statistics_internal(coverage, cx, &error); if (!output_file) { g_critical("Error writing coverage data: %s", error->message); g_error_free(error); return; } GjsAutoChar output_file_path = g_file_get_path(output_file); g_message("Wrote coverage statistics to %s", output_file_path.get()); } static void gjs_coverage_init(GjsCoverage *self) { } static void coverage_tracer(JSTracer *trc, void *data) { GjsCoverage *coverage = (GjsCoverage *) data; GjsCoveragePrivate *priv = (GjsCoveragePrivate *) gjs_coverage_get_instance_private(coverage); JS::TraceEdge(trc, &priv->compartment, "Coverage compartment"); } static bool bootstrap_coverage(GjsCoverage *coverage) { GjsCoveragePrivate *priv = (GjsCoveragePrivate *) gjs_coverage_get_instance_private(coverage); JSContext *context = (JSContext *) gjs_context_get_native_context(priv->context); JSAutoRequest ar(context); JSObject *debuggee = gjs_get_import_global(context); JS::RootedObject debugger_compartment(context, gjs_create_global_object(context)); { JSAutoCompartment compartment(context, debugger_compartment); JS::RootedObject debuggeeWrapper(context, debuggee); if (!JS_WrapObject(context, &debuggeeWrapper)) return false; JS::RootedValue debuggeeWrapperValue(context, JS::ObjectValue(*debuggeeWrapper)); if (!JS_SetProperty(context, debugger_compartment, "debuggee", debuggeeWrapperValue) || !gjs_define_global_properties(context, debugger_compartment, "coverage")) return false; /* Add a tracer, as suggested by jdm on #jsapi */ JS_AddExtraGCRootsTracer(context, coverage_tracer, coverage); priv->compartment = debugger_compartment; } return true; } static void gjs_coverage_constructed(GObject *object) { G_OBJECT_CLASS(gjs_coverage_parent_class)->constructed(object); GjsCoverage *coverage = GJS_COVERAGE(object); GjsCoveragePrivate *priv = (GjsCoveragePrivate *) gjs_coverage_get_instance_private(coverage); new (&priv->compartment) JS::Heap(); if (!bootstrap_coverage(coverage)) { JSContext *context = static_cast(gjs_context_get_native_context(priv->context)); JSAutoCompartment compartment(context, gjs_get_import_global(context)); gjs_log_exception(context); } } static void gjs_coverage_set_property(GObject *object, unsigned int prop_id, const GValue *value, GParamSpec *pspec) { GjsCoverage *coverage = GJS_COVERAGE(object); GjsCoveragePrivate *priv = (GjsCoveragePrivate *) gjs_coverage_get_instance_private(coverage); switch (prop_id) { case PROP_PREFIXES: g_assert(priv->prefixes == NULL); priv->prefixes = (char **) g_value_dup_boxed (value); break; case PROP_CONTEXT: priv->context = GJS_CONTEXT(g_value_dup_object(value)); break; case PROP_CACHE: break; case PROP_OUTPUT_DIRECTORY: priv->output_dir = G_FILE(g_value_dup_object(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } static void gjs_coverage_dispose(GObject *object) { GjsCoverage *coverage = GJS_COVERAGE(object); GjsCoveragePrivate *priv = (GjsCoveragePrivate *) gjs_coverage_get_instance_private(coverage); /* Decomission objects inside of the JSContext before * disposing of the context */ auto cx = static_cast(gjs_context_get_native_context(priv->context)); JS_RemoveExtraGCRootsTracer(cx, coverage_tracer, coverage); priv->compartment = nullptr; g_clear_object(&priv->context); G_OBJECT_CLASS(gjs_coverage_parent_class)->dispose(object); } static void gjs_coverage_finalize (GObject *object) { GjsCoverage *coverage = GJS_COVERAGE(object); GjsCoveragePrivate *priv = (GjsCoveragePrivate *) gjs_coverage_get_instance_private(coverage); g_strfreev(priv->prefixes); g_clear_object(&priv->output_dir); priv->compartment.~Heap(); G_OBJECT_CLASS(gjs_coverage_parent_class)->finalize(object); } static void gjs_coverage_class_init (GjsCoverageClass *klass) { GObjectClass *object_class = (GObjectClass *) klass; object_class->constructed = gjs_coverage_constructed; object_class->dispose = gjs_coverage_dispose; object_class->finalize = gjs_coverage_finalize; object_class->set_property = gjs_coverage_set_property; properties[PROP_PREFIXES] = g_param_spec_boxed("prefixes", "Prefixes", "Prefixes of files on which to perform coverage analysis", G_TYPE_STRV, (GParamFlags) (G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE)); properties[PROP_CONTEXT] = g_param_spec_object("context", "Context", "A context to gather coverage stats for", GJS_TYPE_CONTEXT, (GParamFlags) (G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE)); properties[PROP_CACHE] = g_param_spec_object("cache", "Deprecated property", "Has no effect", G_TYPE_FILE, (GParamFlags) (G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_DEPRECATED)); properties[PROP_OUTPUT_DIRECTORY] = g_param_spec_object("output-directory", "Output directory", "Directory handle at which to output coverage statistics", G_TYPE_FILE, (GParamFlags) (G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_properties(object_class, PROP_N, properties); } /** * gjs_coverage_new: * @prefixes: A null-terminated strv of prefixes of files on which to record * code coverage * @context: A #GjsContext object * @output_dir: A #GFile handle to a directory in which to write coverage * information * * Creates a new #GjsCoverage object that collects coverage information for * any scripts run in @context. * * Scripts which were provided as part of @prefixes will be written out to * @output_dir, in the same directory structure relative to the source dir where * the tests were run. * * Returns: A #GjsCoverage object */ GjsCoverage * gjs_coverage_new (const char * const *prefixes, GjsContext *context, GFile *output_dir) { GjsCoverage *coverage = GJS_COVERAGE(g_object_new(GJS_TYPE_COVERAGE, "prefixes", prefixes, "context", context, "output-directory", output_dir, NULL)); return coverage; }cjs-4.4.0/cjs/coverage.h000066400000000000000000000046521356375132300150400ustar00rootroot00000000000000/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright © 2014 Endless Mobile, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. * * Authored By: Sam Spilsbury */ #ifndef _GJS_COVERAGE_H #define _GJS_COVERAGE_H #include #include #include "context.h" G_BEGIN_DECLS #define GJS_TYPE_COVERAGE gjs_coverage_get_type() #define GJS_COVERAGE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), \ GJS_TYPE_COVERAGE, GjsCoverage)) #define GJS_COVERAGE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), \ GJS_TYPE_COVERAGE, GjsCoverageClass)) #define GJS_IS_COVERAGE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), \ GJS_TYPE_COVERAGE)) #define GJS_IS_COVERAGE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ GJS_TYPE_COVERAGE)) #define GJS_COVERAGE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ GJS_TYPE_COVERAGE, GjsCoverageClass)) typedef struct _GjsCoverage GjsCoverage; typedef struct _GjsCoverageClass GjsCoverageClass; struct _GjsCoverageClass { GObjectClass parent_class; }; GType gjs_coverage_get_type(void); GJS_EXPORT void gjs_coverage_write_statistics(GjsCoverage *self); GJS_EXPORT GjsCoverage * gjs_coverage_new(const char * const *coverage_prefixes, GjsContext *coverage_context, GFile *output_dir); G_END_DECLS #endif cjs-4.4.0/cjs/engine.cpp000066400000000000000000000250671356375132300150500ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2013 Giovanni Campagna * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "jsapi-wrapper.h" #include #include "context-private.h" #include "engine.h" #include "gi/object.h" #include "jsapi-util.h" #include "util/log.h" #ifdef G_OS_WIN32 #define WIN32_LEAN_AND_MEAN #include #endif /* Implementations of locale-specific operations; these are used * in the implementation of String.localeCompare(), Date.toLocaleDateString(), * and so forth. We take the straight-forward approach of converting * to UTF-8, using the appropriate GLib functions, and converting * back if necessary. */ static bool gjs_locale_to_upper_case (JSContext *context, JS::HandleString src, JS::MutableHandleValue retval) { GjsAutoJSChar utf8 = JS_EncodeStringToUTF8(context, src); if (!utf8) return false; GjsAutoChar upper_case_utf8 = g_utf8_strup(utf8, -1); return gjs_string_from_utf8(context, upper_case_utf8, retval); } static bool gjs_locale_to_lower_case (JSContext *context, JS::HandleString src, JS::MutableHandleValue retval) { GjsAutoJSChar utf8 = JS_EncodeStringToUTF8(context, src); if (!utf8) return false; GjsAutoChar lower_case_utf8 = g_utf8_strdown(utf8, -1); return gjs_string_from_utf8(context, lower_case_utf8, retval); } static bool gjs_locale_compare (JSContext *context, JS::HandleString src_1, JS::HandleString src_2, JS::MutableHandleValue retval) { GjsAutoJSChar utf8_1 = JS_EncodeStringToUTF8(context, src_1); if (!utf8_1) return false; GjsAutoJSChar utf8_2 = JS_EncodeStringToUTF8(context, src_2); if (!utf8_2) return false; retval.setInt32(g_utf8_collate(utf8_1, utf8_2)); return true; } static bool gjs_locale_to_unicode (JSContext *context, const char *src, JS::MutableHandleValue retval) { GError *error = NULL; GjsAutoChar utf8 = g_locale_to_utf8(src, -1, NULL, NULL, &error); if (!utf8) { gjs_throw(context, "Failed to convert locale string to UTF8: %s", error->message); g_error_free(error); return false; } return gjs_string_from_utf8(context, utf8, retval); } static JSLocaleCallbacks gjs_locale_callbacks = { gjs_locale_to_upper_case, gjs_locale_to_lower_case, gjs_locale_compare, gjs_locale_to_unicode }; static void gjs_finalize_callback(JSFreeOp *fop, JSFinalizeStatus status, bool isCompartment, void *data) { auto js_context = static_cast(data); /* Implementation note for mozjs 24: sweeping happens in two phases, in the first phase all GC things from the allocation arenas are queued for sweeping, then the actual sweeping happens. The first phase is marked by JSFINALIZE_GROUP_START, the second one by JSFINALIZE_GROUP_END, and finally we will see JSFINALIZE_COLLECTION_END at the end of all GC. (see jsgc.cpp, BeginSweepPhase/BeginSweepingZoneGroup and SweepPhase, all called from IncrementalCollectSlice). Incremental GC muds the waters, because BeginSweepPhase is always run to entirety, but SweepPhase can be run incrementally and mixed with JS code runs or even native code, when MaybeGC/IncrementalGC return. Luckily for us, objects are treated specially, and are not really queued for deferred incremental finalization (unless they are marked for background sweeping). Instead, they are finalized immediately during phase 1, so the following guarantees are true (and we rely on them) - phase 1 of GC will begin and end in the same JSAPI call (ie, our callback will be called with GROUP_START and the triggering JSAPI call will not return until we see a GROUP_END) - object finalization will begin and end in the same JSAPI call - therefore, if there is a finalizer frame somewhere in the stack, gjs_runtime_is_sweeping() will return true. Comments in mozjs24 imply that this behavior might change in the future, but it hasn't changed in mozilla-central as of 2014-02-23. In addition to that, the mozilla-central version has a huge comment in a different portion of the file, explaining why finalization of objects can't be mixed with JS code, so we can probably rely on this behavior. */ if (status == JSFINALIZE_GROUP_START) _gjs_context_set_sweeping(js_context, true); else if (status == JSFINALIZE_GROUP_END) _gjs_context_set_sweeping(js_context, false); } static void on_garbage_collect(JSContext *cx, JSGCStatus status, void *data) { /* We finalize any pending toggle refs before doing any garbage collection, * so that we can collect the JS wrapper objects, and in order to minimize * the chances of objects having a pending toggle up queued when they are * garbage collected. */ if (status == JSGC_BEGIN) gjs_object_clear_toggles(); } static bool on_enqueue_promise_job(JSContext *cx, JS::HandleObject callback, JS::HandleObject allocation_site, JS::HandleObject global, void *data) { auto gjs_context = static_cast(data); return _gjs_context_enqueue_job(gjs_context, callback); } static void on_promise_unhandled_rejection(JSContext *cx, JS::HandleObject promise, PromiseRejectionHandlingState state, void *data) { auto gjs_context = static_cast(data); uint64_t id = JS::GetPromiseID(promise); if (state == PromiseRejectionHandlingState::Handled) { /* This happens when catching an exception from an await expression. */ _gjs_context_unregister_unhandled_promise_rejection(gjs_context, id); return; } JS::RootedObject allocation_site(cx, JS::GetPromiseAllocationSite(promise)); GjsAutoChar stack = gjs_format_stack_trace(cx, allocation_site); _gjs_context_register_unhandled_promise_rejection(gjs_context, id, std::move(stack)); } #ifdef G_OS_WIN32 HMODULE gjs_dll; static bool gjs_is_inited = false; BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { switch (fdwReason) { case DLL_PROCESS_ATTACH: gjs_dll = hinstDLL; gjs_is_inited = JS_Init(); break; case DLL_THREAD_DETACH: JS_ShutDown (); break; default: /* do nothing */ ; } return TRUE; } #else class GjsInit { public: GjsInit() { if (!JS_Init()) g_error("Could not initialize Javascript"); } ~GjsInit() { JS_ShutDown(); } operator bool() { return true; } }; static GjsInit gjs_is_inited; #endif JSContext * gjs_create_js_context(GjsContext *js_context) { g_assert(gjs_is_inited); JSContext *cx = JS_NewContext(32 * 1024 * 1024 /* max bytes */); if (!cx) return nullptr; if (!JS::InitSelfHostedCode(cx)) return nullptr; // commented are defaults in moz-24 JS_SetNativeStackQuota(cx, 1024 * 1024); JS_SetGCParameter(cx, JSGC_MAX_MALLOC_BYTES, 128 * 1024 * 1024); JS_SetGCParameter(cx, JSGC_MAX_BYTES, -1); JS_SetGCParameter(cx, JSGC_MODE, JSGC_MODE_INCREMENTAL); JS_SetGCParameter(cx, JSGC_SLICE_TIME_BUDGET, 10); /* ms */ // JS_SetGCParameter(cx, JSGC_HIGH_FREQUENCY_TIME_LIMIT, 1000); /* ms */ JS_SetGCParameter(cx, JSGC_DYNAMIC_MARK_SLICE, true); JS_SetGCParameter(cx, JSGC_DYNAMIC_HEAP_GROWTH, true); // JS_SetGCParameter(cx, JSGC_LOW_FREQUENCY_HEAP_GROWTH, 150); // JS_SetGCParameter(cx, JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN, 150); // JS_SetGCParameter(cx, JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX, 300); // JS_SetGCParameter(cx, JSGC_HIGH_FREQUENCY_LOW_LIMIT, 100); // JS_SetGCParameter(cx, JSGC_HIGH_FREQUENCY_HIGH_LIMIT, 500); // JS_SetGCParameter(cx, JSGC_ALLOCATION_THRESHOLD, 30); // JS_SetGCParameter(cx, JSGC_DECOMMIT_THRESHOLD, 32); /* set ourselves as the private data */ JS_SetContextPrivate(cx, js_context); JS_AddFinalizeCallback(cx, gjs_finalize_callback, js_context); JS_SetGCCallback(cx, on_garbage_collect, js_context); JS_SetLocaleCallbacks(cx, &gjs_locale_callbacks); JS::SetWarningReporter(cx, gjs_warning_reporter); JS::SetGetIncumbentGlobalCallback(cx, gjs_get_import_global); JS::SetEnqueuePromiseJobCallback(cx, on_enqueue_promise_job, js_context); JS::SetPromiseRejectionTrackerCallback(cx, on_promise_unhandled_rejection, js_context); /* setExtraWarnings: Be extra strict about code that might hide a bug */ if (!g_getenv("GJS_DISABLE_EXTRA_WARNINGS")) { gjs_debug(GJS_DEBUG_CONTEXT, "Enabling extra warnings"); JS::ContextOptionsRef(cx).setExtraWarnings(true); } if (!g_getenv("GJS_DISABLE_JIT")) { gjs_debug(GJS_DEBUG_CONTEXT, "Enabling JIT"); JS::ContextOptionsRef(cx) .setIon(true) .setBaseline(true) .setAsmJS(true); } return cx; } cjs-4.4.0/cjs/engine.h000066400000000000000000000025641356375132300145120ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2013 Giovanni Campagna * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef GJS_ENGINE_H #define GJS_ENGINE_H #include "context.h" #include "jsapi-wrapper.h" JSContext *gjs_create_js_context(GjsContext *js_context); #endif /* GJS_ENGINE_H */ cjs-4.4.0/cjs/gjs.h000066400000000000000000000025431356375132300140250ustar00rootroot00000000000000/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_GJS_H__ #define __GJS_GJS_H__ #include #include #include #include #include #endif /* __GJS_GJS_H__ */ cjs-4.4.0/cjs/global.cpp000066400000000000000000000260011356375132300150300ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * Copyright (c) 2009 Red Hat, Inc. * Copyright (c) 2017 Philip Chimento * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "global.h" #include "importer.h" #include "jsapi-util.h" #include "jsapi-wrapper.h" static bool run_bootstrap(JSContext *cx, const char *bootstrap_script, JS::HandleObject global) { GjsAutoChar path = g_strdup_printf("/org/cinnamon/cjs/modules/_bootstrap/%s.js", bootstrap_script); GError *error = nullptr; std::unique_ptr script_bytes( g_resources_lookup_data(path, G_RESOURCE_LOOKUP_FLAGS_NONE, &error), g_bytes_unref); if (!script_bytes) { gjs_throw_g_error(cx, error); return false; } JSAutoCompartment ac(cx, global); GjsAutoChar uri = g_strconcat("resource://", path.get(), nullptr); JS::CompileOptions options(cx); options.setUTF8(true) .setFileAndLine(uri, 1) .setSourceIsLazy(true); JS::RootedScript compiled_script(cx); size_t script_len; auto script = static_cast(g_bytes_get_data(script_bytes.get(), &script_len)); if (!JS::Compile(cx, options, script, script_len, &compiled_script)) return false; JS::RootedValue ignored(cx); return JS::CloneAndExecuteScript(cx, compiled_script, &ignored); } static bool gjs_log(JSContext *cx, unsigned argc, JS::Value *vp) { JS::CallArgs argv = JS::CallArgsFromVp(argc, vp); if (argc != 1) { gjs_throw(cx, "Must pass a single argument to log()"); return false; } JSAutoRequest ar(cx); /* JS::ToString might throw, in which case we will only log that the value * could not be converted to string */ JS::AutoSaveExceptionState exc_state(cx); JS::RootedString jstr(cx, JS::ToString(cx, argv[0])); exc_state.restore(); if (!jstr) { g_message("JS LOG: "); return true; } GjsAutoJSChar s = JS_EncodeStringToUTF8(cx, jstr); if (!s) return false; g_message("JS LOG: %s", s.get()); argv.rval().setUndefined(); return true; } static bool gjs_log_error(JSContext *cx, unsigned argc, JS::Value *vp) { JS::CallArgs argv = JS::CallArgsFromVp(argc, vp); if ((argc != 1 && argc != 2) || !argv[0].isObject()) { gjs_throw(cx, "Must pass an exception and optionally a message to logError()"); return false; } JSAutoRequest ar(cx); JS::RootedString jstr(cx); if (argc == 2) { /* JS::ToString might throw, in which case we will only log that the * value could not be converted to string */ JS::AutoSaveExceptionState exc_state(cx); jstr = JS::ToString(cx, argv[1]); exc_state.restore(); } gjs_log_exception_full(cx, argv[0], jstr); argv.rval().setUndefined(); return true; } static bool gjs_print_parse_args(JSContext *cx, JS::CallArgs& argv, GjsAutoChar *buffer) { GString *str; guint n; JSAutoRequest ar(cx); str = g_string_new(""); for (n = 0; n < argv.length(); ++n) { /* JS::ToString might throw, in which case we will only log that the * value could not be converted to string */ JS::AutoSaveExceptionState exc_state(cx); JS::RootedString jstr(cx, JS::ToString(cx, argv[n])); exc_state.restore(); if (jstr) { GjsAutoJSChar s = JS_EncodeStringToUTF8(cx, jstr); if (!s) { g_string_free(str, true); return false; } g_string_append(str, s); if (n < (argv.length()-1)) g_string_append_c(str, ' '); } else { *buffer = g_string_free(str, true); if (!*buffer) *buffer = g_strdup(""); return true; } } *buffer = g_string_free(str, false); return true; } static bool gjs_print(JSContext *context, unsigned argc, JS::Value *vp) { JS::CallArgs argv = JS::CallArgsFromVp (argc, vp); GjsAutoChar buffer; if (!gjs_print_parse_args(context, argv, &buffer)) return false; g_print("%s\n", buffer.get()); argv.rval().setUndefined(); return true; } static bool gjs_printerr(JSContext *context, unsigned argc, JS::Value *vp) { JS::CallArgs argv = JS::CallArgsFromVp(argc, vp); GjsAutoChar buffer; if (!gjs_print_parse_args(context, argv, &buffer)) return false; g_printerr("%s\n", buffer.get()); argv.rval().setUndefined(); return true; } class GjsGlobal { static constexpr JSClassOps class_ops = { nullptr, /* addProperty */ nullptr, /* deleteProperty */ nullptr, /* getProperty */ nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ nullptr, /* mayResolve */ nullptr, /* finalize */ nullptr, /* call */ nullptr, /* hasInstance */ nullptr, /* construct */ JS_GlobalObjectTraceHook }; static constexpr JSClass klass = { "GjsGlobal", JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(GJS_GLOBAL_SLOT_LAST), &GjsGlobal::class_ops, }; static constexpr JSFunctionSpec static_funcs[] = { JS_FS("log", gjs_log, 1, GJS_MODULE_PROP_FLAGS), JS_FS("logError", gjs_log_error, 2, GJS_MODULE_PROP_FLAGS), JS_FS("print", gjs_print, 0, GJS_MODULE_PROP_FLAGS), JS_FS("printerr", gjs_printerr, 0, GJS_MODULE_PROP_FLAGS), JS_FS_END }; public: static JSObject * create(JSContext *cx) { JS::CompartmentOptions compartment_options; compartment_options.behaviors().setVersion(JSVERSION_LATEST); JS::RootedObject global(cx, JS_NewGlobalObject(cx, &GjsGlobal::klass, nullptr, JS::FireOnNewGlobalHook, compartment_options)); if (!global) return nullptr; JSAutoCompartment ac(cx, global); if (!JS_InitStandardClasses(cx, global) || !JS_InitReflectParse(cx, global) || !JS_DefineDebuggerObject(cx, global)) return nullptr; return global; } static bool define_properties(JSContext *cx, JS::HandleObject global, const char *bootstrap_script) { if (!JS_DefineProperty(cx, global, "window", global, JSPROP_READONLY | JSPROP_PERMANENT) || !JS_DefineFunctions(cx, global, GjsGlobal::static_funcs)) return false; JS::Value v_importer = gjs_get_global_slot(cx, GJS_GLOBAL_SLOT_IMPORTS); g_assert(((void) "importer should be defined before passing null " "importer to GjsGlobal::define_properties", v_importer.isObject())); JS::RootedObject root_importer(cx, &v_importer.toObject()); /* Wrapping is a no-op if the importer is already in the same * compartment. */ if (!JS_WrapObject(cx, &root_importer) || !gjs_object_define_property(cx, global, GJS_STRING_IMPORTS, root_importer, GJS_MODULE_PROP_FLAGS)) return false; if (bootstrap_script) { if (!run_bootstrap(cx, bootstrap_script, global)) return false; } return true; } }; /** * gjs_create_global_object: * @cx: a #JSContext * * Creates a global object, and initializes it with the default API. * * Returns: the created global object on success, nullptr otherwise, in which * case an exception is pending on @cx */ JSObject * gjs_create_global_object(JSContext *cx) { return GjsGlobal::create(cx); } /** * gjs_define_global_properties: * @cx: a #JSContext * @global: a JS global object that has not yet been passed to this function * @bootstrap_script: (nullable): name of a bootstrap script (found at * resource://org/gnome/gjs/modules/_bootstrap/@bootstrap_script) or %NULL for * none * * Defines properties on the global object such as 'window' and 'imports', and * runs a bootstrap JS script on the global object to define any properties * that can be defined from JS. * This function completes the initialization of a new global object, but it * is separate from gjs_create_global_object() because all globals share the * same root importer. * The code creating the main global for the JS context needs to create the * root importer in between calling gjs_create_global_object() and * gjs_define_global_properties(). * * The caller of this function should be in the compartment for @global. * If the root importer object belongs to a different compartment, this * function will create a cross-compartment wrapper for it. * * Returns: true on success, false otherwise, in which case an exception is * pending on @cx */ bool gjs_define_global_properties(JSContext *cx, JS::HandleObject global, const char *bootstrap_script) { return GjsGlobal::define_properties(cx, global, bootstrap_script); } void gjs_set_global_slot(JSContext *cx, GjsGlobalSlot slot, JS::Value value) { JSObject *global = gjs_get_import_global(cx); JS_SetReservedSlot(global, JSCLASS_GLOBAL_SLOT_COUNT + slot, value); } JS::Value gjs_get_global_slot(JSContext *cx, GjsGlobalSlot slot) { JSObject *global = gjs_get_import_global(cx); return JS_GetReservedSlot(global, JSCLASS_GLOBAL_SLOT_COUNT + slot); } decltype(GjsGlobal::class_ops) constexpr GjsGlobal::class_ops; decltype(GjsGlobal::klass) constexpr GjsGlobal::klass; decltype(GjsGlobal::static_funcs) constexpr GjsGlobal::static_funcs; cjs-4.4.0/cjs/global.h000066400000000000000000000054171356375132300145050ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2017 Philip Chimento * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef GJS_GLOBAL_H #define GJS_GLOBAL_H #include #include "jsapi-wrapper.h" G_BEGIN_DECLS typedef enum { GJS_GLOBAL_SLOT_IMPORTS, GJS_GLOBAL_SLOT_PROTOTYPE_gtype, GJS_GLOBAL_SLOT_PROTOTYPE_function, GJS_GLOBAL_SLOT_PROTOTYPE_ns, GJS_GLOBAL_SLOT_PROTOTYPE_repo, GJS_GLOBAL_SLOT_PROTOTYPE_byte_array, GJS_GLOBAL_SLOT_PROTOTYPE_importer, GJS_GLOBAL_SLOT_PROTOTYPE_cairo_context, GJS_GLOBAL_SLOT_PROTOTYPE_cairo_gradient, GJS_GLOBAL_SLOT_PROTOTYPE_cairo_image_surface, GJS_GLOBAL_SLOT_PROTOTYPE_cairo_linear_gradient, GJS_GLOBAL_SLOT_PROTOTYPE_cairo_path, GJS_GLOBAL_SLOT_PROTOTYPE_cairo_pattern, GJS_GLOBAL_SLOT_PROTOTYPE_cairo_pdf_surface, GJS_GLOBAL_SLOT_PROTOTYPE_cairo_ps_surface, GJS_GLOBAL_SLOT_PROTOTYPE_cairo_radial_gradient, GJS_GLOBAL_SLOT_PROTOTYPE_cairo_region, GJS_GLOBAL_SLOT_PROTOTYPE_cairo_solid_pattern, GJS_GLOBAL_SLOT_PROTOTYPE_cairo_surface, GJS_GLOBAL_SLOT_PROTOTYPE_cairo_surface_pattern, GJS_GLOBAL_SLOT_PROTOTYPE_cairo_svg_surface, GJS_GLOBAL_SLOT_LAST, } GjsGlobalSlot; JSObject *gjs_create_global_object(JSContext *cx); bool gjs_define_global_properties(JSContext *cx, JS::HandleObject global, const char *bootstrap_script); JS::Value gjs_get_global_slot(JSContext *cx, GjsGlobalSlot slot); void gjs_set_global_slot(JSContext *context, GjsGlobalSlot slot, JS::Value value); G_END_DECLS #endif /* GJS_GLOBAL_H */ cjs-4.4.0/cjs/importer.cpp000066400000000000000000000760221356375132300154410ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008-2010 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include "importer.h" #include "jsapi-class.h" #include "jsapi-wrapper.h" #include "mem.h" #include "module.h" #include "native.h" #include "util/glib.h" #include "util/log.h" #ifdef G_OS_WIN32 #define WIN32_LEAN_AND_MEAN #include #endif #include #define MODULE_INIT_FILENAME "__init__.js" static char **gjs_search_path = NULL; typedef struct { bool is_root; } Importer; typedef struct { GPtrArray *elements; unsigned int index; } ImporterIterator; extern const js::Class gjs_importer_real_class; /* Bizarrely, the API for safely casting const js::Class * to const JSClass * * is called "js::Jsvalify" */ static const JSClass gjs_importer_class = *js::Jsvalify(&gjs_importer_real_class); GJS_DEFINE_PRIV_FROM_JS(Importer, gjs_importer_class) static JSObject *gjs_define_importer(JSContext *, JS::HandleObject, const char *, const char * const *, bool); static bool importer_to_string(JSContext *cx, unsigned argc, JS::Value *vp) { GJS_GET_THIS(cx, argc, vp, args, importer); const JSClass *klass = JS_GetClass(importer); JS::RootedValue module_path(cx); if (!gjs_object_get_property(cx, importer, GJS_STRING_MODULE_PATH, &module_path)) return false; GjsAutoChar output; if (module_path.isNull()) { output = g_strdup_printf("[%s root]", klass->name); } else { GjsAutoJSChar path; if (!gjs_string_to_utf8(cx, module_path, &path)) return false; output = g_strdup_printf("[%s %s]", klass->name, path.get()); } args.rval().setString(JS_NewStringCopyZ(cx, output)); return true; } static bool define_meta_properties(JSContext *context, JS::HandleObject module_obj, const char *full_path, const char *module_name, JS::HandleObject parent) { bool parent_is_module; /* For these meta-properties, don't set ENUMERATE since we wouldn't want to * copy these symbols to any other object for example. RESOLVING is used to * make sure we don't try to invoke a "resolve" operation, since this * function may be called from inside one. */ unsigned attrs = JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_RESOLVING; /* We define both __moduleName__ and __parentModule__ to null * on the root importer */ parent_is_module = parent && JS_InstanceOf(context, parent, &gjs_importer_class, nullptr); gjs_debug(GJS_DEBUG_IMPORTER, "Defining parent %p of %p '%s' is mod %d", parent.get(), module_obj.get(), module_name ? module_name : "", parent_is_module); if (full_path != NULL) { JS::RootedString file(context, JS_NewStringCopyZ(context, full_path)); if (!JS_DefineProperty(context, module_obj, "__file__", file, attrs)) return false; } /* Null is used instead of undefined for backwards compatibility with code * that explicitly checks for null. */ JS::RootedValue module_name_val(context, JS::NullValue()); JS::RootedValue parent_module_val(context, JS::NullValue()); JS::RootedValue module_path(context, JS::NullValue()); JS::RootedValue to_string_tag(context); if (parent_is_module) { module_name_val.setString(JS_NewStringCopyZ(context, module_name)); parent_module_val.setObject(*parent); JS::RootedValue parent_module_path(context); if (!gjs_object_get_property(context, parent, GJS_STRING_MODULE_PATH, &parent_module_path)) return false; GjsAutoChar module_path_buf; if (parent_module_path.isNull()) { module_path_buf = g_strdup(module_name); } else { GjsAutoJSChar parent_path; if (!gjs_string_to_utf8(context, parent_module_path, &parent_path)) return false; module_path_buf = g_strdup_printf("%s.%s", parent_path.get(), module_name); } module_path.setString(JS_NewStringCopyZ(context, module_path_buf)); GjsAutoChar to_string_tag_buf = g_strdup_printf("GjsModule %s", module_path_buf.get()); to_string_tag.setString(JS_NewStringCopyZ(context, to_string_tag_buf)); } else { to_string_tag.setString(JS_AtomizeString(context, "GjsModule")); } if (!JS_DefineProperty(context, module_obj, "__moduleName__", module_name_val, attrs)) return false; if (!JS_DefineProperty(context, module_obj, "__parentModule__", parent_module_val, attrs)) return false; if (!JS_DefineProperty(context, module_obj, "__modulePath__", module_path, attrs)) return false; JS::RootedId to_string_tag_name(context, SYMBOL_TO_JSID(JS::GetWellKnownSymbol(context, JS::SymbolCode::toStringTag))); return JS_DefinePropertyById(context, module_obj, to_string_tag_name, to_string_tag, attrs); } static bool import_directory(JSContext *context, JS::HandleObject obj, const char *name, const char * const *full_paths) { JSObject *importer; gjs_debug(GJS_DEBUG_IMPORTER, "Importing directory '%s'", name); /* We define a sub-importer that has only the given directories on * its search path. gjs_define_importer() exits if it fails, so * this always succeeds. */ importer = gjs_define_importer(context, obj, name, full_paths, false); return importer != nullptr; } /* Make the property we set in gjs_module_import() permanent; * we do this after the import succesfully completes. */ static bool seal_import(JSContext *cx, JS::HandleObject obj, JS::HandleId id, const char *name) { JS::Rooted descr(cx); if (!JS_GetOwnPropertyDescriptorById(cx, obj, id, &descr) || !descr.object()) { gjs_debug(GJS_DEBUG_IMPORTER, "Failed to get attributes to seal '%s' in importer", name); return false; } descr.setConfigurable(false); if (!JS_DefinePropertyById(cx, descr.object(), id, descr)) { gjs_debug(GJS_DEBUG_IMPORTER, "Failed to redefine attributes to seal '%s' in importer", name); return false; } return true; } /* An import failed. Delete the property pointing to the import * from the parent namespace. In complicated situations this might * not be sufficient to get us fully back to a sane state. If: * * - We import module A * - module A imports module B * - module B imports module A, storing a reference to the current * module A module object * - module A subsequently throws an exception * * Then module B is left imported, but the imported module B has * a reference to the failed module A module object. To handle this * we could could try to track the entire "import operation" and * roll back *all* modifications made to the namespace objects. * It's not clear that the complexity would be worth the small gain * in robustness. (You can still come up with ways of defeating * the attempt to clean up.) */ static void cancel_import(JSContext *context, JS::HandleObject obj, const char *name) { gjs_debug(GJS_DEBUG_IMPORTER, "Cleaning up from failed import of '%s'", name); if (!JS_DeleteProperty(context, obj, name)) { gjs_debug(GJS_DEBUG_IMPORTER, "Failed to delete '%s' in importer", name); } } /* * gjs_import_native_module: * @cx: the #JSContext * @importer: the root importer * @name: Name under which the module was registered with * gjs_register_native_module() * * Imports a builtin native-code module so that it is available to JS code as * `imports[name]`. * * Returns: true on success, false if an exception was thrown. */ bool gjs_import_native_module(JSContext *cx, JS::HandleObject importer, const char *name) { gjs_debug(GJS_DEBUG_IMPORTER, "Importing '%s'", name); JS::RootedObject module(cx); return gjs_load_native_module(cx, name, &module) && define_meta_properties(cx, module, nullptr, name, importer) && JS_DefineProperty(cx, importer, name, module, GJS_MODULE_PROP_FLAGS); } static bool import_module_init(JSContext *context, GFile *file, JS::HandleObject module_obj) { bool ret = false; char *script = NULL; char *full_path = NULL; gsize script_len = 0; GError *error = NULL; JS::RootedValue ignored(context); if (!(g_file_load_contents(file, NULL, &script, &script_len, NULL, &error))) { if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY) && !g_error_matches(error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY) && !g_error_matches(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) gjs_throw_g_error(context, error); else g_error_free(error); goto out; } g_assert(script != NULL); full_path = g_file_get_parse_name (file); if (!gjs_eval_with_scope(context, module_obj, script, script_len, full_path, &ignored)) goto out; ret = true; out: g_free(script); g_free(full_path); return ret; } static JSObject * load_module_init(JSContext *context, JS::HandleObject in_object, const char *full_path) { bool found; /* First we check if js module has already been loaded */ if (gjs_object_has_property(context, in_object, GJS_STRING_MODULE_INIT, &found) && found) { JS::RootedValue module_obj_val(context); if (gjs_object_get_property(context, in_object, GJS_STRING_MODULE_INIT, &module_obj_val)) { return &module_obj_val.toObject(); } } JS::RootedObject module_obj(context, JS_NewPlainObject(context)); GjsAutoUnref file = g_file_new_for_commandline_arg(full_path); if (!import_module_init(context, file, module_obj)) return module_obj; gjs_object_define_property(context, in_object, GJS_STRING_MODULE_INIT, module_obj, GJS_MODULE_PROP_FLAGS & ~JSPROP_PERMANENT); return module_obj; } static void load_module_elements(JSContext *cx, JS::HandleObject in_object, JS::AutoIdVector& prop_ids, const char *init_path) { size_t ix, length; JS::RootedObject module_obj(cx, load_module_init(cx, in_object, init_path)); if (!module_obj) return; JS::Rooted ids(cx, cx); if (!JS_Enumerate(cx, module_obj, &ids)) return; for (ix = 0, length = ids.length(); ix < length; ix++) if (!prop_ids.append(ids[ix])) g_error("Unable to append to vector"); } /* If error, returns false. If not found, returns true but does not touch * the value at *result. If found, returns true and sets *result = true. */ static bool import_symbol_from_init_js(JSContext *cx, JS::HandleObject importer, const char *dirname, const char *name, bool *result) { bool found; GjsAutoChar full_path = g_build_filename(dirname, MODULE_INIT_FILENAME, NULL); JS::RootedObject module_obj(cx, load_module_init(cx, importer, full_path)); if (!module_obj || !JS_AlreadyHasOwnProperty(cx, module_obj, name, &found)) return false; if (!found) return true; JS::RootedValue obj_val(cx); if (!JS_GetProperty(cx, module_obj, name, &obj_val)) return false; if (obj_val.isUndefined()) return true; if (!JS_DefineProperty(cx, importer, name, obj_val, GJS_MODULE_PROP_FLAGS & ~JSPROP_PERMANENT)) return false; *result = true; return true; } static bool import_file_on_module(JSContext *context, JS::HandleObject obj, JS::HandleId id, const char *name, GFile *file) { bool retval = false; char *full_path = NULL; JS::RootedObject module_obj(context, gjs_module_import(context, obj, id, name, file)); if (!module_obj) goto out; full_path = g_file_get_parse_name (file); if (!define_meta_properties(context, module_obj, full_path, name, obj)) goto out; if (!seal_import(context, obj, id, name)) goto out; retval = true; out: if (!retval) cancel_import(context, obj, name); g_free (full_path); return retval; } static bool do_import(JSContext *context, JS::HandleObject obj, Importer *priv, JS::HandleId id, const char *name) { JS::RootedObject search_path(context); guint32 search_path_len; guint32 i; bool exists, is_array; if (!gjs_object_require_property(context, obj, "importer", GJS_STRING_SEARCH_PATH, &search_path)) return false; if (!JS_IsArrayObject(context, search_path, &is_array)) return false; if (!is_array) { gjs_throw(context, "searchPath property on importer is not an array"); return false; } if (!JS_GetArrayLength(context, search_path, &search_path_len)) { gjs_throw(context, "searchPath array has no length"); return false; } GjsAutoChar filename = g_strdup_printf("%s.js", name); std::vector directories; JS::RootedValue elem(context); JS::RootedString str(context); /* First try importing an internal module like byteArray */ if (priv->is_root && gjs_is_registered_native_module(context, obj, name)) { if (!gjs_import_native_module(context, obj, name)) return false; gjs_debug(GJS_DEBUG_IMPORTER, "successfully imported module '%s'", name); return true; } for (i = 0; i < search_path_len; ++i) { elem.setUndefined(); if (!JS_GetElement(context, search_path, i, &elem)) { /* this means there was an exception, while elem.isUndefined() * means no element found */ return false; } if (elem.isUndefined()) continue; if (!elem.isString()) { gjs_throw(context, "importer searchPath contains non-string"); return false; } str = elem.toString(); GjsAutoJSChar dirname = JS_EncodeStringToUTF8(context, str); if (!dirname) return false; /* Ignore empty path elements */ if (dirname[0] == '\0') continue; /* Try importing __init__.js and loading the symbol from it */ bool found = false; if (!import_symbol_from_init_js(context, obj, dirname, name, &found)) return false; if (found) return true; /* Second try importing a directory (a sub-importer) */ GjsAutoChar full_path = g_build_filename(dirname, name, nullptr); GjsAutoUnref gfile = g_file_new_for_commandline_arg(full_path); if (g_file_query_file_type(gfile, (GFileQueryInfoFlags) 0, NULL) == G_FILE_TYPE_DIRECTORY) { gjs_debug(GJS_DEBUG_IMPORTER, "Adding directory '%s' to child importer '%s'", full_path.get(), name); directories.push_back(std::move(full_path)); } /* If we just added to directories, we know we don't need to * check for a file. If we added to directories on an earlier * iteration, we want to ignore any files later in the * path. So, always skip the rest of the loop block if we have * directories. */ if (!directories.empty()) continue; /* Third, if it's not a directory, try importing a file */ full_path = g_build_filename(dirname, filename.get(), nullptr); gfile = g_file_new_for_commandline_arg(full_path); exists = g_file_query_exists(gfile, NULL); if (!exists) { gjs_debug(GJS_DEBUG_IMPORTER, "JS import '%s' not found in %s", name, dirname.get()); continue; } if (import_file_on_module(context, obj, id, name, gfile)) { gjs_debug(GJS_DEBUG_IMPORTER, "successfully imported module '%s'", name); return true; } /* Don't keep searching path if we fail to load the file for * reasons other than it doesn't exist... i.e. broken files * block searching for nonbroken ones */ return false; } if (!directories.empty()) { /* NULL-terminate the char** */ const char **full_paths = g_new0(const char *, directories.size() + 1); for (size_t ix = 0; ix < directories.size(); ix++) full_paths[ix] = directories[ix].get(); bool result = import_directory(context, obj, name, full_paths); g_free(full_paths); if (!result) return false; gjs_debug(GJS_DEBUG_IMPORTER, "successfully imported directory '%s'", name); return true; } /* If no exception occurred, the problem is just that we got to the * end of the path. Be sure an exception is set. */ g_assert(!JS_IsExceptionPending(context)); gjs_throw_custom(context, JSProto_Error, "ImportError", "No JS module '%s' found in search path", name); return false; } /* Note that in a for ... in loop, this will be called first on the object, * then on its prototype. */ static bool importer_enumerate(JSContext *context, JS::HandleObject object, JS::AutoIdVector& properties, bool enumerable_only) { Importer *priv; guint32 search_path_len; guint32 i; bool is_array; priv = priv_from_js(context, object); if (!priv) /* we are enumerating the prototype properties */ return true; JS::RootedObject search_path(context); if (!gjs_object_require_property(context, object, "importer", GJS_STRING_SEARCH_PATH, &search_path)) return false; if (!JS_IsArrayObject(context, search_path, &is_array)) return false; if (!is_array) { gjs_throw(context, "searchPath property on importer is not an array"); return false; } if (!JS_GetArrayLength(context, search_path, &search_path_len)) { gjs_throw(context, "searchPath array has no length"); return false; } JS::RootedValue elem(context); JS::RootedString str(context); for (i = 0; i < search_path_len; ++i) { char *init_path; elem.setUndefined(); if (!JS_GetElement(context, search_path, i, &elem)) { /* this means there was an exception, while elem.isUndefined() * means no element found */ return false; } if (elem.isUndefined()) continue; if (!elem.isString()) { gjs_throw(context, "importer searchPath contains non-string"); return false; } str = elem.toString(); GjsAutoJSChar dirname = JS_EncodeStringToUTF8(context, str); if (!dirname) return false; init_path = g_build_filename(dirname, MODULE_INIT_FILENAME, NULL); load_module_elements(context, object, properties, init_path); g_free(init_path); /* new_for_commandline_arg handles resource:/// paths */ GjsAutoUnref dir = g_file_new_for_commandline_arg(dirname); GjsAutoUnref direnum = g_file_enumerate_children(dir, "standard::name,standard::type", G_FILE_QUERY_INFO_NONE, NULL, NULL); while (true) { GFileInfo *info; GFile *file; if (!g_file_enumerator_iterate(direnum, &info, &file, NULL, NULL)) break; if (info == NULL || file == NULL) break; GjsAutoChar filename = g_file_get_basename(file); /* skip hidden files and directories (.svn, .git, ...) */ if (filename[0] == '.') continue; /* skip module init file */ if (strcmp(filename, MODULE_INIT_FILENAME) == 0) continue; if (g_file_info_get_file_type(info) == G_FILE_TYPE_DIRECTORY) { if (!properties.append(gjs_intern_string_to_id(context, filename))) g_error("Unable to append to vector"); } else if (g_str_has_suffix(filename, "." G_MODULE_SUFFIX) || g_str_has_suffix(filename, ".js")) { GjsAutoChar filename_noext = g_strndup(filename, strlen(filename) - 3); if (!properties.append(gjs_intern_string_to_id(context, filename_noext))) g_error("Unable to append to vector"); } } } return true; } /* The *resolved out parameter, on success, should be false to indicate that id * was not resolved; and true if id was resolved. */ static bool importer_resolve(JSContext *context, JS::HandleObject obj, JS::HandleId id, bool *resolved) { Importer *priv; jsid module_init_name; if (!JSID_IS_STRING(id)) { *resolved = false; return true; } module_init_name = gjs_context_get_const_string(context, GJS_STRING_MODULE_INIT); if (id == module_init_name) { *resolved = false; return true; } /* let Object.prototype resolve these */ JSFlatString *str = JSID_TO_FLAT_STRING(id); if (JS_FlatStringEqualsAscii(str, "valueOf") || JS_FlatStringEqualsAscii(str, "toString") || JS_FlatStringEqualsAscii(str, "__iterator__")) { *resolved = false; return true; } priv = priv_from_js(context, obj); gjs_debug_jsprop(GJS_DEBUG_IMPORTER, "Resolve prop '%s' hook, obj %s, priv %p", gjs_debug_id(id).c_str(), gjs_debug_object(obj).c_str(), priv); if (!priv) { /* we are the prototype, or have the wrong class */ *resolved = false; return true; } JSAutoRequest ar(context); GjsAutoJSChar name; if (!gjs_get_string_id(context, id, &name)) { *resolved = false; return true; } if (!do_import(context, obj, priv, id, name)) return false; *resolved = true; return true; } GJS_NATIVE_CONSTRUCTOR_DEFINE_ABSTRACT(importer) static void importer_finalize(js::FreeOp *fop, JSObject *obj) { Importer *priv; priv = (Importer*) JS_GetPrivate(obj); gjs_debug_lifecycle(GJS_DEBUG_IMPORTER, "finalize, obj %p priv %p", obj, priv); if (!priv) return; /* we are the prototype, not a real instance */ GJS_DEC_COUNTER(importer); g_slice_free(Importer, priv); } /* The bizarre thing about this vtable is that it applies to both * instances of the object, and to the prototype that instances of the * class have. */ static const js::ClassOps gjs_importer_class_ops = { NULL, /* addProperty */ NULL, /* deleteProperty */ NULL, /* getProperty */ NULL, /* setProperty */ NULL, /* enumerate (see below) */ importer_resolve, nullptr, /* mayResolve */ importer_finalize }; static const js::ObjectOps gjs_importer_object_ops = { NULL, /* lookupProperty */ NULL, /* defineProperty */ NULL, /* hasProperty */ NULL, /* getProperty */ NULL, /* setProperty */ NULL, /* getOwnPropertyDescriptor */ NULL, /* deleteProperty */ NULL, /* watch */ NULL, /* unwatch */ NULL, /* getElements */ importer_enumerate }; const js::Class gjs_importer_real_class = { "GjsFileImporter", JSCLASS_HAS_PRIVATE | JSCLASS_FOREGROUND_FINALIZE, &gjs_importer_class_ops, nullptr, nullptr, &gjs_importer_object_ops }; static JSPropertySpec *gjs_importer_proto_props = nullptr; static JSFunctionSpec *gjs_importer_static_funcs = nullptr; JSFunctionSpec gjs_importer_proto_funcs[] = { JS_FS("toString", importer_to_string, 0, 0), JS_FS_END }; GJS_DEFINE_PROTO_FUNCS(importer) static JSObject* importer_new(JSContext *context, bool is_root) { Importer *priv; JS::RootedObject proto(context); if (!gjs_importer_define_proto(context, nullptr, &proto)) g_error("Error creating importer prototype"); JS::RootedObject importer(context, JS_NewObjectWithGivenProto(context, &gjs_importer_class, proto)); if (!importer) g_error("No memory to create importer"); priv = g_slice_new0(Importer); priv->is_root = is_root; GJS_INC_COUNTER(importer); g_assert(priv_from_js(context, importer) == NULL); JS_SetPrivate(importer, priv); gjs_debug_lifecycle(GJS_DEBUG_IMPORTER, "importer constructor, obj %p priv %p", importer.get(), priv); return importer; } static G_CONST_RETURN char * G_CONST_RETURN * gjs_get_search_path(void) { char **search_path; /* not thread safe */ if (!gjs_search_path) { G_CONST_RETURN gchar* G_CONST_RETURN * system_data_dirs; const char *envstr; GPtrArray *path; gsize i; path = g_ptr_array_new(); /* in order of priority */ /* $GJS_PATH */ envstr = g_getenv("GJS_PATH"); if (envstr) { char **dirs, **d; dirs = g_strsplit(envstr, G_SEARCHPATH_SEPARATOR_S, 0); for (d = dirs; *d != NULL; d++) g_ptr_array_add(path, *d); /* we assume the array and strings are allocated separately */ g_free(dirs); } g_ptr_array_add(path, g_strdup("resource:///org/cinnamon/cjs/modules/")); /* $XDG_DATA_DIRS /gjs-1.0 */ system_data_dirs = g_get_system_data_dirs(); for (i = 0; system_data_dirs[i] != NULL; ++i) { char *s; s = g_build_filename(system_data_dirs[i], "gjs-1.0", NULL); g_ptr_array_add(path, s); } /* ${datadir}/share/gjs-1.0 */ #ifdef G_OS_WIN32 extern HMODULE gjs_dll; char *basedir = g_win32_get_package_installation_directory_of_module (gjs_dll); char *gjs_data_dir = g_build_filename (basedir, "share", "gjs-1.0", NULL); g_ptr_array_add(path, g_strdup(gjs_data_dir)); g_free (gjs_data_dir); g_free (basedir); #else g_ptr_array_add(path, g_strdup(GJS_JS_DIR)); #endif g_ptr_array_add(path, NULL); search_path = (char**)g_ptr_array_free(path, false); gjs_search_path = search_path; } else { search_path = gjs_search_path; } return (G_CONST_RETURN char * G_CONST_RETURN *)search_path; } static JSObject* gjs_create_importer(JSContext *context, const char *importer_name, const char * const *initial_search_path, bool add_standard_search_path, bool is_root, JS::HandleObject in_object) { char **paths[2] = {0}; char **search_path; paths[0] = (char**)initial_search_path; if (add_standard_search_path) { /* Stick the "standard" shared search path after the provided one. */ paths[1] = (char**)gjs_get_search_path(); } search_path = gjs_g_strv_concat(paths, 2); JS::RootedObject importer(context, importer_new(context, is_root)); /* API users can replace this property from JS, is the idea */ if (!gjs_define_string_array(context, importer, "searchPath", -1, (const char **)search_path, /* settable (no READONLY) but not deleteable (PERMANENT) */ JSPROP_PERMANENT | JSPROP_RESOLVING)) g_error("no memory to define importer search path prop"); g_strfreev(search_path); if (!define_meta_properties(context, importer, NULL, importer_name, in_object)) g_error("failed to define meta properties on importer"); return importer; } static JSObject * gjs_define_importer(JSContext *context, JS::HandleObject in_object, const char *importer_name, const char * const *initial_search_path, bool add_standard_search_path) { JS::RootedObject importer(context, gjs_create_importer(context, importer_name, initial_search_path, add_standard_search_path, false, in_object)); if (!JS_DefineProperty(context, in_object, importer_name, importer, GJS_MODULE_PROP_FLAGS)) g_error("no memory to define importer property"); gjs_debug(GJS_DEBUG_IMPORTER, "Defined importer '%s' %p in %p", importer_name, importer.get(), in_object.get()); return importer; } JSObject * gjs_create_root_importer(JSContext *cx, const char * const *search_path) { return gjs_create_importer(cx, "imports", search_path, true, true, nullptr); }cjs-4.4.0/cjs/importer.h000066400000000000000000000031731356375132300151030ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_IMPORTER_H__ #define __GJS_IMPORTER_H__ #include #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS JSObject *gjs_create_root_importer(JSContext *cx, const char * const *search_path); bool gjs_import_native_module(JSContext *cx, JS::HandleObject importer, const char *name); G_END_DECLS #endif /* __GJS_IMPORTER_H__ */ cjs-4.4.0/cjs/jsapi-class.h000066400000000000000000000442351356375132300154570ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2017 Philip Chimento * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef GJS_JSAPI_CLASS_H #define GJS_JSAPI_CLASS_H #include "global.h" #include "jsapi-util.h" #include "jsapi-wrapper.h" #include "util/log.h" G_BEGIN_DECLS bool gjs_init_class_dynamic(JSContext *cx, JS::HandleObject in_object, JS::HandleObject parent_proto, const char *ns_name, const char *class_name, JSClass *clasp, JSNative constructor_native, unsigned nargs, JSPropertySpec *ps, JSFunctionSpec *fs, JSPropertySpec *static_ps, JSFunctionSpec *static_fs, JS::MutableHandleObject prototype, JS::MutableHandleObject constructor); bool gjs_typecheck_instance(JSContext *cx, JS::HandleObject obj, const JSClass *static_clasp, bool throw_error); JSObject *gjs_construct_object_dynamic(JSContext *cx, JS::HandleObject proto, const JS::HandleValueArray& args); /* * Helper methods to access private data: * * do_base_typecheck: checks that object has the right JSClass, and possibly * throw a TypeError exception if the check fails * priv_from_js: accesses the object private field; as a debug measure, * it also checks that the object is of a compatible * JSClass, but it doesn't raise an exception (it * wouldn't be of much use, if subsequent code crashes on * NULL) * priv_from_js_with_typecheck: a convenience function to call * do_base_typecheck and priv_from_js */ #define GJS_DEFINE_PRIV_FROM_JS(type, klass) \ GJS_ALWAYS_INLINE G_GNUC_UNUSED \ static inline bool \ do_base_typecheck(JSContext *context, \ JS::HandleObject object, \ bool throw_error) \ { \ return gjs_typecheck_instance(context, object, &klass, throw_error); \ } \ GJS_ALWAYS_INLINE \ static inline type * \ priv_from_js(JSContext *context, \ JS::HandleObject object) \ { \ type *priv; \ JS_BeginRequest(context); \ priv = (type*) JS_GetInstancePrivate(context, object, &klass, NULL); \ JS_EndRequest(context); \ return priv; \ } \ G_GNUC_UNUSED static bool \ priv_from_js_with_typecheck(JSContext *context, \ JS::HandleObject object, \ type **out) \ { \ if (!do_base_typecheck(context, object, false)) \ return false; \ *out = priv_from_js(context, object); \ return true; \ } /* * GJS_GET_PRIV: * @cx: JSContext pointer passed into JSNative function * @argc: Number of arguments passed into JSNative function * @vp: Argument value array passed into JSNative function * @args: Name for JS::CallArgs variable defined by this code snippet * @to: Name for JS::RootedObject variable referring to function's this * @type: Type of private data * @priv: Name for private data variable defined by this code snippet * * A convenience macro for getting the private data from GJS classes using * priv_from_js(). * Throws an error if the 'this' object is not the right type. * Use in any JSNative function. */ #define GJS_GET_PRIV(cx, argc, vp, args, to, type, priv) \ GJS_GET_THIS(cx, argc, vp, args, to); \ if (!do_base_typecheck(cx, to, true)) \ return false; \ type *priv = priv_from_js(cx, to) /* Helper for GJS_DEFINE_PROTO_* macros with no parent */ static inline JSObject * gjs_no_parent_get_proto(JSContext *cx) { return nullptr; } /** * GJS_DEFINE_PROTO: * @tn: The name of the prototype, as a string * @cn: The name of the prototype, separated by _ * @flags: additional JSClass flags, such as JSCLASS_BACKGROUND_FINALIZE * * A convenience macro for prototype implementations. */ #define GJS_DEFINE_PROTO(tn, cn, flags) \ GJS_NATIVE_CONSTRUCTOR_DECLARE(cn); \ _GJS_DEFINE_PROTO_FULL(tn, cn, gjs_##cn##_constructor, G_TYPE_NONE, flags) /** * GJS_DEFINE_PROTO_ABSTRACT: * @tn: The name of the prototype, as a string * @cn: The name of the prototype, separated by _ * * A convenience macro for prototype implementations. * Similar to GJS_DEFINE_PROTO but marks the prototype as abstract, * you won't be able to instantiate it using the new keyword */ #define GJS_DEFINE_PROTO_ABSTRACT(tn, cn, flags) \ _GJS_DEFINE_PROTO_FULL(tn, cn, no_parent, nullptr, G_TYPE_NONE, \ flags) #define GJS_DEFINE_PROTO_WITH_GTYPE(tn, cn, gtype, flags) \ GJS_NATIVE_CONSTRUCTOR_DECLARE(cn); \ _GJS_DEFINE_PROTO_FULL(tn, cn, no_parent, gjs_##cn##_constructor, \ gtype, flags) #define GJS_DEFINE_PROTO_ABSTRACT_WITH_GTYPE(tn, cn, gtype, flags) \ _GJS_DEFINE_PROTO_FULL(tn, cn, no_parent, nullptr, gtype, flags) #define GJS_DEFINE_PROTO_WITH_PARENT(tn, cn, parent_cn, flags) \ GJS_NATIVE_CONSTRUCTOR_DECLARE(cn); \ _GJS_DEFINE_PROTO_FULL(tn, cn, parent_cn, gjs_##cn##_constructor, \ G_TYPE_NONE, flags) #define GJS_DEFINE_PROTO_ABSTRACT_WITH_PARENT(tn, cn, parent_cn, flags) \ _GJS_DEFINE_PROTO_FULL(tn, cn, parent_cn, nullptr, G_TYPE_NONE, flags) #define _GJS_DEFINE_PROTO_FULL(type_name, cname, parent_cname, ctor, gtype, jsclass_flags) \ extern JSPropertySpec gjs_##cname##_proto_props[]; \ extern JSFunctionSpec gjs_##cname##_proto_funcs[]; \ extern JSFunctionSpec gjs_##cname##_static_funcs[]; \ static void gjs_##cname##_finalize(JSFreeOp *fop, JSObject *obj); \ static const struct JSClassOps gjs_##cname##_class_ops = { \ nullptr, /* addProperty */ \ nullptr, /* deleteProperty */ \ nullptr, /* getProperty */ \ nullptr, /* setProperty */ \ nullptr, /* enumerate */ \ nullptr, /* resolve */ \ nullptr, /* mayResolve */ \ gjs_##cname##_finalize \ }; \ static struct JSClass gjs_##cname##_class = { \ type_name, \ JSCLASS_HAS_PRIVATE | jsclass_flags, \ &gjs_##cname##_class_ops \ }; \ _GJS_DEFINE_GET_PROTO(cname) \ _GJS_DEFINE_DEFINE_PROTO(cname, parent_cname, ctor, gtype) #define GJS_DEFINE_PROTO_FUNCS_WITH_PARENT(cname, parent_cname) \ G_GNUC_UNUSED static \ _GJS_DEFINE_GET_PROTO(cname) \ G_GNUC_UNUSED static \ _GJS_DEFINE_DEFINE_PROTO(cname, parent_cname, \ gjs_##cname##_constructor, G_TYPE_NONE) #define GJS_DEFINE_PROTO_FUNCS(cname) \ GJS_DEFINE_PROTO_FUNCS_WITH_PARENT(cname, no_parent) #define _GJS_DEFINE_GET_PROTO(cname) \ JSObject * \ gjs_##cname##_get_proto(JSContext *cx) \ { \ JS::RootedValue v_proto(cx, \ gjs_get_global_slot(cx, GJS_GLOBAL_SLOT_PROTOTYPE_##cname)); \ g_assert(((void) "gjs_" #cname "_define_proto() must be called before " \ "gjs_" #cname "_get_proto()", !v_proto.isUndefined())); \ g_assert(((void) "Someone stored some weird value in a global slot", \ v_proto.isObject())); \ return &v_proto.toObject(); \ } #define _GJS_DEFINE_DEFINE_PROTO(cname, parent_cname, ctor, gtype) \ bool \ gjs_##cname##_define_proto(JSContext *cx, \ JS::HandleObject module, \ JS::MutableHandleObject proto) \ { \ /* If we've been here more than once, we already have the proto */ \ JS::RootedValue v_proto(cx, \ gjs_get_global_slot(cx, GJS_GLOBAL_SLOT_PROTOTYPE_##cname)); \ if (!v_proto.isUndefined()) { \ g_assert(((void) "Someone stored some weird value in a global slot", \ v_proto.isObject())); \ proto.set(&v_proto.toObject()); \ return true; \ } \ \ /* If module is not given, we are defining a global class */ \ JS::RootedObject in_obj(cx, module); \ if (!in_obj) \ in_obj = gjs_get_import_global(cx); \ \ /* Create the class, prototype, and constructor */ \ JS::RootedObject parent_proto(cx, gjs_##parent_cname##_get_proto(cx)); \ proto.set(JS_InitClass(cx, in_obj, parent_proto, &gjs_##cname##_class, \ ctor, 0, gjs_##cname##_proto_props, \ gjs_##cname##_proto_funcs, nullptr, \ gjs_##cname##_static_funcs)); \ if (!proto) \ g_error("Can't init class %s", gjs_##cname##_class.name); \ gjs_set_global_slot(cx, GJS_GLOBAL_SLOT_PROTOTYPE_##cname, \ JS::ObjectValue(*proto)); \ \ /* Look up the constructor */ \ JS::RootedObject ctor_obj(cx); \ JS::RootedId class_name(cx, \ gjs_intern_string_to_id(cx, gjs_##cname##_class.name)); \ if (!gjs_object_require_property(cx, in_obj, #cname " constructor", \ class_name, &ctor_obj)) \ return false; \ \ /* JS_InitClass defines the constructor as a property on the given \ * "global" object. If it's a module and not the real global object, \ * redefine it with different flags so it's enumerable; cairo copies \ * properties from cairoNative, for example */ \ if (module) { \ if (!JS_DefinePropertyById(cx, module, class_name, ctor_obj, \ GJS_MODULE_PROP_FLAGS)) \ return false; \ } \ \ /* Define the GType value as a "$gtype" property on the constructor */ \ if (gtype != G_TYPE_NONE) { \ JS::RootedObject gtype_obj(cx, \ gjs_gtype_create_gtype_wrapper(cx, gtype)); \ if (!JS_DefineProperty(cx, ctor_obj, "$gtype", gtype_obj, \ JSPROP_PERMANENT)) \ return false; \ } \ gjs_debug(GJS_DEBUG_CONTEXT, "Initialized class %s prototype %p", \ gjs_##cname##_class.name, proto.get()); \ return true; \ } /** * GJS_NATIVE_CONSTRUCTOR_DECLARE: * Prototype a constructor. */ #define GJS_NATIVE_CONSTRUCTOR_DECLARE(name) \ static bool \ gjs_##name##_constructor(JSContext *context, \ unsigned argc, \ JS::Value *vp) /** * GJS_NATIVE_CONSTRUCTOR_VARIABLES: * Declare variables necessary for the constructor; should * be at the very top. */ #define GJS_NATIVE_CONSTRUCTOR_VARIABLES(name) \ JS::RootedObject object(context); \ JS::CallArgs argv G_GNUC_UNUSED = JS::CallArgsFromVp(argc, vp); /** * GJS_NATIVE_CONSTRUCTOR_PRELUDE: * Call after the initial variable declaration. */ #define GJS_NATIVE_CONSTRUCTOR_PRELUDE(name) \ { \ if (!argv.isConstructing()) { \ gjs_throw_constructor_error(context); \ return false; \ } \ object = JS_NewObjectForConstructor(context, &gjs_##name##_class, argv); \ if (!object) \ return false; \ } /** * GJS_NATIVE_CONSTRUCTOR_FINISH: * Call this at the end of a constructor when it's completed * successfully. */ #define GJS_NATIVE_CONSTRUCTOR_FINISH(name) \ argv.rval().setObject(*object); /** * GJS_NATIVE_CONSTRUCTOR_DEFINE_ABSTRACT: * Defines a constructor whose only purpose is to throw an error * and fail. To be used with classes that require a constructor (because they have * instances), but whose constructor cannot be used from JS code. */ #define GJS_NATIVE_CONSTRUCTOR_DEFINE_ABSTRACT(name) \ GJS_NATIVE_CONSTRUCTOR_DECLARE(name) \ { \ JS::CallArgs args = JS::CallArgsFromVp(argc, vp); \ gjs_throw_abstract_constructor_error(context, args); \ return false; \ } G_END_DECLS #endif /* GJS_JSAPI_CLASS_H */ cjs-4.4.0/cjs/jsapi-dynamic-class.cpp000066400000000000000000000157401356375132300174330ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * 2012 Giovanni Campagna * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include "jsapi-class.h" #include "jsapi-util.h" #include "jsapi-wrapper.h" #include #include bool gjs_init_class_dynamic(JSContext *context, JS::HandleObject in_object, JS::HandleObject parent_proto, const char *ns_name, const char *class_name, JSClass *clasp, JSNative constructor_native, unsigned nargs, JSPropertySpec *proto_ps, JSFunctionSpec *proto_fs, JSPropertySpec *static_ps, JSFunctionSpec *static_fs, JS::MutableHandleObject prototype, JS::MutableHandleObject constructor) { /* Force these variables on the stack, so the conservative GC will find them */ JSFunction * volatile constructor_fun; char *full_function_name = NULL; bool res = false; /* Without a name, JS_NewObject fails */ g_assert (clasp->name != NULL); /* gjs_init_class_dynamic only makes sense for instantiable classes, use JS_InitClass for static classes like Math */ g_assert (constructor_native != NULL); JS_BeginRequest(context); /* Class initalization consists of three parts: - building a prototype - defining prototype properties and functions - building a constructor and definining it on the right object - defining constructor properties and functions - linking the constructor and the prototype, so that JS_NewObjectForConstructor can find it */ if (parent_proto) { prototype.set(JS_NewObjectWithGivenProto(context, clasp, parent_proto)); } else { /* JS_NewObject will use Object.prototype as the prototype if the * clasp's constructor is not a built-in class. */ prototype.set(JS_NewObject(context, clasp)); } if (!prototype) goto out; /* Bypass resolve hooks when defining the initial properties */ if (clasp->cOps->resolve) { JSPropertySpec *ps_iter; JSFunctionSpec *fs_iter; for (ps_iter = proto_ps; ps_iter && ps_iter->name; ps_iter++) ps_iter->flags |= JSPROP_RESOLVING; for (fs_iter = proto_fs; fs_iter && fs_iter->name; fs_iter++) fs_iter->flags |= JSPROP_RESOLVING; } if (proto_ps && !JS_DefineProperties(context, prototype, proto_ps)) goto out; if (proto_fs && !JS_DefineFunctions(context, prototype, proto_fs)) goto out; full_function_name = g_strdup_printf("%s_%s", ns_name, class_name); constructor_fun = JS_NewFunction(context, constructor_native, nargs, JSFUN_CONSTRUCTOR, full_function_name); if (!constructor_fun) goto out; constructor.set(JS_GetFunctionObject(constructor_fun)); if (static_ps && !JS_DefineProperties(context, constructor, static_ps)) goto out; if (static_fs && !JS_DefineFunctions(context, constructor, static_fs)) goto out; if (!clasp->cOps->resolve) { if (!JS_LinkConstructorAndPrototype(context, constructor, prototype)) goto out; } else { /* Have to fake it with JSPROP_RESOLVING, otherwise it will trigger * the resolve hook */ if (!gjs_object_define_property(context, constructor, GJS_STRING_PROTOTYPE, prototype, JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_RESOLVING)) goto out; if (!gjs_object_define_property(context, prototype, GJS_STRING_CONSTRUCTOR, constructor, JSPROP_RESOLVING)) goto out; } /* The constructor defined by JS_InitClass has no property attributes, but this is a more useful default for gjs */ if (!JS_DefineProperty(context, in_object, class_name, constructor, GJS_MODULE_PROP_FLAGS)) goto out; res = true; constructor_fun = NULL; out: JS_EndRequest(context); g_free(full_function_name); return res; } static const char* format_dynamic_class_name (const char *name) { if (g_str_has_prefix(name, "_private_")) return name + strlen("_private_"); else return name; } bool gjs_typecheck_instance(JSContext *context, JS::HandleObject obj, const JSClass *static_clasp, bool throw_error) { if (!JS_InstanceOf(context, obj, static_clasp, NULL)) { if (throw_error) { const JSClass *obj_class = JS_GetClass(obj); gjs_throw_custom(context, JSProto_TypeError, nullptr, "Object %p is not a subclass of %s, it's a %s", obj.get(), static_clasp->name, format_dynamic_class_name(obj_class->name)); } return false; } return true; } JSObject* gjs_construct_object_dynamic(JSContext *context, JS::HandleObject proto, const JS::HandleValueArray& args) { JSAutoRequest ar(context); JS::RootedObject constructor(context); if (!gjs_object_require_property(context, proto, "prototype", GJS_STRING_CONSTRUCTOR, &constructor)) return NULL; return JS_New(context, constructor, args); } cjs-4.4.0/cjs/jsapi-util-args.h000066400000000000000000000346411356375132300162610ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright © 2016 Endless Mobile, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. * * Authored by: Philip Chimento */ #include #include #include "jsapi-util.h" #include "jsapi-wrapper.h" GJS_ALWAYS_INLINE static inline bool check_nullable(const char*& fchar, const char*& fmt_string) { if (*fchar != '?') return false; fchar++; fmt_string++; g_assert(((void) "Invalid format string, parameter required after '?'", *fchar != '\0')); return true; } /* This preserves the previous behaviour of gjs_parse_args(), but maybe we want * to use JS::ToBoolean instead? */ GJS_ALWAYS_INLINE static inline void assign(JSContext *cx, char c, bool nullable, JS::HandleValue value, bool *ref) { if (c != 'b') throw g_strdup_printf("Wrong type for %c, got bool*", c); if (!value.isBoolean()) throw g_strdup("Not a boolean"); if (nullable) throw g_strdup("Invalid format string combination ?b"); *ref = value.toBoolean(); } /* This preserves the previous behaviour of gjs_parse_args(), but maybe we want * to box primitive types instead of throwing? */ GJS_ALWAYS_INLINE static inline void assign(JSContext *cx, char c, bool nullable, JS::HandleValue value, JS::MutableHandleObject ref) { if (c != 'o') throw g_strdup_printf("Wrong type for %c, got JS::MutableHandleObject", c); if (nullable && value.isNull()) { ref.set(NULL); return; } if (!value.isObject()) throw g_strdup("Not an object"); ref.set(&value.toObject()); } GJS_ALWAYS_INLINE static inline void assign(JSContext *cx, char c, bool nullable, JS::HandleValue value, GjsAutoJSChar *ref) { if (c != 's') throw g_strdup_printf("Wrong type for %c, got GjsAutoJSChar*", c); if (nullable && value.isNull()) { ref->reset(); return; } if (!gjs_string_to_utf8(cx, value, ref)) throw g_strdup("Couldn't convert to string"); } GJS_ALWAYS_INLINE static inline void assign(JSContext *cx, char c, bool nullable, JS::HandleValue value, GjsAutoChar *ref) { if (c != 'F') throw g_strdup_printf("Wrong type for %c, got GjsAutoChar*", c); if (nullable && value.isNull()) { ref->release(); return; } if (!gjs_string_to_filename(cx, value, ref)) throw g_strdup("Couldn't convert to filename"); } GJS_ALWAYS_INLINE static inline void assign(JSContext *cx, char c, bool nullable, JS::HandleValue value, int32_t *ref) { if (c != 'i') throw g_strdup_printf("Wrong type for %c, got int32_t*", c); if (nullable) throw g_strdup("Invalid format string combination ?i"); if (!JS::ToInt32(cx, value, ref)) throw g_strdup("Couldn't convert to integer"); } GJS_ALWAYS_INLINE static inline void assign(JSContext *cx, char c, bool nullable, JS::HandleValue value, uint32_t *ref) { double num; if (c != 'u') throw g_strdup_printf("Wrong type for %c, got uint32_t*", c); if (nullable) throw g_strdup("Invalid format string combination ?u"); if (!value.isNumber() || !JS::ToNumber(cx, value, &num)) throw g_strdup("Couldn't convert to unsigned integer"); if (num > G_MAXUINT32 || num < 0) throw g_strdup_printf("Value %f is out of range", num); *ref = num; } GJS_ALWAYS_INLINE static inline void assign(JSContext *cx, char c, bool nullable, JS::HandleValue value, int64_t *ref) { if (c != 't') throw g_strdup_printf("Wrong type for %c, got int64_t*", c); if (nullable) throw g_strdup("Invalid format string combination ?t"); if (!JS::ToInt64(cx, value, ref)) throw g_strdup("Couldn't convert to 64-bit integer"); } GJS_ALWAYS_INLINE static inline void assign(JSContext *cx, char c, bool nullable, JS::HandleValue value, double *ref) { if (c != 'f') throw g_strdup_printf("Wrong type for %c, got double*", c); if (nullable) throw g_strdup("Invalid format string combination ?f"); if (!JS::ToNumber(cx, value, ref)) throw g_strdup("Couldn't convert to double"); } /* Special case: treat pointer-to-enum as pointer-to-int, but use enable_if to * prevent instantiation for any other types besides pointer-to-enum */ template::value, int>::type = 0> GJS_ALWAYS_INLINE static inline void assign(JSContext *cx, char c, bool nullable, JS::HandleValue value, T *ref) { /* Sadly, we cannot use std::underlying_type here; the underlying type of * an enum is implementation-defined, so it would not be clear what letter * to use in the format string. For the same reason, we can only support * enum types that are the same width as int. * Additionally, it would be nice to be able to check whether the resulting * value was in range for the enum, but that is not possible (yet?) */ static_assert(sizeof(T) == sizeof(int), "Short or wide enum types not supported"); assign(cx, c, nullable, value, (int *)ref); } /* Force JS::RootedObject * to be converted to JS::MutableHandleObject, * see overload in jsapi-util-args.cpp */ template::value, int>::type = 0> static inline void free_if_necessary(T param_ref) {} GJS_ALWAYS_INLINE static inline void free_if_necessary(JS::MutableHandleObject param_ref) { /* This is not exactly right, since before we consumed a JS::ObjectValue * there may have been something different inside the handle. But it has * already been clobbered at this point anyhow */ param_ref.set(NULL); } template static bool parse_call_args_helper(JSContext *cx, const char *function_name, JS::CallArgs& args, bool ignore_trailing_args, const char*& fmt_required, const char*& fmt_optional, unsigned param_ix, const char *param_name, T param_ref) { bool nullable = false; const char *fchar = fmt_required; g_return_val_if_fail (param_name != NULL, false); if (*fchar != '\0') { nullable = check_nullable(fchar, fmt_required); fmt_required++; } else { /* No more args passed in JS, only optional formats left */ if (args.length() <= param_ix) return true; fchar = fmt_optional; g_assert(((void) "Wrong number of parameters passed to gjs_parse_call_args()", *fchar != '\0')); nullable = check_nullable(fchar, fmt_optional); fmt_optional++; } try { assign(cx, *fchar, nullable, args[param_ix], param_ref); } catch (char *message) { /* Our error messages are going to be more useful than whatever was * thrown by the various conversion functions */ JS_ClearPendingException(cx); gjs_throw(cx, "Error invoking %s, at argument %d (%s): %s", function_name, param_ix, param_name, message); g_free(message); return false; } return true; } template static bool parse_call_args_helper(JSContext *cx, const char *function_name, JS::CallArgs& args, bool ignore_trailing_args, const char*& fmt_required, const char*& fmt_optional, unsigned param_ix, const char *param_name, T param_ref, Args ...params) { bool retval; if (!parse_call_args_helper(cx, function_name, args, ignore_trailing_args, fmt_required, fmt_optional, param_ix, param_name, param_ref)) return false; retval = parse_call_args_helper(cx, function_name, args, ignore_trailing_args, fmt_required, fmt_optional, ++param_ix, params...); /* We still own the strings in the error case, free any we converted */ if (!retval) free_if_necessary(param_ref); return retval; } /* Empty-args version of the template */ G_GNUC_UNUSED static bool gjs_parse_call_args(JSContext *cx, const char *function_name, JS::CallArgs& args, const char *format) { bool ignore_trailing_args = false; if (*format == '!') { ignore_trailing_args = true; format++; } g_assert(((void) "Wrong number of parameters passed to gjs_parse_call_args()", *format == '\0')); if (!ignore_trailing_args && args.length() > 0) { JSAutoRequest ar(cx); gjs_throw(cx, "Error invoking %s: Expected 0 arguments, got %d", function_name, args.length()); return false; } return true; } /** * gjs_parse_call_args: * @context: * @function_name: The name of the function being called * @format: Printf-like format specifier containing the expected arguments * @args: #JS::CallArgs from #JSNative function * @params: for each character in @format, a pair of const char * which is the * name of the argument, and a location to store the value. The type of * location argument depends on the format character, as described below. * * This function is inspired by Python's PyArg_ParseTuple for those * familiar with it. It takes a format specifier which gives the * types of the expected arguments, and a list of argument names and * value location pairs. The currently accepted format specifiers are: * * b: A boolean (pass a bool *) * s: A string, converted into UTF-8 (pass a GjsAutoJSChar *) * F: A string, converted into "filename encoding" (i.e. active locale) (pass * a GjsAutoChar *) * i: A number, will be converted to a 32-bit int (pass an int32_t * or a * pointer to an enum type) * u: A number, converted into a 32-bit unsigned int (pass a uint32_t *) * t: A 64-bit number, converted into a 64-bit int (pass an int64_t *) * f: A number, will be converted into a double (pass a double *) * o: A JavaScript object (pass a JS::MutableHandleObject) * * If the first character in the format string is a '!', then JS is allowed * to pass extra arguments that are ignored, to the function. * * The '|' character introduces optional arguments. All format specifiers * after a '|' when not specified, do not cause any changes in the C * value location. * * A prefix character '?' in front of 's', 'F', or 'o' means that the next value * may be null. For 's' or 'F' a null pointer is returned, for 'o' the handle is * set to null. */ template static bool gjs_parse_call_args(JSContext *cx, const char *function_name, JS::CallArgs& args, const char *format, Args ...params) { const char *fmt_iter, *fmt_required, *fmt_optional; unsigned n_required = 0, n_total = 0; bool optional_args = false, ignore_trailing_args = false, retval; char **parts; if (*format == '!') { ignore_trailing_args = true; format++; } for (fmt_iter = format; *fmt_iter; fmt_iter++) { switch (*fmt_iter) { case '|': n_required = n_total; optional_args = true; continue; case '?': continue; default: n_total++; } } if (!optional_args) n_required = n_total; g_assert(((void) "Wrong number of parameters passed to gjs_parse_call_args()", sizeof...(Args) / 2 == n_total)); JSAutoRequest ar(cx); /* COMPAT: In future, use args.requireAtLeast() * https://bugzilla.mozilla.org/show_bug.cgi?id=1334338 */ if (args.length() < n_required || (args.length() > n_total && !ignore_trailing_args)) { if (n_required == n_total) { gjs_throw(cx, "Error invoking %s: Expected %d arguments, got %d", function_name, n_required, args.length()); } else { gjs_throw(cx, "Error invoking %s: Expected minimum %d arguments (and %d optional), got %d", function_name, n_required, n_total - n_required, args.length()); } return false; } parts = g_strsplit(format, "|", 2); fmt_required = parts[0]; fmt_optional = parts[1]; /* may be NULL */ retval = parse_call_args_helper(cx, function_name, args, ignore_trailing_args, fmt_required, fmt_optional, 0, params...); g_strfreev(parts); return retval; } cjs-4.4.0/cjs/jsapi-util-error.cpp000066400000000000000000000201251356375132300170010ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "jsapi-util.h" #include "jsapi-wrapper.h" #include "gi/gerror.h" #include "util/misc.h" #include #include /* * See: * https://bugzilla.mozilla.org/show_bug.cgi?id=166436 * https://bugzilla.mozilla.org/show_bug.cgi?id=215173 * * Very surprisingly, jsapi.h lacks any way to "throw new Error()" * * So here is an awful hack inspired by * http://egachine.berlios.de/embedding-sm-best-practice/embedding-sm-best-practice.html#error-handling */ static void G_GNUC_PRINTF(4, 0) gjs_throw_valist(JSContext *context, JSProtoKey error_kind, const char *error_name, const char *format, va_list args) { char *s; bool result; s = g_strdup_vprintf(format, args); JSAutoCompartment compartment(context, gjs_get_import_global(context)); JS_BeginRequest(context); if (JS_IsExceptionPending(context)) { /* Often it's unclear whether a given jsapi.h function * will throw an exception, so we will throw ourselves * "just in case"; in those cases, we don't want to * overwrite an exception that already exists. * (Do log in case our second exception adds more info, * but don't log as topic ERROR because if the exception is * caught we don't want an ERROR in the logs.) */ gjs_debug(GJS_DEBUG_CONTEXT, "Ignoring second exception: '%s'", s); g_free(s); JS_EndRequest(context); return; } JS::RootedObject constructor(context); JS::RootedObject global(context, JS::CurrentGlobalOrNull(context)); JS::RootedValue v_constructor(context), exc_val(context); JS::RootedObject new_exc(context); JS::AutoValueArray<1> error_args(context); result = false; if (!gjs_string_from_utf8(context, s, error_args[0])) { JS_ReportErrorUTF8(context, "Failed to copy exception string"); goto out; } if (!JS_GetClassObject(context, error_kind, &constructor)) goto out; /* throw new Error(message) */ new_exc = JS_New(context, constructor, error_args); if (!new_exc) goto out; if (error_name != NULL) { JS::RootedValue name_value(context); if (!gjs_string_from_utf8(context, error_name, &name_value) || !gjs_object_set_property(context, new_exc, GJS_STRING_NAME, name_value)) goto out; } exc_val.setObject(*new_exc); JS_SetPendingException(context, exc_val); result = true; out: if (!result) { /* try just reporting it to error handler? should not * happen though pretty much */ JS_ReportErrorUTF8(context, "Failed to throw exception '%s'", s); } g_free(s); JS_EndRequest(context); } /* Throws an exception, like "throw new Error(message)" * * If an exception is already set in the context, this will * NOT overwrite it. That's an important semantic since * we want the "root cause" exception. To overwrite, * use JS_ClearPendingException() first. */ void gjs_throw(JSContext *context, const char *format, ...) { va_list args; va_start(args, format); gjs_throw_valist(context, JSProto_Error, nullptr, format, args); va_end(args); } /* * Like gjs_throw, but allows to customize the error * class and 'name' property. Mainly used for throwing TypeError instead of * error. */ void gjs_throw_custom(JSContext *cx, JSProtoKey kind, const char *error_name, const char *format, ...) { va_list args; g_return_if_fail(kind == JSProto_Error || kind == JSProto_InternalError || kind == JSProto_EvalError || kind == JSProto_RangeError || kind == JSProto_ReferenceError || kind == JSProto_SyntaxError || kind == JSProto_TypeError || kind == JSProto_URIError || kind == JSProto_StopIteration); va_start(args, format); gjs_throw_valist(cx, kind, error_name, format, args); va_end(args); } /** * gjs_throw_literal: * * Similar to gjs_throw(), but does not treat its argument as * a format string. */ void gjs_throw_literal(JSContext *context, const char *string) { gjs_throw(context, "%s", string); } /** * gjs_throw_g_error: * * Convert a GError into a JavaScript Exception, and * frees the GError. Differently from gjs_throw(), it * will overwrite an existing exception, as it is used * to report errors from C functions. */ void gjs_throw_g_error (JSContext *context, GError *error) { if (error == NULL) return; JS_BeginRequest(context); JS::RootedValue err(context, JS::ObjectOrNullValue(gjs_error_from_gerror(context, error, true))); g_error_free (error); if (!err.isNull()) JS_SetPendingException(context, err); JS_EndRequest(context); } /** * gjs_format_stack_trace: * @cx: the #JSContext * @saved_frame: a SavedFrame #JSObject * * Formats a stack trace as a string in filename encoding, suitable for * printing to stderr. Ignores any errors. * * Returns: unique string in filename encoding, or nullptr if no stack trace */ GjsAutoChar gjs_format_stack_trace(JSContext *cx, JS::HandleObject saved_frame) { JS::AutoSaveExceptionState saved_exc(cx); JS::RootedString stack_trace(cx); GjsAutoJSChar stack_utf8; if (JS::BuildStackString(cx, saved_frame, &stack_trace, 2)) stack_utf8 = JS_EncodeStringToUTF8(cx, stack_trace); saved_exc.restore(); if (!stack_utf8) return nullptr; return g_filename_from_utf8(stack_utf8, -1, nullptr, nullptr, nullptr); } void gjs_warning_reporter(JSContext *context, JSErrorReport *report) { const char *warning; GLogLevelFlags level; g_assert(report); if (gjs_environment_variable_is_set("GJS_ABORT_ON_OOM") && report->flags == JSREPORT_ERROR && report->errorNumber == 137) { /* 137, JSMSG_OUT_OF_MEMORY */ g_error("GJS ran out of memory at %s: %i.", report->filename, report->lineno); } if ((report->flags & JSREPORT_WARNING) != 0) { warning = "WARNING"; level = G_LOG_LEVEL_MESSAGE; /* suppress bogus warnings. See mozilla/js/src/js.msg */ if (report->errorNumber == 162) { /* 162, JSMSG_UNDEFINED_PROP: warns every time a lazy property * is resolved, since the property starts out * undefined. When this is a real bug it should usually * fail somewhere else anyhow. */ return; } } else { warning = "REPORTED"; level = G_LOG_LEVEL_WARNING; } g_log(G_LOG_DOMAIN, level, "JS %s: [%s %d]: %s", warning, report->filename, report->lineno, report->message().c_str()); } cjs-4.4.0/cjs/jsapi-util-root.h000066400000000000000000000263031356375132300163040ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2017 Endless Mobile, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef GJS_JSAPI_UTIL_ROOT_H #define GJS_JSAPI_UTIL_ROOT_H #include #include #include "cjs/context.h" #include "cjs/jsapi-wrapper.h" #include "util/log.h" /* jsapi-util-root.h - Utilities for dealing with the lifetime and ownership of * JS Objects and other things that can be collected by the garbage collector * (collectively called "GC things.") * * GjsMaybeOwned is a multi-purpose wrapper for a GC thing of type T. You can * wrap a thing in one of three ways: * * - trace the thing (tie it to the lifetime of another GC thing), * - root the thing (keep it alive as long as the wrapper is in existence), * - maintain a weak pointer to the thing (not keep it alive at all and have it * possibly be finalized out from under you). * * To trace or maintain a weak pointer, simply assign a thing of type T to the * GjsMaybeOwned wrapper. For tracing, you must call the trace() method when * your other GC thing is traced. * * Rooting requires a JSContext so can't just assign a thing of type T. Instead * you need to call the root() method to set up rooting. * * If the thing is rooted, it will be unrooted either when the GjsMaybeOwned is * destroyed, or when the JSContext is destroyed. In the latter case, you can * get an optional notification by passing a callback to root(). * * To switch between one of the three modes, you must first call reset(). This * drops all references to any GC thing and leaves the GjsMaybeOwned in the * same state as if it had just been constructed. */ /* This struct contains operations that must be implemented differently * depending on the type of the GC thing. Add more types as necessary. If an * implementation is never used, it's OK to leave it out. The compiler will * complain if it's used somewhere but not instantiated here. */ template struct GjsHeapOperation { static bool update_after_gc(JS::Heap *location); static void expose_to_js(JS::Heap& thing); }; template<> struct GjsHeapOperation { static bool update_after_gc(JS::Heap *location) { JS_UpdateWeakPointerAfterGC(location); return (location->unbarrieredGet() == nullptr); } static void expose_to_js(JS::Heap& thing) { JSObject *obj = thing.unbarrieredGet(); /* If the object has been swept already, then the zone is nullptr */ if (!obj || !js::gc::detail::GetGCThingZone(uintptr_t(obj))) return; /* COMPAT: Use JS::CurrentThreadIsHeapCollecting() in mozjs59 */ JS::GCCellPtr ptr(obj, JS::TraceKind::Object); JS::shadow::Runtime *rt = js::gc::detail::GetCellRuntime(ptr.asCell()); if (!rt->isHeapCollecting()) JS::ExposeObjectToActiveJS(obj); } }; template<> struct GjsHeapOperation {}; /* GjsMaybeOwned is intended only for use in heap allocation. Do not allocate it * on the stack, and do not allocate any instances of structures that have it as * a member on the stack either. Unfortunately we cannot enforce this at compile * time with a private constructor; that would prevent the intended usage as a * member of a heap-allocated struct. */ template class GjsMaybeOwned { public: typedef void (*DestroyNotify)(JS::Handle thing, void *data); private: bool m_rooted; /* wrapper is in rooted mode */ bool m_has_weakref; /* we have a weak reference to the GjsContext */ JSContext *m_cx; JS::Heap m_heap; /* should be untouched if in rooted mode */ JS::PersistentRooted *m_root; /* should be null if not in rooted mode */ DestroyNotify m_notify; void *m_data; /* No-op unless GJS_VERBOSE_ENABLE_LIFECYCLE is defined to 1. */ inline void debug(const char *what) { gjs_debug_lifecycle(GJS_DEBUG_KEEP_ALIVE, "GjsMaybeOwned %p %s", this, what); } static void on_context_destroy(void *data, GObject *ex_context) { auto self = static_cast *>(data); self->invalidate(); } void teardown_rooting(void) { debug("teardown_rooting()"); g_assert(m_rooted); delete m_root; m_root = nullptr; m_rooted = false; if (!m_has_weakref) return; auto gjs_cx = static_cast(JS_GetContextPrivate(m_cx)); g_object_weak_unref(G_OBJECT(gjs_cx), on_context_destroy, this); m_has_weakref = false; } /* Called for a rooted wrapper when the JSContext is about to be destroyed. * This calls the destroy-notify callback if one was passed to root(), and * then removes all rooting from the object. */ void invalidate(void) { debug("invalidate()"); g_assert(m_rooted); /* The weak ref is already gone because the context is dead, so no need * to remove it. */ m_has_weakref = false; /* The object is still live entering this callback. The callback * must reset() this wrapper. */ if (m_notify) m_notify(handle(), m_data); else reset(); } public: GjsMaybeOwned(void) : m_rooted(false), m_has_weakref(false), m_cx(nullptr), m_root(nullptr), m_notify(nullptr), m_data(nullptr) { debug("created"); } ~GjsMaybeOwned(void) { debug("destroyed"); if (m_rooted) teardown_rooting(); } /* To access the GC thing, call get(). In many cases you can just use the * GjsMaybeOwned wrapper in place of the GC thing itself due to the implicit * cast operator. But if you want to call methods on the GC thing, for * example if it's a JS::Value, you have to use get(). */ const T get(void) const { return m_rooted ? m_root->get() : m_heap.get(); } operator const T(void) const { return get(); } bool operator==(const T& other) const { if (m_rooted) return m_root->get() == other; return m_heap == other; } inline bool operator!=(const T& other) const { return !(*this == other); } /* We can access the pointer without a read barrier if the only thing we * are doing with it is comparing it to nullptr. */ bool operator==(std::nullptr_t) const { if (m_rooted) return m_root->get() == nullptr; return m_heap.unbarrieredGet() == nullptr; } inline bool operator!=(std::nullptr_t) const { return !(*this == nullptr); } /* You can get a Handle if the thing is rooted, so that you can use this * wrapper with stack rooting. However, you must not do this if the * JSContext can be destroyed while the Handle is live. */ JS::Handle handle(void) { g_assert(m_rooted); return *m_root; } /* Roots the GC thing. You must not use this if you're already using the * wrapper to store a non-rooted GC thing. */ void root(JSContext *cx, const T& thing, DestroyNotify notify = nullptr, void *data = nullptr) { debug("root()"); g_assert(!m_rooted); g_assert(m_heap.get() == JS::GCPolicy::initial()); m_rooted = true; m_cx = cx; m_notify = notify; m_data = data; m_root = new JS::PersistentRooted(m_cx, thing); if (notify) { auto gjs_cx = static_cast(JS_GetContextPrivate(m_cx)); g_assert(GJS_IS_CONTEXT(gjs_cx)); g_object_weak_ref(G_OBJECT(gjs_cx), on_context_destroy, this); m_has_weakref = true; } } /* You can only assign directly to the GjsMaybeOwned wrapper in the * non-rooted case. */ void operator=(const T& thing) { g_assert(!m_rooted); m_heap = thing; } /* Marks an object as reachable for one GC with ExposeObjectToActiveJS(). * Use to avoid stopping tracing an object during GC. This makes no sense * in the rooted case. */ void prevent_collection(void) { debug("prevent_collection()"); g_assert(!m_rooted); GjsHeapOperation::expose_to_js(m_heap); } void reset(void) { debug("reset()"); if (!m_rooted) { m_heap = JS::GCPolicy::initial(); return; } teardown_rooting(); m_cx = nullptr; m_notify = nullptr; m_data = nullptr; } void switch_to_rooted(JSContext *cx, DestroyNotify notify = nullptr, void *data = nullptr) { debug("switch to rooted"); g_assert(!m_rooted); /* Prevent the thing from being garbage collected while it is in neither * m_heap nor m_root */ JSAutoRequest ar(cx); JS::Rooted thing(cx, m_heap); reset(); root(cx, thing, notify, data); g_assert(m_rooted); } void switch_to_unrooted(void) { debug("switch to unrooted"); g_assert(m_rooted); /* Prevent the thing from being garbage collected while it is in neither * m_heap nor m_root */ JSAutoRequest ar(m_cx); JS::Rooted thing(m_cx, *m_root); reset(); m_heap = thing; g_assert(!m_rooted); } /* Tracing makes no sense in the rooted case, because JS::PersistentRooted * already takes care of that. */ void trace(JSTracer *tracer, const char *name) { debug("trace()"); g_assert(!m_rooted); JS::TraceEdge(tracer, &m_heap, name); } /* If not tracing, then you must call this method during GC in order to * update the object's location if it was moved, or null it out if it was * finalized. If the object was finalized, returns true. */ bool update_after_gc(void) { debug("update_after_gc()"); g_assert(!m_rooted); return GjsHeapOperation::update_after_gc(&m_heap); } bool rooted(void) { return m_rooted; } }; #endif /* GJS_JSAPI_UTIL_ROOT_H */ cjs-4.4.0/cjs/jsapi-util-string.cpp000066400000000000000000000353711356375132300171670ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include #include #include "jsapi-util.h" #include "jsapi-wrapper.h" /** * gjs_string_to_utf8: * @cx: JSContext * @value: a JS::Value containing a string * @utf8_string_p: return location for a unique JS chars pointer * * Converts the JSString in @value to UTF-8 and puts it in @utf8_string_p. * * This function is a convenience wrapper around JS_EncodeStringToUTF8() that * typechecks the JS::Value and throws an exception if it's the wrong type. * Don't use this function if you already have a JS::RootedString, or if you * know the value already holds a string; use JS_EncodeStringToUTF8() instead. */ bool gjs_string_to_utf8(JSContext *cx, const JS::Value value, GjsAutoJSChar *utf8_string_p) { JSAutoRequest ar(cx); if (!value.isString()) { gjs_throw(cx, "Value is not a string, cannot convert to UTF-8"); return false; } JS::RootedString str(cx, value.toString()); utf8_string_p->reset(JS_EncodeStringToUTF8(cx, str)); return !!*utf8_string_p; } bool gjs_string_from_utf8(JSContext *context, const char *utf8_string, JS::MutableHandleValue value_p) { JS_BeginRequest(context); JS::ConstUTF8CharsZ chars(utf8_string, strlen(utf8_string)); JS::RootedString str(context, JS_NewStringCopyUTF8Z(context, chars)); if (str) value_p.setString(str); JS_EndRequest(context); return str != nullptr; } bool gjs_string_from_utf8_n(JSContext *cx, const char *utf8_chars, size_t len, JS::MutableHandleValue out) { JSAutoRequest ar(cx); JS::UTF8Chars chars(utf8_chars, len); JS::RootedString str(cx, JS_NewStringCopyUTF8N(cx, chars)); if (str) out.setString(str); return !!str; } bool gjs_string_to_filename(JSContext *context, const JS::Value filename_val, GjsAutoChar *filename_string) { GError *error; GjsAutoJSChar tmp; /* gjs_string_to_filename verifies that filename_val is a string */ if (!gjs_string_to_utf8(context, filename_val, &tmp)) { /* exception already set */ return false; } error = NULL; *filename_string = g_filename_from_utf8(tmp, -1, NULL, NULL, &error); if (!*filename_string) { gjs_throw_g_error(context, error); return false; } return true; } bool gjs_string_from_filename(JSContext *context, const char *filename_string, ssize_t n_bytes, JS::MutableHandleValue value_p) { gsize written; GError *error; error = NULL; GjsAutoChar utf8_string = g_filename_to_utf8(filename_string, n_bytes, nullptr, &written, &error); if (error) { gjs_throw(context, "Could not convert UTF-8 string '%s' to a filename: '%s'", filename_string, error->message); g_error_free(error); return false; } return gjs_string_from_utf8_n(context, utf8_string, written, value_p); } /* Converts a JSString's array of Latin-1 chars to an array of a wider integer * type, by what the compiler believes is the most efficient method possible */ template static bool from_latin1(JSContext *cx, JSString *str, T **data_p, size_t *len_p) { /* No garbage collection should be triggered while we are using the string's * chars. Crash if that happens. */ JS::AutoCheckCannotGC nogc; const JS::Latin1Char *js_data = JS_GetLatin1StringCharsAndLength(cx, nogc, str, len_p); if (js_data == NULL) return false; /* Unicode codepoints 0x00-0xFF are the same as Latin-1 * codepoints, so we can preserve the string length and simply * copy the codepoints to an array of different-sized ints */ *data_p = g_new(T, *len_p); /* This will probably use a loop, unfortunately */ std::copy(js_data, js_data + *len_p, *data_p); return true; } /** * gjs_string_get_char16_data: * @context: js context * @str: a rooted JSString * @data_p: address to return allocated data buffer * @len_p: address to return length of data (number of 16-bit characters) * * Get the binary data (as a sequence of 16-bit characters) in @str. * * Returns: false if exception thrown **/ bool gjs_string_get_char16_data(JSContext *context, JS::HandleString str, char16_t **data_p, size_t *len_p) { JSAutoRequest ar(context); if (JS_StringHasLatin1Chars(str)) return from_latin1(context, str, data_p, len_p); /* From this point on, crash if a GC is triggered while we are using * the string's chars */ JS::AutoCheckCannotGC nogc; const char16_t *js_data = JS_GetTwoByteStringCharsAndLength(context, nogc, str, len_p); if (js_data == NULL) return false; *data_p = (char16_t *) g_memdup(js_data, sizeof(*js_data) * (*len_p)); return true; } /** * gjs_string_to_ucs4: * @cx: a #JSContext * @str: rooted JSString * @ucs4_string_p: return location for a #gunichar array * @len_p: return location for @ucs4_string_p length * * Returns: true on success, false otherwise in which case a JS error is thrown */ bool gjs_string_to_ucs4(JSContext *cx, JS::HandleString str, gunichar **ucs4_string_p, size_t *len_p) { if (ucs4_string_p == NULL) return true; JSAutoRequest ar(cx); size_t len; GError *error = NULL; if (JS_StringHasLatin1Chars(str)) return from_latin1(cx, str, ucs4_string_p, len_p); /* From this point on, crash if a GC is triggered while we are using * the string's chars */ JS::AutoCheckCannotGC nogc; const char16_t *utf16 = JS_GetTwoByteStringCharsAndLength(cx, nogc, str, &len); if (utf16 == NULL) { gjs_throw(cx, "Failed to get UTF-16 string data"); return false; } if (ucs4_string_p != NULL) { long length; *ucs4_string_p = g_utf16_to_ucs4(reinterpret_cast(utf16), len, NULL, &length, &error); if (*ucs4_string_p == NULL) { gjs_throw(cx, "Failed to convert UTF-16 string to UCS-4: %s", error->message); g_clear_error(&error); return false; } if (len_p != NULL) *len_p = (size_t) length; } return true; } /** * gjs_string_from_ucs4: * @cx: a #JSContext * @ucs4_string: string of #gunichar * @n_chars: number of characters in @ucs4_string or -1 for zero-terminated * @value_p: JS::Value that will be filled with a string * * Returns: true on success, false otherwise in which case a JS error is thrown */ bool gjs_string_from_ucs4(JSContext *cx, const gunichar *ucs4_string, ssize_t n_chars, JS::MutableHandleValue value_p) { long u16_string_length; GError *error = NULL; char16_t *u16_string = reinterpret_cast(g_ucs4_to_utf16(ucs4_string, n_chars, NULL, &u16_string_length, &error)); if (!u16_string) { gjs_throw(cx, "Failed to convert UCS-4 string to UTF-16: %s", error->message); g_error_free(error); return false; } JSAutoRequest ar(cx); /* Avoid a copy - assumes that g_malloc == js_malloc == malloc */ JS::RootedString str(cx, JS_NewUCString(cx, u16_string, u16_string_length)); if (!str) { gjs_throw(cx, "Failed to convert UCS-4 string to UTF-16"); return false; } value_p.setString(str); return true; } /** * gjs_get_string_id: * @context: a #JSContext * @id: a jsid that is an object hash key (could be an int or string) * @name_p place to store ASCII string version of key * * If the id is not a string ID, return false and set *name_p to %NULL. * Otherwise, return true and fill in *name_p with ASCII name of id. * * Returns: true if *name_p is non-%NULL **/ bool gjs_get_string_id (JSContext *context, jsid id, GjsAutoJSChar *name_p) { JS::RootedValue id_val(context); if (!JS_IdToValue(context, id, &id_val)) return false; if (id_val.isString()) { JS::RootedString str(context, id_val.toString()); name_p->reset(JS_EncodeStringToUTF8(context, str)); return !!*name_p; } else { return false; } } /** * gjs_unichar_from_string: * @string: A string * @result: (out): A unicode character * * If successful, @result is assigned the Unicode codepoint * corresponding to the first full character in @string. This * function handles characters outside the BMP. * * If @string is empty, @result will be 0. An exception will * be thrown if @string can not be represented as UTF-8. */ bool gjs_unichar_from_string (JSContext *context, JS::Value value, gunichar *result) { GjsAutoJSChar utf8_str; if (gjs_string_to_utf8(context, value, &utf8_str)) { *result = g_utf8_get_char(utf8_str); return true; } return false; } jsid gjs_intern_string_to_id(JSContext *cx, const char *string) { JSAutoRequest ar(cx); JS::RootedString str(cx, JS_AtomizeAndPinString(cx, string)); return INTERNED_STRING_TO_JSID(cx, str); } static std::string gjs_debug_flat_string(JSFlatString *fstr) { JSLinearString *str = js::FlatStringToLinearString(fstr); size_t len = js::GetLinearStringLength(str); JS::AutoCheckCannotGC nogc; if (js::LinearStringHasLatin1Chars(str)) { const JS::Latin1Char *chars = js::GetLatin1LinearStringChars(nogc, str); return std::string(reinterpret_cast(chars), len); } std::ostringstream out; const char16_t *chars = js::GetTwoByteLinearStringChars(nogc, str); for (size_t ix = 0; ix < len; ix++) { char16_t c = chars[ix]; if (c == '\n') out << "\\n"; else if (c == '\t') out << "\\t"; else if (c >= 32 && c < 127) out << c; else if (c <= 255) out << "\\x" << std::setfill('0') << std::setw(2) << unsigned(c); else out << "\\x" << std::setfill('0') << std::setw(4) << unsigned(c); } return out.str(); } std::string gjs_debug_string(JSString *str) { if (!JS_StringIsFlat(str)) { std::ostringstream out("'; return out.str(); } return gjs_debug_flat_string(JS_ASSERT_STRING_IS_FLAT(str)); } std::string gjs_debug_symbol(JS::Symbol * const sym) { /* This is OK because JS::GetSymbolCode() and JS::GetSymbolDescription() * can't cause a garbage collection */ JS::HandleSymbol handle = JS::HandleSymbol::fromMarkedLocation(&sym); JS::SymbolCode code = JS::GetSymbolCode(handle); JSString *descr = JS::GetSymbolDescription(handle); if (size_t(code) < JS::WellKnownSymbolLimit) return gjs_debug_string(descr); std::ostringstream out; if (code == JS::SymbolCode::InSymbolRegistry) { out << "Symbol.for("; if (descr) out << gjs_debug_string(descr); else out << "undefined"; out << ")"; return out.str(); } if (code == JS::SymbolCode::UniqueSymbol) { if (descr) out << "Symbol(" << gjs_debug_string(descr) << ")"; else out << ""; return out.str(); } out << ""; return out.str(); } std::string gjs_debug_object(JSObject * const obj) { std::ostringstream out; const JSClass* clasp = JS_GetClass(obj); out << "name << " at " << obj << '>'; return out.str(); } std::string gjs_debug_value(JS::Value v) { std::ostringstream out; if (v.isNull()) return "null"; if (v.isUndefined()) return "undefined"; if (v.isInt32()) { out << v.toInt32(); return out.str(); } if (v.isDouble()) { out << v.toDouble(); return out.str(); } if (v.isString()) { out << gjs_debug_string(v.toString()); return out.str(); } if (v.isSymbol()) { out << gjs_debug_symbol(v.toSymbol()); return out.str(); } if (v.isObject() && js::IsFunctionObject(&v.toObject())) { JSFunction* fun = JS_GetObjectFunction(&v.toObject()); JSString *display_name = JS_GetFunctionDisplayId(fun); if (display_name) out << "'; return out.str(); } if (v.isObject()) { out << gjs_debug_object(&v.toObject()); return out.str(); } if (v.isBoolean()) return (v.toBoolean() ? "true" : "false"); if (v.isMagic()) return ""; return "unexpected value"; } std::string gjs_debug_id(jsid id) { if (JSID_IS_STRING(id)) return gjs_debug_flat_string(JSID_TO_FLAT_STRING(id)); return gjs_debug_value(js::IdToValue(id)); } cjs-4.4.0/cjs/jsapi-util.cpp000066400000000000000000000641101356375132300156540ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * Copyright (c) 2009 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include "jsapi-wrapper.h" #include #include #include #include #include #include "jsapi-class.h" #include "jsapi-util.h" #include "context-private.h" #include #include #include GQuark gjs_util_error_quark (void) { return g_quark_from_static_string ("gjs-util-error-quark"); } bool gjs_object_get_property(JSContext *cx, JS::HandleObject obj, GjsConstString property_name, JS::MutableHandleValue value_p) { return JS_GetPropertyById(cx, obj, gjs_context_get_const_string(cx, property_name), value_p); } bool gjs_object_set_property(JSContext *cx, JS::HandleObject obj, GjsConstString property_name, JS::HandleValue value) { return JS_SetPropertyById(cx, obj, gjs_context_get_const_string(cx, property_name), value); } bool gjs_object_has_property(JSContext *cx, JS::HandleObject obj, GjsConstString property_name, bool *found) { return JS_HasPropertyById(cx, obj, gjs_context_get_const_string(cx, property_name), found); } bool gjs_object_define_property(JSContext *cx, JS::HandleObject obj, GjsConstString property_name, JS::HandleValue value, unsigned flags) { return JS_DefinePropertyById(cx, obj, gjs_context_get_const_string(cx, property_name), value, flags); } bool gjs_object_define_property(JSContext *cx, JS::HandleObject obj, GjsConstString property_name, JS::HandleObject value, unsigned flags) { return JS_DefinePropertyById(cx, obj, gjs_context_get_const_string(cx, property_name), value, flags); } bool gjs_object_define_property(JSContext *cx, JS::HandleObject obj, GjsConstString property_name, JS::HandleString value, unsigned flags) { return JS_DefinePropertyById(cx, obj, gjs_context_get_const_string(cx, property_name), value, flags); } bool gjs_object_define_property(JSContext *cx, JS::HandleObject obj, GjsConstString property_name, uint32_t value, unsigned flags) { return JS_DefinePropertyById(cx, obj, gjs_context_get_const_string(cx, property_name), value, flags); } static void throw_property_lookup_error(JSContext *cx, JS::HandleObject obj, const char *description, JS::HandleId property_name, const char *reason) { /* remember gjs_throw() is a no-op if JS_GetProperty() * already set an exception */ if (description) gjs_throw(cx, "No property '%s' in %s (or %s)", gjs_debug_id(property_name).c_str(), description, reason); else gjs_throw(cx, "No property '%s' in object %p (or %s)", gjs_debug_id(property_name).c_str(), obj.get(), reason); } /* Returns whether the object had the property; if the object did * not have the property, always sets an exception. Treats * "the property's value is undefined" the same as "no such property,". * Guarantees that *value_p is set to something, if only JS::UndefinedValue(), * even if an exception is set and false is returned. * * SpiderMonkey will emit a warning if the property is not present, so don't * use this if you expect the property not to be present some of the time. * * Requires request. */ bool gjs_object_require_property(JSContext *context, JS::HandleObject obj, const char *obj_description, JS::HandleId property_name, JS::MutableHandleValue value) { value.setUndefined(); if (G_UNLIKELY(!JS_GetPropertyById(context, obj, property_name, value))) return false; if (G_LIKELY(!value.isUndefined())) return true; throw_property_lookup_error(context, obj, obj_description, property_name, "its value was undefined"); return false; } bool gjs_object_require_property(JSContext *cx, JS::HandleObject obj, const char *description, JS::HandleId property_name, bool *value) { JS::RootedValue prop_value(cx); if (JS_GetPropertyById(cx, obj, property_name, &prop_value) && prop_value.isBoolean()) { *value = prop_value.toBoolean(); return true; } throw_property_lookup_error(cx, obj, description, property_name, "it was not a boolean"); return false; } bool gjs_object_require_property(JSContext *cx, JS::HandleObject obj, const char *description, JS::HandleId property_name, int32_t *value) { JS::RootedValue prop_value(cx); if (JS_GetPropertyById(cx, obj, property_name, &prop_value) && prop_value.isInt32()) { *value = prop_value.toInt32(); return true; } throw_property_lookup_error(cx, obj, description, property_name, "it was not a 32-bit integer"); return false; } /* Converts JS string value to UTF-8 string. value must be freed with JS_free. */ bool gjs_object_require_property(JSContext *cx, JS::HandleObject obj, const char *description, JS::HandleId property_name, GjsAutoJSChar *value) { JS::RootedValue prop_value(cx); if (JS_GetPropertyById(cx, obj, property_name, &prop_value) && gjs_string_to_utf8(cx, prop_value, value)) return true; throw_property_lookup_error(cx, obj, description, property_name, "it was not a valid string"); return false; } bool gjs_object_require_property(JSContext *cx, JS::HandleObject obj, const char *description, JS::HandleId property_name, JS::MutableHandleObject value) { JS::RootedValue prop_value(cx); if (JS_GetPropertyById(cx, obj, property_name, &prop_value) && prop_value.isObject()) { value.set(&prop_value.toObject()); return true; } throw_property_lookup_error(cx, obj, description, property_name, "it was not an object"); return false; } bool gjs_object_require_converted_property(JSContext *cx, JS::HandleObject obj, const char *description, JS::HandleId property_name, uint32_t *value) { JS::RootedValue prop_value(cx); if (JS_GetPropertyById(cx, obj, property_name, &prop_value) && JS::ToUint32(cx, prop_value, value)) { return true; } throw_property_lookup_error(cx, obj, description, property_name, "it couldn't be converted to uint32"); return false; } void gjs_throw_constructor_error(JSContext *context) { gjs_throw(context, "Constructor called as normal method. Use 'new SomeObject()' not 'SomeObject()'"); } void gjs_throw_abstract_constructor_error(JSContext *context, JS::CallArgs& args) { const JSClass *proto_class; const char *name = "anonymous"; JS::RootedObject callee(context, &args.callee()); JS::RootedValue prototype(context); if (gjs_object_get_property(context, callee, GJS_STRING_PROTOTYPE, &prototype)) { proto_class = JS_GetClass(&prototype.toObject()); name = proto_class->name; } gjs_throw(context, "You cannot construct new instances of '%s'", name); } JSObject * gjs_build_string_array(JSContext *context, gssize array_length, char **array_values) { int i; if (array_length == -1) array_length = g_strv_length(array_values); JS::AutoValueVector elems(context); if (!elems.reserve(array_length)) g_error("Unable to reserve memory for vector"); for (i = 0; i < array_length; ++i) { JS::ConstUTF8CharsZ chars(array_values[i], strlen(array_values[i])); JS::RootedValue element(context, JS::StringValue(JS_NewStringCopyUTF8Z(context, chars))); if (!elems.append(element)) g_error("Unable to append to vector"); } return JS_NewArrayObject(context, elems); } JSObject* gjs_define_string_array(JSContext *context, JS::HandleObject in_object, const char *array_name, ssize_t array_length, const char **array_values, unsigned attrs) { JSAutoRequest ar(context); JS::RootedObject array(context, gjs_build_string_array(context, array_length, (char **) array_values)); if (!array) return nullptr; if (!JS_DefineProperty(context, in_object, array_name, array, attrs)) return nullptr; return array; } /** * gjs_string_readable: * * Return a string that can be read back by gjs-console; for * JS strings that contain valid Unicode, we return a UTF-8 formatted * string. Otherwise, we return one where non-ASCII-printable bytes * are \x escaped. * */ static char * gjs_string_readable(JSContext *context, JS::HandleString string) { GString *buf = g_string_new(""); JS_BeginRequest(context); g_string_append_c(buf, '"'); GjsAutoJSChar chars = JS_EncodeStringToUTF8(context, string); if (!chars) { /* I'm not sure this code will actually ever be reached except in the * case of OOM, since JS_EncodeStringToUTF8() seems to happily output * non-valid UTF-8 bytes. However, let's leave this in, since * SpiderMonkey may decide to do validation in the future. */ /* Find out size of buffer to allocate, not counting 0-terminator */ size_t len = JS_PutEscapedString(context, NULL, 0, string, '"'); char *escaped = g_new(char, len + 1); JS_PutEscapedString(context, escaped, len, string, '"'); g_string_append(buf, escaped); g_free(escaped); } else { g_string_append(buf, chars); } g_string_append_c(buf, '"'); JS_EndRequest(context); return g_string_free(buf, false); } static char * _gjs_g_utf8_make_valid (const char *name) { GString *string; const char *remainder, *invalid; int remaining_bytes, valid_bytes; g_return_val_if_fail (name != NULL, NULL); string = nullptr; remainder = name; remaining_bytes = strlen (name); while (remaining_bytes != 0) { if (g_utf8_validate (remainder, remaining_bytes, &invalid)) break; valid_bytes = invalid - remainder; if (!string) string = g_string_sized_new (remaining_bytes); g_string_append_len (string, remainder, valid_bytes); /* append U+FFFD REPLACEMENT CHARACTER */ g_string_append (string, "\357\277\275"); remaining_bytes -= valid_bytes + 1; remainder = invalid + 1; } if (!string) return g_strdup (name); g_string_append (string, remainder); g_assert (g_utf8_validate (string->str, -1, NULL)); return g_string_free (string, false); } /** * gjs_value_debug_string: * @context: * @value: Any JavaScript value * * Returns: A UTF-8 encoded string describing @value */ char* gjs_value_debug_string(JSContext *context, JS::HandleValue value) { char *bytes; char *debugstr; /* Special case debug strings for strings */ if (value.isString()) { JS::RootedString str(context, value.toString()); return gjs_string_readable(context, str); } JS_BeginRequest(context); JS::RootedString str(context, JS::ToString(context, value)); if (!str) { if (value.isObject()) { /* Specifically the Call object (see jsfun.c in spidermonkey) * does not have a toString; there may be others also. */ const JSClass *klass = JS_GetClass(&value.toObject()); if (klass != NULL) { str = JS_NewStringCopyZ(context, klass->name); JS_ClearPendingException(context); if (!str) { JS_EndRequest(context); return g_strdup("[out of memory copying class name]"); } } else { gjs_log_exception(context); JS_EndRequest(context); return g_strdup("[unknown object]"); } } else { JS_EndRequest(context); return g_strdup("[unknown non-object]"); } } g_assert(str); bytes = JS_EncodeStringToUTF8(context, str); JS_EndRequest(context); debugstr = _gjs_g_utf8_make_valid(bytes); JS_free(context, bytes); return debugstr; } static char * utf8_exception_from_non_gerror_value(JSContext *cx, JS::HandleValue exc) { JS::RootedString exc_str(cx, JS::ToString(cx, exc)); if (!exc_str) return nullptr; GjsAutoJSChar utf8_exception = JS_EncodeStringToUTF8(cx, exc_str); return utf8_exception.copy(); } bool gjs_log_exception_full(JSContext *context, JS::HandleValue exc, JS::HandleString message) { char *utf8_exception; bool is_syntax; JS_BeginRequest(context); JS::RootedObject exc_obj(context); is_syntax = false; if (!exc.isObject()) { utf8_exception = utf8_exception_from_non_gerror_value(context, exc); } else { exc_obj = &exc.toObject(); if (gjs_typecheck_boxed(context, exc_obj, NULL, G_TYPE_ERROR, false)) { GError *gerror = (GError *) gjs_c_struct_from_boxed(context, exc_obj); utf8_exception = g_strdup_printf("GLib.Error %s: %s", g_quark_to_string(gerror->domain), gerror->message); } else { const JSClass *syntax_error = js::Jsvalify(js::ProtoKeyToClass(JSProto_SyntaxError)); is_syntax = JS_InstanceOf(context, exc_obj, syntax_error, nullptr); utf8_exception = utf8_exception_from_non_gerror_value(context, exc); } } GjsAutoJSChar utf8_message; if (message) utf8_message = JS_EncodeStringToUTF8(context, message); /* We log syntax errors differently, because the stack for those includes only the referencing module, but we want to print out the filename and line number from the exception. */ if (is_syntax) { JS::RootedValue js_lineNumber(context), js_fileName(context); unsigned lineNumber; gjs_object_get_property(context, exc_obj, GJS_STRING_LINE_NUMBER, &js_lineNumber); gjs_object_get_property(context, exc_obj, GJS_STRING_FILENAME, &js_fileName); GjsAutoJSChar utf8_filename; if (js_fileName.isString()) { JS::RootedString str(context, js_fileName.toString()); utf8_filename = JS_EncodeStringToUTF8(context, str); } if (!utf8_filename) utf8_filename = JS_strdup(context, "unknown"); lineNumber = js_lineNumber.toInt32(); if (message) { g_critical("JS ERROR: %s: %s @ %s:%u", utf8_message.get(), utf8_exception, utf8_filename.get(), lineNumber); } else { g_critical("JS ERROR: %s @ %s:%u", utf8_exception, utf8_filename.get(), lineNumber); } } else { GjsAutoJSChar utf8_stack; JS::RootedValue stack(context); if (exc.isObject() && gjs_object_get_property(context, exc_obj, GJS_STRING_STACK, &stack) && stack.isString()) { JS::RootedString str(context, stack.toString()); utf8_stack = JS_EncodeStringToUTF8(context, str); } if (message) { if (utf8_stack) g_warning("JS ERROR: %s: %s\n%s", utf8_message.get(), utf8_exception, utf8_stack.get()); else g_warning("JS ERROR: %s: %s", utf8_message.get(), utf8_exception); } else { if (utf8_stack) g_warning("JS ERROR: %s\n%s", utf8_exception, utf8_stack.get()); else g_warning("JS ERROR: %s", utf8_exception); } } g_free(utf8_exception); JS_EndRequest(context); return true; } bool gjs_log_exception(JSContext *context) { bool retval = false; JS_BeginRequest(context); JS::RootedValue exc(context); if (!JS_GetPendingException(context, &exc)) goto out; JS_ClearPendingException(context); gjs_log_exception_full(context, exc, nullptr); retval = true; out: JS_EndRequest(context); return retval; } bool gjs_call_function_value(JSContext *context, JS::HandleObject obj, JS::HandleValue fval, const JS::HandleValueArray& args, JS::MutableHandleValue rval) { bool result; JS_BeginRequest(context); result = JS_CallFunctionValue(context, obj, fval, args, rval); if (result) gjs_schedule_gc_if_needed(context); JS_EndRequest(context); return result; } #ifdef __linux__ static void _linux_get_self_process_size (gulong *vm_size, gulong *rss_size) { char *contents; char *iter; gsize len; int i; *vm_size = *rss_size = 0; if (!g_file_get_contents ("/proc/self/stat", &contents, &len, NULL)) return; iter = contents; /* See "man proc" for where this 22 comes from */ for (i = 0; i < 22; i++) { iter = strchr (iter, ' '); if (!iter) goto out; iter++; } sscanf (iter, " %lu", vm_size); iter = strchr (iter, ' '); if (iter) sscanf (iter, " %lu", rss_size); out: g_free (contents); } static gulong linux_rss_trigger; static int64_t last_gc_check_time; #endif void gjs_gc_if_needed (JSContext *context) { #ifdef __linux__ { /* We initiate a GC if VM or RSS has grown by this much */ gulong vmsize; gulong rss_size; gint64 now; /* We rate limit GCs to at most one per 5 frames. One frame is 16666 microseconds (1000000/60)*/ now = g_get_monotonic_time(); if (now - last_gc_check_time < 5 * 16666) return; last_gc_check_time = now; _linux_get_self_process_size (&vmsize, &rss_size); /* linux_rss_trigger is initialized to 0, so currently * we always do a full GC early. * * Here we see if the RSS has grown by 25% since * our last look; if so, initiate a full GC. In * theory using RSS is bad if we get swapped out, * since we may be overzealous in GC, but on the * other hand, if swapping is going on, better * to GC. */ if (rss_size > linux_rss_trigger) { linux_rss_trigger = (gulong) MIN(G_MAXULONG, rss_size * 1.25); JS::GCForReason(context, GC_SHRINK, JS::gcreason::Reason::API); } else if (rss_size < (0.75 * linux_rss_trigger)) { /* If we've shrunk by 75%, lower the trigger */ linux_rss_trigger = (rss_size * 1.25); } } #endif } /** * gjs_maybe_gc: * * Low level version of gjs_context_maybe_gc(). */ void gjs_maybe_gc (JSContext *context) { JS_MaybeGC(context); gjs_gc_if_needed(context); } void gjs_schedule_gc_if_needed (JSContext *context) { GjsContext *gjs_context; /* We call JS_MaybeGC immediately, but defer a check for a full * GC cycle to an idle handler. */ JS_MaybeGC(context); gjs_context = (GjsContext *) JS_GetContextPrivate(context); if (gjs_context) _gjs_context_schedule_gc_if_needed(gjs_context); } /** * gjs_strip_unix_shebang: * * @script: (in): A pointer to a JS script * @script_len: (inout): A pointer to the script length. The * pointer will be modified if a shebang is stripped. * @new_start_line_number: (out) (allow-none): A pointer to * write the start-line number to account for the offset * as a result of stripping the shebang. * * Returns a pointer to the beginning of a script with unix * shebangs removed. The outparams are useful to know the * new length of the script and on what line of the * original script we're executing from, so that any relevant * offsets can be applied to the results of an execution pass. */ const char * gjs_strip_unix_shebang(const char *script, size_t *script_len, int *start_line_number_out) { g_assert(script_len); /* handle scripts with UNIX shebangs */ if (strncmp(script, "#!", 2) == 0) { /* If we found a newline, advance the script by one line */ const char *s = (const char *) strstr (script, "\n"); if (s != NULL) { if (*script_len > 0) *script_len -= (s + 1 - script); script = s + 1; if (start_line_number_out) *start_line_number_out = 2; return script; } else { /* Just a shebang */ if (start_line_number_out) *start_line_number_out = -1; *script_len = 0; return NULL; } } /* No shebang, return the original script */ if (start_line_number_out) *start_line_number_out = 1; return script; } bool gjs_eval_with_scope(JSContext *context, JS::HandleObject object, const char *script, ssize_t script_len, const char *filename, JS::MutableHandleValue retval) { int start_line_number = 1; JSAutoRequest ar(context); size_t real_len = script_len; if (script_len < 0) real_len = strlen(script); script = gjs_strip_unix_shebang(script, &real_len, &start_line_number); /* log and clear exception if it's set (should not be, normally...) */ if (JS_IsExceptionPending(context)) { g_warning("gjs_eval_in_scope called with a pending exception"); return false; } JS::RootedObject eval_obj(context, object); if (!eval_obj) eval_obj = JS_NewPlainObject(context); JS::CompileOptions options(context); options.setFileAndLine(filename, start_line_number) .setSourceIsLazy(true); std::wstring_convert, char16_t> convert; std::u16string utf16_string = convert.from_bytes(script); JS::SourceBufferHolder buf(utf16_string.c_str(), utf16_string.size(), JS::SourceBufferHolder::NoOwnership); JS::AutoObjectVector scope_chain(context); if (!scope_chain.append(eval_obj)) g_error("Unable to append to vector"); if (!JS::Evaluate(context, scope_chain, options, buf, retval)) return false; gjs_schedule_gc_if_needed(context); if (JS_IsExceptionPending(context)) { g_warning("EvaluateScript returned true but exception was pending; " "did somebody call gjs_throw() without returning false?"); return false; } gjs_debug(GJS_DEBUG_CONTEXT, "Script evaluation succeeded"); return true; } cjs-4.4.0/cjs/jsapi-util.h000066400000000000000000000366531356375132300153340ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_JSAPI_UTIL_H__ #define __GJS_JSAPI_UTIL_H__ #include #include #include #include #include "jsapi-wrapper.h" #include "gi/gtype.h" #ifdef __GNUC__ #define GJS_ALWAYS_INLINE __attribute__((always_inline)) #else #define GJS_ALWAYS_INLINE #endif class GjsAutoChar : public std::unique_ptr { public: GjsAutoChar(char *str = nullptr) : unique_ptr(str, g_free) {} operator const char *() { return get(); } void operator= (char *str) { reset(str); } void operator= (const char *str) { reset(g_strdup(str)); } }; template class GjsAutoUnref : public std::unique_ptr { public: GjsAutoUnref(T *ptr = nullptr) : GjsAutoUnref::unique_ptr(ptr, g_object_unref) {} operator T *() { return GjsAutoUnref::unique_ptr::get(); } }; struct GjsJSFreeArgs { void operator() (char *str) { JS_free(nullptr, str); } }; class GjsAutoJSChar : public std::unique_ptr { public: GjsAutoJSChar(char *str = nullptr) : unique_ptr(str, GjsJSFreeArgs()) { } operator const char*() { return get(); } void operator=(char *str) { reset(str); } char* copy() { /* Strings acquired by this should be g_free()'ed */ return g_strdup(get()); } }; G_BEGIN_DECLS #define GJS_UTIL_ERROR gjs_util_error_quark () GQuark gjs_util_error_quark (void); enum { GJS_UTIL_ERROR_NONE, GJS_UTIL_ERROR_ARGUMENT_INVALID, GJS_UTIL_ERROR_ARGUMENT_UNDERFLOW, GJS_UTIL_ERROR_ARGUMENT_OVERFLOW, GJS_UTIL_ERROR_ARGUMENT_TYPE_MISMATCH }; typedef struct GjsRootedArray GjsRootedArray; /* Flags that should be set on properties exported from native code modules. * Basically set these on API, but do NOT set them on data. * * PERMANENT: forbid deleting the prop * ENUMERATE: allows copyProperties to work among other reasons to have it */ #define GJS_MODULE_PROP_FLAGS (JSPROP_PERMANENT | JSPROP_ENUMERATE) /* * GJS_GET_THIS: * @cx: JSContext pointer passed into JSNative function * @argc: Number of arguments passed into JSNative function * @vp: Argument value array passed into JSNative function * @args: Name for JS::CallArgs variable defined by this code snippet * @to: Name for JS::RootedObject variable referring to function's this * * A convenience macro for getting the 'this' object a function was called with. * Use in any JSNative function. */ #define GJS_GET_THIS(cx, argc, vp, args, to) \ JS::CallArgs args = JS::CallArgsFromVp(argc, vp); \ JS::RootedObject to(cx, &args.computeThis(cx).toObject()) JSObject* gjs_get_import_global (JSContext *context); void gjs_throw_constructor_error (JSContext *context); void gjs_throw_abstract_constructor_error(JSContext *context, JS::CallArgs& args); JSObject* gjs_build_string_array (JSContext *context, gssize array_length, char **array_values); JSObject *gjs_define_string_array(JSContext *context, JS::HandleObject obj, const char *array_name, ssize_t array_length, const char **array_values, unsigned attrs); void gjs_throw (JSContext *context, const char *format, ...) G_GNUC_PRINTF (2, 3); void gjs_throw_custom (JSContext *context, JSProtoKey error_kind, const char *error_name, const char *format, ...) G_GNUC_PRINTF (4, 5); void gjs_throw_literal (JSContext *context, const char *string); void gjs_throw_g_error (JSContext *context, GError *error); bool gjs_log_exception (JSContext *context); bool gjs_log_exception_full(JSContext *context, JS::HandleValue exc, JS::HandleString message); char *gjs_value_debug_string(JSContext *context, JS::HandleValue value); bool gjs_call_function_value(JSContext *context, JS::HandleObject obj, JS::HandleValue fval, const JS::HandleValueArray& args, JS::MutableHandleValue rval); void gjs_warning_reporter(JSContext *cx, JSErrorReport *report); bool gjs_string_to_utf8 (JSContext *context, const JS::Value string_val, GjsAutoJSChar *utf8_string_p); bool gjs_string_from_utf8(JSContext *context, const char *utf8_string, JS::MutableHandleValue value_p); bool gjs_string_from_utf8_n(JSContext *cx, const char *utf8_chars, size_t len, JS::MutableHandleValue out); bool gjs_string_to_filename(JSContext *cx, const JS::Value string_val, GjsAutoChar *filename_string); bool gjs_string_from_filename(JSContext *context, const char *filename_string, ssize_t n_bytes, JS::MutableHandleValue value_p); bool gjs_string_get_char16_data(JSContext *cx, JS::HandleString str, char16_t **data_p, size_t *len_p); bool gjs_string_to_ucs4(JSContext *cx, JS::HandleString value, gunichar **ucs4_string_p, size_t *len_p); bool gjs_string_from_ucs4(JSContext *cx, const gunichar *ucs4_string, ssize_t n_chars, JS::MutableHandleValue value_p); bool gjs_get_string_id (JSContext *context, jsid id, GjsAutoJSChar *name_p); jsid gjs_intern_string_to_id (JSContext *context, const char *string); bool gjs_unichar_from_string (JSContext *context, JS::Value string, gunichar *result); /* Functions intended for more "internal" use */ void gjs_maybe_gc (JSContext *context); void gjs_schedule_gc_if_needed(JSContext *cx); void gjs_gc_if_needed(JSContext *cx); bool gjs_eval_with_scope(JSContext *context, JS::HandleObject object, const char *script, ssize_t script_len, const char *filename, JS::MutableHandleValue retval); typedef enum { GJS_STRING_CONSTRUCTOR, GJS_STRING_PROTOTYPE, GJS_STRING_LENGTH, GJS_STRING_IMPORTS, GJS_STRING_PARENT_MODULE, GJS_STRING_MODULE_INIT, GJS_STRING_SEARCH_PATH, GJS_STRING_KEEP_ALIVE_MARKER, GJS_STRING_PRIVATE_NS_MARKER, GJS_STRING_GI_MODULE, GJS_STRING_GI_VERSIONS, GJS_STRING_GI_OVERRIDES, GJS_STRING_GOBJECT_INIT, GJS_STRING_INSTANCE_INIT, GJS_STRING_NEW_INTERNAL, GJS_STRING_NEW, GJS_STRING_MESSAGE, GJS_STRING_CODE, GJS_STRING_STACK, GJS_STRING_FILENAME, GJS_STRING_LINE_NUMBER, GJS_STRING_COLUMN_NUMBER, GJS_STRING_NAME, GJS_STRING_X, GJS_STRING_Y, GJS_STRING_WIDTH, GJS_STRING_HEIGHT, GJS_STRING_MODULE_PATH, GJS_STRING_LAST } GjsConstString; const char * gjs_strip_unix_shebang(const char *script, size_t *script_len, int *new_start_line_number); /* These four functions wrap JS_GetPropertyById(), etc., but with a * GjsConstString constant instead of a jsid. */ bool gjs_object_get_property(JSContext *cx, JS::HandleObject obj, GjsConstString property_name, JS::MutableHandleValue value_p); bool gjs_object_set_property(JSContext *cx, JS::HandleObject obj, GjsConstString property_name, JS::HandleValue value); bool gjs_object_has_property(JSContext *cx, JS::HandleObject obj, GjsConstString property_name, bool *found); G_END_DECLS GjsAutoChar gjs_format_stack_trace(JSContext *cx, JS::HandleObject saved_frame); bool gjs_object_define_property(JSContext *cx, JS::HandleObject obj, GjsConstString property_name, JS::HandleValue value, unsigned flags); bool gjs_object_define_property(JSContext *cx, JS::HandleObject obj, GjsConstString property_name, JS::HandleObject value, unsigned flags); bool gjs_object_define_property(JSContext *cx, JS::HandleObject obj, GjsConstString property_name, JS::HandleString value, unsigned flags); bool gjs_object_define_property(JSContext *cx, JS::HandleObject obj, GjsConstString property_name, uint32_t value, unsigned flags); JS::HandleId gjs_context_get_const_string(JSContext *cx, GjsConstString string); /* Overloaded functions, must be outside G_DECLS. More types are intended to be * added as the opportunity arises. */ bool gjs_object_require_property(JSContext *context, JS::HandleObject obj, const char *obj_description, JS::HandleId property_name, JS::MutableHandleValue value); bool gjs_object_require_property(JSContext *cx, JS::HandleObject obj, const char *description, JS::HandleId property_name, bool *value); bool gjs_object_require_property(JSContext *cx, JS::HandleObject obj, const char *description, JS::HandleId property_name, int32_t *value); bool gjs_object_require_property(JSContext *cx, JS::HandleObject obj, const char *description, JS::HandleId property_name, GjsAutoJSChar *value); bool gjs_object_require_property(JSContext *cx, JS::HandleObject obj, const char *description, JS::HandleId property_name, JS::MutableHandleObject value); bool gjs_object_require_converted_property(JSContext *context, JS::HandleObject obj, const char *description, JS::HandleId property_name, uint32_t *value); /* Here, too, we have wrappers that take a GjsConstString. */ template bool gjs_object_require_property(JSContext *cx, JS::HandleObject obj, const char *description, GjsConstString property_name, T value) { return gjs_object_require_property(cx, obj, description, gjs_context_get_const_string(cx, property_name), value); } template bool gjs_object_require_converted_property(JSContext *cx, JS::HandleObject obj, const char *description, GjsConstString property_name, T value) { return gjs_object_require_converted_property(cx, obj, description, gjs_context_get_const_string(cx, property_name), value); } std::string gjs_debug_string(JSString *str); std::string gjs_debug_symbol(JS::Symbol * const sym); std::string gjs_debug_object(JSObject *obj); std::string gjs_debug_value(JS::Value v); std::string gjs_debug_id(jsid id); #endif /* __GJS_JSAPI_UTIL_H__ */ cjs-4.4.0/cjs/jsapi-wrapper.h000066400000000000000000000034031356375132300160220ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2009 litl, LLC * Copyright (c) 2010 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef GJS_JSAPI_WRAPPER_H #define GJS_JSAPI_WRAPPER_H #include /* COMPAT: SpiderMonkey headers in some places use DEBUG instead of JS_DEBUG */ /* https://bugzilla.mozilla.org/show_bug.cgi?id=1261161 */ /*#ifdef HAVE_DEBUG_SPIDERMONKEY #define DEBUG 1 #endif */ #include /* SpiderMonkey's #defines that affect public API */ #if defined(__clang__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) #pragma GCC system_header #endif #include #include #include #endif /* GJS_JSAPI_WRAPPER_H */ cjs-4.4.0/cjs/macros.h000066400000000000000000000026501356375132300145250ustar00rootroot00000000000000/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2017 Chun-wei Fan * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef GJS_MACROS_H #define GJS_MACROS_H #include #ifdef G_OS_WIN32 # ifdef GJS_COMPILATION # define GJS_EXPORT __declspec(dllexport) # else # define GJS_EXPORT __declspec(dllimport) # endif #else # define GJS_EXPORT #endif #endif /* GJS_MACROS_H */ cjs-4.4.0/cjs/mem.cpp000066400000000000000000000061171356375132300143540ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "mem.h" #include #define GJS_DEFINE_COUNTER(name) \ GjsMemCounter gjs_counter_ ## name = { \ 0, #name \ }; GJS_DEFINE_COUNTER(everything) GJS_DEFINE_COUNTER(boxed) GJS_DEFINE_COUNTER(closure) GJS_DEFINE_COUNTER(function) GJS_DEFINE_COUNTER(fundamental) GJS_DEFINE_COUNTER(gerror) GJS_DEFINE_COUNTER(importer) GJS_DEFINE_COUNTER(interface) GJS_DEFINE_COUNTER(ns) GJS_DEFINE_COUNTER(object) GJS_DEFINE_COUNTER(param) GJS_DEFINE_COUNTER(repo) #define GJS_LIST_COUNTER(name) \ & gjs_counter_ ## name static GjsMemCounter* counters[] = { GJS_LIST_COUNTER(boxed), GJS_LIST_COUNTER(closure), GJS_LIST_COUNTER(function), GJS_LIST_COUNTER(fundamental), GJS_LIST_COUNTER(gerror), GJS_LIST_COUNTER(importer), GJS_LIST_COUNTER(interface), GJS_LIST_COUNTER(ns), GJS_LIST_COUNTER(object), GJS_LIST_COUNTER(param), GJS_LIST_COUNTER(repo), }; void gjs_memory_report(const char *where, bool die_if_leaks) { int i; int n_counters; int total_objects; gjs_debug(GJS_DEBUG_MEMORY, "Memory report: %s", where); n_counters = G_N_ELEMENTS(counters); total_objects = 0; for (i = 0; i < n_counters; ++i) { total_objects += counters[i]->value; } if (total_objects != GJS_GET_COUNTER(everything)) { gjs_debug(GJS_DEBUG_MEMORY, "Object counts don't add up!"); } gjs_debug(GJS_DEBUG_MEMORY, " %d objects currently alive", GJS_GET_COUNTER(everything)); for (i = 0; i < n_counters; ++i) { gjs_debug(GJS_DEBUG_MEMORY, " %12s = %d", counters[i]->name, counters[i]->value); } if (die_if_leaks && GJS_GET_COUNTER(everything) > 0) { g_error("%s: JavaScript objects were leaked.", where); } } cjs-4.4.0/cjs/mem.h000066400000000000000000000047261356375132300140250ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_MEM_H__ #define __GJS_MEM_H__ #include #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS typedef struct { volatile int value; const char *name; } GjsMemCounter; #define GJS_DECLARE_COUNTER(name) \ extern GjsMemCounter gjs_counter_ ## name ; GJS_DECLARE_COUNTER(everything) GJS_DECLARE_COUNTER(boxed) GJS_DECLARE_COUNTER(closure) GJS_DECLARE_COUNTER(function) GJS_DECLARE_COUNTER(fundamental) GJS_DECLARE_COUNTER(gerror) GJS_DECLARE_COUNTER(importer) GJS_DECLARE_COUNTER(interface) GJS_DECLARE_COUNTER(ns) GJS_DECLARE_COUNTER(object) GJS_DECLARE_COUNTER(param) GJS_DECLARE_COUNTER(repo) #define GJS_INC_COUNTER(name) \ do { \ g_atomic_int_add(&gjs_counter_everything.value, 1); \ g_atomic_int_add(&gjs_counter_ ## name .value, 1); \ } while (0) #define GJS_DEC_COUNTER(name) \ do { \ g_atomic_int_add(&gjs_counter_everything.value, -1); \ g_atomic_int_add(&gjs_counter_ ## name .value, -1); \ } while (0) #define GJS_GET_COUNTER(name) \ g_atomic_int_get(&gjs_counter_ ## name .value) void gjs_memory_report(const char *where, bool die_if_leaks); G_END_DECLS #endif /* __GJS_MEM_H__ */ cjs-4.4.0/cjs/module.cpp000066400000000000000000000207431356375132300150640ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2017 Philip Chimento * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include "jsapi-util.h" #include "jsapi-wrapper.h" #include "module.h" #include "util/log.h" class GjsModule { char *m_name; GjsModule(const char *name) { m_name = g_strdup(name); } ~GjsModule() { g_free(m_name); } /* Private data accessors */ static inline GjsModule * priv(JSObject *module) { return static_cast(JS_GetPrivate(module)); } /* Creates a JS module object. Use instead of the class's constructor */ static JSObject * create(JSContext *cx, const char *name) { JSObject *module = JS_NewObject(cx, &GjsModule::klass); JS_SetPrivate(module, new GjsModule(name)); return module; } /* Defines the empty module as a property on the importer */ bool define_import(JSContext *cx, JS::HandleObject module, JS::HandleObject importer, JS::HandleId name) { if (!JS_DefinePropertyById(cx, importer, name, module, GJS_MODULE_PROP_FLAGS & ~JSPROP_PERMANENT)) { gjs_debug(GJS_DEBUG_IMPORTER, "Failed to define '%s' in importer", m_name); return false; } return true; } /* Carries out the actual execution of the module code */ bool evaluate_import(JSContext *cx, JS::HandleObject module, const char *script, size_t script_len, const char *filename, int line_number) { JS::CompileOptions options(cx); options.setFileAndLine(filename, line_number) .setSourceIsLazy(true); std::wstring_convert, char16_t> convert; std::u16string utf16_string = convert.from_bytes(script); JS::SourceBufferHolder buf(utf16_string.c_str(), utf16_string.size(), JS::SourceBufferHolder::NoOwnership); JS::AutoObjectVector scope_chain(cx); if (!scope_chain.append(module)) g_error("Unable to append to vector"); JS::RootedValue ignored_retval(cx); if (!JS::Evaluate(cx, scope_chain, options, buf, &ignored_retval)) return false; gjs_schedule_gc_if_needed(cx); gjs_debug(GJS_DEBUG_IMPORTER, "Importing module %s succeeded", m_name); return true; } /* Loads JS code from a file and imports it */ bool import_file(JSContext *cx, JS::HandleObject module, GFile *file) { GError *error = nullptr; char *unowned_script; size_t script_len = 0; int start_line_number = 1; if (!(g_file_load_contents(file, nullptr, &unowned_script, &script_len, nullptr, &error))) { gjs_throw_g_error(cx, error); return false; } GjsAutoChar script = unowned_script; /* steals ownership */ g_assert(script != nullptr); const char *stripped_script = gjs_strip_unix_shebang(script, &script_len, &start_line_number); GjsAutoChar full_path = g_file_get_parse_name(file); return evaluate_import(cx, module, stripped_script, script_len, full_path, start_line_number); } /* JSClass operations */ bool resolve_impl(JSContext *cx, JS::HandleObject module, JS::HandleId id, bool *resolved) { JS::RootedObject lexical(cx, JS_ExtensibleLexicalEnvironment(module)); if (!lexical) { *resolved = false; return true; /* nothing imported yet */ } if (!JS_HasPropertyById(cx, lexical, id, resolved)) return false; if (!*resolved) return true; /* The property is present in the lexical environment. This should not * be supported according to ES6. For compatibility with earlier GJS, * we treat it as if it were a real property, but warn about it. */ g_warning("Some code accessed the property '%s' on the module '%s'. " "That property was defined with 'let' or 'const' inside the " "module. This was previously supported, but is not correct " "according to the ES6 standard. Any symbols to be exported " "from a module must be defined with 'var'. The property " "access will work as previously for the time being, but " "please fix your code anyway.", gjs_debug_id(id).c_str(), m_name); JS::Rooted desc(cx); return JS_GetPropertyDescriptorById(cx, lexical, id, &desc) && JS_DefinePropertyById(cx, module, id, desc); } static bool resolve(JSContext *cx, JS::HandleObject module, JS::HandleId id, bool *resolved) { return priv(module)->resolve_impl(cx, module, id, resolved); } static void finalize(JSFreeOp *op, JSObject *module) { delete priv(module); } static constexpr JSClassOps class_ops = { nullptr, /* addProperty */ nullptr, /* deleteProperty */ nullptr, /* getProperty */ nullptr, /* setProperty */ nullptr, /* enumerate */ &GjsModule::resolve, nullptr, /* mayResolve */ &GjsModule::finalize, }; static constexpr JSClass klass = { "GjsModule", JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE, &GjsModule::class_ops, }; public: /* Carries out the import operation */ static JSObject * import(JSContext *cx, JS::HandleObject importer, JS::HandleId id, const char *name, GFile *file) { JS::RootedObject module(cx, GjsModule::create(cx, name)); if (!module || !priv(module)->define_import(cx, module, importer, id) || !priv(module)->import_file(cx, module, file)) return nullptr; return module; } }; /** * gjs_module_import: * @cx: the JS context * @importer: the JS importer object, parent of the module to be imported * @id: module name in the form of a jsid * @name: module name, used for logging and identification * @file: location of the file to import * * Carries out an import of a GJS module. * Defines a property @name on @importer pointing to the module object, which * is necessary in the case of cyclic imports. * This property is not permanent; the caller is responsible for making it * permanent if the import succeeds. * * Returns: the JS module object, or nullptr on failure. */ JSObject * gjs_module_import(JSContext *cx, JS::HandleObject importer, JS::HandleId id, const char *name, GFile *file) { return GjsModule::import(cx, importer, id, name, file); } decltype(GjsModule::klass) constexpr GjsModule::klass; decltype(GjsModule::class_ops) constexpr GjsModule::class_ops; cjs-4.4.0/cjs/module.h000066400000000000000000000030571356375132300145300ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2017 Philip Chimento * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef GJS_MODULE_H #define GJS_MODULE_H #include #include "jsapi-wrapper.h" G_BEGIN_DECLS JSObject * gjs_module_import(JSContext *cx, JS::HandleObject importer, JS::HandleId id, const char *name, GFile *file); G_END_DECLS #endif /* GJS_MODULE_H */ cjs-4.4.0/cjs/native.cpp000066400000000000000000000067161356375132300150710ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008-2010 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include "native.h" #include "jsapi-wrapper.h" #include "jsapi-util.h" static GHashTable *modules = NULL; void gjs_register_native_module (const char *module_id, GjsDefineModuleFunc func) { if (modules == NULL) modules = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); if (g_hash_table_lookup(modules, module_id) != NULL) { g_warning("A second native module tried to register the same id '%s'", module_id); return; } g_hash_table_replace(modules, g_strdup(module_id), (void*) func); gjs_debug(GJS_DEBUG_NATIVE, "Registered native JS module '%s'", module_id); } /** * gjs_is_registered_native_module: * @context: * @parent: the parent object defining the namespace * @name: name of the module * * Checks if a native module corresponding to @name has already * been registered. This is used to check to see if a name is a * builtin module without starting to try and load it. */ bool gjs_is_registered_native_module(JSContext *context, JSObject *parent, const char *name) { if (modules == NULL) return false; return g_hash_table_lookup(modules, name) != NULL; } /** * gjs_load_native_module: * @context: the #JSContext * @name: Name under which the module was registered with * gjs_register_native_module() * @module_out: Return location for a #JSObject * * Loads a builtin native-code module called @name into @module_out. * * Returns: true on success, false if an exception was thrown. */ bool gjs_load_native_module(JSContext *context, const char *name, JS::MutableHandleObject module_out) { GjsDefineModuleFunc func; gjs_debug(GJS_DEBUG_NATIVE, "Defining native module '%s'", name); if (modules != NULL) func = (GjsDefineModuleFunc) g_hash_table_lookup(modules, name); else func = NULL; if (!func) { gjs_throw(context, "No native module '%s' has registered itself", name); return false; } return func (context, module_out); } cjs-4.4.0/cjs/native.h000066400000000000000000000041731356375132300145310ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_NATIVE_H__ #define __GJS_NATIVE_H__ #include #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS typedef bool (* GjsDefineModuleFunc) (JSContext *context, JS::MutableHandleObject module_out); /* called on context init */ void gjs_register_native_module (const char *module_id, GjsDefineModuleFunc func); /* called by importer.c to to check for already loaded modules */ bool gjs_is_registered_native_module(JSContext *context, JSObject *parent, const char *name); /* called by importer.cpp to load a statically linked native module */ bool gjs_load_native_module(JSContext *cx, const char *name, JS::MutableHandleObject module_out); G_END_DECLS #endif /* __GJS_NATIVE_H__ */ cjs-4.4.0/cjs/profiler-private.h000066400000000000000000000030651356375132300165340ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2018 Endless Mobile, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef GJS_PROFILER_PRIVATE_H #define GJS_PROFILER_PRIVATE_H #include "context.h" #include "profiler.h" G_BEGIN_DECLS GjsProfiler *_gjs_profiler_new(GjsContext *context); void _gjs_profiler_free(GjsProfiler *self); bool _gjs_profiler_is_running(GjsProfiler *self); void _gjs_profiler_setup_signals(GjsProfiler *self, GjsContext *context); G_END_DECLS #endif /* GJS_PROFILER_PRIVATE_H */ cjs-4.4.0/cjs/profiler.cpp000066400000000000000000000445021356375132300154200ustar00rootroot00000000000000/* profiler.cpp * * Copyright (C) 2016 Christian Hergert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include "jsapi-wrapper.h" #include #include "context.h" #include "jsapi-util.h" #include "profiler-private.h" #ifdef ENABLE_PROFILER # include # include "util/sp-capture-writer.h" #endif /* * This is mostly non-exciting code wrapping the builtin Profiler in * mozjs. In particular, the profiler consumer is required to "bring your * own sampler". We do the very non-surprising thing of using POSIX * timers to deliver SIGPROF to the thread containing the JSContext. * * However, we do use a Linux'ism that allows us to deliver the signal * to only a single thread. Doing this in a generic fashion would * require thread-registration so that we can mask SIGPROF from all * threads execpt the JS thread. The gecko engine uses tgkill() to do * this with a secondary thread instead of using POSIX timers. We could * do this too, but it would still be Linux-only. * * Another option might be to use pthread_kill() and a secondary thread * to perform the notification. * * From within the signal handler, we process the current stack as * delivered to us from the JSContext. Any pointer data that comes from * the runtime has to be copied, so we keep our own dedup'd string * pointers for JavaScript file/line information. Non-JS instruction * pointers are just fine, as they can be resolved by parsing the ELF for * the file mapped on disk containing that address. * * As much of this code has to run from signal handlers, it is very * important that we don't use anything that can malloc() or lock, or * deadlocks are very likely. Most of GjsProfilerCapture is signal-safe. */ #define SAMPLES_PER_SEC G_GUINT64_CONSTANT(1000) #define NSEC_PER_SEC G_GUINT64_CONSTANT(1000000000) G_DEFINE_POINTER_TYPE(GjsProfiler, gjs_profiler) struct _GjsProfiler { #ifdef ENABLE_PROFILER /* The stack for the JSContext profiler to use for current stack * information while executing. We will look into this during our * SIGPROF handler. */ js::ProfileEntry stack[1024]; /* The context being profiled */ JSContext *cx; /* Buffers and writes our sampled stacks */ SpCaptureWriter *capture; #endif /* ENABLE_PROFILER */ /* The filename to write to */ char *filename; #ifdef ENABLE_PROFILER /* Our POSIX timer to wakeup SIGPROF */ timer_t timer; /* The depth of @stack. This value may be larger than the * number of elements in stack, and so you MUST ensure you * don't walk past the end of stack[] when iterating. */ uint32_t stack_depth; /* Cached copy of our pid */ GPid pid; /* GLib signal handler ID for SIGUSR2 */ unsigned sigusr2_id; #endif /* ENABLE_PROFILER */ /* If we are currently sampling */ unsigned running : 1; }; static GjsContext *profiling_context; #ifdef ENABLE_PROFILER /* * gjs_profiler_extract_maps: * * This function will write the mapped section information to the * capture file so that the callgraph builder can generate symbols * from the stack addresses provided. * * Returns: %TRUE if successful; otherwise %FALSE and the profile * should abort. */ static bool gjs_profiler_extract_maps(GjsProfiler *self) { using AutoStrv = std::unique_ptr; int64_t now = g_get_monotonic_time() * 1000L; g_assert(((void) "Profiler must be set up before extracting maps", self)); GjsAutoChar path = g_strdup_printf("/proc/%jd/maps", intmax_t(self->pid)); char *content_tmp; size_t len; if (!g_file_get_contents(path, &content_tmp, &len, nullptr)) return false; GjsAutoChar content = content_tmp; AutoStrv lines(g_strsplit(content, "\n", 0), g_strfreev); for (size_t ix = 0; lines.get()[ix]; ix++) { char file[256]; unsigned long start; unsigned long end; unsigned long offset; unsigned long inode; file[sizeof file - 1] = '\0'; int r = sscanf(lines.get()[ix], "%lx-%lx %*15s %lx %*x:%*x %lu %255s", &start, &end, &offset, &inode, file); if (r != 5) continue; if (strcmp("[vdso]", file) == 0) { offset = 0; inode = 0; } if (!sp_capture_writer_add_map(self->capture, now, -1, self->pid, start, end, offset, inode, file)) return false; } return true; } #endif /* ENABLE_PROFILER */ /* * _gjs_profiler_new: * @context: The #GjsContext to profile * * This creates a new profiler for the #JSContext. It is important that * this instance is freed with _gjs_profiler_free() before the context is * destroyed. * * Call gjs_profiler_start() to enable the profiler, and gjs_profiler_stop() * when you have finished. * * The profiler works by enabling the JS profiler in spidermonkey so that * sample information is available. A POSIX timer is used to signal SIGPROF * to the process on a regular interval to collect the most recent profile * sample and stash it away. It is a programming error to mask SIGPROF from * the thread controlling the JS context. * * If another #GjsContext already has a profiler, or @context already has one, * then returns %NULL instead. * * Returns: (transfer full) (nullable): A newly allocated #GjsProfiler */ GjsProfiler * _gjs_profiler_new(GjsContext *context) { g_return_val_if_fail(context, nullptr); if (profiling_context == context) { g_critical("You can only create one profiler at a time."); return nullptr; } if (profiling_context) { g_message("Not going to profile GjsContext %p; you can only profile " "one context at a time.", context); return nullptr; } GjsProfiler *self = g_new0(GjsProfiler, 1); #ifdef ENABLE_PROFILER self->cx = static_cast(gjs_context_get_native_context(context)); self->pid = getpid(); #endif profiling_context = context; return self; } /* * _gjs_profiler_free: * @self: A #GjsProfiler * * Frees a profiler instance and cleans up any allocated data. * * If the profiler is running, it will be stopped. This may result in blocking * to write the contents of the buffer to the underlying file-descriptor. */ void _gjs_profiler_free(GjsProfiler *self) { if (!self) return; if (self->running) gjs_profiler_stop(self); profiling_context = nullptr; g_clear_pointer(&self->filename, g_free); #ifdef ENABLE_PROFILER g_clear_pointer(&self->capture, sp_capture_writer_unref); #endif g_free(self); } /* * _gjs_profiler_is_running: * @self: A #GjsProfiler * * Checks if the profiler is currently running. This means that the JS * profiler is enabled and POSIX signal timers are registered. * * Returns: %TRUE if the profiler is active. */ bool _gjs_profiler_is_running(GjsProfiler *self) { g_return_val_if_fail(self, false); return self->running; } #ifdef ENABLE_PROFILER /* Run from a signal handler */ static inline unsigned gjs_profiler_get_stack_size(GjsProfiler *self) { g_assert(((void) "Profiler must be set up before getting stack size", self)); /* * Note that stack_depth could be larger than the number of * items we have in our stack space. We must protect ourselves * against overflowing by discarding anything after that depth * of the stack. */ return std::min(self->stack_depth, uint32_t(G_N_ELEMENTS(self->stack))); } static void gjs_profiler_sigprof(int signum, siginfo_t *info, void *unused) { GjsProfiler *self = gjs_context_get_profiler(profiling_context); g_assert(((void) "SIGPROF handler called with invalid signal info", info)); g_assert(((void) "SIGPROF handler called with other signal", info->si_signo == SIGPROF)); /* * NOTE: * * This is the SIGPROF signal handler. Everything done in this thread * needs to be things that are safe to do in a signal handler. One thing * that is not okay to do, is *malloc*. */ if (!self || info->si_code != SI_TIMER) return; size_t depth = gjs_profiler_get_stack_size(self); if (depth == 0) return; static_assert(G_N_ELEMENTS(self->stack) < G_MAXUSHORT, "Number of elements in profiler stack should be expressible" "in an unsigned short"); int64_t now = g_get_monotonic_time() * 1000L; /* NOTE: cppcheck warns that alloca() is not recommended since it can * easily overflow the stack; however, dynamic allocation is not an option * here since we are in a signal handler. * Another option would be to always allocate G_N_ELEMENTS(self->stack), * but that is by definition at least as large of an allocation and * therefore is more likely to overflow. */ // cppcheck-suppress allocaCalled SpCaptureAddress *addrs = static_cast(alloca(sizeof *addrs * depth)); for (size_t ix = 0; ix < depth; ix++) { js::ProfileEntry& entry = self->stack[ix]; const char *label = entry.label(); size_t flipped = depth - 1 - ix; /* * SPSProfiler will put "js::RunScript" on the stack, but it has * a stack address of "this", which is not terribly useful since * everything will show up as [stack] when building callgraphs. */ if (label) addrs[flipped] = sp_capture_writer_add_jitmap(self->capture, label); else addrs[flipped] = SpCaptureAddress(entry.stackAddress()); } if (!sp_capture_writer_add_sample(self->capture, now, -1, self->pid, addrs, depth)) gjs_profiler_stop(self); } #endif /* ENABLE_PROFILER */ /** * gjs_profiler_start: * @self: A #GjsProfiler * * As expected, this starts the GjsProfiler. * * This will enable the underlying JS profiler and register a POSIX timer to * deliver SIGPROF on the configured sampling frequency. * * To reduce sampling overhead, #GjsProfiler stashes information about the * profile to be calculated once the profiler has been disabled. Calling * gjs_profiler_stop() will result in that delayed work to be completed. * * You should call gjs_profiler_stop() when the profiler is no longer needed. */ void gjs_profiler_start(GjsProfiler *self) { g_return_if_fail(self); if (self->running) return; #ifdef ENABLE_PROFILER g_return_if_fail(!self->capture); struct sigaction sa = { 0 }; struct sigevent sev = { 0 }; struct itimerspec its = { 0 }; struct itimerspec old_its; GjsAutoChar path = g_strdup(self->filename); if (!path) path = g_strdup_printf("gjs-%jd.syscap", intmax_t(self->pid)); self->capture = sp_capture_writer_new(path, 0); if (!self->capture) { g_warning("Failed to open profile capture"); return; } if (!gjs_profiler_extract_maps(self)) { g_warning("Failed to extract proc maps"); g_clear_pointer(&self->capture, sp_capture_writer_unref); return; } self->stack_depth = 0; /* Setup our signal handler for SIGPROF delivery */ sa.sa_flags = SA_RESTART | SA_SIGINFO; sa.sa_sigaction = gjs_profiler_sigprof; sigemptyset(&sa.sa_mask); if (sigaction(SIGPROF, &sa, nullptr) == -1) { g_warning("Failed to register sigaction handler: %s", g_strerror(errno)); g_clear_pointer(&self->capture, sp_capture_writer_unref); return; } /* * Create our SIGPROF timer * * We want to receive a SIGPROF signal on the JS thread using our * configured sampling frequency. Instead of allowing any thread to be * notified, we set the _tid value to ensure that only our thread gets * delivery of the signal. This feature is generally just for * threading implementations, but it works for us as well and ensures * that the thread is blocked while we capture the stack. */ sev.sigev_notify = SIGEV_THREAD_ID; sev.sigev_signo = SIGPROF; sev._sigev_un._tid = syscall(__NR_gettid); if (timer_create(CLOCK_MONOTONIC, &sev, &self->timer) == -1) { g_warning("Failed to create profiler timer: %s", g_strerror(errno)); g_clear_pointer(&self->capture, sp_capture_writer_unref); return; } /* Calculate sampling interval */ its.it_interval.tv_sec = 0; its.it_interval.tv_nsec = NSEC_PER_SEC / SAMPLES_PER_SEC; its.it_value.tv_sec = 0; its.it_value.tv_nsec = NSEC_PER_SEC / SAMPLES_PER_SEC; /* Now start this timer */ if (timer_settime(self->timer, 0, &its, &old_its) != 0) { g_warning("Failed to enable profiler timer: %s", g_strerror(errno)); timer_delete(self->timer); g_clear_pointer(&self->capture, sp_capture_writer_unref); return; } self->running = true; /* Notify the JS runtime of where to put stack info */ js::SetContextProfilingStack(self->cx, self->stack, &self->stack_depth, G_N_ELEMENTS(self->stack)); /* Start recording stack info */ js::EnableContextProfilingStack(self->cx, true); g_message("Profiler started"); #else /* !ENABLE_PROFILER */ self->running = true; g_message("Profiler is disabled. Recompile with --enable-profiler to use."); #endif /* ENABLE_PROFILER */ } /** * gjs_profiler_stop: * @self: A #GjsProfiler * * Stops a currently running #GjsProfiler. If the profiler is not running, * this function will do nothing. * * Some work may be delayed until the end of the capture. Such delayed work * includes flushing the resulting samples and file location information to * disk. * * This may block while writing to disk. Generally, the writes are delivered * to a tmpfs device, and are therefore negligible. */ void gjs_profiler_stop(GjsProfiler *self) { /* Note: can be called from a signal handler */ g_assert(self); if (!self->running) return; #ifdef ENABLE_PROFILER struct itimerspec its = { 0 }; timer_settime(self->timer, 0, &its, nullptr); timer_delete(self->timer); js::EnableContextProfilingStack(self->cx, false); js::SetContextProfilingStack(self->cx, nullptr, nullptr, 0); sp_capture_writer_flush(self->capture); g_clear_pointer(&self->capture, sp_capture_writer_unref); self->stack_depth = 0; g_message("Profiler stopped"); #endif /* ENABLE_PROFILER */ self->running = false; } #ifdef ENABLE_PROFILER static gboolean gjs_profiler_sigusr2(void *data) { auto context = static_cast(data); GjsProfiler *current_profiler = gjs_context_get_profiler(context); if (current_profiler) { if (_gjs_profiler_is_running(current_profiler)) gjs_profiler_stop(current_profiler); else gjs_profiler_start(current_profiler); } return G_SOURCE_CONTINUE; } #endif /* ENABLE_PROFILER */ /* * _gjs_profiler_setup_signals: * @context: a #GjsContext with a profiler attached * * If you want to simply allow profiling of your process with minimal * fuss, simply call gjs_profiler_setup_signals(). This will allow * enabling and disabling the profiler with SIGUSR2. You must call * this from main() immediately when your program starts and must not * block SIGUSR2 from your signal mask. * * If this is not sufficient, use gjs_profiler_chain_signal() from your * own signal handler to pass the signal to a GjsProfiler. */ void _gjs_profiler_setup_signals(GjsProfiler *self, GjsContext *context) { g_return_if_fail(context == profiling_context); #ifdef ENABLE_PROFILER if (self->sigusr2_id != 0) return; self->sigusr2_id = g_unix_signal_add(SIGUSR2, gjs_profiler_sigusr2, context); #else /* !ENABLE_PROFILER */ g_message("Profiler is disabled. Not setting up signals."); #endif /* ENABLE_PROFILER */ } /** * gjs_profiler_chain_signal: * @context: a #GjsContext with a profiler attached * @info: #siginfo_t passed in to signal handler * * Use this to pass a signal info caught by another signal handler to a * GjsProfiler. This might be needed if you have your own complex signal * handling system for which GjsProfiler cannot simply add a SIGUSR2 handler. * * This function should only be called from the JS thread. * * Returns: %TRUE if the signal was handled. */ bool gjs_profiler_chain_signal(GjsContext *context, siginfo_t *info) { #ifdef ENABLE_PROFILER if (info) { if (info->si_signo == SIGPROF) { gjs_profiler_sigprof(SIGPROF, info, nullptr); return true; } if (info->si_signo == SIGUSR2) { gjs_profiler_sigusr2(context); return true; } } #endif /* ENABLE_PROFILER */ return false; } /** * gjs_profiler_set_filename: * @self: A #GjsProfiler * @filename: string containing a filename * * Set the file to which profiling data is written when the @self is stopped. * By default, this is `gjs-$PID.syscap` in the current directory. */ void gjs_profiler_set_filename(GjsProfiler *self, const char *filename) { g_return_if_fail(self); g_return_if_fail(!self->running); g_free(self->filename); self->filename = g_strdup(filename); } cjs-4.4.0/cjs/profiler.h000066400000000000000000000032161356375132300150620ustar00rootroot00000000000000/* profiler.h * * Copyright (C) 2016 Christian Hergert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef GJS_PROFILER_H #define GJS_PROFILER_H #include #include G_BEGIN_DECLS #define GJS_TYPE_PROFILER (gjs_profiler_get_type()) typedef struct _GjsProfiler GjsProfiler; GJS_EXPORT GType gjs_profiler_get_type(void); GJS_EXPORT void gjs_profiler_set_filename(GjsProfiler *self, const char *filename); GJS_EXPORT void gjs_profiler_start(GjsProfiler *self); GJS_EXPORT void gjs_profiler_stop(GjsProfiler *self); G_END_DECLS #endif /* GJS_PROFILER_H */ cjs-4.4.0/cjs/stack.cpp000066400000000000000000000050461356375132300147030ustar00rootroot00000000000000/* This file contains code derived from xpcdebug.cpp in Mozilla. The license * for that file follows: */ /* * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * 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 Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * John Bandhauer (original author) * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include #include #include #include "context.h" #include "jsapi-util.h" #include "jsapi-wrapper.h" void gjs_context_print_stack_stderr(GjsContext *context) { JSContext *cx = (JSContext*) gjs_context_get_native_context(context); g_printerr("== Stack trace for context %p ==\n", context); js::DumpBacktrace(cx, stderr); } void gjs_dumpstack(void) { GList *contexts = gjs_context_get_all(); GList *iter; for (iter = contexts; iter; iter = iter->next) { GjsContext *context = (GjsContext*)iter->data; gjs_context_print_stack_stderr(context); g_object_unref(context); } g_list_free(contexts); } cjs-4.4.0/cjs/type-module.cpp000066400000000000000000000044021356375132300160350ustar00rootroot00000000000000/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2012 Giovanni Campagna * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "type-module.h" struct _GjsTypeModule { GTypeModule parent; }; struct _GjsTypeModuleClass { GTypeModuleClass parent_class; }; G_DEFINE_TYPE (GjsTypeModule, gjs_type_module, G_TYPE_TYPE_MODULE) static GjsTypeModule *global_type_module; GjsTypeModule * gjs_type_module_get () { if (global_type_module == NULL) { global_type_module = (GjsTypeModule *) g_object_new (GJS_TYPE_TYPE_MODULE, NULL); } return global_type_module; } static gboolean gjs_type_module_load (GTypeModule *self) { return true; } G_GNUC_NORETURN static void gjs_type_module_unload (GTypeModule *self) { g_assert_not_reached (); } static void gjs_type_module_class_init (GjsTypeModuleClass *klass) { GTypeModuleClass *type_module_class; type_module_class = G_TYPE_MODULE_CLASS (klass); type_module_class->load = gjs_type_module_load; type_module_class->unload = gjs_type_module_unload; } static void gjs_type_module_init (GjsTypeModule *self) { /* Prevent the use count from ever dropping to zero */ g_type_module_use (G_TYPE_MODULE (self)); } cjs-4.4.0/cjs/type-module.h000066400000000000000000000041331356375132300155030ustar00rootroot00000000000000/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2012 Giovanni Campagna * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef GJS_TYPE_MODULE_H #define GJS_TYPE_MODULE_H #include typedef struct _GjsTypeModule GjsTypeModule; typedef struct _GjsTypeModuleClass GjsTypeModuleClass; #define GJS_TYPE_TYPE_MODULE (gjs_type_module_get_type ()) #define GJS_TYPE_MODULE(module) (G_TYPE_CHECK_INSTANCE_CAST ((module), GJS_TYPE_TYPE_MODULE, GjsTypeModule)) #define GJS_TYPE_MODULE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GJS_TYPE_TYPE_MODULE, GjsTypeModuleClass)) #define GJS_IS_TYPE_MODULE(module) (G_TYPE_CHECK_INSTANCE_TYPE ((module), GJS_TYPE_TYPE_MODULE)) #define GJS_IS_TYPE_MODULE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GJS_TYPE_TYPE_MODULE)) #define GJS_TYPE_MODULE_GET_CLASS(module) (G_TYPE_INSTANCE_GET_CLASS ((module), GJS_TYPE_TYPE_MODULE, GjsTypeModuleClass)) GType gjs_type_module_get_type (void) G_GNUC_CONST; GjsTypeModule *gjs_type_module_get (void); #endif cjs-4.4.0/configure.ac000066400000000000000000000265511356375132300146050ustar00rootroot00000000000000# -*- Autoconf -*- # Process this file with autoconf to produce a configure script. m4_define(pkg_major_version, 4) m4_define(pkg_minor_version, 4) m4_define(pkg_micro_version, 0) m4_define(pkg_version, pkg_major_version.pkg_minor_version.pkg_micro_version) m4_define(pkg_int_version, (pkg_major_version * 100 + pkg_minor_version) * 100 + pkg_micro_version) AC_PREREQ([2.64]) AC_INIT([cjs],[pkg_version],[https://github.com/linuxmint/cjs/issues/new],[cjs],[https://github.com/linuxmint/cjs]) AM_INIT_AUTOMAKE([1.11.1 subdir-objects dist-xz no-dist-gzip tar-ustar -Wno-portability]) m4_ifdef([AX_IS_RELEASE], [AX_IS_RELEASE([git-directory])]) AC_CONFIG_SRCDIR([cjs/console.cpp]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_HEADERS([config.h]) AC_REQUIRE_AUX_FILE([tap-driver.sh]) AC_SUBST(GJS_VERSION, m4_eval(pkg_int_version)) AC_DEFINE([GJS_VERSION], pkg_int_version, [The gjs version as an integer]) AC_SUBST([GETTEXT_PACKAGE], [cjs]) AC_DEFINE_UNQUOTED([GETTEXT_PACKAGE], ["$GETTEXT_PACKAGE"], [The name of the gettext domain]) AM_MAINTAINER_MODE([enable]) AM_SILENT_RULES([yes]) # our first pkg-config invocation is conditional, ensure macros still work PKG_PROG_PKG_CONFIG PKG_INSTALLDIR AC_LANG([C++]) AC_USE_SYSTEM_EXTENSIONS AC_PROG_CXX AX_CXX_COMPILE_STDCXX_11 LT_PREREQ([2.2.0]) # no stupid static libraries LT_INIT([disable-static]) # Other programs AC_PROG_MKDIR_P AC_PROG_LN_S AC_PROG_SED AC_PROG_AWK AX_COMPILER_FLAGS AX_CODE_COVERAGE dnl Clang does not need to link with -lgcov AX_CHECK_LINK_FLAG([-lgcov],, [ CODE_COVERAGE_LIBS= CODE_COVERAGE_LDFLAGS= ]) # Checks for libraries. m4_define(glib_required_version, 2.42.0) GOBJECT_INTROSPECTION_REQUIRE([1.41.4]) GOBJECT_REQUIREMENT="gobject-2.0 >= glib_required_version" gjs_base_packages="$GOBJECT_REQUIREMENT gio-2.0" common_packages="gthread-2.0 gio-2.0 >= glib_required_version mozjs-52" gjs_packages="gobject-introspection-1.0 libffi $common_packages" gjs_cairo_packages="cairo cairo-gobject $common_packages" gjs_gtk_packages="gtk+-3.0 >= 3.14.0" # gjs-tests links against everything gjstests_packages="gio-unix-2.0 $gjs_packages" CJS_PACKAGE_REQUIRES="$GOBJECT_REQUIREMENT" CJS_PACKAGE_REQUIRES_PRIVATE="$gjs_packages" PKG_CHECK_MODULES([GJS], [$GOBJECT_REQUIREMENT]) PKG_CHECK_MODULES([GJS_PRIVATE], [$gjs_packages]) PKG_CHECK_MODULES([GJS_GDBUS], [$gjs_base_packages]) PKG_CHECK_MODULES([GJS_CONSOLE], [$gjs_base_packages]) PKG_CHECK_MODULES([GJSTESTS], [$gjstests_packages]) # Optional cairo dep (enabled by default) AC_ARG_WITH(cairo, [AS_HELP_STRING([--without-cairo], [Don't build cairo module])]) AS_IF([test "x$with_cairo" != "xno"], [PKG_CHECK_MODULES([GJS_CAIRO], [$gjs_cairo_packages], [have_cairo=yes], [have_cairo=no]) CJS_PACKAGE_REQUIRES_PRIVATE="$CJS_PACKAGE_REQUIRES_PRIVATE $gjs_cairo_packages"], [have_cairo=no]) AM_CONDITIONAL(ENABLE_CAIRO, test x$have_cairo = xyes) AS_IF([test x$have_cairo = xyes], [ AC_DEFINE([ENABLE_CAIRO],[1],[Define if you want to build with cairo support]) PKG_CHECK_MODULES([GJS_CAIRO_XLIB], [cairo-xlib], [], [AC_MSG_WARN([Cairo-xlib support not found]) CJS_PACKAGE_REQUIRES_PRIVATE="$CJS_PACKAGE_REQUIRES_PRIVATE cairo-xlib"]) ], [AS_IF([test "x$with_cairo" = "xyes"], [AC_MSG_ERROR([Cairo requested but not found])])]) # Optional GTK+ dep (enabled by default) AC_ARG_WITH(gtk, [AS_HELP_STRING([--without-gtk], [Don't build GTK-related code])]) AS_IF([test "x$with_gtk" != "xno"], [PKG_CHECK_MODULES([GJS_GTK], [$gjs_gtk_packages], [have_gtk=yes], [have_gtk=no]) CJS_PACKAGE_REQUIRES_PRIVATE="$CJS_PACKAGE_REQUIRES_PRIVATE $gjs_gtk_packages"], [have_gtk=no]) AM_CONDITIONAL(ENABLE_GTK, test x$have_gtk = xyes) AS_IF([test x$have_gtk = xyes], [ AC_DEFINE([ENABLE_GTK],[1],[Define if you want to build with GTK+ support]) ], [AS_IF([test "x$with_gtk" = "xyes"], [AC_MSG_ERROR([GTK requested but not found])])]) # Some Linux APIs required for profiler AC_ARG_ENABLE([profiler], [AS_HELP_STRING([--disable-profiler], [Don't build profiler])]) AS_IF([test x$enable_profiler != xno], [ gl_TIMER_TIME AS_IF([test x$ac_cv_func_timer_settime = xno], [AC_MSG_ERROR([The profiler is currently only supported on Linux. Configure with --disable-profiler to skip it on other platforms.])]) AC_DEFINE([ENABLE_PROFILER], [1], [Define if the profiler should be built.]) ]) AM_CONDITIONAL([ENABLE_PROFILER], [test x$enable_profiler != xno]) PKG_CHECK_VAR([GI_DATADIR], [gobject-introspection-1.0], [gidatadir]) AC_SUBST([CJS_PACKAGE_REQUIRES]) AC_SUBST([CJS_PACKAGE_REQUIRES_PRIVATE]) # readline LIBS_no_readline=$LIBS # On some systems we need to link readline to a termcap compatible # library. gjs_cv_lib_readline=no AC_MSG_CHECKING([how to link readline libs]) for gjs_libtermcap in "" ncursesw ncurses curses termcap; do if test -z "$gjs_libtermcap"; then READLINE_LIBS="-lreadline" else READLINE_LIBS="-lreadline -l$gjs_libtermcap" fi LIBS="$READLINE_LIBS $LIBS_no_readline" AC_LINK_IFELSE( [AC_LANG_CALL([],[readline])], [gjs_cv_lib_readline=yes]) if test $gjs_cv_lib_readline = yes; then break fi done if test $gjs_cv_lib_readline = no; then AC_MSG_RESULT([none]) READLINE_LIBS="" ac_cv_header_readline_readline_h=no else AC_MSG_RESULT([$READLINE_LIBS]) AC_CHECK_HEADERS([readline/readline.h]) fi AC_SUBST([READLINE_LIBS]) # End of readline checks: restore LIBS LIBS=$LIBS_no_readline AC_MSG_CHECKING([whether printf() accepts '%Id' for alternative integer output]) CXXFLAGS_save="$CXXFLAGS" CXXFLAGS="-Werror -Wformat -pedantic-errors" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[printf("%Id", (int)0);]])], [ AC_MSG_RESULT([yes]) AC_DEFINE([HAVE_PRINTF_ALTERNATIVE_INT], [1], [Define to 1 if printf() accepts '%Id' for alternative integer output]) ], [AC_MSG_RESULT([no])]) CXXFLAGS="$CXXFLAGS_save" AC_ARG_ENABLE(installed_tests, [AS_HELP_STRING([--enable-installed-tests], [Install test programs @<:@default: no@:>@])]) AM_CONDITIONAL(BUILDOPT_INSTALL_TESTS, test x$enable_installed_tests = xyes) dnl dnl Tracing dnl AC_ARG_ENABLE([dtrace], [AS_HELP_STRING([--enable-dtrace], [Include dtrace trace support @<:@default: no@:>@])]) AC_ARG_ENABLE([systemtap], [AS_HELP_STRING([--enable-systemtap], [Include systemtap trace support (implies --enable-dtrace) @<:@default: no@:>@])]) AS_IF([test "x$enable_systemtap" = "xyes"], [enable_dtrace=yes]) AM_CONDITIONAL([ENABLE_SYSTEMTAP], [test "x$enable_systemtap" = "xyes"]) AS_IF([test "x$enable_dtrace" = "xyes"], [ AC_PATH_PROG([DTRACE], [dtrace]) AS_IF([test -z "$DTRACE"], [AC_MSG_ERROR([dtrace is required for --enable-dtrace or --enable-systemtap])]) AC_CHECK_HEADER([sys/sdt.h],, [AC_MSG_ERROR([sys/sdt.h header is required for --enable-dtrace or --enable-systemtap])]) AC_DEFINE([HAVE_DTRACE], [1], [Define to 1 if using dtrace probes.]) ]) AM_CONDITIONAL([ENABLE_DTRACE], [test "x$enable_dtrace" = "xyes"]) dnl dnl Check for -Bsymbolic-functions linker flag used to avoid dnl intra-library PLT jumps, if available. dnl AC_ARG_ENABLE([Bsymbolic], [AS_HELP_STRING([--disable-Bsymbolic], [avoid linking with -Bsymbolic])]) AS_IF([test "x$enable_Bsymbolic" != "xno"], [AX_APPEND_LINK_FLAGS([-Bsymbolic-functions])]) dnl If SpiderMonkey was compiled with --enable-debug, then we need to define dnl -DDEBUG before including js-config.h. AC_MSG_CHECKING([whether SpiderMonkey was configured with --enable-debug]) CPPFLAGS_save="$CPPFLAGS" CPPFLAGS="$GJS_CFLAGS" AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ #include #ifdef JS_DEBUG #error debug yes, if we didn't already error out due to DEBUG not being defined #endif ]])], [AC_MSG_RESULT([no])], [ AC_DEFINE([HAVE_DEBUG_SPIDERMONKEY], [1], [Define to 1 if SpiderMonkey was compiled with --enable-debug]) AC_MSG_RESULT([yes]) ]) CPPFLAGS="$CPPFLAGS_save" dnl dnl Check for -fsanitize=address and -fsanitize=undefined support dnl AC_ARG_ENABLE([asan], [AS_HELP_STRING([--enable-asan], [Build with address sanitizer support @<:@default: no@:>@])]) AS_IF([test "x$enable_asan" = "xyes"], [ AX_CHECK_COMPILE_FLAG([-fsanitize=address -fno-omit-frame-pointer -g], [ AX_CHECK_LINK_FLAG([-fsanitize=address], [SAN_FLAGS="-fsanitize=address"]) ]) ]) AC_ARG_ENABLE([ubsan], [AS_HELP_STRING([--enable-ubsan], [Build with undefined behavior sanitizer support @<:@default: no@:>@])]) AS_IF([test "x$enable_ubsan" = "xyes"], [ AX_CHECK_COMPILE_FLAG([-fsanitize=undefined -fno-sanitize=vptr -fno-omit-frame-pointer -g], [ AX_CHECK_LINK_FLAG([-fsanitize=undefined -fno-sanitize=vptr], [SAN_FLAGS="$SAN_FLAGS -fsanitize=undefined -fno-sanitize=vptr"]) ]) ]) # According to https://github.com/google/sanitizers/issues/380, asan is not # compatible with -no-undefined. NO_UNDEFINED_FLAG=-no-undefined AS_IF([test -n "${SAN_FLAGS}"], [ GJS_CFLAGS="$GJS_CFLAGS $SAN_FLAGS -fno-omit-frame-pointer -g" # We have to clobber LDFLAGS here and not use AM_LDFLAGS, or else # g-ir-scanner will not pick it up. LDFLAGS="$LDFLAGS $SAN_FLAGS" NO_UNDEFINED_FLAG= ]) AC_SUBST([NO_UNDEFINED_FLAG]) AC_ARG_WITH([xvfb-tests], [AS_HELP_STRING([--with-xvfb-tests], [Run all tests under an XVFB server @<:@default=no@:>@])]) AS_IF([test "x$with_xvfb_tests" = "xyes"], [ AC_PATH_PROG([XVFB], [Xvfb]) AS_IF([test -z "$XVFB"], [AC_MSG_ERROR([Xvfb is required for --with-xvfb-tests])]) ]) AM_CONDITIONAL([XVFB_TESTS], [test "x$with_xvfb_tests" = "xyes"]) AC_ARG_WITH([dbus-tests], [AS_HELP_STRING([--without-dbus-tests], [Don't try to use DBus during make check @<:@default=use@:>@])]) AS_IF([test "x$with_dbus_tests" != "xno"], [ AC_PATH_PROG([DBUS_RUN_SESSION], [dbus-run-session]) AS_IF([test -z "$DBUS_RUN_SESSION"], [AC_MSG_ERROR([dbus-run-session is required for --with-dbus-tests])]) ]) AM_CONDITIONAL([DBUS_TESTS], [test "x$with_dbus_tests" != "xno"]) AC_SUBST([gjsjsdir], [\${datadir}/cjs-1.0]) dnl automake 1.11/1.12 defines this but does not substitute it AC_SUBST([pkglibexecdir], ["${libexecdir}/${PACKAGE}"]) AC_CONFIG_FILES([Makefile cjs-1.0.pc win32/config.h.win32]) dnl Symlink the files from gobject-introspection's test libraries into the tree; dnl Automake plans to drop support for compiling them in-place. AC_CONFIG_LINKS([ installed-tests/gitestmacros.h:$GI_DATADIR/tests/gitestmacros.h installed-tests/regress.c:$GI_DATADIR/tests/regress.c installed-tests/regress.h:$GI_DATADIR/tests/regress.h installed-tests/warnlib.c:$GI_DATADIR/tests/warnlib.c installed-tests/warnlib.h:$GI_DATADIR/tests/warnlib.h installed-tests/gimarshallingtests.c:$GI_DATADIR/tests/gimarshallingtests.c installed-tests/gimarshallingtests.h:$GI_DATADIR/tests/gimarshallingtests.h ]) AC_OUTPUT # Warn about conditions that affect runtime PKG_CHECK_EXISTS([gobject-introspection-1.0 >= 1.46.0], [], [ AC_MSG_WARN([You do not have a new enough version of gobject-introspection to run the tests. You can still build CJS, but some tests will fail.])]) TEST_MSG= AM_COND_IF([XVFB_TESTS], [TEST_MSG="xvfb "]) AM_COND_IF([DBUS_TESTS], [TEST_MSG="${TEST_MSG}dbus"]) AS_IF([test -z "$TEST_MSG"], [TEST_MSG="nothing special"]) AC_MSG_RESULT([ $PACKAGE_NAME $VERSION GJS_CFLAGS: ${GJS_CFLAGS} GJS_LIBS: ${GJS_LIBS} cairo: ${have_cairo} GTK+: ${have_gtk} readline: ${ac_cv_header_readline_readline_h} dtrace: ${enable_dtrace:-no} systemtap: ${enable_systemtap:-no} Profiler: ${enable_profiler:-yes} Run tests under: ${TEST_MSG} Code coverage: ${enable_code_coverage} ]) cjs-4.4.0/debian/000077500000000000000000000000001356375132300135305ustar00rootroot00000000000000cjs-4.4.0/debian/changelog000066400000000000000000000607641356375132300154170ustar00rootroot00000000000000cjs (4.4.0) tricia; urgency=medium * 4.4.0 -- Clement Lefebvre Sat, 16 Nov 2019 11:42:59 +0100 cjs (4.2.0) tina; urgency=medium [ Eli Schwartz ] * update deprecated glib function (#72) [ Leigh Scott ] * Fix new autoconf-archive compile issue (#73) -- Clement Lefebvre Fri, 14 Jun 2019 12:43:24 +0100 cjs (4.0.0) tessa; urgency=medium [ Giovanni Campagna ] * Add marshalling for native JS errors [ Jason Hicks ] * object: Associate callbacks with the object on which they're installed [ Giovanni Campagna ] * Use the GIVFuncInfo to hook up vfuncs instead of the GICallbackInfo * Capture JS exceptions from invoked virtual functions [ Jason Hicks ] * global: Add a bootstrap system * coverage: Use global bootstrap to add JS coverage code * coverage: Don't error out on various files * coverage: Scan ES6 classes for functions [ Philip Chimento ] * coverage: Correct AST walking for labelled statements * coverage: Walk AST in more cases [ Jason Hicks ] * coverage: Refactor bootstrap code to use ES6 classes [ Philip Chimento ] * error: Use JSProtoKey to get Error constructor * object: Remove hash-x32 implementation * object: Merge weak_pointer_list and dissociate_list * object: Remove vfunc list [ Jason Hicks ] * jsapi-util: Remove jsapi-private * tests: Use assertions in coverage tests [ Philip Chimento ] * js: Convert scripts to UTF-16 before evaluating [ Jason Hicks ] * coverage: Use LCOV data from SpiderMonkey [ Philip Chimento ] * coverage: Propagate gjs_coverage_write_statistics() errors * tests: Add regression test for vfunc crash avoidance * jsapi-util-string: Use mozjs UTF8-to-JSString conversion * jsapi-util-string: Missing error check * importer: Missing error check * jsapi-util: Improve check for SyntaxError * js: Use JS_EncodeStringToUTF8() directly where advantageous * param, importer: Spurious error when resolving non-string * js: Don't convert to UTF-8 to compare ASCII names * suppressions: Eliminate machine-dependent expressions [ Marco Trevisan (Treviño) ] * object: check in gjs_typecheck_object if the object has been finalized * object: don't resolve or set,get properties on finalized elements * object: add better logging when invalid access happens * object: reset the keep alive flag on wrapper when object is disposed * installed-tests/js: add testGObjectDestructionAccess to verify access to destryed objects [ Philip Chimento ] * object: Leave out param untouched on error [ Jason Hicks ] * maint: highlight the license * release: Add NEWS for 1.51.3 [ Andy Holmes ] * Amend gtk-application.js * mv gtk-window.js to amend gtk.js, including suggestions [ makepost ] * arg: String as GdkAtom, and GdkAtom as string [ Valentín Barros ] * Test case improved in `installed-tests/js/testCairo.js` [fixes #27] [ Marco Trevisan (Treviño) ] * object: Only show critical errors on access to finalized objects (Closes: #21) [ Philip Chimento ] * coverage: Remove unused JSCompartmentOptions * js: Remove unnecessary property getters and setters [ Marco Trevisan (Treviño) ] * object: Show error when using proto functions (connect*, emit) on destroyed object * object: make clear in toString() proto method when the object is finalized * proxyutils: remove trailing spaces [ Philip Chimento ] * release: Prepare for 1.51.4 * arg: Marshal GType of 0 to null * release: Include NEWS from 1.50.3 [ Jason Hicks ] * profiler: add SPSProfiler implementation [ Philip Chimento ] * profiler: Implement --profile flag behaviour * profiler: Allow enabling with GJS_ENABLE_PROFILER=1 * profiler: Disable profiler in interactive mode * profiler: Disable SIGUSR2 start/stop [ Jason Hicks ] * profiler: Suppress cppcheck warning * profiler: Allow configuring with --disable-profiler * profiler: Always activate profiler if GJS_ENABLE_PROFILER=1 [ Philip Chimento ] * foreign: Use gjs_eval_with_scope() instead of public API [ Jason Hicks ] * profiler: Reduce API surface [ Philip Chimento ] * object: Restore resolve return value * tests: Don't compare Rooted against NULL * tests: Add pending test for composite template subclass * maint: Fix obsolete comments on resolve hooks * js: Remove context from GjsAutoJSChar [ Jason Hicks ] * Fix overwriting of libcjs_la_SOURCES [ Philip Chimento ] * jsapi-util-string: Fix return of gjs_get_string_id() * Revert "object: reset the keep alive flag on wrapper when object is disposed" * release: Include NEWS from 1.50.4 * maint: Update DOAP file [ Jason Hicks ] * Allow using GObject.signal_* functions in place of Object methods [ Simon McVittie ] * gjs_callback_closure: Log uncatchable exceptions, don't just exit * gjs_callback_closure: Use g_error, not exit, on uncatchable exceptions * _gjs_context_run_jobs: Log uncatchable exceptions, except System.exit() * gjs_context_eval: Use different GError message for uncatchable exceptions * gjs_context_eval: Log a critical on uncatchable exceptions [ Philip Chimento ] * build: Install suppressions files [ Jason Hicks ] * Rename gjs-1.0 * CI: use a more robuts test for profiling dumping [ Philip Chimento ] * release: Prepare for 1.51.90 * build: Ignore -Wcast-function-type in G_DEFINE_TYPE * jsapi-dynamic-class: Use interned strings to link prototype [ Giovanni Campagna ] * function: move completed trampolines out of gjs_invoke_c_function [ Philip Chimento ] * function: Fix outdated comment [ Giovanni Campagna ] * tests: Add tests for GObject-valued properties [ Philip Chimento ] * profiler: Remove unused function in !ENABLE_PROFILER case [ Ting-Wei Lan ] * profiler: Don't include alloca.h when disabled [ Philip Chimento ] * toggle: Add debug logging * context: Add more debug logging to dispose * log: Allow logging thread ID of each message * context: Shut down toggle queue before dispose notify [ Jason Hicks ] * context: Fix usage of g_idle_add for resolving promises [ Philip Chimento ] * release: Prepare for 1.51.91 * function: Refactor "callback blocked" message into helper function * function: Don't call into JS if on the wrong thread [ Jason Hicks ] * Profiler: fix build on Debian/Ubuntu * Revert "build: Install suppressions files" * Remove test/travis-ci.sh * Tests: Fix failures from gjs renaming [ Philip Chimento ] * console: Free profiler output path at different points * sysprof: Initialize buffer in SpCaptureWriter * tests: Reduce length of profiler start/stop test [ Florian Müllner ] * overrides/Gio: Add D-Bus convenience to more proxy methods [ Philip Chimento ] * context: Job queue should not swallow pending exceptions * overrides: Add override for Cairo that merges in imports.cairo [ James Cowgill ] * profiler: Don't assume layout of struct sigaction [ Philip Chimento ] * context: Always import byteArray [ Jason Hicks ] * Remove dead code [ Philip Chimento ] * Gio: Remove obsolete code * tests: Add missing async test parameter * Remove unnecessary semicolons [ Tommi Komulainen ] * context: dump heaps on signal [ Claudio André ] * installed-tests/js: skip a test GTK dependent, if no GTK [ Karen Medina ] * Update tweener.js -- 48 eslint errors fixed [ Philip Chimento ] * log: Remove unused debug topics * object: Improve lifecycle logging * engine: Free Cairo static data on shutdown * object: Ensure weak ref callback is removed on finalize [ Andy Holmes ] * initial commit of heapgraph scripts * rename README.md to heapgraph.md * tweak heapgraph.md * heapdot.py: remove shape compression * omit 'self-hosting-global' unless explicitly targetted [ Jason Hicks ] * release: Prepare for 1.52.1 [ Philip Chimento ] * Merge branch 'jaszhix/gjs-tweener-fix' * Revert "engine: Free Cairo static data on shutdown" [ Andy Holmes ] * heapgraph.py: adjust terminal output style * don't use colors in terminal output * remove unused label function [ rockon999 ] * Fix UTF8 encoding for some GJS string arrays. - The function that constructs string arrays for imports and argv utilized a JS_NewStringCopyZ call instead of the proper JS_NewStringCopyUTF8Z (the input is UTF8). * Add testing for ARGV encoding changes. * Automate testing for unicode encoding. * Test new environment flag against pipeline. [ Giovanni Campagna ] * object: don't use toggle references unless necessary [ Georges Basile Stavracas Neto ] * object: properly disassociate wrappers * context: Add API to force GC schedule * object: Queue a forced GC when toggling down * object: Only weak unref if GObject is still alive [ Andy Holmes ] * overrides: support Gtk template callbacks [ Carlos Garnacho ] * context: Ensure force_gc flag is not lost if the idle is scheduled (Closes: #150) * object: Embed wrapped object list into ObjectInstance (Closes: #142) [ Philip Chimento ] * jsapi-util-root: Add operator!=(nullptr_t) [ Tomasz Miąsko ] * Support interface signal handlers [ Philip Chimento ] * importer: Refactor awkward 'goto out' situation * log: Add gjs_debug_id() * js: Don't convert to UTF-8 just for debug logging [ Jason Hicks ] * console: --jsversion shell option [ Philip Chimento ] * object: Adjust arguments of method * object: Upgrade severity of toggle warning to error * jsapi-util: Remove gjs_get_type_name() * mem: Remove unused counters [ Ole Jørgen Brønner ] * importer: Retrieve names while iterating search path (Closes: #154) [ Philip Chimento ] * function: Check for invalid closure [ Emmanuele Bassi ] * Remove unnecessary inline [ Philip Chimento ] * jsapi-util-string: Fix object logging [ Carlos Garnacho ] * util-root: Allow null notification functions when rooting (Closes: #144) * object: Set up a single weak ref on the context (Closes: #144) [ Philip Chimento ] * object: Move context dispose notify to GjsContext * object: Fix unused variable * object: Change linked list size() to return size_t * object: Fix logging statement [ Carlos Garnacho ] * js: Schedule a compacting GC on gjs_gc_if_needed() (Closes: #151) [ Philip Chimento ] * boxed: Update outdated comment * boxed: Remove limitation of 256 fields * tests: Debug-log coverage output * tests: Remove unused length argument in coverage tests * tests: Correct typo in test name * jsapi-util-string: Avoid unnecessary root * boxed: Remove unnecessary roots [ Clement Lefebvre ] * CI: Remove Mint 18 -- Clement Lefebvre Tue, 30 Oct 2018 13:45:13 +0000 cjs (3.8.0) tara; urgency=medium [ Cosimo Cecchi ] * arg: don't crash when asked to convert a null strv to an array [ Tom Schoonjans ] * arg: Include headers for std::abs() [ Claudio André ] * maintenance: modernize shell scripts [ Philip Chimento ] * modules/console: Update fancy error reporter to mozjs38 [ Jason Hicks ] * jsapi-util-args: Mark functions as always-inline [ Patrick Griffis ] * package: Support running Meson projects from source * package: Fix initSubmodule() with Meson. * package: Don't rely upon directory configuration for initSubmodule() * package: Set GSETTINGS_SCHEMA_DIR when ran from source [ Philip Chimento ] * build: Autodetect SpiderMonkey's debug mode * context: Use GThread to determine owner thread * jsapi-constructor-proxy: Inherit from js::Wrapper * system: Switch from JS::CallReceiver to JS::CallArgs * coverage: Root using context, not runtime [ Florian Müllner ] * package: Add checkSymbol() to check for symbol availability * package: Add requireSymbol() method [ Patrick Griffis ] * overrides: Implement Gio.ListStore[Symbol.iterator] [ Juan Pablo Ugarte ] * modules/system.cpp: added dumpHeapComplete() method [ Chun-wei Fan ] * Use std::unique_ptr instead of g_autofree/g_autoptr() * modules/cairo-*-surface.cpp: Silence compiler warnings [ Philip Chimento ] * js: Stop using flags argument to String.replace() * tests: Root using context, not runtime [ Chun-wei Fan ] * Windows: Build against SpiderMonkey 52 * Partially revert "Windows: Build against SpiderMonkey 52" [ Philip Chimento ] * autofree: Avoid using local typedefs in classes [ Daniel Boles ] * GjsAutoChar: Do not take ownership of const char* [ Claudio André ] * maint: fix a wrong sscanf argument [ Philip Chimento ] * js: Use autoptr in gjs_object_require_property() * jsapi-util-string: Remove useless length calculation [ Claudio André ] * maint: add a static code analysis 'make target' [ Tomas Popela ] * Fix the build with debug logs enabled [ Jason Hicks ] * js: Module exports use ES6 scope rules * console: Refactor read-eval-print loop * js: Use a special object for modules * js: Refactor global object creation [ Philip Chimento ] * build: Build with mozj45 * js: new JS_Enumerate API [ Jason Hicks ] * js: Switch from JS::NullPtr() to nullptr [ Philip Chimento ] * js: Global object is implicit in many functions * js: JSCLASS_IMPLEMENTS_BARRIERS is now implicit * js: Weak pointer callback API change * js: setProperty operations with triple result state * js: New JS_IsArrayObject() API * js: Rename JS_InternString to JS_AtomizeAndPinString * importer: API change in enumerate operation * js: Set JSPROP_RESOLVING when defining properties * modules/console: Update to js::PrintError from upstream [ Jason Hicks ] * js: Various API changes for SpiderMonkey 45 [ Philip Chimento ] * coverage: Misc Javascript-side API changes * importer: Seal import with JSPropertyDescriptor directly * js: Update obsolete comments * js: Adapt to new JS::TraceEdge API * build: Build with mozjs52 [ Chun-wei Fan ] * Windows: Build against SpiderMonkey 52 [ Philip Chimento ] * js: New JSClass struct layout * js: Add JSCLASS_FOREGROUND_FINALIZE flag * js: Adapt to options changes * js: Report warnings and errors with encoding * tests: Refactor to avoid error reporter * js: Replace error reporter callbacks * js: Replace JSRuntime APIs that now take JSContext [ Jason Hicks ] * js: Remove JSRuntime [ Philip Chimento ] * js: Adapt to misc API changes in SpiderMonkey 52 * js: Unbarriered read while in weak ptr callback [ Jason Hicks ] * docs: Overview of SpiderMonkey 52 features in NEWS * GjsAutoChar: Add a proper operator= (const char*) [ Philip Chimento ] * js: Allow access to modules' lexical scope * tests: Re-enable JIT * release: Include NEWS from 1.48.5 * release: Prepare for 1.49.4 * build: Use 'always inline' macro in more places * js: Use correct autoptr in gjs_string_to_filename() * function: Better message about call during GC [ Jason Hicks ] * promise: Move to native promises [ Philip Chimento ] * promise: Report unhandled rejections * context: Properly create const strings array * importer: Give module objects a [Symbol.toStringTag] * release: Include NEWS from 1.48.6 [ Jason Hicks ] * object: Don't let a method shadow a property [ luke.nukem.jones@gmail.com ] * Backport of patch required for JSObject compare to nullptr * Backport of patch required for vectors MOZ_MUST_USE returns [ Jason Hicks ] * Revert "js: Workaround for function with custom prototype" [ Philip Chimento ] * Revert "build: Allow compiling without RTTI" [ Jason Hicks ] * lang: Move all legacy Lang.Class code [ Philip Chimento ] * class: Move to ES6 classes in internal code [ Jason Hicks ] * GObject: Move all legacy GObject class code [ Philip Chimento ] * GObject: Adapt GObject class framework to ES6 * class: Move to ES6 GObject classes in internal code * tests: Add ES6 class inheriting from legacy class * build: Fix distcheck * release: Prepare for 1.49.90 [ Jason Hicks ] * Gtk: Use GObject.registerClass() for Gtk.Widgets [ Philip Chimento ] * legacy class: Add name property to class object * legacy class: Reinstate Lang.getMetaClass() * gerror: Use JS::CaptureCurrentStack for stack info * stack: Remove gjs_context_get_frame_info() * gerror: Define a columnNumber on GError-backed errors * console: Print exception even when not from SpiderMonkey * tests: Re-enable accidentally disabled tests [ Benjamin Berg ] * jsapi-util: Also rate limit checking the process size [ Philip Chimento ] * release: Prepare for 1.49.91 * object: Remove unused argument [ Jason Hicks ] * maint: add ASAN Address Sanitizer [ Philip Chimento ] * docs: Correct leftover mozjs38 * arg: Avoid assigning out-of-range values * build: Add LSan suppression file * build: Don't use the vptr sanitizer * arg: Avoid taking abs() of -2^63 * tests: Suppress message about too-big integer * main: Use g_option_context_parse_strv() [ Jason Hicks ] * build: Put dbus-run-session in AM_TESTS_ENVIRONMENT * build: Don't use LOG_COMPILER for shell scripts [ Philip Chimento ] * object: Avoid taking address of empty vector * context: Avoid null pointer dereference * build: Update Valgrind suppressions rules * build: Valgrind with AX_VALGRIND_CHECK * maint: Add SpiderMonkey helgrind suppression rules * tests: Split legacy Gtk.Widget tests out [ Jason Hicks ] * Revert freeing closures in idle handler [ Philip Chimento ] * closure: Debug message on invalidate signal * object: Refactor out ConnectData * closure: Prevent collection of invalidated closure * release: Prepare for 1.49.92 * coverage: Correct licensing mistake * release: Prepare for 1.50.0 * stack: Use js::DumpBacktrace() for gjs_dumpstack() * coverage: Don't error out on various files * coverage: Scan ES6 classes for functions * coverage: Correct AST walking for labelled statements * coverage: Walk AST in more cases * tests: Create test ASTs by parsing JS * coverage: Don't count literals as executable * coverage: Don't mark empty var declarations executable * coverage: Misc refactors * coverage: Fix coverage prefixes * release: Prepare for 1.50.1 * jsapi-util-args: Update out-of-date comments * build: Tweak code coverage options [ Jason Hicks ] * tweener: Fix a couple of warnings [ Florian Müllner ] * legacy: Allow ES6 classes to inherit from abstract Lang.Class class [ Philip Chimento ] * release: Prepare for 1.50.2 [ Jason Hicks ] * importer.cpp: Update search path for Cinnamon * Remove valgrind configuration * Fix DEBUG compiler error in Spidermonkey 52 * Depend on libmozjs-52 * Get most tests passing * Fix usage of g_idle_add for resolving promises [ Clement Lefebvre ] * Add CI configuration [ Jason Hicks ] * Fix scope warning for restrictedWords in the tweener module (#64) -- Clement Lefebvre Mon, 16 Apr 2018 12:14:11 +0100 cjs (3.6.1) sylvia; urgency=medium [ Simon Brown ] * cairo: Free popped pattern (#59) * value: Fix memory leak while marshalling GValue (#57) * arg: Fix memory leaks (#58) -- Clement Lefebvre Thu, 09 Nov 2017 13:31:37 +0000 cjs (3.6.0) sylvia; urgency=medium [ leigh123linux ] * object: Keep proper track of pending closure invalidations * revert b66d7c2965f20c2cf51628840682c404a01bf408 [ Michael Webster ] * Add a debug package. [ Jason Hicks ] * object: Zero out new ConnectData [ Clement Lefebvre ] * Update README * Add a README.md [ Philip Chimento ] * Revert freeing closures in idle handler * closure: Prevent collection of invalidated closure -- Clement Lefebvre Sat, 21 Oct 2017 13:49:00 +0100 cjs (3.4.3) sonya; urgency=medium [ Michael Webster ] * object.cpp: Follow-up to 503fa20e07ce - handler ID's *must* be managed from start to finish - they should be initialized to 0, and re-set to 0 whenever they're finished being used. [ leigh123linux ] * arg: don't crash when asked to convert a null strv to an array (#48) [ Michael Webster ] * Apply upstream: https://github.com/GNOME/gjs/commit/bace908922aa6ee9ee3885eef01b75816ece842f [ Philip Chimento ] * closure: Remove pointer to runtime * closure: Prevent use-after-free in closures -- Clement Lefebvre Thu, 06 Jul 2017 11:10:34 +0200 cjs (3.4.2) sonya; urgency=medium [ leigh123linux ] * tweener: Add undefined property check (#45) [ Michael Webster ] * tweener.js: silence some additional warnings due to missing/unused properties. [ leigh123linux ] * object: Prevent use-after-free in signal connections * util-root: Require GjsMaybeOwned callback to reset -- Clement Lefebvre Mon, 26 Jun 2017 12:18:57 +0200 cjs (3.4.1) sonya; urgency=medium [ Clement Lefebvre ] * Packaging: Don't use build profiles in build-deps [ Alexei Sorokin ] * Better GLib and autotools compatibility (#44) -- Clement Lefebvre Tue, 23 May 2017 15:28:25 +0100 cjs (3.4.0) sonya; urgency=medium [ Michael Webster ] * Rebase to upstream gjs 1.48.0 (a7bcfb9f3e00ca9849bf44a). * Always reload extension js modules. [ pixunil ] * min / max properties to specify a minimal / maximal value [ dalcde ] * Throw error when attempting to instantiate abstract class [ Dexter Chua ] * signals.js: add signalHandlerIsConnected method * Use python2 instead of python [ pixunil ] * importer.cpp: Add a method to the global imports object addSubImporter(name, path) [ Michael Webster ] * modules/GObject.js: Add access to handler_block and handler_unblock. * Fix file location problem from rebasing [ leigh123linux ] * lost commit: remove aclocal -- Clement Lefebvre Fri, 14 Apr 2017 08:30:32 -0400 cjs (3.2.0) serena; urgency=medium [ Maximiliano Curia ] * Migrate away from gnome-common deprecated vars and macros [ Michael Webster ] * modules/GObject.js: Add access to handler_block and handler_unblock. [ Clement Lefebvre ] * Fixed build [ Maximiliano Curia ] * Make AX_ macros optional * Drop glib-gettextize, gtkdocize and intltoolize [ leigh123linux ] * build: Require C++11 [ Michael Webster ] * Fix build when extra debugging is enabled (util/log.h) [ Clement Lefebvre ] * pkg: Add missing autoconf-archive in debian/control.in -- Clement Lefebvre Mon, 07 Nov 2016 10:28:25 +0000 cjs (3.0.1) sarah; urgency=medium [ Michael Webster ] * repo.cpp: Add Gvc->Cvc mapping for backward compatibility of sound xlets. -- Clement Lefebvre Tue, 10 May 2016 16:26:22 +0100 cjs (3.0.0) sarah; urgency=medium * 3.0 -- Clement Lefebvre Sat, 23 Apr 2016 10:50:03 +0100 cjs (2.8.0) rosa; urgency=medium [ pixunil ] * importer.cpp: Add a method to the global imports object addSubImporter(name, path) -- Clement Lefebvre Fri, 16 Oct 2015 13:56:06 +0100 cjs (2.6.2) rafaela; urgency=medium [ Dexter Chua ] * signals.js: add signalHandlerIsConnected method -- Clement Lefebvre Mon, 22 Jun 2015 22:45:02 +0200 cjs (2.6.1) rafaela; urgency=medium [ Dexter Chua ] * Use python2 instead of python -- Clement Lefebvre Sun, 14 Jun 2015 12:44:28 +0200 cjs (2.6.0) rafaela; urgency=medium * 2.6.0 -- Clement Lefebvre Tue, 19 May 2015 17:01:22 +0200 cjs (2.5.0) unstable; urgency=medium * bump for development -- Michael Webster Sat, 11 Apr 2015 09:02:17 -0400 cjs (2.4.1) rebecca; urgency=medium * Lowered gobject-introspection requirement to 1.38.0 * Imports: fixed GMenu-to-CMenu proxy for legacy applets. -- Clement Lefebvre Sun, 23 Nov 2014 12:01:41 +0100 cjs (2.4.0) rebecca; urgency=medium * 2.4.0 -- Clement Lefebvre Thu, 30 Oct 2014 15:29:36 +0100 cjs (2.3.1) unstable; urgency=medium * Rebase to upstream version: - gjs 1.40.1 (gjs (1.40.1-0ubuntu0.2) trusty) -- Michael Webster Tue, 12 Aug 2014 21:58:00 -0400 cjs (2.3.0) unstable; urgency=medium * 2.3.0 -- Clement Lefebvre Fri, 27 Jun 2014 14:28:58 +0100 cjs (2.2.1) qiana; urgency=medium * 2.2.1 -- Clement Lefebvre Wed, 21 May 2014 11:30:18 +0100 cjs (2.2.0) qiana; urgency=medium * 2.2.0 -- Clement Lefebvre Sat, 12 Apr 2014 10:33:49 +0100 cjs (2.0.0) petra; urgency=low * 2.0.0 -- Clement Lefebvre Wed, 02 Oct 2013 13:43:45 +0100 cjs (1.9.1) petra; urgency=low * Based on 1.34.0-0ubuntu1 quantal -- Clement Lefebvre Mon, 30 Sep 2013 13:31:38 +0100 cjs-4.4.0/debian/cjs.docs000066400000000000000000000000141356375132300151540ustar00rootroot00000000000000NEWS README cjs-4.4.0/debian/cjs.install000066400000000000000000000000101356375132300156660ustar00rootroot00000000000000usr/bin cjs-4.4.0/debian/compat000066400000000000000000000000031356375132300147270ustar00rootroot0000000000000010 cjs-4.4.0/debian/control000066400000000000000000000056261356375132300151440ustar00rootroot00000000000000# This file is autogenerated. DO NOT EDIT! # # Modifications should be made to debian/control.in instead. # This file is regenerated automatically in the clean target. Source: cjs Section: interpreters Priority: optional Maintainer: Linux Mint Build-Depends: debhelper (>= 10), dpkg-dev (>= 1.17.14), gnome-common, gnome-pkg-tools, pkg-config, libglib2.0-dev (>= 2.42.0), libgirepository1.0-dev (>= 1.46.0), gobject-introspection (>= 1.46.0), libmozjs-52-dev, libreadline-dev, libgtk-3-dev, libcairo2-dev, dbus, dbus-x11, at-spi2-core, xvfb, libxml2-utils Standards-Version: 3.9.8 Package: cjs Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: Mozilla-based javascript bindings for the Cinnamon platform Makes it possible for applications to use all of Cinnamon's platform libraries using the JavaScript language. It's mainly based on the Mozilla JavaScript engine and the GObject introspection framework. . This package contains the interactive console application. Package: libcjs0f Architecture: any Section: libs Depends: ${shlibs:Depends}, ${misc:Depends}, ${gir:Depends} Conflicts: libcjs0, libcjs0a, libcjs0c, libcjs0e Replaces: libcjs0, libcjs0a, libcjs0c, libcjs0e Provides: ${cjs:Provides} Description: Mozilla-based javascript bindings for the Cinnamon platform Makes it possible for applications to use all of Cinnamon's platform libraries using the JavaScript language. It's mainly based on the Mozilla JavaScript engine and the GObject introspection framework. . This is the shared library applications link to. Package: libcjs-dev Architecture: any Section: libdevel Depends: ${misc:Depends}, cjs, libcjs0f (= ${binary:Version}), libgirepository1.0-dev (>= 1.41.4), libgtk-3-dev, libmozjs-52-dev Description: Mozilla-based javascript bindings for the Cinnamon platform Makes it possible for applications to use all of Cinnamon's platform libraries using the JavaScript language. It's mainly based on the Mozilla JavaScript engine and the GObject introspection framework. . This package contains the development files applications need to build against. Package: libcjs-dbg Section: debug Architecture: any Priority: extra Depends: libcjs0f (= ${binary:Version}), ${misc:Depends} Description: Mozilla-based javascript bindings for the Cinnamon platform Makes it possible for applications to use all of Cinnamon's platform libraries using the JavaScript language. It's mainly based on the Mozilla JavaScript engine and the GObject introspection framework. . This development package contains unstripped binaries compiled with debugging symbols needed by gdb. cjs-4.4.0/debian/control.in000066400000000000000000000053171356375132300155460ustar00rootroot00000000000000Source: cjs Section: interpreters Priority: optional Maintainer: Linux Mint Build-Depends: debhelper (>= 10), dpkg-dev (>= 1.17.14), gnome-common, gnome-pkg-tools, pkg-config, libglib2.0-dev (>= 2.42.0), libgirepository1.0-dev (>= 1.46.0), gobject-introspection (>= 1.46.0), libmozjs-52-dev, libreadline-dev, libgtk-3-dev, libcairo2-dev, dbus, dbus-x11, at-spi2-core, xvfb Standards-Version: 3.9.8 Package: cjs Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: Mozilla-based javascript bindings for the Cinnamon platform Makes it possible for applications to use all of Cinnamon's platform libraries using the JavaScript language. It's mainly based on the Mozilla JavaScript engine and the GObject introspection framework. . This package contains the interactive console application. Package: libcjs0f Architecture: any Section: libs Depends: ${shlibs:Depends}, ${misc:Depends}, ${gir:Depends} Conflicts: libcjs0, libcjs0a, libcjs0c, libcjs0e Replaces: libcjs0, libcjs0a, libcjs0c, libcjs0e Provides: ${cjs:Provides} Description: Mozilla-based javascript bindings for the Cinnamon platform Makes it possible for applications to use all of Cinnamon's platform libraries using the JavaScript language. It's mainly based on the Mozilla JavaScript engine and the GObject introspection framework. . This is the shared library applications link to. Package: libcjs-dev Architecture: any Section: libdevel Depends: ${misc:Depends}, cjs, libcjs0f (= ${binary:Version}), libgirepository1.0-dev (>= 1.41.4), libgtk-3-dev, libmozjs-52-dev Description: Mozilla-based javascript bindings for the Cinnamon platform Makes it possible for applications to use all of Cinnamon's platform libraries using the JavaScript language. It's mainly based on the Mozilla JavaScript engine and the GObject introspection framework. . This package contains the development files applications need to build against. Package: libcjs-dbg Section: debug Architecture: any Priority: extra Depends: libcjs0f (= ${binary:Version}), ${misc:Depends} Description: Mozilla-based javascript bindings for the Cinnamon platform Makes it possible for applications to use all of Cinnamon's platform libraries using the JavaScript language. It's mainly based on the Mozilla JavaScript engine and the GObject introspection framework. . This development package contains unstripped binaries compiled with debugging symbols needed by gdb. cjs-4.4.0/debian/copyright000066400000000000000000000125471356375132300154740ustar00rootroot00000000000000This work was packaged for Debian by: Gustavo Noronha Silva on Mon, 12 Oct 2009 18:38:36 -0300 It was downloaded from https://download.gnome.org/sources/gjs/ Upstream Author: litl, LLC Copyright: Copyright © 2008 litl, LLC For the following files: gjs/jsapi-util.c modules/gettext-native.c modules/gettext.js modules/jsUnit.js modules/gettext-native.h Copyright © 2009 Red Hat, Inc. For the following files: modules/tweener/tweener.js modules/tweener/tweenList.js Copyright © 2006-2007 Zeh Fernando and Nate Chatellier License: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. The Debian packaging is: Copyright © 2009 Collabora Ltd. and is licensed under the same license as the upstream source. For the following files, the information bellow applies: modules/console.c gjs/stack.c License: Version: MPL 1.1/GPL 2.0/LGPL 2.1 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 Mozilla Communicator client code, released March 31, 1998. The Initial Developer of the Original Code is Netscape Communications Corporation. Portions created by the Initial Developer are Copyright (C) 1998 the Initial Developer. All Rights Reserved. Contributor(s): Alternatively, the contents of this file may be used under the terms of either of the GNU General Public License Version 2 or later (the "GPL"), or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), in which case the provisions of the GPL or the LGPL are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of either the GPL or the LGPL, and not to allow others to use your version of this file under the terms of the MPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the GPL or the LGPL. If you do not delete the provisions above, a recipient may use your version of this file under the terms of any one of the MPL, the GPL or the LGPL. Copyright © 2008 litl, LLC © 1998 Netscape Communications Corporation The GPLv2 and LGPLv2.1 files can be obtained, on Debian systems, at /usr/share/common-licenses/GPL-2, and /usr/share-common-licenses/LGPL-2.1, respectively. For the following files the information bellow applies: modules/tweener/equations.js Copyright © 2001 Robert Penner License: BSD Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: . * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the author nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. . THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. cjs-4.4.0/debian/docs000066400000000000000000000000141356375132300143760ustar00rootroot00000000000000NEWS README cjs-4.4.0/debian/libcjs-dev.install000066400000000000000000000000621356375132300171400ustar00rootroot00000000000000usr/include usr/lib/libcjs*.so usr/lib/pkgconfig/ cjs-4.4.0/debian/libcjs0f.install000066400000000000000000000000741356375132300166150ustar00rootroot00000000000000usr/lib/libcjs*.so.* usr/lib/cjs/girepository-1.0/*.typelib cjs-4.4.0/debian/rules000077500000000000000000000021331356375132300146070ustar00rootroot00000000000000#!/usr/bin/make -f include /usr/share/gnome-pkg-tools/1/rules/gnome-get-source.mk # Use recursive variables since this variable must not be expanded until # files have been installed. #LIBMOZJS = $(shell objdump -p debian/tmp/usr/lib/libgjs.so | awk '$$1=="NEEDED" && $$2~/^libmozjs/ { print $$2 }' | sed s/\\.so\\./-/ ) # Hard code the mozjs version here since the above line didn't seem to work LIBMOZJS = libmozjs-52-0 %: dh $@ --with gnome,gir override_dh_auto_configure: dh_auto_configure -- \ --libdir=\$${prefix}/lib \ --libexecdir=\$${prefix}/lib/cjs \ --disable-Werror \ --with-xvfb-tests \ --with-dbus-tests override_dh_girepository: dh_girepository -l $(CURDIR) /usr/lib/cjs/girepository-1.0 override_dh_install: dh_install --fail-missing -X.la override_dh_auto_test: dh_auto_test || true override_dh_strip: dh_strip --dbg-package=libcjs-dbg override_dh_makeshlibs: dh_makeshlibs -Xusr/lib/cjs-1.0/ -V'libcjs0f (>= $(DEB_UPSTREAM_VERSION)), libcjs0-$(LIBMOZJS)' -- -c4 override_dh_gencontrol: echo cjs:Provides=libcjs0-$(LIBMOZJS) >> debian/libcjs0f.substvars dh_gencontrol cjs-4.4.0/debian/source/000077500000000000000000000000001356375132300150305ustar00rootroot00000000000000cjs-4.4.0/debian/source/format000066400000000000000000000000151356375132300162370ustar00rootroot000000000000003.0 (native) cjs-4.4.0/debian/tests/000077500000000000000000000000001356375132300146725ustar00rootroot00000000000000cjs-4.4.0/debian/tests/build000066400000000000000000000010611356375132300157120ustar00rootroot00000000000000#!/bin/sh # autopkgtest check: Build and run a program against glib, to verify that the # headers and pkg-config file are installed correctly # (C) 2012 Canonical Ltd. # Author: Martin Pitt set -e WORKDIR=$(mktemp -d) trap "rm -rf $WORKDIR" 0 INT QUIT ABRT PIPE TERM cd $WORKDIR cat < gjstest.c #include int main() { GjsContext* context; context = gjs_context_new(); return 0; } EOF gcc -o gjstest gjstest.c `pkg-config --cflags --libs cjs-1.0` echo "build: OK" [ -x gjstest ] ./gjstest echo "run: OK" cjs-4.4.0/debian/tests/control000066400000000000000000000002321356375132300162720ustar00rootroot00000000000000Tests: build Depends: libgjs-dev, build-essential Tests: installed-tests Depends: gjs-tests, dbus-x11, dbus (>= 1.8), xauth, xvfb, gnome-desktop-testing cjs-4.4.0/debian/tests/installed-tests000066400000000000000000000006311356375132300177340ustar00rootroot00000000000000#!/bin/bash # autopkgtest check: Run the installed-tests to verify gjs works correctly # (C) 2013-2015 Canonical Ltd. # Author: Iain Lane set -e export XDG_RUNTIME_DIR=$ADTTMP export LC_ALL=C # dbus outputs activation messages to stderr which fails the test dbus-run-session -- xvfb-run -a gnome-desktop-testing-runner gjs 2> >(grep -vE '^(Activating|Successfully activated)')>&2 cjs-4.4.0/debian/watch000066400000000000000000000001451356375132300145610ustar00rootroot00000000000000version=4 https://download.gnome.org/sources/@PACKAGE@/([0-9.]+)/ \ @PACKAGE@@ANY_VERSION@\.tar\.xz cjs-4.4.0/doc/000077500000000000000000000000001356375132300130535ustar00rootroot00000000000000cjs-4.4.0/doc/ByteArray.md000066400000000000000000000150651356375132300153060ustar00rootroot00000000000000The ByteArray type in the `imports.byteArray` module is based on an ECMAScript 4 proposal that was never adopted. This can be found at: http://wiki.ecmascript.org/doku.php?id=proposals:bytearray and the wikitext of that page is appended to this file. The main difference from the ECMA proposal is that gjs's ByteArray is inside a module, and `toString()`/`fromString()` default to UTF-8 and take optional encoding arguments. There are a number of more elaborate byte array proposals in the Common JS project at http://wiki.commonjs.org/wiki/Binary We went with the ECMA proposal because it seems simplest, and the main goal for most gjs users will be to shovel bytes between various C APIs, for example reading from an IO stream and then pushing the bytes into a parser. Actually manipulating bytes in JS is likely to be pretty rare, and slow ... an advantage of the gjs/gobject-introspection setup is that stuff best done in C, like messing with bytes, can be done in C. --- ECMAScript proposal follows; remember it's almost but not quite like gjs ByteArray, in particular we use UTF-8 instead of busted Latin-1 as default encoding. --- # ByteArray # (Also see the [discussion page][1] for this proposal) ## Overview ## In previous versions of ECMAScript, there wasn't a good way to efficiently represent a packed array of arbitrary bytes. The predefined core object Array is inefficient for this purpose; some developers have (mis)used character strings for this purpose, which may be slightly more efficient for some implementations, but still a misuse of the string type and either a less efficient use of memory (if one byte per character was stored) or cycles (if two bytes per char). Edition 4 will add a new predefined core object, ByteArray. A ByteArray can be thought of as similar to an Array of uint ([uint]) with each element truncated to the integer range of 0..255. ## Creating a ByteArray ## To create a ByteArray object: ```js byteArrayObject = new ByteArray(byteArrayLength:uint) ``` byteArrayLength is the initial length of the ByteArray, in bytes. If omitted, the initial length is zero. All elements in a ByteArray are initialized to the value of zero. Unlike Array, there is no special form that allows you to list the initial values for the ByteArray's elements. However, the ByteArray class has an `intrinsic::to` static method that can convert an Array to a ByteArray, and implementations are free to optimize away the Array instance if it is used exclusively to initialize a ByteArray: ```js var values:ByteArray = [1, 2, 3, 4]; // legal by virtue of ByteArray.intrinsic::to ``` ## Populating a ByteArray ## You can populate a ByteArray by assigning values to its elements. Each element can hold only an unsigned integer in the range 0..255 (inclusive). Values will be converted to unsigned integer (if necessary), then truncated to the least-significant 8 bits. For example, ```js var e = new ByteArray(3); e[0] = 0x12; // stores 18 e[1] = Math.PI; // stores 3 e[2] = "foo"; // stores 0 (generates compile error in strict mode) e[2] = "42"; // stores 42 (generates compile error in strict mode) e[2] = null; // stores 0 e[2] = undefined; // stores 0 ``` ## Referring to ByteArray Elements ## You refer to a ByteArray's elements by using the element's ordinal number; you refer to the first element of the array as `myArray[0]` and the second element of the array as `myArray[1]`, etc. The index of the elements begins with zero (0), but the length of array (for example, `myArray.length`) reflects the number of elements in the array. ## ByteArray Methods ## The ByteArray object has the follow methods: ### `static function fromString(s:String):ByteArray` ### Convert a String into newly constructed ByteArray; this creates a new ByteArray of the same length as the String, then assigns each ByteArray entry the corresponding charCodeAt() value of the String. As with other ByteArray assignments, only the lower 8 bits of the charCode value will be retained. ```js class ByteArray { ... static function fromString(s:String):ByteArray { var a:ByteArray = new Bytearray(s.length); for (var i:int = 0; i < s.length; ++i) a[i] = s.charCodeAt(i); return a; } ... } ``` ### `static function fromArray(s:Array):ByteArray` ### Converts an Array into a newly constructed ByteArray; this creates a new ByteArray of the same length as the input Array, then assigns each ByteArray entry the corresponding entry value of the Array. As with other ByteArray assignments, only the lower 8 bits of the charCode value will be retained. ```js class ByteArray { ... static function fromArray(s:Array):ByteArray { var a:ByteArray = new Bytearray(s.length); for (var i:int = 0; i < s.length; ++i) a[i] = s[i]; return a; ... } ``` ### `function toString():String` ### Converts the ByteArray into a literal string, with each character entry set to the value of the corresponding ByteArray entry. The resulting string is guaranteed to round-trip back into an identical ByteArray by passing the result to `ByteArray.fromString()`, i.e., `b === ByteArray.fromString(b.toString())`. (Note that the reverse is not guaranteed to be true: `ByteArray.fromString(s).toString != s` unless all character codes in `s` are <= 255) ```js class ByteArray { ... function toString():String { // XXX return String.fromCharCode.apply(String, this); var s:String = ""; for (var i:int = 0; i < this.length; ++i) s += String.fromCharCode(this[i]); return s; } ... } ``` ## ByteArray Properties ## The ByteArray object has the following properties: ### `length:uint` ### This property contains the number of bytes in the ByteArray. Setting the length property to a smaller value decreases the size of the ByteArray, discarding the unused bytes. Setting the length property to a larger value increases the size of the ByteArray, initializing all newly-allocated elements to zero. ### `prototype:Object` ### This property contains the methods of ByteArray. ## Prior Art ## Adobe's ActionScript 3.0 provides [`flash.utils.ByteArray`][2], which was the primary inspiration for this proposal. Note that the ActionScript version of ByteArray includes additional functionality which has been omitted here for the sake of allowing small implementations; it is anticipated that the extra functionality can be written in ES4 itself and provided as a standard library. [Synopsis of ActionScript's implementation too detailed and moved to [discussion][1] page] [1] http://wiki.ecmascript.org/doku.php?id=discussion:bytearray [2] http://livedocs.macromedia.com/flex/2/langref/flash/utils/ByteArray.html cjs-4.4.0/doc/Hacking.md000066400000000000000000000102611356375132300147410ustar00rootroot00000000000000# Hacking on GJS # ## Setting up ## For the time being, we recommend using JHBuild to develop GJS. Follow the [instructions from GNOME][jhbuild]. Even if your system includes a development package for mozjs, we recommend building it on JHBuild so that you can enable the debugging features. Add this to your JHBuild configuration file: ```python module_autogenargs['mozjs52'] = '--enable-debug' ``` Make sure it is built first with `jhbuild build mozjs52`, otherwise `jhbuild build gjs` will skip it if you have the system package installed. Debugging features in mozjs reduce performance by quite a lot, in exchange for performing many runtime checks that can alert you when you're not using the JS API correctly. ## Making Sure Your Stuff Doesn't Break Anything Else ## Each changeset should ensure that the test suite still passes. In fact, each commit should ensure that the test suite still passes, though there are some exceptions to this rule. You can run the test suite with `jhbuild make check`. ## Debugging ## Mozilla has some pretty-printers that make debugging JSAPI code easier. Unfortunately they're not included in most packaged distributions of mozjs, but you can grab them from your JHBuild copy of mozjs. After reaching a breakpoint in your program, type this to activate the pretty-printers: ``` source ~/.cache/jhbuild/build/mozjs-52.Y.Z/js/src/shell/js-gdb.py ``` (replace `Y.Z` with mozjs's minor and micro version numbers) ## Checking Things More Thoroughly Before A Release ## ### Distcheck ### Run `jhbuild make distcheck` once before every release to make sure the package builds and the tests run from a clean release tarball. If there are any errors, they must be fixed before making the release. ### GC Zeal ### Run the test suite with "GC zeal" to make non-deterministic GC errors more likely to show up. To see which GC zeal options are available: ```sh JS_GC_ZEAL=-1 jhbuild run gjs ``` To run the test suite under it: ```sh JS_GC_ZEAL=... jhbuild make check ``` Good parameters for `...` are `1`, `2,100`, `2,1` (really slow and can cause the tests to time out), `4`, `6`, `7`, `11`. Failures in mode 4 (pre barrier verification) usually point to a GC thing not being traced when it should have been. Failures in mode 11 (post barrier verification) usually point to a weak pointer's location not being updated after GC moved it. ### Valgrind ### Valgrind catches memory leak errors in the C++ code. It's a good idea to run the test suite under Valgrind before each release. To run the test suite under a succession of Valgrind tools: ```sh jhbuild make check-valgrind ``` The logs from each run will be in `~/.cache/jhbuild/build/gjs/test-suite-.log`, where `` is `drd`, `helgrind`, `memcheck`, and `sgcheck`. Note that LeakSanitizer, part of ASan (see below) can catch many, but not all, errors that Valgrind can catch. LSan executes faster than Valgrind, however. ### Static Code Analysis ### To execute cppcheck, a static code analysis tool for the C and C++, run: ```sh jhbuild make cppcheck ``` It is a versatile tool that can check non-standard code, including: variable checking, bounds checking, leaks, etc. It can detect the types of bugs that the compilers normally fail to detect. ### Sanitizers ### To add instrumentation code to gjs, put this (both, or any one of them) in your JHBuild configuration file: ```python module_autogenargs['gjs'] = '--enable-asan --enable-ubsan' ``` Sanitizers are based on compile-time instrumentation. They are available in gcc and clang for a range of supported operating systems and platforms. Please, keep in mind that instrumentation is limited by execution coverage. So, if your "testing" session never reaches a particular point of execution, then instrumentation at that point collects no data. ### Test Coverage ### To generate a test coverage report, put this in your JHBuild configuration file: ```python module_autogenargs['gjs'] = '--enable-code-coverage' ``` Then run: ```sh jhbuild cleanone gjs jhbuild buildone gjs jhbuild make check-code-coverage xdg-open ~/.cache/jhbuild/build/gjs/gjs-X.Y.Z-coverage/index.html ``` (replace `X.Y.Z` with the version number, e.g. `1.48.0`) [jhbuild] https://wiki.gnome.org/HowDoI/Jhbuild cjs-4.4.0/doc/SpiderMonkey_Memory.md000066400000000000000000000153611356375132300173440ustar00rootroot00000000000000# Memory management in SpiderMonkey # When writing JavaScript extensions in C++, we have to understand and be careful about memory management. This document only applies to C++ code using the jsapi.h API. If you simply write a GObject-style library and describe it via gobject-introspection typelib, there is no need to understand garbage collection details. ## Mark-and-sweep collector ## As background, SpiderMonkey uses mark-and-sweep garbage collection. (see [this page][1] for one explanation, if not familiar with this.) This is a good approach for "embeddable" interpreters, because unlike say the Boehm GC, it doesn't rely on any weird hacks like scanning the entire memory or stack of the process. The collector only has to know about stuff that the language runtime created itself. Also, mark-and-sweep is simple to understand when working with the embedding API. ## Representation of objects ## An object has two forms. * `JS::Value` is a type-tagged version, think of `GValue` (though it is much more efficient) * inside a `JS::Value` can be one of: a 32-bit integer, a boolean, a double, a `JSString*`, or a `JSObject*`. `JS::Value` is a 64 bits-wide union. Some of the bits are a type tag. However, don't rely on the layout of `JS::Value`, as it may change between API versions. You check the type tag with the methods `val.isObject()`, `val.isInt32()`, `val.isDouble()`, `val.isString()`, `val.isBoolean()`. Use `val.isNull()` and `val.isUndefined()` rather than comparing `val == JSVAL_NULL` and `val == JSVAL_VOID` to avoid an extra memory access. null does not count as an object, so `val.isObject()` does not return true for null. This contrasts with the behavior of `JSVAL_IS_OBJECT(val)`, which was the previous API, but this was changed because the object-or-null behavior was a source of bugs. If you still want this behaviour use `val.isObjectOrNull()`. The methods `val.toObject()`, `val.toInt32()`, etc. are just accessing the appropriate members of the union. The jsapi.h header is pretty readable, if you want to learn more. Types you see in there not mentioned above, such as `JSFunction*`, would show up as an object - `val.isObject()` would return true. From a `JS::Value` perspective, everything is one of object, string, double, int, boolean, null, or undefined. ## Value types vs. allocated types; "gcthing" ## For integers, booleans, doubles, null, and undefined there is no pointer. The value is just part of the `JS::Value` union. So there is no way to "free" these, and no way for them to be finalized or become dangling. The importance is: these types just get ignored by the garbage collector. However, strings and objects are all allocated pointers that get finalized eventually. These are what garbage collection applies to. The API refers to these allocated types as "GC things." The macro `val.toGCThing()` returns the value part of the union as a pointer. `val.isGCThing()` returns true for string, object, null; and false for void, boolean, double, integer. ## Tracing ## The general rule is that SpiderMonkey has a set of GC roots. To do the garbage collection, it finds all objects accessible from those roots, and finalizes all objects that are not. So if you have a `JS::Value` or `JSObject*`/`JSString*`/`JSFunction*` somewhere that is not reachable from one of SpiderMonkey's GC roots - say, declared on the stack or in the private data of an object - that will not be found. SpiderMonkey may try to finalize this object even though you have a reference to it. If you reference JavaScript objects from your custom object, you have to use `JS::Heap` and set the `JSCLASS_MARK_IS_TRACE` flag in your JSClass, and define a trace function in the class struct. A trace function just invokes `JS_CallHeapValueTracer()`, `JS_CallHeapObjectTracer()`, etc. to tell SpiderMonkey about any objects you reference. See [JSTraceOp docs][2]. Tracing doesn't add a GC thing to the GC root set! It just notifies the interpreter that a thing is reachable from another thing. ## Global roots ## The GC roots include anything you have declared with `JS::Rooted` and the global object set on each `JSContext*`. You can also manually add roots with [`JS::PersistentRooted()`][3]. Anything reachable from any of these root objects will not be collected. `JS::PersistentRooted` pins an object in memory forever until it is destructed, so be careful of leaks. Basically `JS::PersistentRooted` changes memory management of an object to manual mode. Note that the wrapped T in `JS::PersistentRooted` is the location of your value, not the value itself. That is, a `JSObject**` or `JS::Value*`. Some implications are: * the location can't go away (don't use a stack address that will vanish before the `JS::PersistentRooted` is destructed, for example) * the root is keeping "whatever is at the location" from being collected, not "whatever was originally at the location" ## Local roots ## Here is the trickier part. If you create an object, say: ```c++ JSObject *obj = JS_New(cx, whatever, ...); ``` `obj` is NOT now referenced by any other object. If the GC ran right away, `obj` would be collected. This is what `JS::Rooted` is for, and its specializations `JS::RootedValue`, `JS::RootedObject`, etc. `JS::Rooted` adds its wrapped `T` to the GC root set, and removes it when the `JS::Rooted` goes out of scope. Note that `JS::Rooted` can only be used on the stack. For optimization reasons, roots that are added with `JS::Rooted` must be removed in LIFO order, and the stack scoping ensures that. Any SpiderMonkey APIs that can cause a garbage collection will force you to use `JS:Rooted` by taking a `JS::Handle` instead of a bare GC thing. `JS::Handle` can only be created from `JS::Rooted. So instead of the above code, you would write ```c++ JS::RootedObject obj(cx, JS_New(cx, whatever, ...)); ``` ### JSFunctionSpec and extra local roots ### When SpiderMonkey is calling a native function, it will pass in an argv of `JS::Value`. It already has to add all the argv values as GC roots. The "extra local roots" feature tells SpiderMonkey to stick some extra slots on the end of argv that are also GC roots. You can then assign to `argv[MAX(min_args, actual_argc)]` and whatever you put in there won't get garbage collected. This is kind of a confusing and unreadable hack IMO, though it is probably efficient and thus justified in certain cases. I don't know really. ## More tips ## For another attempt to explain all this, see [Rooting Guide from Mozilla.org][4]. [1] http://www.brpreiss.com/books/opus5/html/page424.html [2] http://developer.mozilla.org/en/docs/JSTraceOp [3] https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/JSAPI_reference/JS::PersistentRooted [4] https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/GC_Rooting_Guide GC cjs-4.4.0/doc/Style_Guide.md000066400000000000000000000116201356375132300156120ustar00rootroot00000000000000# Coding style # Our goal is to have all JavaScript code in GNOME follow a consistent style. In a dynamic language like JavaScript, it is essential to be rigorous about style (and unit tests), or you rapidly end up with a spaghetti-code mess. ## Semicolons ## JavaScript allows omitting semicolons at the end of lines, but don't. Always end statements with a semicolon. ## js2-mode ## If using Emacs, try js2-mode. It functions as a "lint" by highlighting missing semicolons and the like. ## Imports ## Use CamelCase when importing modules to distinguish them from ordinary variables, e.g. ```js const Big = imports.big; const GLib = imports.gi.GLib; ``` ## Variable declaration ## Always use one of `const`, `var`, or `let` when defining a variable. Always use `let` when block scope is intended; in particular, inside `for()` and `while()` loops, `let` is almost always correct. ```js // Iterating over an array for (let i = 0; i < 10; ++i) { let foo = bar(i); } // Iterating over an object's properties for (let prop in someobj) { ... } ``` If you don't use `let` then the variable is added to function scope, not the for loop block scope. See [What's new in JavaScript 1.7][1] A common case where this matters is when you have a closure inside a loop: ```js for (let i = 0; i < 10; ++i) { mainloop.idle_add(function() { log("number is: " + i); }); } ``` If you used `var` instead of `let` it would print "10" a bunch of times. Inside functions, `let` is always correct instead of `var` as far as we know. `var` is useful when you want to add something to the `with()` object, though... in particular we think you need `var` to define module variables, since our module system loads modules with the equivalent of `with (moduleObject)` ## `this` in closures ## `this` will not be captured in a closure; `this` is relative to how the closure is invoked, not to the value of this where the closure is created, because `this` is a keyword with a value passed in at function invocation time, it is not a variable that can be captured in closures. To solve this, use `Function.bind()`, or fat arrow functions, e.g.: ```js let closure = function() { this._fnorbate() }.bind(this); // or let closure = () => { this._fnorbate(); }; ``` A more realistic example would be connecting to a signal on a method of a prototype: ```js const Lang = imports.lang; MyPrototype = { _init : function() { fnorb.connect('frobate', this._onFnorbFrobate.bind(this)); }, _onFnorbFrobate : function(fnorb) { this._updateFnorb(); }, }; ``` ## Object literal syntax ## JavaScript allows equivalently: ```js foo = { 'bar' : 42 }; foo = { bar: 42 }; ``` and ```js var b = foo['bar']; var b = foo.bar; ``` If your usage of an object is like an object, then you're defining "member variables." For member variables, use the no-quotes no-brackets syntax, that is, `{ bar: 42 }` and `foo.bar`. If your usage of an object is like a hash table (and thus conceptually the keys can have special chars in them), don't use quotes, but use brackets, `{ bar: 42 }`, `foo['bar']`. ## Variable naming ## - We use javaStyle variable names, with CamelCase for type names and lowerCamelCase for variable and method names. However, when calling a C method with underscore-based names via introspection, we just keep them looking as they do in C for simplicity. - Private variables, whether object member variables or module-scoped variables, should begin with `_`. - True global variables (in the global or 'window' object) should be avoided whenever possible. If you do create them, the variable name should have a namespace in it, like `BigFoo` - When you assign a module to an alias to avoid typing `imports.foo.bar` all the time, the alias should be `const TitleCase` so `const Bar = imports.foo.bar;` - If you need to name a variable something weird to avoid a namespace collision, add a trailing `_` (not leading, leading `_` means private). - For GObject constructors, always use the `lowerCamelCase` style for property names instead of dashes or underscores. ## Whitespace ## * 4-space indentation (the Java style) * No trailing whitespace. * No tabs. * If you `chmod +x .git/hooks/pre-commit` it will not let you commit with messed-up whitespace (well, it doesn't catch tabs. turn off tabs in your text editor.) ## JavaScript attributes ## Don't use the getter/setter syntax when getting and setting has side effects, that is, the code: ```js foo.bar = 10; ``` should not do anything other than save "10" as a property of `foo`. It's obfuscated otherwise; if the setting has side effects, it's better if it looks like a method. In practice this means the only use of attributes is to create read-only properties: ```js get bar() { return this._bar; } ``` If the property requires a setter, or if getting it has side effects, methods are probably clearer. [1] http://developer.mozilla.org/en/docs/index.php?title=New_in_JavaScript_1.7&printable=yes#Block_scope_with_let cjs-4.4.0/doc/cairo.md000066400000000000000000000042461356375132300145000ustar00rootroot00000000000000The cairo bindings follows the C API pretty closely. ## Naming ## The module name is called 'cairo' and usually imported into the namespace as 'Cairo'. ```js const Cairo = imports.cairo; ``` Methods are studlyCaps, similar to other JavaScript apis, eg `cairo_move_to` is wrapped to `Cairo.Context.moveTo()` `cairo_surface_write_to_png` to `Cairo.Context.writeToPNG()`. Abbrevations such as RGB, RGBA, PNG, PDF, SVG are always upper-case. Enums are set in the cairo namespace, the enum names are capitalized: `CAIRO_FORMAT_ARGB32` is mapped to `Cairo.Format.ARGB32` etc. ## Surfaces (`cairo_surface_t`) ## Prototype hierarchy * `Surface` (abstract) * `ImageSurface` * `PDFSurface` * `SVGSurface` * `PostScriptSurface` The native surfaces (win32, quartz, xlib) are not supported at this point. Methods manipulating a surface are present in the surface class. Creating an ImageSurface from a PNG is done by calling a static method: ```js let surface = Cairo.ImageSurface.createFromPNG("filename.png"); ``` ## Context (`cairo_t`) ## `cairo_t` is mapped as `Cairo.Context`. You will either get a context from a third-party library such as Clutter/Gtk/Poppler or by calling the `Cairo.Context` constructor. ```js let cr = new Cairo.Context(surface); let cr = Gdk.cairo_create(...); ``` All introspection methods taking or returning a `cairo_t` will automatically create a `Cairo.Context`. ## Patterns (`cairo_pattern_t`) ## Prototype hierarchy * `Pattern` * `Gradient` * `LinearGradient` * `RadialGradient` * `SurfacePattern` * `SolidPattern` You can create a linear gradient by calling the constructor: Constructors: ```js let pattern = new Cairo.LinearGradient(0, 0, 100, 100); let pattern = new Cairo.RadialGradient(0, 0, 10, 100, 100, 10); let pattern = new Cairo.SurfacePattern(surface); let pattern = new Cairo.SolidPattern.createRGB(0, 0, 0); let pattern = new Cairo.SolidPattern.createRGBA(0, 0, 0, 0); ``` TODO: * context: wrap the remaning methods * surface methods * image surface methods * matrix * version * iterating over `cairo_path_t` Fonts & Glyphs are not wrapped, use PangoCairo instead. * glyphs * text cluster * font face * scaled font * font options cjs-4.4.0/examples/000077500000000000000000000000001356375132300141245ustar00rootroot00000000000000cjs-4.4.0/examples/README000066400000000000000000000001151356375132300150010ustar00rootroot00000000000000In order to run those example scripts, do: gjs-console script-filename.js cjs-4.4.0/examples/clutter.js000066400000000000000000000010011356375132300161340ustar00rootroot00000000000000const Clutter = imports.gi.Clutter; Clutter.init(null); let stage = new Clutter.Stage(); let texture = new Clutter.Texture({ filename: 'test.jpg', reactive: true }); texture.connect('button-press-event', function(o, event) { log('Clicked!'); return true; }); let color = new Clutter.Color(); color.from_string('Black'); stage.color = color; stage.add_actor(texture); stage.show(); Clutter.main(); cjs-4.4.0/examples/gettext.js000066400000000000000000000005511356375132300161470ustar00rootroot00000000000000imports.gi.versions.Gtk = '3.0'; const Gettext = imports.gettext; const Gtk = imports.gi.Gtk; Gettext.bindtextdomain("gnome-panel-3.0", "/usr/share/locale"); Gettext.textdomain("gnome-panel-3.0"); Gtk.init(null); let w = new Gtk.Window({ type: Gtk.WindowType.TOPLEVEL }); w.add(new Gtk.Label({ label: Gettext.gettext("Panel") })); w.show_all(); Gtk.main(); cjs-4.4.0/examples/gio-cat.js000066400000000000000000000011401356375132300160010ustar00rootroot00000000000000 const GLib = imports.gi.GLib; const Gio = imports.gi.Gio; let loop = GLib.MainLoop.new(null, false); function cat(filename) { let f = Gio.file_new_for_path(filename); f.load_contents_async(null, function(f, res) { let contents; try { contents = f.load_contents_finish(res)[1]; } catch (e) { log("*** ERROR: " + e.message); loop.quit(); return; } print(contents); loop.quit(); }); loop.run(); } if (ARGV.length != 1) { printerr("Usage: gio-cat.js filename"); } else { cat(ARGV[0]); } cjs-4.4.0/examples/gtk-application.js000066400000000000000000000101571356375132300175540ustar00rootroot00000000000000// See the note about Application.run() at the bottom of the script const System = imports.system; // Include this in case both GTK3 and GTK4 installed, otherwise an exception // will be thrown imports.gi.versions.Gtk = "3.0"; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Gtk = imports.gi.Gtk; // An example GtkApplication with a few bells and whistles, see also: // https://wiki.gnome.org/HowDoI/GtkApplication var ExampleApplication = GObject.registerClass({ Properties: { "exampleprop": GObject.ParamSpec.string( "exampleprop", // property name "ExampleProperty", // nickname "An example read write property", // description GObject.ParamFlags.READWRITE, // read/write/construct... "" // implement defaults manually ) }, Signals: { "examplesig": { param_types: [ GObject.TYPE_INT ] } }, }, class ExampleApplication extends Gtk.Application { _init() { super._init({ application_id: "org.gnome.gjs.ExampleApplication", flags: Gio.ApplicationFlags.FLAGS_NONE }); } // Example property getter/setter get exampleprop() { if (typeof this._exampleprop === "undefined") { return "a default value"; } return this._exampleprop; } set exampleprop(value) { this._exampleprop = value; // notify() has to be called, if you want it this.notify("exampleprop"); } // Example signal emission emit_examplesig(number) { this.emit("examplesig", number); } vfunc_startup() { super.vfunc_startup(); // An example GAction, see: https://wiki.gnome.org/HowDoI/GAction let exampleAction = new Gio.SimpleAction({ name: "exampleAction", parameter_type: new GLib.VariantType("s") }); exampleAction.connect("activate", (action, param) => { param = param.deep_unpack().toString(); if (param === "exampleParameter") { log("Yes!"); } }); this.add_action(exampleAction); } vfunc_activate() { super.vfunc_activate(); this.hold(); // Example ApplicationWindow let window = new Gtk.ApplicationWindow({ application: this, title: "Example Application Window", default_width: 300, default_height: 200 }); let label = new Gtk.Label({ label: this.exampleprop }); window.add(label); window.connect("delete-event", () => { this.quit(); }); window.show_all(); // Example GNotification, see: https://developer.gnome.org/GNotification/ let notif = new Gio.Notification(); notif.set_title("Example Notification"); notif.set_body("Example Body"); notif.set_icon( new Gio.ThemedIcon({ name: "dialog-information-symbolic" }) ); // A default action for when the body of the notification is clicked notif.set_default_action("app.exampleAction('exampleParameter')"); // A button for the notification notif.add_button( "Button Text", "app.exampleAction('exampleParameter')" ); // This won't actually be shown, since an application needs a .desktop // file with a base name matching the application id this.send_notification("example-notification", notif); // Withdraw this.withdraw_notification("example-notification"); } }); // The proper way to run a Gtk.Application or Gio.Application is take ARGV and // prepend the program name to it, and pass that to run() let app = new ExampleApplication(); app.run([System.programInvocationName].concat(ARGV)); // Or a one-liner... // (new ExampleApplication()).run([System.programInvocationName].concat(ARGV)); cjs-4.4.0/examples/gtk.js000066400000000000000000000047351356375132300152600ustar00rootroot00000000000000// Include this in case both GTK3 and GTK4 installed, otherwise an exception // will be thrown imports.gi.versions.Gtk = "3.0"; const Gtk = imports.gi.Gtk; // Initialize Gtk before you start calling anything from the import Gtk.init(null); // Construct a top-level window let window = new Gtk.Window ({ type: Gtk.WindowType.TOPLEVEL, title: "A default title", default_width: 300, default_height: 250, // A decent example of how constants are mapped: // 'Gtk' and 'WindowPosition' from the enum name GtkWindowPosition, // 'CENTER' from the enum's constant GTK_WIN_POS_CENTER window_position: Gtk.WindowPosition.CENTER }); // Object properties can also be set or changed after construction, unless they // are marked construct-only. window.title = "Hello World!"; // This is a callback function function onDeleteEvent(widget, event) { log("delete-event emitted"); // If you return false in the "delete_event" signal handler, Gtk will emit // the "destroy" signal. // // Returning true gives you a chance to pop up 'are you sure you want to // quit?' type dialogs. return false; } // When the window is given the "delete_event" signal (this is given by the // window manager, usually by the "close" option, or on the titlebar), we ask // it to call the onDeleteEvent() function as defined above. window.connect("delete-event", onDeleteEvent); // GJS will warn when calling a C function with unexpected arguments... // // window.connect("destroy", Gtk.main_quit); // // ...so use arrow functions for inline callbacks with arguments to adjust window.connect("destroy", () => { Gtk.main_quit(); }); // Create a button to close the window let button = new Gtk.Button({ label: "Close the Window", // Set visible to 'true' if you don't want to call button.show() later visible: true, // Another example of constant mapping: // 'Gtk' and 'Align' are taken from the GtkAlign enum, // 'CENTER' from the constant GTK_ALIGN_CENTER valign: Gtk.Align.CENTER, halign: Gtk.Align.CENTER }); // Connect to the 'clicked' signal, using another way to call an arrow function button.connect("clicked", () => window.destroy()); // Add the button to the window window.add(button); // Show the window window.show(); // All gtk applications must have a Gtk.main(). Control will end here and wait // for an event to occur (like a key press or mouse event). The main loop will // run until Gtk.main_quit is called. Gtk.main(); cjs-4.4.0/examples/http-server.js000066400000000000000000000017131356375132300167470ustar00rootroot00000000000000// This is a simple example of a HTTP server in Gjs using libsoup const Soup = imports.gi.Soup; function main() { let handler = function(server, msg, path, query, client) { msg.status_code = 200; msg.response_headers.set_content_type('text/html', {}); msg.response_body.append('Greetings, visitor from ' + client.get_host() + '
What is your name?
\n'); }; let helloHandler = function(server, msg, path, query, client) { if (!query) { msg.set_redirect(302, '/'); return; } msg.status_code = 200; msg.response_headers.set_content_type('text/html', { charset: 'UTF-8' }); msg.response_body.append('Hello, ' + query.myname + '! \u263A
Go back'); }; let server = new Soup.Server({ port: 1080 }); server.add_handler('/', handler); server.add_handler('/hello', helloHandler); server.run(); } main(); cjs-4.4.0/examples/test.jpg000066400000000000000000000344201356375132300156100ustar00rootroot00000000000000JFIFHHC     C    B !1AQa"q2 #B3Rb$CSr46!1A"Qa2qB#3CRS ?;4jOkJiMZqh=T%x hB2>ZdbHb?MO]K_~}~/VyUշR,GjyYKGۼH=7vN3n.)4$gvR1 E!:4̤ls5b}>H*"eٲOdԃ3RQKt@UwD5Ov* RJOR AJH!iBic)R\cJagZkJ!=L92\u<,%Oo[o8 {V~͉hB"*<ϽV[\.(*hѩ8M +je,SXN{SߒHiǵA?(*$sxQrpa=@ "&ExLQ^QPjgLwoNy?XҬ׿|;!ꙣoq_~ <лh&ڪkjfJwcc8895` Mg}_i {ԄD mz(g?E,ʱ ƒj*L3$#I-(!R "TqS  B}9ܛb[+ORڌJ1KjXnu^GVI7ž]IkmR^XrN5ȔV{54}Rs.N(JjGێ,p*%%^$n|=ϕnvύrzaZOz:r5-;*PRxHQV w A#MU9[yzlٯIJuN9% uN)Grs%ɘ ݣÙM\z:Fی-`m4:,io ёsr+t7RjS:1NŭgnohO'KȚ9Pk # WI/DoB)Xkp bEcPi,ET,At$sH(<ԐщME$ )Sx*]ّMi ~ TiH<△U() )*Q)P   *\)zSZtbAHrDOj`#R3P' tCv47/8S `V$mۜe#*W?ʫJZ^|"6fP`C1Og`.Ǡ+|S֖Oq1v7<\-X @=ƗI>C@^dۚGʩжmnU?|6zVGC&7X,۴RGi)~IYܵ-dg'!t`U7{ll ͣi[9I>沤pzihJ_LCX9,l- ,I%\*67]!FH)jQXW-ncctTx^$RJAM6<$8SkNPF%dԻIRF)%HXE=!I::bqUJ?(āQ%JtOM)#iJӣSQ)ѩR;nV@{n,hKWۜK!Jn>6)`'g򾋦[Q#(:MC0Gʕm #8 z&Y7Ȗ&Z puwda#YݳeU~Kk#plMûℽOZFa,|T{!Ê 6AH!*#NHCN[ u2;ޕTN|7}} D94tґ-/B_r1kesb*3 Y@BpxQDp\DeOmd))Hޞ ?Z^t 415_bՈ!bA:HJGMGlFBauy Ax (ʰJ"P\1I1_1NGDI|SJ!=r:j* htNÿ"R14#8I?X}PA۸R@==𛨵B'j ҵ| ih) oy`@nl.:S|ًW}幹+RI VOl`r{ js(4FUgnrq-rmJm,>O$ym ۇgAfIY)8O^j$k5pϨv@⣷]14#n|W 9\@c6Ԗ f@VEs9+5Y 7hC-W "C6HtC3=M$x7{\CmJF28~lUonP㑢>qAZކ|qNJf|d󸸤^rPكr16=).# u=Ч>$OG] Ҳ@|rp79Ț9qCXI+W R+;wQVH8ӴF@=14m<29roc Z ֬ʸsǭ0#-{}? uFF1'{Ի~"M:#N]$"3/Q$g+60.|?K:6w[Z|j)DyA]R7r$m ~|Q&k$icJ!y)!d'p8ϒ{/e<8X(ǯv.4 G(ʎ2qϿ4BMYrolU!*_*{<ȸ8!ۡ3e;!{iNA+I'$KoNưQ긛e̒vC<e%nG ʕ1Uuԭ>dqZ+*#tm)uFIJ\uA{P$r0yCf3o*BTN!{>[J5`OgE|>N g[%60kԽ(l234HQGm߿iahy#>UAQV_PFGZ„/,Kh:<CI'aXo6j΁5s Caeѐ2#5.A BAPsI5yr}vCu,OmjjOM5ݴѮQ -l/,X^kmh .|GK,qj)”>^@vt_Gb`cqTE' @=ϕ]kKY;MfRPQD Q Y{{ä3Ze+< wI'?"# )Gtkjt-^udg$>yX ?Vݹ7=P.HBZCHSM$.1IP$܀qt:26ϑ??}gֽJބ7[r4X!I*.$R{`C5+dzif:Kz|72z;IR-o-mބ(AT8զL׀*Y2@m~^BqR쇶ʘda)@ 8˜ zRe 8RC9- EbH{fx ji̩. qp^@ۼU;Ge$G N0GjcaT9u-6ƂBɞBJtWm6AJV*4~tVŭ:[K|`)4~Fiov&b` RW;;\( MGa͒c&Fɩ𣞨 m@RQ!?%1C[GiɒaqA)B{j$z'ܷoW";V=(b318U`^i}!Lm%@浣 c>BeH*i%JT"tצZYz$^Vv'U<=DXCyZ:)Nt7D\ _D?e R#?Pl](5ڙcNq"3H8ӳIi=G^U^V£FH9'}g>_4Q(qH (dDyjN8“ZmbqVLp a0ac܃SJvvF-1_\2Yi88d^7_M=;MXNa\;BgVЗΨ JV8A#jE'|Ni<^~W1.]F Z JqC~VR6%9+>X4$g~b%w4ܝÂqֳ 4.CytFXS% -e#*x2EV{=nP7jh3VlχDzjz] vjͻtDSuЛ[ڢ>omPu di`ehM*غ݆ʔ`zt>0dT2jO^SW)p!b=CWB )u$ȅ tQXD]ic#C0NDc Ə:{QXAÉ:{ :- m ;9 )SڕR#*BZu R"1W5}BߵWZrnf,Fm606{j?xf:q7+˫[<$y+6}NMͭulf+x N\:Ǭշ,iK<䓵+nx#$qkB _ߚ$p YK2^5n/[600MD3Jr}PŧC6SMy2F>6>~sC NGiyԱW75qݐt^Oz~c,eS$:i8s؟j\Ҽ|:^ 6-޻qBfL' emI U۱Kz-3bèL]cC 1n.&`g )o 품 8m 'j>p9^*R$ V,cWűgcC J| (c!N녊>Uq-\}a6v*8'3} BѺե.+Ks(PRǨUPSZ/\Oj$lyK[&4*gEhڬE"ިUƝ$ét=WhkBJvP(Q⻤t3c(rԥ+$X%caRjPMH l%\62Kc5]PKٟG=46}Gik'P;GEw%͠ CMĠQ$3n65hަvʹ1TAs9Z? ɊVů]eΏq.&Pg˻%Jڡ?js#9P{r)d:Rv{Z eR:ׂ w~Jԭ sP-V@:I) m\B@7K4}6!lZy'''Fjg*p:#b\wPk JNA< nmlG /!(xIeҟH8ڑ1ul.!(v*d\yT`?_jf2[Z1٣ DzjSuHe0$ҐVR=|+?W3ġ:h}ډ|B_5OصkG%拉"״ 䁟L͘u[K s+Z>4)t}[ܢ\ܖ%_7*Q)$sUked6NЁѽx4sDg)G׊sSjk5-)wN\a Tq@8)CP.ݒ);<$ ٺ^47XEYJ %+8N{cmlDW9:nʏD\"%ثT9*d>j@װ8ΖqoUxr(Jvť]K" /N,)06js@A|v,v*?tGr0k1)]e8Rz^_^=nVMg͉v`$}생#i>$~}S{Zŕ2)Pw$[B #$v=5A@JD) .czr}k~Ju'P8(i$xc wֲmW8ulv)PۉI'r'w}1چ3jN=c!}z)<0m?. oq[$ VlgJՍ}qH|IzGHO&5ZM7V\Y8fH\Ҡ0Bv{[RM =G/7G+V /Kܗ}e+[N iAن8h Imv AmA +"N\PZJx%~X]Kp6ą O|PSaL5}$RۈV33pTe{T{xI Y/T!67>9ZZR^Jÿ8AZsֶKv<)G<"*{7=lÁH ϪEYhr+i)2AEE$#Y3\rGjy.9IA,5uʡSU '? [{cr7m#je-unb )*Vx5kWhǼ9PR*bAl$@ǸVuNmpЙ'i',+mU?y=5BkkK:Ng"v݇wBf (r&AQQŽ삐pG>Hƛ)-/-›n/\PwbmNS+jtofVzjY\.q "dYqw.QTD87mVRqGi22'<myNI7L5CTZVw6?0F@ϯߎ1& -ڂFcQxz-突xѱ^#pq~mbFǾ|)b}Jy 20W A5gژݢ)b+˯evyG)JBN}<|}"öxxm7BHT p{J_EVs_lOTV~`3ϵFW S v[3Bb,q%Rv6G9oh[uҰ,Xw+~/)+ZBKkHWIfGM[zu 靲ElyѮPI7; +#}-VmUR$֔qbbZ㎁o 2ʂR YNp7u#^ua)HPOG3y秩BkL8BQVwSwpe:A9؟H(PQ'WէH1JB}0פCYwU]IUS߅.(um=&LͮC#psM*'P|:id\ ^Rɬ!ّ)[]DyX HvaJ0SO6? oifLiR l3ZHPQsAR FЌbo Z $Xx={ 'f8Ef]Pe H Pޭ22{zI iiuYٰ9S B`s-XPm['b r;wjZgu*ctyh+L T3#I \ݧszEc]Ӡ+uil9|\FP[ ##`>=ɫ-mI"Ypa)}+:PmU.Su6 IZharzEҔd#=&B0ֆǒC"X$)drqVYPBtC4y4Ue2Hyr2|{x)&:gB^&У @Unn>]J9_`%n-|U'ԉZ6P\NsڇGNR2ATr)¤Rid%vcIѽPNS+ 4e>E*RBɅ91y/R~>94XmBm2}ojM9r:]niCL7|j0PK.1Q'=YFf[[C%C  _¦k\w;fVŃ;({YҴhhE#wkuࣨVRnm+`x3?ڍqsG~88tTewӒy˴NaM/P fmlǮAjPmEJY94--&78bZۓ>kkQ(BO Y>S3Nvi/ •}[noCۡ '>Ϡ9 P:ok-VRXma$Rk#5 KPBRY=kIwTaT!ݤRH+9|tţ\v.U>Hl]RJ ChkI@?ohKm$0 U>w0B;qԴ9@$-!Yl]mkhhpRsœȸ{6=>ә ϟA6)^Ou?ZHjz~ډjaDF=+GU{lp֊ Dt '>{+9S AJ8wNn^t'Z;Pex|Ǝg%rk|tWP\VR}yU33>*bى7f6~e2A(FFȷR<"rhmmԜbZ *z{ilrܖRQ@s|3$s FwmZNJS!9'<?,P\|@gYϸ+ܥꁺY3|!VM# r~nHXSENR@R @귐ZEtz} hsaLNLer%2*27Df랟.w |0 B) *FrG% hI̓d&m8ФPIlv{VƛPE8Ҷ^X\ڨs*+%);e k8M\VXSe#9²yҳD.iaǘF_H77޴ s&3tCI8䁐2{WMهv'漇O=@]/ee@H`V \~BAܯAɩo 梾ʆUqSlr;rR{ .br@:)+@Hcmg~~>e"ܭ}އ*"ٙ ^TCuzu/>H"as9톽Җw;#џzb`"kyޒ<+q\ܥ%Gi4PDhXHA4" NQ<+)WC% JJ8!(T('9+uH>G&q'Ί†iCeA!pbP55 ]ec:ZqM: )ROP"k#![=?NV؇}rtdc3I_p<40 QV۪x_:mR$ACLj;CYO6/dN)?ai~ pi7ƟMd꾄Աټ0҈{ ړug3-HYgb]ZO9V?CE'k9KfLGXqK4J4S3w I ;Vꛕ SWZqPg"{C|f31hme)@?47̳tj<K7̶C*a:+H2{e}%I$'hMĩ{ZyQXꉠ;EJ8:ڝZ>8hzm'F%T}>8= k?&4e/ha0#sԽNGJjWQ/fK#O;#lK٥)MD?uzD腫S!SF*PsU; $c8D9KڟP;Z5֣[vq$~Iio P: #"S.>p[*?\ۊJAڌ;~QRXOΆHV#P2sP.Fhە/> r0oTJTA㏥1!L9KBrG3Mip%)^)Lz\ڕ (`9!F_cjs-4.4.0/examples/webkit.js000066400000000000000000000005141356375132300157470ustar00rootroot00000000000000imports.gi.versions.Gtk = '3.0'; imports.gi.versions.WebKit2 = '4.0'; const Gtk = imports.gi.Gtk; const WebKit = imports.gi.WebKit2; Gtk.init(null); let win = new Gtk.Window(); let view = new WebKit.WebView(); view.load_uri("http://www.google.com/"); win.add(view); win.set_size_request(640, 480); win.show_all(); Gtk.main(); cjs-4.4.0/gi/000077500000000000000000000000001356375132300127055ustar00rootroot00000000000000cjs-4.4.0/gi/arg.cpp000066400000000000000000003644311356375132300141750ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include "arg.h" #include "gtype.h" #include "object.h" #include "interface.h" #include "foreign.h" #include "fundamental.h" #include "boxed.h" #include "union.h" #include "param.h" #include "value.h" #include "gerror.h" #include "cjs/byteArray.h" #include "cjs/jsapi-wrapper.h" #include bool _gjs_flags_value_is_valid(JSContext *context, GType gtype, gint64 value) { GFlagsValue *v; guint32 tmpval; void *klass; /* FIXME: Do proper value check for flags with GType's */ if (gtype == G_TYPE_NONE) return true; klass = g_type_class_ref(gtype); /* check all bits are defined for flags.. not necessarily desired */ tmpval = (guint32)value; if (tmpval != value) { /* Not a guint32 */ gjs_throw(context, "0x%" G_GINT64_MODIFIER "x is not a valid value for flags %s", value, g_type_name(G_TYPE_FROM_CLASS(klass))); return false; } while (tmpval) { v = g_flags_get_first_value((GFlagsClass *) klass, tmpval); if (!v) { gjs_throw(context, "0x%x is not a valid value for flags %s", (guint32)value, g_type_name(G_TYPE_FROM_CLASS(klass))); return false; } tmpval &= ~v->value; } g_type_class_unref(klass); return true; } static bool _gjs_enum_value_is_valid(JSContext *context, GIEnumInfo *enum_info, gint64 value) { bool found; int n_values; int i; n_values = g_enum_info_get_n_values(enum_info); found = false; for (i = 0; i < n_values; ++i) { GIValueInfo *value_info; gint64 enum_value; value_info = g_enum_info_get_value(enum_info, i); enum_value = g_value_info_get_value(value_info); g_base_info_unref((GIBaseInfo *)value_info); if (enum_value == value) { found = true; break; } } if (!found) { gjs_throw(context, "%" G_GINT64_MODIFIER "d is not a valid value for enumeration %s", value, g_base_info_get_name((GIBaseInfo *)enum_info)); } return found; } static bool _gjs_enum_uses_signed_type (GIEnumInfo *enum_info) { GITypeTag storage = g_enum_info_get_storage_type(enum_info); return (storage == GI_TYPE_TAG_INT8 || storage == GI_TYPE_TAG_INT16 || storage == GI_TYPE_TAG_INT32 || storage == GI_TYPE_TAG_INT64); } /* This is hacky - g_function_info_invoke() and g_field_info_get/set_field() expect * arg->v_int to have the enum value in arg->v_int and depend on all flags and * enumerations being passed on the stack in a 32-bit field. See FIXME comment in * g_field_info_get_field. The same assumption of enums cast to 32-bit signed integers * is found in g_value_set_enum/g_value_set_flags(). */ gint64 _gjs_enum_from_int (GIEnumInfo *enum_info, int int_value) { if (_gjs_enum_uses_signed_type (enum_info)) return (gint64)int_value; else return (gint64)(guint32)int_value; } /* Here for symmetry, but result is the same for the two cases */ static int _gjs_enum_to_int (GIEnumInfo *enum_info, gint64 value) { return (int)value; } /* Check if an argument of the given needs to be released if we created it * from a JS value to pass it into a function and aren't transfering ownership. */ static bool type_needs_release (GITypeInfo *type_info, GITypeTag type_tag) { if (type_tag == GI_TYPE_TAG_UTF8 || type_tag == GI_TYPE_TAG_FILENAME || type_tag == GI_TYPE_TAG_ARRAY || type_tag == GI_TYPE_TAG_GLIST || type_tag == GI_TYPE_TAG_GSLIST || type_tag == GI_TYPE_TAG_GHASH || type_tag == GI_TYPE_TAG_ERROR) return true; if (type_tag == GI_TYPE_TAG_INTERFACE) { GIBaseInfo* interface_info; GIInfoType interface_type; GType gtype; bool needs_release; interface_info = g_type_info_get_interface(type_info); g_assert(interface_info != NULL); interface_type = g_base_info_get_type(interface_info); if (interface_type == GI_INFO_TYPE_STRUCT || interface_type == GI_INFO_TYPE_ENUM || interface_type == GI_INFO_TYPE_FLAGS || interface_type == GI_INFO_TYPE_OBJECT || interface_type == GI_INFO_TYPE_INTERFACE || interface_type == GI_INFO_TYPE_UNION || interface_type == GI_INFO_TYPE_BOXED) { /* These are subtypes of GIRegisteredTypeInfo for which the * cast is safe */ gtype = g_registered_type_info_get_g_type ((GIRegisteredTypeInfo*)interface_info); } else if (interface_type == GI_INFO_TYPE_VALUE) { /* Special case for GValues */ gtype = G_TYPE_VALUE; } else { /* Everything else */ gtype = G_TYPE_NONE; } if (g_type_is_a(gtype, G_TYPE_CLOSURE)) needs_release = true; else if (g_type_is_a(gtype, G_TYPE_VALUE)) needs_release = g_type_info_is_pointer(type_info); else needs_release = false; g_base_info_unref(interface_info); return needs_release; } return false; } /* Check if an argument of the given needs to be released if we obtained it * from out argument (or the return value), and we're transferring ownership */ static bool type_needs_out_release(GITypeInfo *type_info, GITypeTag type_tag) { if (type_tag == GI_TYPE_TAG_UTF8 || type_tag == GI_TYPE_TAG_FILENAME || type_tag == GI_TYPE_TAG_ARRAY || type_tag == GI_TYPE_TAG_GLIST || type_tag == GI_TYPE_TAG_GSLIST || type_tag == GI_TYPE_TAG_GHASH || type_tag == GI_TYPE_TAG_ERROR) return true; if (type_tag == GI_TYPE_TAG_INTERFACE) { GIBaseInfo* interface_info; GIInfoType interface_type; bool needs_release = true; interface_info = g_type_info_get_interface(type_info); g_assert(interface_info != NULL); interface_type = g_base_info_get_type(interface_info); if (interface_type == GI_INFO_TYPE_ENUM || interface_type == GI_INFO_TYPE_FLAGS) needs_release = false; else if (interface_type == GI_INFO_TYPE_STRUCT || interface_type == GI_INFO_TYPE_UNION) needs_release = g_type_info_is_pointer(type_info); g_base_info_unref(interface_info); return needs_release; } return false; } static bool gjs_array_to_g_list(JSContext *context, JS::Value array_value, unsigned int length, GITypeInfo *param_info, GITransfer transfer, GITypeTag list_type, GList **list_p, GSList **slist_p) { guint32 i; GList *list; GSList *slist; list = NULL; slist = NULL; if (transfer == GI_TRANSFER_CONTAINER) { if (type_needs_release (param_info, g_type_info_get_tag(param_info))) { /* FIXME: to make this work, we'd have to keep a list of temporary * GArguments for the function call so we could free them after * the surrounding container had been freed by the callee. */ gjs_throw(context, "Container transfer for in parameters not supported"); return false; } transfer = GI_TRANSFER_NOTHING; } JS::RootedObject array(context, array_value.toObjectOrNull()); JS::RootedValue elem(context); for (i = 0; i < length; ++i) { GArgument elem_arg = { 0 }; elem = JS::UndefinedValue(); if (!JS_GetElement(context, array, i, &elem)) { gjs_throw(context, "Missing array element %u", i); return false; } /* FIXME we don't know if the list elements can be NULL. * gobject-introspection needs to tell us this. * Always say they can't for now. */ if (!gjs_value_to_g_argument(context, elem, param_info, NULL, GJS_ARGUMENT_LIST_ELEMENT, transfer, false, &elem_arg)) { return false; } if (list_type == GI_TYPE_TAG_GLIST) { /* GList */ list = g_list_prepend(list, elem_arg.v_pointer); } else { /* GSList */ slist = g_slist_prepend(slist, elem_arg.v_pointer); } } list = g_list_reverse(list); slist = g_slist_reverse(slist); *list_p = list; *slist_p = slist; return true; } static GHashTable * create_hash_table_for_key_type(GITypeInfo *key_param_info) { /* Don't use key/value destructor functions here, because we can't * construct correct ones in general if the value type is complex. * Rely on the type-aware g_argument_release functions. */ GITypeTag key_type = g_type_info_get_tag(key_param_info); if (key_type == GI_TYPE_TAG_UTF8 || key_type == GI_TYPE_TAG_FILENAME) return g_hash_table_new(g_str_hash, g_str_equal); return g_hash_table_new(NULL, NULL); } /* Converts a JS::Value to a GHashTable key, stuffing it into @pointer_out if * possible, otherwise giving the location of an allocated key in @pointer_out. */ static bool value_to_ghashtable_key(JSContext *cx, JS::HandleValue value, GITypeInfo *type_info, gpointer *pointer_out) { GITypeTag type_tag = g_type_info_get_tag((GITypeInfo*) type_info); bool out_of_range = false; bool unsupported = false; g_return_val_if_fail(value.isString() || value.isInt32(), false); gjs_debug_marshal(GJS_DEBUG_GFUNCTION, "Converting JS::Value to GHashTable key %s", g_type_tag_to_string(type_tag)); switch (type_tag) { case GI_TYPE_TAG_BOOLEAN: /* This doesn't seem particularly useful, but it's easy */ *pointer_out = GUINT_TO_POINTER(JS::ToBoolean(value)); break; case GI_TYPE_TAG_UNICHAR: if (value.isInt32()) { *pointer_out = GINT_TO_POINTER(value.toInt32()); } else { uint32_t ch; if (!gjs_unichar_from_string(cx, value, &ch)) return false; *pointer_out = GUINT_TO_POINTER(ch); } break; #define HANDLE_SIGNED_INT(bits) \ case GI_TYPE_TAG_INT##bits: { \ int32_t i; \ if (!JS::ToInt32(cx, value, &i)) \ return false; \ if (i > G_MAXINT##bits || i < G_MININT##bits) \ out_of_range = true; \ *pointer_out = GINT_TO_POINTER(i); \ break; \ } HANDLE_SIGNED_INT(8); HANDLE_SIGNED_INT(16); HANDLE_SIGNED_INT(32); #undef HANDLE_SIGNED_INT #define HANDLE_UNSIGNED_INT(bits) \ case GI_TYPE_TAG_UINT##bits: { \ uint32_t i; \ if (!JS::ToUint32(cx, value, &i)) \ return false; \ if (i > G_MAXUINT##bits) \ out_of_range = true; \ *pointer_out = GUINT_TO_POINTER(i); \ break; \ } HANDLE_UNSIGNED_INT(8); HANDLE_UNSIGNED_INT(16); HANDLE_UNSIGNED_INT(32); #undef HANDLE_UNSIGNED_INT case GI_TYPE_TAG_FILENAME: { GjsAutoChar cstr; JS::RootedValue str_val(cx, value); if (!str_val.isString()) { JS::RootedString str(cx, JS::ToString(cx, str_val)); str_val.setString(str); } if (!gjs_string_to_filename(cx, str_val, &cstr)) return false; *pointer_out = cstr.release(); break; } case GI_TYPE_TAG_UTF8: { JS::RootedString str(cx); if (!value.isString()) str = JS::ToString(cx, value); else str = value.toString(); GjsAutoJSChar cstr = JS_EncodeStringToUTF8(cx, str); if (!cstr) return false; *pointer_out = cstr.copy(); break; } case GI_TYPE_TAG_FLOAT: case GI_TYPE_TAG_DOUBLE: case GI_TYPE_TAG_INT64: case GI_TYPE_TAG_UINT64: /* FIXME: The above four could be supported, but are currently not. The ones * below cannot be key types in a regular JS object; we would need to allow * marshalling Map objects into GHashTables to support those. */ case GI_TYPE_TAG_VOID: case GI_TYPE_TAG_GTYPE: case GI_TYPE_TAG_ERROR: case GI_TYPE_TAG_INTERFACE: case GI_TYPE_TAG_GLIST: case GI_TYPE_TAG_GSLIST: case GI_TYPE_TAG_GHASH: case GI_TYPE_TAG_ARRAY: unsupported = true; break; default: g_warning("Unhandled type %s for GHashTable key conversion", g_type_tag_to_string(type_tag)); unsupported = true; break; } if (G_UNLIKELY(unsupported)) { gjs_throw(cx, "Type %s not supported for hash table keys", g_type_tag_to_string(type_tag)); return false; } if (G_UNLIKELY(out_of_range)) { gjs_throw(cx, "value is out of range for hash table key of type %s", g_type_tag_to_string(type_tag)); return false; } return true; } static bool gjs_object_to_g_hash(JSContext *context, JS::Value hash_value, GITypeInfo *key_param_info, GITypeInfo *val_param_info, GITransfer transfer, GHashTable **hash_p) { GHashTable *result = NULL; size_t id_ix, id_len; g_assert(hash_value.isObjectOrNull()); JS::RootedObject props(context, hash_value.toObjectOrNull()); if (transfer == GI_TRANSFER_CONTAINER) { if (type_needs_release (key_param_info, g_type_info_get_tag(key_param_info)) || type_needs_release (val_param_info, g_type_info_get_tag(val_param_info))) { /* FIXME: to make this work, we'd have to keep a list of temporary * GArguments for the function call so we could free them after * the surrounding container had been freed by the callee. */ gjs_throw(context, "Container transfer for in parameters not supported"); return false; } transfer = GI_TRANSFER_NOTHING; } JS::Rooted ids(context, context); if (!JS_Enumerate(context, props, &ids)) return false; result = create_hash_table_for_key_type(key_param_info); JS::RootedValue key_js(context), val_js(context); JS::RootedId cur_id(context); for (id_ix = 0, id_len = ids.length(); id_ix < id_len; ++id_ix) { cur_id = ids[id_ix]; gpointer key_ptr, val_ptr; GIArgument val_arg = { 0 }; if (!JS_IdToValue(context, cur_id, &key_js)) goto free_hash_and_fail; /* Type check key type. */ if (!value_to_ghashtable_key(context, key_js, key_param_info, &key_ptr)) goto free_hash_and_fail; if (!JS_GetPropertyById(context, props, cur_id, &val_js)) goto free_hash_and_fail; /* Type check and convert value to a c type */ if (!gjs_value_to_g_argument(context, val_js, val_param_info, NULL, GJS_ARGUMENT_HASH_ELEMENT, transfer, true /* allow null */, &val_arg)) goto free_hash_and_fail; GITypeTag val_type = g_type_info_get_tag(val_param_info); /* Use heap-allocated values for types that don't fit in a pointer */ if (val_type == GI_TYPE_TAG_INT64) { int64_t *heap_val = g_new(int64_t, 1); *heap_val = val_arg.v_int64; val_ptr = heap_val; } else if (val_type == GI_TYPE_TAG_UINT64) { uint64_t *heap_val = g_new(uint64_t, 1); *heap_val = val_arg.v_uint64; val_ptr = heap_val; } else if (val_type == GI_TYPE_TAG_FLOAT) { float *heap_val = g_new(float, 1); *heap_val = val_arg.v_float; val_ptr = heap_val; } else if (val_type == GI_TYPE_TAG_DOUBLE) { double *heap_val = g_new(double, 1); *heap_val = val_arg.v_double; val_ptr = heap_val; } else { /* Other types are simply stuffed inside v_pointer */ val_ptr = val_arg.v_pointer; } g_hash_table_insert(result, key_ptr, val_ptr); } *hash_p = result; return true; free_hash_and_fail: g_hash_table_destroy(result); return false; } bool gjs_array_from_strv(JSContext *context, JS::MutableHandleValue value_p, const char **strv) { guint i; JS::AutoValueVector elems(context); /* We treat a NULL strv as an empty array, since this function should always * set an array value when returning true. * Another alternative would be to set value_p to JS::NullValue, but clients * would need to always check for both an empty array and null if that was * the case. */ for (i = 0; strv != NULL && strv[i] != NULL; i++) { if (!elems.growBy(1)) g_error("Unable to grow vector"); if (!gjs_string_from_utf8(context, strv[i], elems[i])) return false; } JS::RootedObject obj(context, JS_NewArrayObject(context, elems)); if (!obj) return false; value_p.setObject(*obj); return true; } bool gjs_array_to_strv(JSContext *context, JS::Value array_value, unsigned int length, void **arr_p) { char **result; guint32 i; JS::RootedObject array(context, array_value.toObjectOrNull()); JS::RootedValue elem(context); result = g_new0(char *, length+1); for (i = 0; i < length; ++i) { elem = JS::UndefinedValue(); if (!JS_GetElement(context, array, i, &elem)) { g_free(result); gjs_throw(context, "Missing array element %u", i); return false; } GjsAutoJSChar tmp_result; if (!gjs_string_to_utf8(context, elem, &tmp_result)) { g_strfreev(result); return false; } result[i] = tmp_result.copy(); } *arr_p = result; return true; } static bool gjs_string_to_intarray(JSContext *context, JS::HandleString str, GITypeInfo *param_info, void **arr_p, size_t *length) { GITypeTag element_type; char16_t *result16; element_type = g_type_info_get_tag(param_info); if (element_type == GI_TYPE_TAG_INT8 || element_type == GI_TYPE_TAG_UINT8) { GjsAutoJSChar result = JS_EncodeStringToUTF8(context, str); if (!result) return false; *length = strlen(result); *arr_p = result.copy(); return true; } if (element_type == GI_TYPE_TAG_INT16 || element_type == GI_TYPE_TAG_UINT16) { if (!gjs_string_get_char16_data(context, str, &result16, length)) return false; *arr_p = result16; return true; } if (element_type == GI_TYPE_TAG_UNICHAR) { gunichar *result_ucs4; if (!gjs_string_to_ucs4(context, str, &result_ucs4, length)) return false; *arr_p = result_ucs4; return true; } /* can't convert a string to this type */ gjs_throw(context, "Cannot convert string to array of '%s'", g_type_tag_to_string (element_type)); return false; } static bool gjs_array_to_gboolean_array(JSContext *cx, JS::Value array_value, unsigned length, void **arr_p) { unsigned i; JS::RootedObject array(cx, array_value.toObjectOrNull()); JS::RootedValue elem(cx); gboolean *result = g_new0(gboolean, length); for (i = 0; i < length; i++) { if (!JS_GetElement(cx, array, i, &elem)) { g_free(result); gjs_throw(cx, "Missing array element %u", i); return false; } bool val = JS::ToBoolean(elem); result[i] = val; } *arr_p = result; return true; } static bool gjs_array_to_intarray(JSContext *context, JS::Value array_value, unsigned int length, void **arr_p, unsigned intsize, bool is_signed) { /* nasty union types in an attempt to unify the various int types */ union { uint64_t u; int64_t i; } intval; void *result; unsigned i; JS::RootedObject array(context, array_value.toObjectOrNull()); JS::RootedValue elem(context); /* add one so we're always zero terminated */ result = g_malloc0((length+1) * intsize); for (i = 0; i < length; ++i) { bool success; elem = JS::UndefinedValue(); if (!JS_GetElement(context, array, i, &elem)) { g_free(result); gjs_throw(context, "Missing array element %u", i); return false; } /* do whatever sign extension is appropriate */ success = (is_signed) ? JS::ToInt64(context, elem, &(intval.i)) : JS::ToUint64(context, elem, &(intval.u)); if (!success) { g_free(result); gjs_throw(context, "Invalid element in int array"); return false; } /* Note that this is truncating assignment. */ switch (intsize) { case 1: ((guint8*)result)[i] = (gint8) intval.u; break; case 2: ((guint16*)result)[i] = (gint16) intval.u; break; case 4: ((guint32*)result)[i] = (gint32) intval.u; break; case 8: ((uint64_t *)result)[i] = (int64_t) intval.u; break; default: g_assert_not_reached(); } } *arr_p = result; return true; } static bool gjs_gtypearray_to_array(JSContext *context, JS::Value array_value, unsigned int length, void **arr_p) { GType *result; unsigned i; /* add one so we're always zero terminated */ result = (GType *) g_malloc0((length+1) * sizeof(GType)); JS::RootedObject elem_obj(context), array(context, array_value.toObjectOrNull()); JS::RootedValue elem(context); for (i = 0; i < length; ++i) { GType gtype; elem = JS::UndefinedValue(); if (!JS_GetElement(context, array, i, &elem)) { g_free(result); gjs_throw(context, "Missing array element %u", i); return false; } if (!elem.isObjectOrNull()) goto err; elem_obj = elem.toObjectOrNull(); gtype = gjs_gtype_get_actual_gtype(context, elem_obj); if (gtype == G_TYPE_INVALID) goto err; result[i] = gtype; } *arr_p = result; return true; err: g_free(result); gjs_throw(context, "Invalid element in GType array"); return false; } static bool gjs_array_to_floatarray(JSContext *context, JS::Value array_value, unsigned int length, void **arr_p, bool is_double) { unsigned int i; void *result; JS::RootedObject array(context, array_value.toObjectOrNull()); JS::RootedValue elem(context); /* add one so we're always zero terminated */ result = g_malloc0((length+1) * (is_double ? sizeof(double) : sizeof(float))); for (i = 0; i < length; ++i) { double val; bool success; elem = JS::UndefinedValue(); if (!JS_GetElement(context, array, i, &elem)) { g_free(result); gjs_throw(context, "Missing array element %u", i); return false; } /* do whatever sign extension is appropriate */ success = JS::ToNumber(context, elem, &val); if (!success) { g_free(result); gjs_throw(context, "Invalid element in array"); return false; } /* Note that this is truncating assignment. */ if (is_double) { double *darray = (double*)result; darray[i] = val; } else { float *farray = (float*)result; farray[i] = val; } } *arr_p = result; return true; } static bool gjs_array_to_ptrarray(JSContext *context, JS::Value array_value, unsigned int length, GITransfer transfer, GITypeInfo *param_info, void **arr_p) { unsigned int i; JS::RootedObject array_obj(context, array_value.toObjectOrNull()); JS::RootedValue elem(context); /* Always one extra element, to cater for null terminated arrays */ void **array = (void **) g_malloc((length + 1) * sizeof(gpointer)); array[length] = NULL; for (i = 0; i < length; i++) { GIArgument arg; arg.v_pointer = NULL; bool success; elem = JS::UndefinedValue(); if (!JS_GetElement(context, array_obj, i, &elem)) { g_free(array); gjs_throw(context, "Missing array element %u", i); return false; } success = gjs_value_to_g_argument (context, elem, param_info, NULL, /* arg name */ GJS_ARGUMENT_ARRAY_ELEMENT, transfer, false, /* absent better information, false for now */ &arg); if (!success) { g_free(array); gjs_throw(context, "Invalid element in array"); return false; } array[i] = arg.v_pointer; } *arr_p = array; return true; } static bool gjs_array_to_flat_gvalue_array(JSContext *context, JS::Value array_value, unsigned int length, void **arr_p) { GValue *values = g_new0(GValue, length); unsigned int i; bool result = true; JS::RootedObject array(context, array_value.toObjectOrNull()); JS::RootedValue elem(context); for (i = 0; i < length; i ++) { elem = JS::UndefinedValue(); if (!JS_GetElement(context, array, i, &elem)) { g_free(values); gjs_throw(context, "Missing array element %u", i); return false; } result = gjs_value_to_g_value(context, elem, &values[i]); if (!result) break; } if (result) *arr_p = values; return result; } static bool gjs_array_from_flat_gvalue_array(JSContext *context, gpointer array, unsigned length, JS::MutableHandleValue value) { GValue *values = (GValue *)array; unsigned int i; JS::AutoValueVector elems(context); if (!elems.resize(length)) g_error("Unable to resize vector"); bool result = true; for (i = 0; i < length; i ++) { GValue *gvalue = &values[i]; result = gjs_value_from_g_value(context, elems[i], gvalue); if (!result) break; } if (result) { JSObject *jsarray; jsarray = JS_NewArrayObject(context, elems); value.setObjectOrNull(jsarray); } return result; } static bool is_gvalue(GIBaseInfo *info, GIInfoType info_type) { if (info_type == GI_INFO_TYPE_VALUE) return true; if (info_type == GI_INFO_TYPE_STRUCT || info_type == GI_INFO_TYPE_OBJECT || info_type == GI_INFO_TYPE_INTERFACE || info_type == GI_INFO_TYPE_BOXED) { GType gtype = g_registered_type_info_get_g_type((GIRegisteredTypeInfo *) info); return g_type_is_a(gtype, G_TYPE_VALUE); } return false; } static bool is_gvalue_flat_array(GITypeInfo *param_info, GITypeTag element_type) { GIBaseInfo *interface_info; GIInfoType info_type; bool result; if (element_type != GI_TYPE_TAG_INTERFACE) return false; interface_info = g_type_info_get_interface(param_info); info_type = g_base_info_get_type(interface_info); /* Special case for GValue "flat arrays" */ result = (is_gvalue(interface_info, info_type) && !g_type_info_is_pointer(param_info)); g_base_info_unref(interface_info); return result; } static bool gjs_array_to_array(JSContext *context, JS::Value array_value, gsize length, GITransfer transfer, GITypeInfo *param_info, void **arr_p) { enum { UNSIGNED=false, SIGNED=true }; GITypeTag element_type; element_type = g_type_info_get_tag(param_info); /* Special case for GValue "flat arrays" */ if (is_gvalue_flat_array(param_info, element_type)) return gjs_array_to_flat_gvalue_array(context, array_value, length, arr_p); if (element_type == GI_TYPE_TAG_INTERFACE) { GIBaseInfo *interface_info = g_type_info_get_interface(param_info); GIInfoType info_type = g_base_info_get_type(interface_info); if (info_type == GI_INFO_TYPE_ENUM || info_type == GI_INFO_TYPE_FLAGS) element_type = g_enum_info_get_storage_type ((GIEnumInfo*) interface_info); g_base_info_unref(interface_info); } switch (element_type) { case GI_TYPE_TAG_UTF8: return gjs_array_to_strv (context, array_value, length, arr_p); case GI_TYPE_TAG_BOOLEAN: return gjs_array_to_gboolean_array(context, array_value, length, arr_p); case GI_TYPE_TAG_UNICHAR: return gjs_array_to_intarray(context, array_value, length, arr_p, sizeof(gunichar), UNSIGNED); case GI_TYPE_TAG_UINT8: return gjs_array_to_intarray (context, array_value, length, arr_p, 1, UNSIGNED); case GI_TYPE_TAG_INT8: return gjs_array_to_intarray (context, array_value, length, arr_p, 1, SIGNED); case GI_TYPE_TAG_UINT16: return gjs_array_to_intarray (context, array_value, length, arr_p, 2, UNSIGNED); case GI_TYPE_TAG_INT16: return gjs_array_to_intarray (context, array_value, length, arr_p, 2, SIGNED); case GI_TYPE_TAG_UINT32: return gjs_array_to_intarray (context, array_value, length, arr_p, 4, UNSIGNED); case GI_TYPE_TAG_INT32: return gjs_array_to_intarray (context, array_value, length, arr_p, 4, SIGNED); case GI_TYPE_TAG_INT64: return gjs_array_to_intarray(context, array_value, length, arr_p, 8, SIGNED); case GI_TYPE_TAG_UINT64: return gjs_array_to_intarray(context, array_value, length, arr_p, 8, UNSIGNED); case GI_TYPE_TAG_FLOAT: return gjs_array_to_floatarray (context, array_value, length, arr_p, false); case GI_TYPE_TAG_DOUBLE: return gjs_array_to_floatarray (context, array_value, length, arr_p, true); case GI_TYPE_TAG_GTYPE: return gjs_gtypearray_to_array (context, array_value, length, arr_p); /* Everything else is a pointer type */ case GI_TYPE_TAG_INTERFACE: case GI_TYPE_TAG_ARRAY: case GI_TYPE_TAG_GLIST: case GI_TYPE_TAG_GSLIST: case GI_TYPE_TAG_GHASH: case GI_TYPE_TAG_ERROR: case GI_TYPE_TAG_FILENAME: return gjs_array_to_ptrarray(context, array_value, length, transfer == GI_TRANSFER_CONTAINER ? GI_TRANSFER_NOTHING : transfer, param_info, arr_p); case GI_TYPE_TAG_VOID: default: gjs_throw(context, "Unhandled array element type %d", element_type); return false; } } static GArray* gjs_g_array_new_for_type(JSContext *context, unsigned int length, GITypeInfo *param_info) { GITypeTag element_type; guint element_size; element_type = g_type_info_get_tag(param_info); if (element_type == GI_TYPE_TAG_INTERFACE) { GIInterfaceInfo *interface_info = g_type_info_get_interface(param_info); GIInfoType interface_type = g_base_info_get_type(interface_info); if (interface_type == GI_INFO_TYPE_ENUM || interface_type == GI_INFO_TYPE_FLAGS) element_type = g_enum_info_get_storage_type((GIEnumInfo*) interface_info); g_base_info_unref((GIBaseInfo*) interface_info); } switch (element_type) { case GI_TYPE_TAG_BOOLEAN: element_size = sizeof(gboolean); break; case GI_TYPE_TAG_UNICHAR: element_size = sizeof(gunichar); break; case GI_TYPE_TAG_UINT8: case GI_TYPE_TAG_INT8: element_size = sizeof(guint8); break; case GI_TYPE_TAG_UINT16: case GI_TYPE_TAG_INT16: element_size = sizeof(guint16); break; case GI_TYPE_TAG_UINT32: case GI_TYPE_TAG_INT32: element_size = sizeof(guint32); break; case GI_TYPE_TAG_UINT64: case GI_TYPE_TAG_INT64: element_size = sizeof(guint64); break; case GI_TYPE_TAG_FLOAT: element_size = sizeof(gfloat); break; case GI_TYPE_TAG_DOUBLE: element_size = sizeof(gdouble); break; case GI_TYPE_TAG_GTYPE: element_size = sizeof(GType); break; case GI_TYPE_TAG_INTERFACE: case GI_TYPE_TAG_UTF8: case GI_TYPE_TAG_FILENAME: case GI_TYPE_TAG_ARRAY: case GI_TYPE_TAG_GLIST: case GI_TYPE_TAG_GSLIST: case GI_TYPE_TAG_GHASH: case GI_TYPE_TAG_ERROR: element_size = sizeof(gpointer); break; case GI_TYPE_TAG_VOID: default: gjs_throw(context, "Unhandled GArray element-type %d", element_type); return NULL; } return g_array_sized_new(true, false, element_size, length); } static gchar * get_argument_display_name(const char *arg_name, GjsArgumentType arg_type) { switch (arg_type) { case GJS_ARGUMENT_ARGUMENT: return g_strdup_printf("Argument '%s'", arg_name); case GJS_ARGUMENT_RETURN_VALUE: return g_strdup("Return value"); case GJS_ARGUMENT_FIELD: return g_strdup_printf("Field '%s'", arg_name); case GJS_ARGUMENT_LIST_ELEMENT: return g_strdup("List element"); case GJS_ARGUMENT_HASH_ELEMENT: return g_strdup("Hash element"); case GJS_ARGUMENT_ARRAY_ELEMENT: return g_strdup("Array element"); default: g_assert_not_reached (); } } static const char * type_tag_to_human_string(GITypeInfo *type_info) { GITypeTag tag; tag = g_type_info_get_tag(type_info); if (tag == GI_TYPE_TAG_INTERFACE) { GIBaseInfo *interface; const char *ret; interface = g_type_info_get_interface(type_info); ret = g_info_type_to_string(g_base_info_get_type(interface)); g_base_info_unref(interface); return ret; } else { return g_type_tag_to_string(tag); } } static void throw_invalid_argument(JSContext *context, JS::HandleValue value, GITypeInfo *arginfo, const char *arg_name, GjsArgumentType arg_type) { gchar *display_name = get_argument_display_name(arg_name, arg_type); gjs_throw(context, "Expected type %s for %s but got type '%s'", type_tag_to_human_string(arginfo), display_name, JS::InformalValueTypeName(value)); g_free(display_name); } static bool gjs_array_to_explicit_array_internal(JSContext *context, JS::HandleValue value, GITypeInfo *type_info, const char *arg_name, GjsArgumentType arg_type, GITransfer transfer, bool may_be_null, gpointer *contents, gsize *length_p) { bool ret = false; GITypeInfo *param_info; bool found_length; param_info = g_type_info_get_param_type(type_info, 0); if ((value.isNull() && !may_be_null) || (!value.isString() && !value.isObjectOrNull())) { throw_invalid_argument(context, value, param_info, arg_name, arg_type); g_base_info_unref((GIBaseInfo*) param_info); return false; } if (value.isNull()) { *contents = NULL; *length_p = 0; } else if (value.isString()) { /* Allow strings as int8/uint8/int16/uint16 arrays */ JS::RootedString str(context, value.toString()); if (!gjs_string_to_intarray(context, str, param_info, contents, length_p)) goto out; } else { JS::RootedObject array_obj(context, &value.toObject()); if (gjs_object_has_property(context, array_obj, GJS_STRING_LENGTH, &found_length) && found_length) { guint32 length; if (!gjs_object_require_converted_property(context, array_obj, NULL, GJS_STRING_LENGTH, &length)) { goto out; } else { if (!gjs_array_to_array(context, value, length, transfer, param_info, contents)) goto out; *length_p = length; } } else { throw_invalid_argument(context, value, param_info, arg_name, arg_type); goto out; } } ret = true; out: g_base_info_unref((GIBaseInfo*) param_info); return ret; } static bool is_gdk_atom(GIBaseInfo *info) { return (strcmp("Atom", g_base_info_get_name(info)) == 0 && strcmp("Gdk", g_base_info_get_namespace(info)) == 0); } static void intern_gdk_atom(const char *name, GArgument *ret) { GIRepository *repo = g_irepository_get_default(); GIFunctionInfo *atom_intern_fun = g_irepository_find_by_name(repo, "Gdk", "atom_intern"); GIArgument atom_intern_args[2]; /* Can only store char * in GIArgument. First argument to gdk_atom_intern * is const char *, string isn't modified. */ atom_intern_args[0].v_string = const_cast(name); atom_intern_args[1].v_boolean = false; g_function_info_invoke(atom_intern_fun, atom_intern_args, 2, nullptr, 0, ret, nullptr); g_base_info_unref(atom_intern_fun); } bool gjs_value_to_g_argument(JSContext *context, JS::HandleValue value, GITypeInfo *type_info, const char *arg_name, GjsArgumentType arg_type, GITransfer transfer, bool may_be_null, GArgument *arg) { GITypeTag type_tag; bool wrong; bool out_of_range; bool report_type_mismatch; bool nullable_type; type_tag = g_type_info_get_tag( (GITypeInfo*) type_info); gjs_debug_marshal(GJS_DEBUG_GFUNCTION, "Converting JS::Value to GArgument %s", g_type_tag_to_string(type_tag)); nullable_type = false; wrong = false; /* return false */ out_of_range = false; report_type_mismatch = false; /* wrong=true, and still need to gjs_throw a type problem */ switch (type_tag) { case GI_TYPE_TAG_VOID: nullable_type = true; arg->v_pointer = NULL; /* just so it isn't uninitialized */ break; case GI_TYPE_TAG_INT8: { gint32 i; if (!JS::ToInt32(context, value, &i)) wrong = true; if (i > G_MAXINT8 || i < G_MININT8) out_of_range = true; arg->v_int8 = (gint8)i; break; } case GI_TYPE_TAG_UINT8: { guint32 i; if (!JS::ToUint32(context, value, &i)) wrong = true; if (i > G_MAXUINT8) out_of_range = true; arg->v_uint8 = (guint8)i; break; } case GI_TYPE_TAG_INT16: { gint32 i; if (!JS::ToInt32(context, value, &i)) wrong = true; if (i > G_MAXINT16 || i < G_MININT16) out_of_range = true; arg->v_int16 = (gint16)i; break; } case GI_TYPE_TAG_UINT16: { guint32 i; if (!JS::ToUint32(context, value, &i)) wrong = true; if (i > G_MAXUINT16) out_of_range = true; arg->v_uint16 = (guint16)i; break; } case GI_TYPE_TAG_INT32: if (!JS::ToInt32(context, value, &arg->v_int)) wrong = true; break; case GI_TYPE_TAG_UINT32: { gdouble i; if (!JS::ToNumber(context, value, &i)) wrong = true; if (i > G_MAXUINT32 || i < 0) out_of_range = true; arg->v_uint32 = CLAMP(i, 0, G_MAXUINT32); break; } case GI_TYPE_TAG_INT64: { double v; if (!JS::ToNumber(context, value, &v)) wrong = true; if (v > G_MAXINT64 || v < G_MININT64) out_of_range = true; arg->v_int64 = v; } break; case GI_TYPE_TAG_UINT64: { double v; if (!JS::ToNumber(context, value, &v)) wrong = true; if (v < 0) out_of_range = true; /* XXX we fail with values close to G_MAXUINT64 */ arg->v_uint64 = MAX(v, 0); } break; case GI_TYPE_TAG_BOOLEAN: arg->v_boolean = JS::ToBoolean(value); break; case GI_TYPE_TAG_FLOAT: { double v; if (!JS::ToNumber(context, value, &v)) wrong = true; if (v > G_MAXFLOAT || v < - G_MAXFLOAT) out_of_range = true; arg->v_float = (gfloat)v; } break; case GI_TYPE_TAG_DOUBLE: if (!JS::ToNumber(context, value, &arg->v_double)) wrong = true; break; case GI_TYPE_TAG_UNICHAR: if (value.isString()) { if (!gjs_unichar_from_string(context, value, &arg->v_uint32)) wrong = true; } else { wrong = true; report_type_mismatch = true; } break; case GI_TYPE_TAG_GTYPE: if (value.isObjectOrNull()) { GType gtype; JS::RootedObject obj(context, value.toObjectOrNull()); gtype = gjs_gtype_get_actual_gtype(context, obj); if (gtype == G_TYPE_INVALID) wrong = true; arg->v_ssize = gtype; } else { wrong = true; report_type_mismatch = true; } break; case GI_TYPE_TAG_FILENAME: nullable_type = true; if (value.isNull()) { arg->v_pointer = NULL; } else if (value.isString()) { GjsAutoChar filename_str; if (gjs_string_to_filename(context, value, &filename_str)) arg->v_pointer = filename_str.release(); else wrong = true; } else { wrong = true; report_type_mismatch = true; } break; case GI_TYPE_TAG_UTF8: nullable_type = true; if (value.isNull()) { arg->v_pointer = NULL; } else if (value.isString()) { JS::RootedString str(context, value.toString()); GjsAutoJSChar utf8_str = JS_EncodeStringToUTF8(context, str); if (utf8_str) arg->v_pointer = utf8_str.copy(); else wrong = true; } else { wrong = true; report_type_mismatch = true; } break; case GI_TYPE_TAG_ERROR: nullable_type = true; if (value.isNull()) { arg->v_pointer = NULL; } else if (value.isObject()) { JS::RootedObject obj(context, &value.toObject()); if (gjs_typecheck_gerror(context, obj, true)) { arg->v_pointer = gjs_gerror_from_error(context, obj); if (transfer != GI_TRANSFER_NOTHING) arg->v_pointer = g_error_copy ((const GError *) arg->v_pointer); } else { wrong = true; } } else { wrong = true; report_type_mismatch = true; } break; case GI_TYPE_TAG_INTERFACE: { GIBaseInfo* interface_info; GIInfoType interface_type; GType gtype; bool expect_object; interface_info = g_type_info_get_interface(type_info); g_assert(interface_info != NULL); interface_type = g_base_info_get_type(interface_info); if (interface_type == GI_INFO_TYPE_ENUM || interface_type == GI_INFO_TYPE_FLAGS) { nullable_type = false; expect_object = false; } else { nullable_type = true; expect_object = true; } if (interface_type == GI_INFO_TYPE_STRUCT && g_struct_info_is_foreign((GIStructInfo*)interface_info)) { bool ret; ret = gjs_struct_foreign_convert_to_g_argument( context, value, interface_info, arg_name, arg_type, transfer, may_be_null, arg); g_base_info_unref(interface_info); return ret; } if (interface_type == GI_INFO_TYPE_STRUCT || interface_type == GI_INFO_TYPE_ENUM || interface_type == GI_INFO_TYPE_FLAGS || interface_type == GI_INFO_TYPE_OBJECT || interface_type == GI_INFO_TYPE_INTERFACE || interface_type == GI_INFO_TYPE_UNION || interface_type == GI_INFO_TYPE_BOXED) { /* These are subtypes of GIRegisteredTypeInfo for which the * cast is safe */ gtype = g_registered_type_info_get_g_type ((GIRegisteredTypeInfo*)interface_info); } else if (interface_type == GI_INFO_TYPE_VALUE) { /* Special case for GValues */ gtype = G_TYPE_VALUE; } else { gtype = G_TYPE_NONE; } if (gtype != G_TYPE_NONE) gjs_debug_marshal(GJS_DEBUG_GFUNCTION, "gtype of INTERFACE is %s", g_type_name(gtype)); if (gtype == G_TYPE_VALUE) { GValue gvalue = { 0, }; if (gjs_value_to_g_value(context, value, &gvalue)) { arg->v_pointer = g_boxed_copy (G_TYPE_VALUE, &gvalue); g_value_unset (&gvalue); } else { arg->v_pointer = NULL; wrong = true; } } else if (is_gdk_atom(interface_info)) { if (!value.isNull() && !value.isString()) { wrong = true; report_type_mismatch = true; } else if (value.isNull()) { intern_gdk_atom("NONE", arg); } else { JS::RootedString str(context, value.toString()); GjsAutoJSChar atom_name = JS_EncodeStringToUTF8(context, str); if (!atom_name) { wrong = true; g_base_info_unref(interface_info); break; } intern_gdk_atom(atom_name, arg); } } else if (expect_object != value.isObjectOrNull()) { wrong = true; report_type_mismatch = true; break; } else if (value.isNull()) { arg->v_pointer = NULL; } else if (value.isObject()) { JS::RootedObject obj(context, &value.toObject()); if (interface_type == GI_INFO_TYPE_STRUCT && g_struct_info_is_gtype_struct((GIStructInfo*)interface_info)) { GType actual_gtype; gpointer klass; actual_gtype = gjs_gtype_get_actual_gtype(context, obj); if (actual_gtype == G_TYPE_NONE) { wrong = true; report_type_mismatch = true; break; } /* We use peek here to simplify reference counting (we just ignore transfer annotation, as GType classes are never really freed) We know that the GType class is referenced at least once when the JS constructor is initialized. */ if (g_type_is_a(actual_gtype, G_TYPE_INTERFACE)) klass = g_type_default_interface_peek(actual_gtype); else klass = g_type_class_peek(actual_gtype); arg->v_pointer = klass; } else if ((interface_type == GI_INFO_TYPE_STRUCT || interface_type == GI_INFO_TYPE_BOXED) && /* Handle Struct/Union first since we don't necessarily need a GType for them */ /* We special case Closures later, so skip them here */ !g_type_is_a(gtype, G_TYPE_CLOSURE)) { if (g_type_is_a(gtype, G_TYPE_BYTES) && gjs_typecheck_bytearray(context, obj, false)) { arg->v_pointer = gjs_byte_array_get_bytes(context, obj); } else if (g_type_is_a(gtype, G_TYPE_ERROR)) { if (!gjs_typecheck_gerror(context, obj, true)) { arg->v_pointer = NULL; wrong = true; } else { arg->v_pointer = gjs_gerror_from_error(context, obj); } } else { if (!gjs_typecheck_boxed(context, obj, interface_info, gtype, true)) { arg->v_pointer = NULL; wrong = true; } else { arg->v_pointer = gjs_c_struct_from_boxed(context, obj); } } if (!wrong && transfer != GI_TRANSFER_NOTHING) { if (g_type_is_a(gtype, G_TYPE_BOXED)) arg->v_pointer = g_boxed_copy (gtype, arg->v_pointer); else if (g_type_is_a(gtype, G_TYPE_VARIANT)) g_variant_ref ((GVariant *) arg->v_pointer); else { gjs_throw(context, "Can't transfer ownership of a structure type not registered as boxed"); arg->v_pointer = NULL; wrong = true; } } } else if (interface_type == GI_INFO_TYPE_UNION) { if (gjs_typecheck_union(context, obj, interface_info, gtype, true)) { arg->v_pointer = gjs_c_union_from_union(context, obj); if (transfer != GI_TRANSFER_NOTHING) { if (g_type_is_a(gtype, G_TYPE_BOXED)) arg->v_pointer = g_boxed_copy (gtype, arg->v_pointer); else { gjs_throw(context, "Can't transfer ownership of a union type not registered as boxed"); arg->v_pointer = NULL; wrong = true; } } } else { arg->v_pointer = NULL; wrong = true; } } else if (gtype != G_TYPE_NONE) { if (g_type_is_a(gtype, G_TYPE_OBJECT)) { if (gjs_typecheck_object(context, obj, gtype, true)) { arg->v_pointer = gjs_g_object_from_object(context, obj); if (transfer != GI_TRANSFER_NOTHING) g_object_ref(G_OBJECT(arg->v_pointer)); } else { arg->v_pointer = NULL; wrong = true; } } else if (g_type_is_a(gtype, G_TYPE_PARAM)) { if (gjs_typecheck_param(context, obj, gtype, true)) { arg->v_pointer = gjs_g_param_from_param(context, obj); if (transfer != GI_TRANSFER_NOTHING) g_param_spec_ref(G_PARAM_SPEC(arg->v_pointer)); } else { arg->v_pointer = NULL; wrong = true; } } else if (g_type_is_a(gtype, G_TYPE_BOXED)) { if (g_type_is_a(gtype, G_TYPE_CLOSURE)) { arg->v_pointer = gjs_closure_new_marshaled(context, &value.toObject(), "boxed"); g_closure_ref((GClosure *) arg->v_pointer); g_closure_sink((GClosure *) arg->v_pointer); } else { /* Should have been caught above as STRUCT/BOXED/UNION */ gjs_throw(context, "Boxed type %s registered for unexpected interface_type %d", g_type_name(gtype), interface_type); } } else if (G_TYPE_IS_INSTANTIATABLE(gtype)) { if (gjs_typecheck_fundamental(context, obj, gtype, true)) { arg->v_pointer = gjs_g_fundamental_from_object(context, obj); if (transfer != GI_TRANSFER_NOTHING) gjs_fundamental_ref(context, arg->v_pointer); } else { arg->v_pointer = NULL; wrong = true; } } else if (G_TYPE_IS_INTERFACE(gtype)) { /* Could be a GObject interface that's missing a prerequisite, or could be a fundamental */ if (gjs_typecheck_object(context, obj, gtype, false)) { arg->v_pointer = gjs_g_object_from_object(context, obj); if (transfer != GI_TRANSFER_NOTHING) g_object_ref(arg->v_pointer); } else if (gjs_typecheck_fundamental(context, obj, gtype, false)) { arg->v_pointer = gjs_g_fundamental_from_object(context, obj); if (transfer != GI_TRANSFER_NOTHING) gjs_fundamental_ref(context, arg->v_pointer); } else { /* Call again with throw=true to set the exception */ gjs_typecheck_object(context, obj, gtype, true); arg->v_pointer = NULL; wrong = true; } } else { gjs_throw(context, "Unhandled GType %s unpacking GArgument from Object", g_type_name(gtype)); arg->v_pointer = NULL; wrong = true; } } else { gjs_throw(context, "Unexpected unregistered type unpacking GArgument from Object"); } if (arg->v_pointer == NULL) { gjs_debug(GJS_DEBUG_GFUNCTION, "conversion of JSObject %p type %s to type %s failed", &value.toObject(), JS::InformalValueTypeName(value), g_base_info_get_name ((GIBaseInfo *)interface_info)); /* gjs_throw should have been called already */ wrong = true; } } else if (value.isNumber()) { if (interface_type == GI_INFO_TYPE_ENUM) { int64_t value_int64; if (!JS::ToInt64(context, value, &value_int64)) wrong = true; else if (!_gjs_enum_value_is_valid(context, (GIEnumInfo *)interface_info, value_int64)) wrong = true; else arg->v_int = _gjs_enum_to_int ((GIEnumInfo *)interface_info, value_int64); } else if (interface_type == GI_INFO_TYPE_FLAGS) { int64_t value_int64; if (!JS::ToInt64(context, value, &value_int64)) wrong = true; else if (!_gjs_flags_value_is_valid(context, gtype, value_int64)) wrong = true; else arg->v_int = _gjs_enum_to_int ((GIEnumInfo *)interface_info, value_int64); } else if (gtype == G_TYPE_NONE) { gjs_throw(context, "Unexpected unregistered type unpacking GArgument from Number"); wrong = true; } else { gjs_throw(context, "Unhandled GType %s unpacking GArgument from Number", g_type_name(gtype)); wrong = true; } } else { gjs_debug(GJS_DEBUG_GFUNCTION, "JSObject type '%s' is neither null nor an object", JS::InformalValueTypeName(value)); wrong = true; report_type_mismatch = true; } g_base_info_unref( (GIBaseInfo*) interface_info); } break; case GI_TYPE_TAG_GLIST: case GI_TYPE_TAG_GSLIST: { /* nullable_type=false; while a list can be NULL in C, that * means empty array in JavaScript, it doesn't mean null in * JavaScript. */ if (value.isObject()) { bool found_length; JS::RootedObject array_obj(context, &value.toObject()); if (gjs_object_has_property(context, array_obj, GJS_STRING_LENGTH, &found_length) && found_length) { guint32 length; if (!gjs_object_require_converted_property(context, array_obj, NULL, GJS_STRING_LENGTH, &length)) { wrong = true; } else { GList *list; GSList *slist; GITypeInfo *param_info; param_info = g_type_info_get_param_type(type_info, 0); g_assert(param_info != NULL); list = NULL; slist = NULL; if (!gjs_array_to_g_list(context, value, length, param_info, transfer, type_tag, &list, &slist)) { wrong = true; } if (type_tag == GI_TYPE_TAG_GLIST) { arg->v_pointer = list; } else { arg->v_pointer = slist; } g_base_info_unref((GIBaseInfo*) param_info); } break; } } /* At this point we should have broken out already if the value was an * object and had a length property */ wrong = true; report_type_mismatch = true; break; } case GI_TYPE_TAG_GHASH: if (value.isNull()) { arg->v_pointer = NULL; if (!may_be_null) { wrong = true; report_type_mismatch = true; } } else if (!value.isObject()) { wrong = true; report_type_mismatch = true; } else { GITypeInfo *key_param_info, *val_param_info; GHashTable *ghash; key_param_info = g_type_info_get_param_type(type_info, 0); g_assert(key_param_info != NULL); val_param_info = g_type_info_get_param_type(type_info, 1); g_assert(val_param_info != NULL); if (!gjs_object_to_g_hash(context, value, key_param_info, val_param_info, transfer, &ghash)) { wrong = true; } else { arg->v_pointer = ghash; } g_base_info_unref((GIBaseInfo*) key_param_info); g_base_info_unref((GIBaseInfo*) val_param_info); } break; case GI_TYPE_TAG_ARRAY: { gpointer data; gsize length; GIArrayType array_type = g_type_info_get_array_type(type_info); /* First, let's handle the case where we're passed an instance * of our own byteArray class. */ if (value.isObjectOrNull()) { JS::RootedObject bytearray_obj(context, value.toObjectOrNull()); if (gjs_typecheck_bytearray(context, bytearray_obj, false) && array_type == GI_ARRAY_TYPE_BYTE_ARRAY) { arg->v_pointer = gjs_byte_array_get_byte_array(context, bytearray_obj); break; } else { /* Fall through, !handled */ } } if (!gjs_array_to_explicit_array_internal(context, value, type_info, arg_name, arg_type, transfer, may_be_null, &data, &length)) { wrong = true; break; } GITypeInfo *param_info = g_type_info_get_param_type(type_info, 0); if (array_type == GI_ARRAY_TYPE_C) { arg->v_pointer = data; } else if (array_type == GI_ARRAY_TYPE_ARRAY) { GArray *array = gjs_g_array_new_for_type(context, length, param_info); if (!array) wrong = true; else { g_array_append_vals(array, data, length); arg->v_pointer = array; } g_free(data); } else if (array_type == GI_ARRAY_TYPE_BYTE_ARRAY) { GByteArray *byte_array = g_byte_array_sized_new(length); g_byte_array_append(byte_array, (const guint8 *) data, length); arg->v_pointer = byte_array; g_free(data); } else if (array_type == GI_ARRAY_TYPE_PTR_ARRAY) { GPtrArray *array = g_ptr_array_sized_new(length); g_ptr_array_set_size(array, length); memcpy(array->pdata, data, sizeof(gpointer) * length); arg->v_pointer = array; g_free(data); } g_base_info_unref((GIBaseInfo*) param_info); break; } default: g_warning("Unhandled type %s for JavaScript to GArgument conversion", g_type_tag_to_string(type_tag)); wrong = true; report_type_mismatch = true; break; } if (G_UNLIKELY(wrong)) { if (report_type_mismatch) { throw_invalid_argument(context, value, type_info, arg_name, arg_type); } return false; } else if (G_UNLIKELY(out_of_range)) { gchar *display_name = get_argument_display_name (arg_name, arg_type); gjs_throw(context, "value is out of range for %s (type %s)", display_name, g_type_tag_to_string(type_tag)); g_free (display_name); return false; } else if (nullable_type && arg->v_pointer == NULL && !may_be_null) { gchar *display_name = get_argument_display_name (arg_name, arg_type); gjs_throw(context, "%s (type %s) may not be null", display_name, g_type_tag_to_string(type_tag)); g_free (display_name); return false; } else { return true; } } /* If a callback function with a return value throws, we still have * to return something to C. This function defines what that something * is. It basically boils down to memset(arg, 0, sizeof(*arg)), but * gives as a bit more future flexibility and also will work if * libffi passes us a buffer that only has room for the appropriate * branch of GArgument. (Currently it appears that the return buffer * has a fixed size large enough for the union of all types.) */ void gjs_g_argument_init_default(JSContext *context, GITypeInfo *type_info, GArgument *arg) { GITypeTag type_tag; type_tag = g_type_info_get_tag( (GITypeInfo*) type_info); switch (type_tag) { case GI_TYPE_TAG_VOID: arg->v_pointer = NULL; /* just so it isn't uninitialized */ break; case GI_TYPE_TAG_INT8: arg->v_int8 = 0; break; case GI_TYPE_TAG_UINT8: arg->v_uint8 = 0; break; case GI_TYPE_TAG_INT16: arg->v_int16 = 0; break; case GI_TYPE_TAG_UINT16: arg->v_uint16 = 0; break; case GI_TYPE_TAG_INT32: arg->v_int = 0; break; case GI_TYPE_TAG_UINT32: case GI_TYPE_TAG_UNICHAR: arg->v_uint32 = 0; break; case GI_TYPE_TAG_INT64: arg->v_int64 = 0; break; case GI_TYPE_TAG_UINT64: arg->v_uint64 = 0; break; case GI_TYPE_TAG_BOOLEAN: arg->v_boolean = false; break; case GI_TYPE_TAG_FLOAT: arg->v_float = 0.0f; break; case GI_TYPE_TAG_DOUBLE: arg->v_double = 0.0; break; case GI_TYPE_TAG_GTYPE: arg->v_ssize = 0; break; case GI_TYPE_TAG_FILENAME: case GI_TYPE_TAG_UTF8: case GI_TYPE_TAG_GLIST: case GI_TYPE_TAG_GSLIST: case GI_TYPE_TAG_ERROR: arg->v_pointer = NULL; break; case GI_TYPE_TAG_INTERFACE: { GIBaseInfo* interface_info; GIInfoType interface_type; interface_info = g_type_info_get_interface(type_info); g_assert(interface_info != NULL); interface_type = g_base_info_get_type(interface_info); if (interface_type == GI_INFO_TYPE_ENUM || interface_type == GI_INFO_TYPE_FLAGS) arg->v_int = 0; else if (interface_type == GI_INFO_TYPE_VALUE) /* Better to use a non-NULL value holding NULL? */ arg->v_pointer = NULL; else arg->v_pointer = NULL; g_base_info_unref( (GIBaseInfo*) interface_info); } break; case GI_TYPE_TAG_GHASH: /* Possibly better to return an empty hash table? */ arg->v_pointer = NULL; break; case GI_TYPE_TAG_ARRAY: arg->v_pointer = NULL; break; default: g_warning("Unhandled type %s for default GArgument initialization", g_type_tag_to_string(type_tag)); break; } } bool gjs_value_to_arg(JSContext *context, JS::HandleValue value, GIArgInfo *arg_info, GIArgument *arg) { GITypeInfo type_info; g_arg_info_load_type(arg_info, &type_info); return gjs_value_to_g_argument(context, value, &type_info, g_base_info_get_name( (GIBaseInfo*) arg_info), (g_arg_info_is_return_value(arg_info) ? GJS_ARGUMENT_RETURN_VALUE : GJS_ARGUMENT_ARGUMENT), g_arg_info_get_ownership_transfer(arg_info), g_arg_info_may_be_null(arg_info), arg); } bool gjs_value_to_explicit_array (JSContext *context, JS::HandleValue value, GIArgInfo *arg_info, GIArgument *arg, size_t *length_p) { GITypeInfo type_info; g_arg_info_load_type(arg_info, &type_info); return gjs_array_to_explicit_array_internal(context, value, &type_info, g_base_info_get_name((GIBaseInfo*) arg_info), GJS_ARGUMENT_ARGUMENT, g_arg_info_get_ownership_transfer(arg_info), g_arg_info_may_be_null(arg_info), &arg->v_pointer, length_p); } static bool gjs_array_from_g_list (JSContext *context, JS::MutableHandleValue value_p, GITypeTag list_tag, GITypeInfo *param_info, GList *list, GSList *slist) { unsigned int i; GArgument arg; JS::AutoValueVector elems(context); i = 0; if (list_tag == GI_TYPE_TAG_GLIST) { for ( ; list != NULL; list = list->next) { arg.v_pointer = list->data; if (!elems.growBy(1)) g_error("Unable to grow vector"); if (!gjs_value_from_g_argument(context, elems[i], param_info, &arg, true)) return false; ++i; } } else { for ( ; slist != NULL; slist = slist->next) { arg.v_pointer = slist->data; if (!elems.growBy(1)) g_error("Unable to grow vector"); if (!gjs_value_from_g_argument(context, elems[i], param_info, &arg, true)) return false; ++i; } } JS::RootedObject obj(context, JS_NewArrayObject(context, elems)); if (!obj) return false; value_p.setObject(*obj); return true; } static bool gjs_array_from_carray_internal (JSContext *context, JS::MutableHandleValue value_p, GITypeInfo *param_info, guint length, gpointer array) { GArgument arg; GITypeTag element_type; guint i; element_type = g_type_info_get_tag(param_info); if (is_gvalue_flat_array(param_info, element_type)) return gjs_array_from_flat_gvalue_array(context, array, length, value_p); /* Special case array(guint8) */ if (element_type == GI_TYPE_TAG_UINT8) { GByteArray gbytearray; gbytearray.data = (guint8 *) array; gbytearray.len = length; JS::RootedObject obj(context, gjs_byte_array_from_byte_array(context, &gbytearray)); if (!obj) return false; value_p.setObject(*obj); return true; } /* Special case array(unichar) to be a string in JS */ if (element_type == GI_TYPE_TAG_UNICHAR) return gjs_string_from_ucs4(context, (gunichar *) array, length, value_p); JS::AutoValueVector elems(context); if (!elems.resize(length)) g_error("Unable to resize vector"); #define ITERATE(type) \ for (i = 0; i < length; i++) { \ arg.v_##type = *(((g##type*)array) + i); \ if (!gjs_value_from_g_argument(context, elems[i], param_info, \ &arg, true)) \ return false; \ } switch (element_type) { /* Special cases handled above */ case GI_TYPE_TAG_UINT8: case GI_TYPE_TAG_UNICHAR: g_assert_not_reached(); case GI_TYPE_TAG_BOOLEAN: ITERATE(boolean); break; case GI_TYPE_TAG_INT8: ITERATE(int8); break; case GI_TYPE_TAG_UINT16: ITERATE(uint16); break; case GI_TYPE_TAG_INT16: ITERATE(int16); break; case GI_TYPE_TAG_UINT32: ITERATE(uint32); break; case GI_TYPE_TAG_INT32: ITERATE(int32); break; case GI_TYPE_TAG_UINT64: ITERATE(uint64); break; case GI_TYPE_TAG_INT64: ITERATE(int64); break; case GI_TYPE_TAG_FLOAT: ITERATE(float); break; case GI_TYPE_TAG_DOUBLE: ITERATE(double); break; case GI_TYPE_TAG_INTERFACE: { GIBaseInfo *interface_info = g_type_info_get_interface (param_info); GIInfoType info_type = g_base_info_get_type (interface_info); if ((info_type == GI_INFO_TYPE_STRUCT || info_type == GI_INFO_TYPE_UNION) && !g_type_info_is_pointer (param_info)) { size_t struct_size; if (info_type == GI_INFO_TYPE_UNION) struct_size = g_union_info_get_size (interface_info); else struct_size = g_struct_info_get_size (interface_info); for (i = 0; i < length; i++) { arg.v_pointer = static_cast(array) + (struct_size * i); if (!gjs_value_from_g_argument(context, elems[i], param_info, &arg, true)) return false; } g_base_info_unref(interface_info); break; } g_base_info_unref(interface_info); } /* fallthrough */ case GI_TYPE_TAG_GTYPE: case GI_TYPE_TAG_UTF8: case GI_TYPE_TAG_FILENAME: case GI_TYPE_TAG_ARRAY: case GI_TYPE_TAG_GLIST: case GI_TYPE_TAG_GSLIST: case GI_TYPE_TAG_GHASH: case GI_TYPE_TAG_ERROR: ITERATE(pointer); break; case GI_TYPE_TAG_VOID: default: gjs_throw(context, "Unknown Array element-type %d", element_type); return false; } #undef ITERATE JS::RootedObject obj(context, JS_NewArrayObject(context, elems)); if (!obj) return false; value_p.setObject(*obj); return true; } static bool gjs_array_from_fixed_size_array (JSContext *context, JS::MutableHandleValue value_p, GITypeInfo *type_info, gpointer array) { gint length; GITypeInfo *param_info; bool res; length = g_type_info_get_array_fixed_size(type_info); g_assert (length != -1); param_info = g_type_info_get_param_type(type_info, 0); res = gjs_array_from_carray_internal(context, value_p, param_info, length, array); g_base_info_unref((GIBaseInfo*)param_info); return res; } bool gjs_value_from_explicit_array(JSContext *context, JS::MutableHandleValue value_p, GITypeInfo *type_info, GIArgument *arg, int length) { GITypeInfo *param_info; bool res; param_info = g_type_info_get_param_type(type_info, 0); res = gjs_array_from_carray_internal(context, value_p, param_info, length, arg->v_pointer); g_base_info_unref((GIBaseInfo*)param_info); return res; } static bool gjs_array_from_boxed_array (JSContext *context, JS::MutableHandleValue value_p, GIArrayType array_type, GITypeInfo *param_info, GArgument *arg) { GArray *array; GPtrArray *ptr_array; gpointer data = NULL; gsize length = 0; if (arg->v_pointer == NULL) { value_p.setNull(); return true; } switch(array_type) { case GI_ARRAY_TYPE_BYTE_ARRAY: /* GByteArray is just a typedef for GArray internally */ case GI_ARRAY_TYPE_ARRAY: array = (GArray*)(arg->v_pointer); data = array->data; length = array->len; break; case GI_ARRAY_TYPE_PTR_ARRAY: ptr_array = (GPtrArray*)(arg->v_pointer); data = ptr_array->pdata; length = ptr_array->len; break; case GI_ARRAY_TYPE_C: /* already checked in gjs_value_from_g_argument() */ default: g_assert_not_reached(); } return gjs_array_from_carray_internal(context, value_p, param_info, length, data); } static bool gjs_array_from_zero_terminated_c_array (JSContext *context, JS::MutableHandleValue value_p, GITypeInfo *param_info, gpointer c_array) { GArgument arg; GITypeTag element_type; guint i; element_type = g_type_info_get_tag(param_info); /* Special case array(guint8) */ if (element_type == GI_TYPE_TAG_UINT8) { GByteArray gbytearray; gbytearray.data = (guint8 *) c_array; gbytearray.len = strlen((const char *) c_array); JS::RootedObject obj(context, gjs_byte_array_from_byte_array(context, &gbytearray)); if (!obj) return false; value_p.setObject(*obj); return true; } /* Special case array(gunichar) to JS string */ if (element_type == GI_TYPE_TAG_UNICHAR) return gjs_string_from_ucs4(context, (gunichar *) c_array, -1, value_p); JS::AutoValueVector elems(context); #define ITERATE(type) \ do { \ g##type *array = (g##type *) c_array; \ for (i = 0; array[i]; i++) { \ arg.v_##type = array[i]; \ if (!elems.growBy(1)) \ g_error("Unable to grow vector"); \ if (!gjs_value_from_g_argument(context, elems[i], \ param_info, &arg, true)) \ return false; \ } \ } while(0); switch (element_type) { /* Special cases handled above. */ case GI_TYPE_TAG_UINT8: case GI_TYPE_TAG_UNICHAR: g_assert_not_reached(); case GI_TYPE_TAG_INT8: ITERATE(int8); break; case GI_TYPE_TAG_UINT16: ITERATE(uint16); break; case GI_TYPE_TAG_INT16: ITERATE(int16); break; case GI_TYPE_TAG_UINT32: ITERATE(uint32); break; case GI_TYPE_TAG_INT32: ITERATE(int32); break; case GI_TYPE_TAG_UINT64: ITERATE(uint64); break; case GI_TYPE_TAG_INT64: ITERATE(int64); break; case GI_TYPE_TAG_FLOAT: ITERATE(float); break; case GI_TYPE_TAG_DOUBLE: ITERATE(double); break; case GI_TYPE_TAG_GTYPE: case GI_TYPE_TAG_UTF8: case GI_TYPE_TAG_FILENAME: case GI_TYPE_TAG_ARRAY: case GI_TYPE_TAG_INTERFACE: case GI_TYPE_TAG_GLIST: case GI_TYPE_TAG_GSLIST: case GI_TYPE_TAG_GHASH: case GI_TYPE_TAG_ERROR: ITERATE(pointer); break; /* Boolean zero-terminated array makes no sense, because FALSE is also * zero */ case GI_TYPE_TAG_BOOLEAN: gjs_throw(context, "Boolean zero-terminated array not supported"); return false; case GI_TYPE_TAG_VOID: default: gjs_throw(context, "Unknown element-type %d", element_type); return false; } #undef ITERATE JS::RootedObject obj(context, JS_NewArrayObject(context, elems)); if (!obj) return false; value_p.setObject(*obj); return true; } static bool gjs_object_from_g_hash (JSContext *context, JS::MutableHandleValue value_p, GITypeInfo *key_param_info, GITypeInfo *val_param_info, GHashTable *hash) { GHashTableIter iter; GArgument keyarg, valarg; // a NULL hash table becomes a null JS value if (hash==NULL) { value_p.setNull(); return true; } JS::RootedObject obj(context, JS_NewPlainObject(context)); if (!obj) return false; value_p.setObject(*obj); JS::RootedValue keyjs(context), valjs(context); JS::RootedString keystr(context); g_hash_table_iter_init(&iter, hash); while (g_hash_table_iter_next (&iter, &keyarg.v_pointer, &valarg.v_pointer)) { if (!gjs_value_from_g_argument(context, &keyjs, key_param_info, &keyarg, true)) return false; keystr = JS::ToString(context, keyjs); if (!keystr) return false; GjsAutoJSChar keyutf8 = JS_EncodeStringToUTF8(context, keystr); if (!keyutf8) return false; if (!gjs_value_from_g_argument(context, &valjs, val_param_info, &valarg, true)) return false; if (!JS_DefineProperty(context, obj, keyutf8, valjs, JSPROP_ENUMERATE)) return false; } return true; } static const int64_t MAX_SAFE_INT64 = int64_t(1) << std::numeric_limits::digits; bool gjs_value_from_g_argument (JSContext *context, JS::MutableHandleValue value_p, GITypeInfo *type_info, GArgument *arg, bool copy_structs) { GITypeTag type_tag; type_tag = g_type_info_get_tag( (GITypeInfo*) type_info); gjs_debug_marshal(GJS_DEBUG_GFUNCTION, "Converting GArgument %s to JS::Value", g_type_tag_to_string(type_tag)); value_p.setNull(); switch (type_tag) { case GI_TYPE_TAG_VOID: value_p.setUndefined(); /* or .setNull() ? */ break; case GI_TYPE_TAG_BOOLEAN: value_p.setBoolean(!!arg->v_int); break; case GI_TYPE_TAG_INT32: value_p.setInt32(arg->v_int); break; case GI_TYPE_TAG_UINT32: value_p.setNumber(arg->v_uint); break; case GI_TYPE_TAG_INT64: if (arg->v_int64 == G_MININT64 || std::abs(arg->v_int64) > MAX_SAFE_INT64) g_warning("Value %" G_GINT64_FORMAT " cannot be safely stored in " "a JS Number and may be rounded", arg->v_int64); value_p.setNumber(static_cast(arg->v_int64)); break; case GI_TYPE_TAG_UINT64: if (arg->v_uint64 > MAX_SAFE_INT64) g_warning("Value %" G_GUINT64_FORMAT " cannot be safely stored in " "a JS Number and may be rounded", arg->v_uint64); value_p.setNumber(static_cast(arg->v_uint64)); break; case GI_TYPE_TAG_UINT16: value_p.setInt32(arg->v_uint16); break; case GI_TYPE_TAG_INT16: value_p.setInt32(arg->v_int16); break; case GI_TYPE_TAG_UINT8: value_p.setInt32(arg->v_uint8); break; case GI_TYPE_TAG_INT8: value_p.setInt32(arg->v_int8); break; case GI_TYPE_TAG_FLOAT: value_p.setNumber(arg->v_float); break; case GI_TYPE_TAG_DOUBLE: value_p.setNumber(arg->v_double); break; case GI_TYPE_TAG_GTYPE: { GType gtype = arg->v_ssize; if (gtype == 0) return true; /* value_p is set to JS null */ JS::RootedObject obj(context, gjs_gtype_create_gtype_wrapper(context, gtype)); if (!obj) return false; value_p.setObject(*obj); return true; } break; case GI_TYPE_TAG_UNICHAR: { char utf8[7]; gint bytes; /* Preserve the bidirectional mapping between 0 and "" */ if (arg->v_uint32 == 0) { value_p.set(JS_GetEmptyStringValue(context)); return true; } else if (!g_unichar_validate (arg->v_uint32)) { gjs_throw(context, "Invalid unicode codepoint %" G_GUINT32_FORMAT, arg->v_uint32); return false; } else { bytes = g_unichar_to_utf8 (arg->v_uint32, utf8); return gjs_string_from_utf8_n(context, utf8, bytes, value_p); } } case GI_TYPE_TAG_FILENAME: if (arg->v_pointer) return gjs_string_from_filename(context, (const char *) arg->v_pointer, -1, value_p); else { /* For NULL we'll return JS::NullValue(), which is already set * in *value_p */ return true; } case GI_TYPE_TAG_UTF8: if (arg->v_pointer) { return gjs_string_from_utf8(context, reinterpret_cast(arg->v_pointer), value_p); } else { /* For NULL we'll return JS::NullValue(), which is already set * in *value_p */ return true; } case GI_TYPE_TAG_ERROR: { if (arg->v_pointer) { JSObject *obj = gjs_error_from_gerror(context, (GError *) arg->v_pointer, false); if (obj) { value_p.setObject(*obj); return true; } return false; } return true; } case GI_TYPE_TAG_INTERFACE: { JS::RootedValue value(context); GIBaseInfo* interface_info; GIInfoType interface_type; GType gtype; interface_info = g_type_info_get_interface(type_info); g_assert(interface_info != NULL); interface_type = g_base_info_get_type(interface_info); if (interface_type == GI_INFO_TYPE_UNRESOLVED) { gjs_throw(context, "Unable to resolve arg type '%s'", g_base_info_get_name(interface_info)); goto out; } /* Enum/Flags are aren't pointer types, unlike the other interface subtypes */ if (interface_type == GI_INFO_TYPE_ENUM) { gint64 value_int64 = _gjs_enum_from_int ((GIEnumInfo *)interface_info, arg->v_int); if (_gjs_enum_value_is_valid(context, (GIEnumInfo *)interface_info, value_int64)) { value = JS::NumberValue(value_int64); } goto out; } else if (interface_type == GI_INFO_TYPE_FLAGS) { gint64 value_int64 = _gjs_enum_from_int ((GIEnumInfo *)interface_info, arg->v_int); gtype = g_registered_type_info_get_g_type((GIRegisteredTypeInfo*)interface_info); if (_gjs_flags_value_is_valid(context, gtype, value_int64)) { value = JS::NumberValue(value_int64); } goto out; } else if (interface_type == GI_INFO_TYPE_STRUCT && g_struct_info_is_foreign((GIStructInfo*)interface_info)) { bool ret; ret = gjs_struct_foreign_convert_from_g_argument(context, value_p, interface_info, arg); g_base_info_unref(interface_info); return ret; } /* Everything else is a pointer type, NULL is the easy case */ if (arg->v_pointer == NULL) { value = JS::NullValue(); goto out; } if (interface_type == GI_INFO_TYPE_STRUCT && g_struct_info_is_gtype_struct((GIStructInfo*)interface_info)) { bool ret; /* XXX: here we make the implicit assumption that GTypeClass is the same as GTypeInterface. This is true for the GType field, which is what we use, but not for the rest of the structure! */ gtype = G_TYPE_FROM_CLASS(arg->v_pointer); if (g_type_is_a(gtype, G_TYPE_INTERFACE)) ret = gjs_lookup_interface_constructor(context, gtype, value_p); else ret = gjs_lookup_object_constructor(context, gtype, value_p); g_base_info_unref(interface_info); return ret; } gtype = g_registered_type_info_get_g_type((GIRegisteredTypeInfo*)interface_info); if (G_TYPE_IS_INSTANTIATABLE(gtype) || G_TYPE_IS_INTERFACE(gtype)) gtype = G_TYPE_FROM_INSTANCE(arg->v_pointer); gjs_debug_marshal(GJS_DEBUG_GFUNCTION, "gtype of INTERFACE is %s", g_type_name(gtype)); /* Test GValue and GError before Struct, or it will be handled as the latter */ if (g_type_is_a(gtype, G_TYPE_VALUE)) { if (!gjs_value_from_g_value(context, &value, (const GValue *) arg->v_pointer)) value = JS::UndefinedValue(); /* Make sure error is flagged */ goto out; } if (g_type_is_a(gtype, G_TYPE_ERROR)) { JSObject *obj; obj = gjs_error_from_gerror(context, (GError *) arg->v_pointer, false); if (obj) value = JS::ObjectValue(*obj); else value = JS::UndefinedValue(); goto out; } if (interface_type == GI_INFO_TYPE_STRUCT || interface_type == GI_INFO_TYPE_BOXED) { if (is_gdk_atom(interface_info)) { GIFunctionInfo *atom_name_fun = g_struct_info_find_method(interface_info, "name"); GIArgument atom_name_ret; g_function_info_invoke(atom_name_fun, arg, 1, nullptr, 0, &atom_name_ret, nullptr); g_base_info_unref(atom_name_fun); g_base_info_unref(interface_info); if (strcmp("NONE", atom_name_ret.v_string) == 0) { g_free(atom_name_ret.v_string); value = JS::NullValue(); return true; } bool atom_name_ok = gjs_string_from_utf8(context, atom_name_ret.v_string, value_p); g_free(atom_name_ret.v_string); return atom_name_ok; } JSObject *obj; GjsBoxedCreationFlags flags; if (copy_structs) flags = GJS_BOXED_CREATION_NONE; else if (g_type_is_a(gtype, G_TYPE_VARIANT)) flags = GJS_BOXED_CREATION_NONE; else flags = GJS_BOXED_CREATION_NO_COPY; obj = gjs_boxed_from_c_struct(context, (GIStructInfo *)interface_info, arg->v_pointer, flags); if (obj) value = JS::ObjectValue(*obj); goto out; } else if (interface_type == GI_INFO_TYPE_UNION) { JSObject *obj; obj = gjs_union_from_c_union(context, (GIUnionInfo *)interface_info, arg->v_pointer); if (obj) value = JS::ObjectValue(*obj); goto out; } if (g_type_is_a(gtype, G_TYPE_OBJECT)) { JSObject *obj; obj = gjs_object_from_g_object(context, G_OBJECT(arg->v_pointer)); if (obj) value = JS::ObjectValue(*obj); } else if (g_type_is_a(gtype, G_TYPE_BOXED) || g_type_is_a(gtype, G_TYPE_ENUM) || g_type_is_a(gtype, G_TYPE_FLAGS)) { /* Should have been handled above */ gjs_throw(context, "Type %s registered for unexpected interface_type %d", g_type_name(gtype), interface_type); return false; } else if (g_type_is_a(gtype, G_TYPE_PARAM)) { JSObject *obj; obj = gjs_param_from_g_param(context, G_PARAM_SPEC(arg->v_pointer)); if (obj) value = JS::ObjectValue(*obj); } else if (gtype == G_TYPE_NONE) { gjs_throw(context, "Unexpected unregistered type packing GArgument into JS::Value"); } else if (G_TYPE_IS_INSTANTIATABLE(gtype) || G_TYPE_IS_INTERFACE(gtype)) { JSObject *obj; obj = gjs_object_from_g_fundamental(context, (GIObjectInfo *)interface_info, arg->v_pointer); if (obj) value = JS::ObjectValue(*obj); } else { gjs_throw(context, "Unhandled GType %s packing GArgument into JS::Value", g_type_name(gtype)); } out: g_base_info_unref( (GIBaseInfo*) interface_info); if (value.isUndefined()) return false; value_p.set(value); } break; case GI_TYPE_TAG_ARRAY: if (arg->v_pointer == NULL) { /* OK, but no conversion to do */ } else if (g_type_info_get_array_type(type_info) == GI_ARRAY_TYPE_C) { if (g_type_info_is_zero_terminated(type_info)) { GITypeInfo *param_info; bool result; param_info = g_type_info_get_param_type(type_info, 0); g_assert(param_info != NULL); result = gjs_array_from_zero_terminated_c_array(context, value_p, param_info, arg->v_pointer); g_base_info_unref((GIBaseInfo*) param_info); return result; } else { /* arrays with length are handled outside of this function */ g_assert(((void) "Use gjs_value_from_explicit_array() for " "arrays with length param", g_type_info_get_array_length(type_info) == -1)); return gjs_array_from_fixed_size_array(context, value_p, type_info, arg->v_pointer); } } else if (g_type_info_get_array_type(type_info) == GI_ARRAY_TYPE_BYTE_ARRAY) { JSObject *array = gjs_byte_array_from_byte_array(context, (GByteArray*)arg->v_pointer); if (!array) { gjs_throw(context, "Couldn't convert GByteArray to a ByteArray"); return false; } value_p.setObject(*array); } else { /* this assumes the array type is one of GArray, GPtrArray or * GByteArray */ GITypeInfo *param_info; bool result; param_info = g_type_info_get_param_type(type_info, 0); g_assert(param_info != NULL); result = gjs_array_from_boxed_array(context, value_p, g_type_info_get_array_type(type_info), param_info, arg); g_base_info_unref((GIBaseInfo*) param_info); return result; } break; case GI_TYPE_TAG_GLIST: case GI_TYPE_TAG_GSLIST: { GITypeInfo *param_info; bool result; param_info = g_type_info_get_param_type(type_info, 0); g_assert(param_info != NULL); result = gjs_array_from_g_list(context, value_p, type_tag, param_info, type_tag == GI_TYPE_TAG_GLIST ? (GList *) arg->v_pointer : NULL, type_tag == GI_TYPE_TAG_GSLIST ? (GSList *) arg->v_pointer : NULL); g_base_info_unref((GIBaseInfo*) param_info); return result; } break; case GI_TYPE_TAG_GHASH: { GITypeInfo *key_param_info, *val_param_info; bool result; key_param_info = g_type_info_get_param_type(type_info, 0); g_assert(key_param_info != NULL); val_param_info = g_type_info_get_param_type(type_info, 1); g_assert(val_param_info != NULL); result = gjs_object_from_g_hash(context, value_p, key_param_info, val_param_info, (GHashTable *) arg->v_pointer); g_base_info_unref((GIBaseInfo*) key_param_info); g_base_info_unref((GIBaseInfo*) val_param_info); return result; } break; default: g_warning("Unhandled type %s converting GArgument to JavaScript", g_type_tag_to_string(type_tag)); return false; } return true; } static bool gjs_g_arg_release_internal(JSContext *context, GITransfer transfer, GITypeInfo *type_info, GITypeTag type_tag, GArgument *arg); typedef struct { JSContext *context; GITypeInfo *key_param_info, *val_param_info; GITransfer transfer; bool failed; } GHR_closure; static gboolean gjs_ghr_helper(gpointer key, gpointer val, gpointer user_data) { GHR_closure *c = (GHR_closure *) user_data; GArgument key_arg, val_arg; key_arg.v_pointer = key; val_arg.v_pointer = val; if (!gjs_g_arg_release_internal(c->context, c->transfer, c->key_param_info, g_type_info_get_tag(c->key_param_info), &key_arg)) c->failed = true; GITypeTag val_type = g_type_info_get_tag(c->val_param_info); if (val_type == GI_TYPE_TAG_INT64 || val_type == GI_TYPE_TAG_UINT64 || val_type == GI_TYPE_TAG_FLOAT || val_type == GI_TYPE_TAG_DOUBLE) { g_free(val_arg.v_pointer); } else if (!gjs_g_arg_release_internal(c->context, c->transfer, c->val_param_info, val_type, &val_arg)) { c->failed = true; } return true; } /* We need to handle GI_TRANSFER_NOTHING differently for out parameters * (free nothing) and for in parameters (free any temporaries we've * allocated */ #define TRANSFER_IN_NOTHING (GI_TRANSFER_EVERYTHING + 1) static bool gjs_g_arg_release_internal(JSContext *context, GITransfer transfer, GITypeInfo *type_info, GITypeTag type_tag, GArgument *arg) { bool failed; g_assert(transfer != GI_TRANSFER_NOTHING); failed = false; switch (type_tag) { case GI_TYPE_TAG_VOID: case GI_TYPE_TAG_BOOLEAN: case GI_TYPE_TAG_INT8: case GI_TYPE_TAG_UINT8: case GI_TYPE_TAG_INT16: case GI_TYPE_TAG_UINT16: case GI_TYPE_TAG_INT32: case GI_TYPE_TAG_UINT32: case GI_TYPE_TAG_INT64: case GI_TYPE_TAG_UINT64: case GI_TYPE_TAG_FLOAT: case GI_TYPE_TAG_DOUBLE: case GI_TYPE_TAG_UNICHAR: case GI_TYPE_TAG_GTYPE: break; case GI_TYPE_TAG_FILENAME: case GI_TYPE_TAG_UTF8: g_free(arg->v_pointer); break; case GI_TYPE_TAG_ERROR: if (transfer != TRANSFER_IN_NOTHING) g_error_free ((GError *) arg->v_pointer); break; case GI_TYPE_TAG_INTERFACE: { GIBaseInfo* interface_info; GIInfoType interface_type; GType gtype; interface_info = g_type_info_get_interface(type_info); g_assert(interface_info != NULL); interface_type = g_base_info_get_type(interface_info); if (interface_type == GI_INFO_TYPE_STRUCT && g_struct_info_is_foreign((GIStructInfo*)interface_info)) return gjs_struct_foreign_release_g_argument(context, transfer, interface_info, arg); if (interface_type == GI_INFO_TYPE_ENUM || interface_type == GI_INFO_TYPE_FLAGS) goto out; /* Anything else is a pointer */ if (arg->v_pointer == NULL) goto out; gtype = g_registered_type_info_get_g_type((GIRegisteredTypeInfo*)interface_info); if (G_TYPE_IS_INSTANTIATABLE(gtype) || G_TYPE_IS_INTERFACE(gtype)) gtype = G_TYPE_FROM_INSTANCE(arg->v_pointer); gjs_debug_marshal(GJS_DEBUG_GFUNCTION, "gtype of INTERFACE is %s", g_type_name(gtype)); /* In gjs_value_from_g_argument we handle Struct/Union types without a * registered GType, but here we are specifically handling a GArgument that * *owns* its value, and that is non-sensical for such types, so we * don't have to worry about it. */ if (g_type_is_a(gtype, G_TYPE_OBJECT)) { if (transfer != TRANSFER_IN_NOTHING) g_object_unref(G_OBJECT(arg->v_pointer)); } else if (g_type_is_a(gtype, G_TYPE_PARAM)) { if (transfer != TRANSFER_IN_NOTHING) g_param_spec_unref(G_PARAM_SPEC(arg->v_pointer)); } else if (g_type_is_a(gtype, G_TYPE_CLOSURE)) { g_closure_unref((GClosure *) arg->v_pointer); } else if (g_type_is_a(gtype, G_TYPE_VALUE)) { /* G_TYPE_VALUE is-a G_TYPE_BOXED, but we special case it */ if (g_type_info_is_pointer (type_info)) g_boxed_free(gtype, arg->v_pointer); else g_value_unset ((GValue *) arg->v_pointer); } else if (g_type_is_a(gtype, G_TYPE_BOXED)) { if (transfer != TRANSFER_IN_NOTHING) g_boxed_free(gtype, arg->v_pointer); } else if (g_type_is_a(gtype, G_TYPE_VARIANT)) { if (transfer != TRANSFER_IN_NOTHING) g_variant_unref ((GVariant *) arg->v_pointer); } else if (gtype == G_TYPE_NONE) { if (transfer != TRANSFER_IN_NOTHING) { gjs_throw(context, "Don't know how to release GArgument: not an object or boxed type"); failed = true; } } else if (G_TYPE_IS_INSTANTIATABLE(gtype)) { if (transfer != TRANSFER_IN_NOTHING) gjs_fundamental_unref(context, arg->v_pointer); } else { gjs_throw(context, "Unhandled GType %s releasing GArgument", g_type_name(gtype)); failed = true; } out: g_base_info_unref( (GIBaseInfo*) interface_info); } break; case GI_TYPE_TAG_GLIST: if (transfer != GI_TRANSFER_CONTAINER) { GITypeInfo *param_info; GList *list; param_info = g_type_info_get_param_type(type_info, 0); g_assert(param_info != NULL); for (list = (GList *) arg->v_pointer; list != NULL; list = list->next) { GArgument elem; elem.v_pointer = list->data; if (!gjs_g_arg_release_internal(context, transfer, param_info, g_type_info_get_tag(param_info), &elem)) { failed = true; } } g_base_info_unref((GIBaseInfo*) param_info); } g_list_free((GList *) arg->v_pointer); break; case GI_TYPE_TAG_ARRAY: { GIArrayType array_type = g_type_info_get_array_type(type_info); if (arg->v_pointer == NULL) { /* OK */ } else if (array_type == GI_ARRAY_TYPE_C) { GITypeInfo *param_info; GITypeTag element_type; param_info = g_type_info_get_param_type(type_info, 0); element_type = g_type_info_get_tag(param_info); if (is_gvalue_flat_array(param_info, element_type)) { if (transfer != GI_TRANSFER_CONTAINER) { gint len = g_type_info_get_array_fixed_size(type_info); gint i; if (len < 0) { gjs_throw(context, "Releasing a flat GValue array that was not fixed-size or was nested" "inside another container. This is not supported (and will leak)"); g_base_info_unref(param_info); return false; } for (i = 0; i < len; i++) { GValue *v = ((GValue*)arg->v_pointer) + i; g_value_unset(v); } } g_free(arg->v_pointer); g_base_info_unref(param_info); return true; } switch (element_type) { case GI_TYPE_TAG_UTF8: case GI_TYPE_TAG_FILENAME: if (transfer == GI_TRANSFER_CONTAINER) g_free(arg->v_pointer); else g_strfreev ((gchar **) arg->v_pointer); break; case GI_TYPE_TAG_BOOLEAN: case GI_TYPE_TAG_UINT8: case GI_TYPE_TAG_UINT16: case GI_TYPE_TAG_UINT32: case GI_TYPE_TAG_UINT64: case GI_TYPE_TAG_INT8: case GI_TYPE_TAG_INT16: case GI_TYPE_TAG_INT32: case GI_TYPE_TAG_INT64: case GI_TYPE_TAG_FLOAT: case GI_TYPE_TAG_DOUBLE: case GI_TYPE_TAG_UNICHAR: case GI_TYPE_TAG_GTYPE: g_free (arg->v_pointer); break; case GI_TYPE_TAG_INTERFACE: case GI_TYPE_TAG_GLIST: case GI_TYPE_TAG_GSLIST: case GI_TYPE_TAG_ARRAY: case GI_TYPE_TAG_GHASH: case GI_TYPE_TAG_ERROR: if (transfer != GI_TRANSFER_CONTAINER && type_needs_out_release(param_info, element_type)) { if (g_type_info_is_zero_terminated (type_info)) { gpointer *array; GArgument elem; for (array = (void **) arg->v_pointer; *array; array++) { elem.v_pointer = *array; if (!gjs_g_arg_release_internal(context, GI_TRANSFER_EVERYTHING, param_info, element_type, &elem)) { failed = true; } } } else { gint len = g_type_info_get_array_fixed_size(type_info); gint i; GArgument elem; g_assert(len != -1); for (i = 0; i < len; i++) { elem.v_pointer = ((gpointer*)arg->v_pointer)[i]; if (!gjs_g_arg_release_internal(context, GI_TRANSFER_EVERYTHING, param_info, element_type, &elem)) { failed = true; } } } } g_free (arg->v_pointer); break; case GI_TYPE_TAG_VOID: default: gjs_throw(context, "Releasing a C array with explicit length, that was nested" "inside another container. This is not supported (and will leak)"); failed = true; } g_base_info_unref((GIBaseInfo*) param_info); } else if (array_type == GI_ARRAY_TYPE_ARRAY) { GITypeInfo *param_info; GITypeTag element_type; param_info = g_type_info_get_param_type(type_info, 0); element_type = g_type_info_get_tag(param_info); switch (element_type) { case GI_TYPE_TAG_BOOLEAN: case GI_TYPE_TAG_UNICHAR: case GI_TYPE_TAG_UINT8: case GI_TYPE_TAG_UINT16: case GI_TYPE_TAG_UINT32: case GI_TYPE_TAG_UINT64: case GI_TYPE_TAG_INT8: case GI_TYPE_TAG_INT16: case GI_TYPE_TAG_INT32: case GI_TYPE_TAG_INT64: case GI_TYPE_TAG_FLOAT: case GI_TYPE_TAG_DOUBLE: case GI_TYPE_TAG_GTYPE: g_array_free((GArray*) arg->v_pointer, true); break; case GI_TYPE_TAG_UTF8: case GI_TYPE_TAG_FILENAME: case GI_TYPE_TAG_ARRAY: case GI_TYPE_TAG_INTERFACE: case GI_TYPE_TAG_GLIST: case GI_TYPE_TAG_GSLIST: case GI_TYPE_TAG_GHASH: case GI_TYPE_TAG_ERROR: if (transfer == GI_TRANSFER_CONTAINER) { g_array_free((GArray*) arg->v_pointer, true); } else if (type_needs_out_release (param_info, element_type)) { GArray *array = (GArray *) arg->v_pointer; guint i; for (i = 0; i < array->len; i++) { GArgument arg_iter; arg_iter.v_pointer = g_array_index (array, gpointer, i); gjs_g_arg_release_internal(context, transfer, param_info, element_type, &arg_iter); } g_array_free (array, true); } break; case GI_TYPE_TAG_VOID: default: gjs_throw(context, "Don't know how to release GArray element-type %d", element_type); failed = true; } g_base_info_unref((GIBaseInfo*) param_info); } else if (array_type == GI_ARRAY_TYPE_BYTE_ARRAY) { g_byte_array_unref ((GByteArray*)arg->v_pointer); } else if (array_type == GI_ARRAY_TYPE_PTR_ARRAY) { GITypeInfo *param_info; GPtrArray *array; param_info = g_type_info_get_param_type(type_info, 0); array = (GPtrArray *) arg->v_pointer; if (transfer != GI_TRANSFER_CONTAINER) { guint i; for (i = 0; i < array->len; i++) { GArgument arg_iter; arg_iter.v_pointer = g_ptr_array_index (array, i); gjs_g_argument_release(context, transfer, param_info, &arg_iter); } } g_ptr_array_free(array, true); g_base_info_unref((GIBaseInfo*) param_info); } else { g_assert_not_reached(); } break; } case GI_TYPE_TAG_GSLIST: if (transfer != GI_TRANSFER_CONTAINER) { GITypeInfo *param_info; GSList *slist; param_info = g_type_info_get_param_type(type_info, 0); g_assert(param_info != NULL); for (slist = (GSList *) arg->v_pointer; slist != NULL; slist = slist->next) { GArgument elem; elem.v_pointer = slist->data; if (!gjs_g_arg_release_internal(context, transfer, param_info, g_type_info_get_tag(param_info), &elem)) { failed = true; } } g_base_info_unref((GIBaseInfo*) param_info); } g_slist_free((GSList *) arg->v_pointer); break; case GI_TYPE_TAG_GHASH: if (arg->v_pointer) { if (transfer == GI_TRANSFER_CONTAINER) g_hash_table_steal_all ((GHashTable *) arg->v_pointer); else { GHR_closure c = { context, NULL, NULL, transfer, false }; c.key_param_info = g_type_info_get_param_type(type_info, 0); g_assert(c.key_param_info != NULL); c.val_param_info = g_type_info_get_param_type(type_info, 1); g_assert(c.val_param_info != NULL); g_hash_table_foreach_steal ((GHashTable *) arg->v_pointer, gjs_ghr_helper, &c); failed = c.failed; g_base_info_unref ((GIBaseInfo *)c.key_param_info); g_base_info_unref ((GIBaseInfo *)c.val_param_info); } g_hash_table_destroy ((GHashTable *) arg->v_pointer); } break; default: g_warning("Unhandled type %s releasing GArgument", g_type_tag_to_string(type_tag)); return false; } return !failed; } bool gjs_g_argument_release(JSContext *context, GITransfer transfer, GITypeInfo *type_info, GArgument *arg) { GITypeTag type_tag; if (transfer == GI_TRANSFER_NOTHING) return true; type_tag = g_type_info_get_tag( (GITypeInfo*) type_info); gjs_debug_marshal(GJS_DEBUG_GFUNCTION, "Releasing GArgument %s out param or return value", g_type_tag_to_string(type_tag)); return gjs_g_arg_release_internal(context, transfer, type_info, type_tag, arg); } bool gjs_g_argument_release_in_arg(JSContext *context, GITransfer transfer, GITypeInfo *type_info, GArgument *arg) { GITypeTag type_tag; /* GI_TRANSFER_EVERYTHING: we don't own the argument anymore. * GI_TRANSFER_CONTAINER: * - non-containers: treated as GI_TRANSFER_EVERYTHING * - containers: See FIXME in gjs_array_to_g_list(); currently * an error and we won't get here. */ if (transfer != GI_TRANSFER_NOTHING) return true; type_tag = g_type_info_get_tag( (GITypeInfo*) type_info); gjs_debug_marshal(GJS_DEBUG_GFUNCTION, "Releasing GArgument %s in param", g_type_tag_to_string(type_tag)); if (type_needs_release (type_info, type_tag)) return gjs_g_arg_release_internal(context, (GITransfer) TRANSFER_IN_NOTHING, type_info, type_tag, arg); return true; } bool gjs_g_argument_release_in_array (JSContext *context, GITransfer transfer, GITypeInfo *type_info, guint length, GArgument *arg) { GITypeInfo *param_type; gpointer *array; GArgument elem; guint i; bool ret = true; GITypeTag type_tag; if (transfer != GI_TRANSFER_NOTHING) return true; gjs_debug_marshal(GJS_DEBUG_GFUNCTION, "Releasing GArgument array in param"); array = (gpointer *) arg->v_pointer; param_type = g_type_info_get_param_type(type_info, 0); type_tag = g_type_info_get_tag(param_type); if (is_gvalue_flat_array(param_type, type_tag)) { for (i = 0; i < length; i++) { GValue *v = ((GValue*)array) + i; g_value_unset(v); } } if (type_needs_release(param_type, type_tag)) { for (i = 0; i < length; i++) { elem.v_pointer = array[i]; if (!gjs_g_arg_release_internal(context, (GITransfer) TRANSFER_IN_NOTHING, param_type, type_tag, &elem)) { ret = false; break; } } } g_base_info_unref(param_type); g_free(array); return ret; } bool gjs_g_argument_release_out_array (JSContext *context, GITransfer transfer, GITypeInfo *type_info, guint length, GArgument *arg) { GITypeInfo *param_type; gpointer *array; GArgument elem; guint i; bool ret = true; GITypeTag type_tag; if (transfer == GI_TRANSFER_NOTHING) return true; gjs_debug_marshal(GJS_DEBUG_GFUNCTION, "Releasing GArgument array out param"); array = (gpointer *) arg->v_pointer; param_type = g_type_info_get_param_type(type_info, 0); type_tag = g_type_info_get_tag(param_type); if (transfer != GI_TRANSFER_CONTAINER && type_needs_out_release(param_type, type_tag)) { for (i = 0; i < length; i++) { elem.v_pointer = array[i]; if (!gjs_g_arg_release_internal(context, GI_TRANSFER_EVERYTHING, param_type, type_tag, &elem)) { ret = false; } } } g_base_info_unref(param_type); g_free(array); return ret; } cjs-4.4.0/gi/arg.h000066400000000000000000000116601356375132300136330ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_ARG_H__ #define __GJS_ARG_H__ #include #include #include "cjs/jsapi-util.h" #include G_BEGIN_DECLS /* Different roles for a GArgument */ typedef enum { GJS_ARGUMENT_ARGUMENT, GJS_ARGUMENT_RETURN_VALUE, GJS_ARGUMENT_FIELD, GJS_ARGUMENT_LIST_ELEMENT, GJS_ARGUMENT_HASH_ELEMENT, GJS_ARGUMENT_ARRAY_ELEMENT } GjsArgumentType; bool gjs_value_to_arg(JSContext *context, JS::HandleValue value, GIArgInfo *arg_info, GIArgument *arg); bool gjs_value_to_explicit_array(JSContext *context, JS::HandleValue value, GIArgInfo *arg_info, GIArgument *arg, size_t *length_p); void gjs_g_argument_init_default (JSContext *context, GITypeInfo *type_info, GArgument *arg); bool gjs_value_to_g_argument (JSContext *context, JS::HandleValue value, GITypeInfo *type_info, const char *arg_name, GjsArgumentType argument_type, GITransfer transfer, bool may_be_null, GArgument *arg); bool gjs_value_from_g_argument(JSContext *context, JS::MutableHandleValue value_p, GITypeInfo *type_info, GIArgument *arg, bool copy_structs); bool gjs_value_from_explicit_array(JSContext *context, JS::MutableHandleValue value_p, GITypeInfo *type_info, GIArgument *arg, int length); bool gjs_g_argument_release (JSContext *context, GITransfer transfer, GITypeInfo *type_info, GArgument *arg); bool gjs_g_argument_release_out_array (JSContext *context, GITransfer transfer, GITypeInfo *type_info, guint length, GArgument *arg); bool gjs_g_argument_release_in_array (JSContext *context, GITransfer transfer, GITypeInfo *type_info, guint length, GArgument *arg); bool gjs_g_argument_release_in_arg (JSContext *context, GITransfer transfer, GITypeInfo *type_info, GArgument *arg); bool _gjs_flags_value_is_valid (JSContext *context, GType gtype, gint64 value); gint64 _gjs_enum_from_int (GIEnumInfo *enum_info, int int_value); bool gjs_array_from_strv(JSContext *context, JS::MutableHandleValue value_p, const char **strv); bool gjs_array_to_strv (JSContext *context, JS::Value array_value, unsigned int length, void **arr_p); G_END_DECLS #endif /* __GJS_ARG_H__ */ cjs-4.4.0/gi/boxed.cpp000066400000000000000000001272711356375132300145240ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include "boxed.h" #include "arg.h" #include "object.h" #include "cjs/jsapi-class.h" #include "cjs/jsapi-wrapper.h" #include "cjs/mem.h" #include "repo.h" #include "proxyutils.h" #include "function.h" #include "gtype.h" #include #include /* Reserved slots of JSNative accessor wrappers */ enum { SLOT_PROP_NAME, }; struct Boxed { /* prototype info */ GIBoxedInfo *info; GType gtype; gint zero_args_constructor; /* -1 if none */ JS::Heap zero_args_constructor_name; gint default_constructor; /* -1 if none */ JS::Heap default_constructor_name; /* instance info */ void *gboxed; /* NULL if we are the prototype and not an instance */ GHashTable *field_map; guint can_allocate_directly : 1; guint allocated_directly : 1; guint not_owning_gboxed : 1; /* if set, the JS wrapper does not own the reference to the C gboxed */ }; static bool struct_is_simple(GIStructInfo *info); static bool boxed_set_field_from_value(JSContext *context, Boxed *priv, GIFieldInfo *field_info, JS::HandleValue value); extern struct JSClass gjs_boxed_class; GJS_DEFINE_PRIV_FROM_JS(Boxed, gjs_boxed_class) static bool gjs_define_static_methods(JSContext *context, JS::HandleObject constructor, GType gtype, GIStructInfo *boxed_info) { int i; int n_methods; n_methods = g_struct_info_get_n_methods(boxed_info); for (i = 0; i < n_methods; i++) { GIFunctionInfo *meth_info; GIFunctionInfoFlags flags; meth_info = g_struct_info_get_method (boxed_info, i); flags = g_function_info_get_flags (meth_info); /* Anything that isn't a method we put on the prototype of the * constructor. This includes introspection * methods, as well as the forthcoming "static methods" * support. We may want to change this to use * GI_FUNCTION_IS_CONSTRUCTOR and GI_FUNCTION_IS_STATIC or the * like in the near future. */ if (!(flags & GI_FUNCTION_IS_METHOD)) { gjs_define_function(context, constructor, gtype, (GICallableInfo *)meth_info); } g_base_info_unref((GIBaseInfo*) meth_info); } return true; } /* The *resolved out parameter, on success, should be false to indicate that id * was not resolved; and true if id was resolved. */ static bool boxed_resolve(JSContext *context, JS::HandleObject obj, JS::HandleId id, bool *resolved) { Boxed *priv = priv_from_js(context, obj); gjs_debug_jsprop(GJS_DEBUG_GBOXED, "Resolve prop '%s' hook, obj %s, priv %p", gjs_debug_id(id).c_str(), gjs_debug_object(obj).c_str(), priv); if (priv == nullptr) return false; /* wrong class */ if (priv->gboxed) { /* We are an instance, not a prototype, so look for * per-instance props that we want to define on the * JSObject. Generally we do not want to cache these in JS, we * want to always pull them from the C object, or JS would not * see any changes made from C. So we use the get/set prop * hooks, not this resolve hook. */ *resolved = false; return true; } GjsAutoJSChar name; if (!gjs_get_string_id(context, id, &name)) { *resolved = false; return true; } /* We are the prototype, so look for methods and other class properties */ GIFunctionInfo *method_info = g_struct_info_find_method(priv->info, name); if (!method_info) { *resolved = false; return true; } #if GJS_VERBOSE_ENABLE_GI_USAGE _gjs_log_info_usage(method_info); #endif if (g_function_info_get_flags(method_info) & GI_FUNCTION_IS_METHOD) { const char *method_name = g_base_info_get_name(method_info); gjs_debug(GJS_DEBUG_GBOXED, "Defining method %s in prototype for %s.%s", method_name, g_base_info_get_namespace( (GIBaseInfo*) priv->info), g_base_info_get_name( (GIBaseInfo*) priv->info)); /* obj is the Boxed prototype */ if (!gjs_define_function(context, obj, priv->gtype, method_info)) { g_base_info_unref( (GIBaseInfo*) method_info); return false; } *resolved = true; } else { *resolved = false; } g_base_info_unref(method_info); return true; } /* Check to see if JS::Value passed in is another Boxed object of the same, * and if so, retrieves the Boxed private structure for it. */ static bool boxed_get_copy_source(JSContext *context, Boxed *priv, JS::Value value, Boxed **source_priv_out) { Boxed *source_priv; if (!value.isObject()) return false; JS::RootedObject object(context, &value.toObject()); if (!priv_from_js_with_typecheck(context, object, &source_priv)) return false; if (!g_base_info_equal((GIBaseInfo*) priv->info, (GIBaseInfo*) source_priv->info)) return false; *source_priv_out = source_priv; return true; } static void boxed_new_direct(Boxed *priv) { g_assert(priv->can_allocate_directly); priv->gboxed = g_slice_alloc0(g_struct_info_get_size (priv->info)); priv->allocated_directly = true; gjs_debug_lifecycle(GJS_DEBUG_GBOXED, "JSObject created by directly allocating %s", g_base_info_get_name ((GIBaseInfo *)priv->info)); } /* When initializing a boxed object from a hash of properties, we don't want * to do n O(n) lookups, so put put the fields into a hash table and store it on proto->priv * for fast lookup. */ static GHashTable * get_field_map(GIStructInfo *struct_info) { GHashTable *result; int n_fields; int i; result = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, (GDestroyNotify)g_base_info_unref); n_fields = g_struct_info_get_n_fields(struct_info); for (i = 0; i < n_fields; i++) { GIFieldInfo *field_info = g_struct_info_get_field(struct_info, i); g_hash_table_insert(result, (char *)g_base_info_get_name((GIBaseInfo *)field_info), field_info); } return result; } /* Initialize a newly created Boxed from an object that is a "hash" of * properties to set as fieds of the object. We don't require that every field * of the object be set. */ static bool boxed_init_from_props(JSContext *context, JSObject *obj, Boxed *priv, JS::Value props_value) { size_t ix, length; if (!props_value.isObject()) { gjs_throw(context, "argument should be a hash with fields to set"); return false; } JS::RootedObject props(context, &props_value.toObject()); JS::Rooted ids(context, context); if (!JS_Enumerate(context, props, &ids)) { gjs_throw(context, "Failed to enumerate fields hash"); return false; } if (!priv->field_map) priv->field_map = get_field_map(priv->info); JS::RootedValue value(context); for (ix = 0, length = ids.length(); ix < length; ix++) { GIFieldInfo *field_info; GjsAutoJSChar name; if (!gjs_get_string_id(context, ids[ix], &name)) return false; field_info = (GIFieldInfo *) g_hash_table_lookup(priv->field_map, name); if (field_info == NULL) { gjs_throw(context, "No field %s on boxed type %s", name.get(), g_base_info_get_name((GIBaseInfo *)priv->info)); return false; } /* ids[ix] is reachable because props is rooted, but require_property * doesn't know that */ if (!gjs_object_require_property(context, props, "property list", JS::HandleId::fromMarkedLocation(ids[ix].address()), &value)) return false; if (!boxed_set_field_from_value(context, priv, field_info, value)) return false; } return true; } static bool boxed_invoke_constructor(JSContext *context, JS::HandleObject obj, JS::HandleId constructor_name, JS::CallArgs& args) { JS::RootedObject js_constructor(context); if (!gjs_object_require_property(context, obj, NULL, GJS_STRING_CONSTRUCTOR, &js_constructor)) return false; JS::RootedValue js_constructor_func(context); if (!gjs_object_require_property(context, js_constructor, NULL, constructor_name, &js_constructor_func)) return false; return gjs_call_function_value(context, nullptr, js_constructor_func, args, args.rval()); } static bool boxed_new(JSContext *context, JS::HandleObject obj, /* "this" for constructor */ Boxed *priv, JS::CallArgs& args) { if (priv->gtype == G_TYPE_VARIANT) { /* Short-circuit construction for GVariants by calling into the JS packing function */ JS::HandleId constructor_name = gjs_context_get_const_string(context, GJS_STRING_NEW_INTERNAL); return boxed_invoke_constructor(context, obj, constructor_name, args); } /* If the structure is registered as a boxed, we can create a new instance by * looking for a zero-args constructor and calling it. * Constructors don't really make sense for non-boxed types, since there is no * memory management for the return value, and zero_args_constructor and * default_constructor are always -1 for them. * * For backward compatibility, we choose the zero args constructor if one * exists, otherwise we choose the internal slice allocator if possible; * finally, we fallback on the default constructor */ if (priv->zero_args_constructor >= 0) { GIFunctionInfo *func_info = g_struct_info_get_method (priv->info, priv->zero_args_constructor); GIArgument rval_arg; GError *error = NULL; if (!g_function_info_invoke(func_info, NULL, 0, NULL, 0, &rval_arg, &error)) { gjs_throw(context, "Failed to invoke boxed constructor: %s", error->message); g_clear_error(&error); g_base_info_unref((GIBaseInfo*) func_info); return false; } g_base_info_unref((GIBaseInfo*) func_info); priv->gboxed = rval_arg.v_pointer; gjs_debug_lifecycle(GJS_DEBUG_GBOXED, "JSObject created with boxed instance %p type %s", priv->gboxed, g_type_name(priv->gtype)); } else if (priv->can_allocate_directly) { boxed_new_direct(priv); } else if (priv->default_constructor >= 0) { /* for simplicity, we simply delegate all the work to the actual JS * constructor function (which we retrieve from the JS constructor, * that is, Namespace.BoxedType, or object.constructor, given that * object was created with the right prototype. The ID is traced from * the object, so it's OK to create a handle from it. */ return boxed_invoke_constructor(context, obj, JS::HandleId::fromMarkedLocation(priv->default_constructor_name.address()), args); } else { gjs_throw(context, "Unable to construct struct type %s since it has no default constructor and cannot be allocated directly", g_base_info_get_name((GIBaseInfo*) priv->info)); return false; } /* If we reach this code, we need to init from a map of fields */ if (args.length() == 0) return true; if (args.length() > 1) { gjs_throw(context, "Constructor with multiple arguments not supported for %s", g_base_info_get_name((GIBaseInfo *)priv->info)); return false; } return boxed_init_from_props(context, obj, priv, args[0]); } GJS_NATIVE_CONSTRUCTOR_DECLARE(boxed) { GJS_NATIVE_CONSTRUCTOR_VARIABLES(boxed) Boxed *priv; Boxed *proto_priv; JS::RootedObject proto(context); Boxed *source_priv; bool retval; GJS_NATIVE_CONSTRUCTOR_PRELUDE(boxed); priv = g_slice_new0(Boxed); new (priv) Boxed(); GJS_INC_COUNTER(boxed); g_assert(priv_from_js(context, object) == NULL); JS_SetPrivate(object, priv); gjs_debug_lifecycle(GJS_DEBUG_GBOXED, "boxed constructor, obj %p priv %p", object.get(), priv); JS_GetPrototype(context, object, &proto); gjs_debug_lifecycle(GJS_DEBUG_GBOXED, "boxed instance __proto__ is %p", proto.get()); /* If we're the prototype, then post-construct we'll fill in priv->info. * If we are not the prototype, though, then we'll get ->info from the * prototype and then create a GObject if we don't have one already. */ proto_priv = priv_from_js(context, proto); if (proto_priv == NULL) { gjs_debug(GJS_DEBUG_GBOXED, "Bad prototype set on boxed? Must match JSClass of object. JS error should have been reported."); return false; } *priv = *proto_priv; g_base_info_ref( (GIBaseInfo*) priv->info); /* Short-circuit copy-construction in the case where we can use g_boxed_copy or memcpy */ if (argc == 1 && boxed_get_copy_source(context, priv, argv[0], &source_priv)) { if (g_type_is_a (priv->gtype, G_TYPE_BOXED)) { priv->gboxed = g_boxed_copy(priv->gtype, source_priv->gboxed); GJS_NATIVE_CONSTRUCTOR_FINISH(boxed); return true; } else if (priv->can_allocate_directly) { boxed_new_direct (priv); memcpy(priv->gboxed, source_priv->gboxed, g_struct_info_get_size (priv->info)); GJS_NATIVE_CONSTRUCTOR_FINISH(boxed); return true; } } /* we may need to return a value different from object (for example because we delegate to another constructor) */ argv.rval().setUndefined(); retval = boxed_new(context, object, priv, argv); if (argv.rval().isUndefined()) GJS_NATIVE_CONSTRUCTOR_FINISH(boxed); return retval; } static void boxed_finalize(JSFreeOp *fop, JSObject *obj) { Boxed *priv; priv = (Boxed *) JS_GetPrivate(obj); gjs_debug_lifecycle(GJS_DEBUG_GBOXED, "finalize, obj %p priv %p", obj, priv); if (priv == NULL) return; /* wrong class? */ if (priv->gboxed && !priv->not_owning_gboxed) { if (priv->allocated_directly) { g_slice_free1(g_struct_info_get_size (priv->info), priv->gboxed); } else { if (g_type_is_a (priv->gtype, G_TYPE_BOXED)) g_boxed_free (priv->gtype, priv->gboxed); else if (g_type_is_a (priv->gtype, G_TYPE_VARIANT)) g_variant_unref ((GVariant *) priv->gboxed); else g_assert_not_reached (); } priv->gboxed = NULL; } if (priv->info) { g_base_info_unref( (GIBaseInfo*) priv->info); priv->info = NULL; } if (priv->field_map) { g_hash_table_destroy(priv->field_map); } GJS_DEC_COUNTER(boxed); priv->~Boxed(); g_slice_free(Boxed, priv); } static GIFieldInfo * get_field_info(JSContext *cx, Boxed *priv, uint32_t id) { GIFieldInfo *field_info = g_struct_info_get_field(priv->info, id); if (field_info == NULL) { gjs_throw(cx, "No field %d on boxed type %s", id, g_base_info_get_name((GIBaseInfo *)priv->info)); return NULL; } return field_info; } static bool get_nested_interface_object(JSContext *context, JSObject *parent_obj, Boxed *parent_priv, GIFieldInfo *field_info, GITypeInfo *type_info, GIBaseInfo *interface_info, JS::MutableHandleValue value) { JSObject *obj; int offset; Boxed *priv; Boxed *proto_priv; if (!struct_is_simple ((GIStructInfo *)interface_info)) { gjs_throw(context, "Reading field %s.%s is not supported", g_base_info_get_name ((GIBaseInfo *)parent_priv->info), g_base_info_get_name ((GIBaseInfo *)field_info)); return false; } JS::RootedObject proto(context, gjs_lookup_generic_prototype(context, (GIBoxedInfo*) interface_info)); proto_priv = priv_from_js(context, proto); offset = g_field_info_get_offset (field_info); obj = JS_NewObjectWithGivenProto(context, JS_GetClass(proto), proto); if (!obj) return false; GJS_INC_COUNTER(boxed); priv = g_slice_new0(Boxed); new (priv) Boxed(); JS_SetPrivate(obj, priv); priv->info = (GIBoxedInfo*) interface_info; g_base_info_ref( (GIBaseInfo*) priv->info); priv->gtype = g_registered_type_info_get_g_type ((GIRegisteredTypeInfo*) interface_info); priv->can_allocate_directly = proto_priv->can_allocate_directly; /* A structure nested inside a parent object; doesn't have an independent allocation */ priv->gboxed = ((char *)parent_priv->gboxed) + offset; priv->not_owning_gboxed = true; /* We never actually read the reserved slot, but we put the parent object * into it to hold onto the parent object. */ JS_SetReservedSlot(obj, 0, JS::ObjectValue(*parent_obj)); value.setObject(*obj); return true; } static JSObject * define_native_accessor_wrapper(JSContext *cx, JSNative call, unsigned nargs, const char *func_name, uint32_t id) { JSFunction *func = js::NewFunctionWithReserved(cx, call, nargs, 0, func_name); if (!func) return NULL; JSObject *func_obj = JS_GetFunctionObject(func); js::SetFunctionNativeReserved(func_obj, SLOT_PROP_NAME, JS::PrivateUint32Value(id)); return func_obj; } static uint32_t native_accessor_slot(JSObject *func_obj) { return js::GetFunctionNativeReserved(func_obj, SLOT_PROP_NAME) .toPrivateUint32(); } static bool boxed_field_getter(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_PRIV(context, argc, vp, args, obj, Boxed, priv); GIFieldInfo *field_info; GITypeInfo *type_info; GArgument arg; bool success = false; field_info = get_field_info(context, priv, native_accessor_slot(&args.callee())); if (!field_info) return false; type_info = g_field_info_get_type (field_info); if (priv->gboxed == NULL) { /* direct access to proto field */ gjs_throw(context, "Can't get field %s.%s from a prototype", g_base_info_get_name ((GIBaseInfo *)priv->info), g_base_info_get_name ((GIBaseInfo *)field_info)); goto out; } if (!g_type_info_is_pointer (type_info) && g_type_info_get_tag (type_info) == GI_TYPE_TAG_INTERFACE) { GIBaseInfo *interface_info = g_type_info_get_interface(type_info); if (g_base_info_get_type (interface_info) == GI_INFO_TYPE_STRUCT || g_base_info_get_type (interface_info) == GI_INFO_TYPE_BOXED) { success = get_nested_interface_object (context, obj, priv, field_info, type_info, interface_info, args.rval()); g_base_info_unref ((GIBaseInfo *)interface_info); goto out; } g_base_info_unref ((GIBaseInfo *)interface_info); } if (!g_field_info_get_field (field_info, priv->gboxed, &arg)) { gjs_throw(context, "Reading field %s.%s is not supported", g_base_info_get_name ((GIBaseInfo *)priv->info), g_base_info_get_name ((GIBaseInfo *)field_info)); goto out; } if (!gjs_value_from_g_argument(context, args.rval(), type_info, &arg, true)) goto out; success = true; out: g_base_info_unref ((GIBaseInfo *)field_info); g_base_info_unref ((GIBaseInfo *)type_info); return success; } static bool set_nested_interface_object (JSContext *context, Boxed *parent_priv, GIFieldInfo *field_info, GITypeInfo *type_info, GIBaseInfo *interface_info, JS::HandleValue value) { int offset; Boxed *proto_priv; Boxed *source_priv; if (!struct_is_simple ((GIStructInfo *)interface_info)) { gjs_throw(context, "Writing field %s.%s is not supported", g_base_info_get_name ((GIBaseInfo *)parent_priv->info), g_base_info_get_name ((GIBaseInfo *)field_info)); return false; } JS::RootedObject proto(context, gjs_lookup_generic_prototype(context, (GIBoxedInfo*) interface_info)); proto_priv = priv_from_js(context, proto); /* If we can't directly copy from the source object we need * to construct a new temporary object. */ if (!boxed_get_copy_source(context, proto_priv, value, &source_priv)) { JS::AutoValueArray<1> args(context); args[0].set(value); JS::RootedObject tmp_object(context, gjs_construct_object_dynamic(context, proto, args)); if (!tmp_object) return false; source_priv = priv_from_js(context, tmp_object); if (!source_priv) return false; } offset = g_field_info_get_offset (field_info); memcpy(((char *)parent_priv->gboxed) + offset, source_priv->gboxed, g_struct_info_get_size (source_priv->info)); return true; } static bool boxed_set_field_from_value(JSContext *context, Boxed *priv, GIFieldInfo *field_info, JS::HandleValue value) { GITypeInfo *type_info; GArgument arg; bool success = false; bool need_release = false; type_info = g_field_info_get_type (field_info); if (!g_type_info_is_pointer (type_info) && g_type_info_get_tag (type_info) == GI_TYPE_TAG_INTERFACE) { GIBaseInfo *interface_info = g_type_info_get_interface(type_info); if (g_base_info_get_type (interface_info) == GI_INFO_TYPE_STRUCT || g_base_info_get_type (interface_info) == GI_INFO_TYPE_BOXED) { success = set_nested_interface_object (context, priv, field_info, type_info, interface_info, value); g_base_info_unref ((GIBaseInfo *)interface_info); goto out; } g_base_info_unref ((GIBaseInfo *)interface_info); } if (!gjs_value_to_g_argument(context, value, type_info, g_base_info_get_name ((GIBaseInfo *)field_info), GJS_ARGUMENT_FIELD, GI_TRANSFER_NOTHING, true, &arg)) goto out; need_release = true; if (!g_field_info_set_field (field_info, priv->gboxed, &arg)) { gjs_throw(context, "Writing field %s.%s is not supported", g_base_info_get_name ((GIBaseInfo *)priv->info), g_base_info_get_name ((GIBaseInfo *)field_info)); goto out; } success = true; out: if (need_release) gjs_g_argument_release (context, GI_TRANSFER_NOTHING, type_info, &arg); g_base_info_unref ((GIBaseInfo *)type_info); return success; } static bool boxed_field_setter(JSContext *cx, unsigned argc, JS::Value *vp) { GJS_GET_PRIV(cx, argc, vp, args, obj, Boxed, priv); GIFieldInfo *field_info; bool success = false; field_info = get_field_info(cx, priv, native_accessor_slot(&args.callee())); if (!field_info) return false; if (priv->gboxed == NULL) { /* direct access to proto field */ gjs_throw(cx, "Can't set field %s.%s on prototype", g_base_info_get_name ((GIBaseInfo *)priv->info), g_base_info_get_name ((GIBaseInfo *)field_info)); goto out; } if (!boxed_set_field_from_value(cx, priv, field_info, args[0])) goto out; args.rval().setUndefined(); /* No stored value */ success = true; out: g_base_info_unref ((GIBaseInfo *)field_info); return success; } static bool define_boxed_class_fields(JSContext *cx, Boxed *priv, JS::HandleObject proto) { int n_fields = g_struct_info_get_n_fields (priv->info); int i; /* We define all fields as read/write so that the user gets an * error message. If we omitted fields or defined them read-only * we'd: * * - Store a new property for a non-accessible field * - Silently do nothing when writing a read-only field * * Which is pretty confusing if the only reason a field isn't * writable is language binding or memory-management restrictions. * * We just go ahead and define the fields immediately for the * class; doing it lazily in boxed_resolve() would be possible * as well if doing it ahead of time caused to much start-up * memory overhead. */ for (i = 0; i < n_fields; i++) { GIFieldInfo *field = g_struct_info_get_field (priv->info, i); const char *field_name = g_base_info_get_name ((GIBaseInfo *)field); GjsAutoChar getter_name = g_strconcat("boxed_field_get::", field_name, NULL); GjsAutoChar setter_name = g_strconcat("boxed_field_set::", field_name, NULL); g_base_info_unref ((GIBaseInfo *)field); /* In order to have one getter and setter for all the properties * we define, we must provide the property index in a "reserved * slot" for which we must unfortunately use the jsfriendapi. */ JS::RootedObject getter(cx, define_native_accessor_wrapper(cx, boxed_field_getter, 0, getter_name, i)); if (!getter) return false; JS::RootedObject setter(cx, define_native_accessor_wrapper(cx, boxed_field_setter, 1, setter_name, i)); if (!setter) return false; if (!JS_DefineProperty(cx, proto, field_name, JS::UndefinedHandleValue, JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_GETTER | JSPROP_SETTER, JS_DATA_TO_FUNC_PTR(JSNative, getter.get()), JS_DATA_TO_FUNC_PTR(JSNative, setter.get()))) return false; } return true; } static bool to_string_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_PRIV(context, argc, vp, rec, obj, Boxed, priv); return _gjs_proxy_to_string_func(context, obj, "boxed", (GIBaseInfo*)priv->info, priv->gtype, priv->gboxed, rec.rval()); } static void boxed_trace(JSTracer *tracer, JSObject *obj) { Boxed *priv = reinterpret_cast(JS_GetPrivate(obj)); if (priv == NULL) return; JS::TraceEdge(tracer, &priv->zero_args_constructor_name, "Boxed::zero_args_constructor_name"); JS::TraceEdge(tracer, &priv->default_constructor_name, "Boxed::default_constructor_name"); } /* The bizarre thing about this vtable is that it applies to both * instances of the object, and to the prototype that instances of the * class have. */ static const struct JSClassOps gjs_boxed_class_ops = { NULL, /* addProperty */ NULL, /* deleteProperty */ NULL, /* getProperty */ NULL, /* setProperty */ NULL, /* enumerate */ boxed_resolve, nullptr, /* mayResolve */ boxed_finalize, NULL, /* call */ NULL, /* hasInstance */ NULL, /* construct */ boxed_trace }; /* We allocate 1 reserved slot; this is typically unused, but if the * boxed is for a nested structure inside a parent structure, the * reserved slot is used to hold onto the parent Javascript object and * make sure it doesn't get freed. */ struct JSClass gjs_boxed_class = { "GObject_Boxed", JSCLASS_HAS_PRIVATE | JSCLASS_FOREGROUND_FINALIZE | JSCLASS_HAS_RESERVED_SLOTS(1), &gjs_boxed_class_ops }; JSPropertySpec gjs_boxed_proto_props[] = { JS_PS_END }; JSFunctionSpec gjs_boxed_proto_funcs[] = { JS_FS("toString", to_string_func, 0, 0), JS_FS_END }; static bool type_can_be_allocated_directly(GITypeInfo *type_info) { bool is_simple = true; if (g_type_info_is_pointer(type_info)) { if (g_type_info_get_tag(type_info) == GI_TYPE_TAG_ARRAY && g_type_info_get_array_type(type_info) == GI_ARRAY_TYPE_C) { GITypeInfo *param_info; param_info = g_type_info_get_param_type(type_info, 0); is_simple = type_can_be_allocated_directly(param_info); g_base_info_unref((GIBaseInfo*)param_info); } else { is_simple = false; } } else { switch (g_type_info_get_tag(type_info)) { case GI_TYPE_TAG_INTERFACE: { GIBaseInfo *interface = g_type_info_get_interface(type_info); switch (g_base_info_get_type(interface)) { case GI_INFO_TYPE_BOXED: case GI_INFO_TYPE_STRUCT: if (!struct_is_simple((GIStructInfo *)interface)) is_simple = false; break; case GI_INFO_TYPE_UNION: /* FIXME: Need to implement */ is_simple = false; break; case GI_INFO_TYPE_OBJECT: case GI_INFO_TYPE_VFUNC: case GI_INFO_TYPE_CALLBACK: case GI_INFO_TYPE_INVALID: case GI_INFO_TYPE_INTERFACE: case GI_INFO_TYPE_FUNCTION: case GI_INFO_TYPE_CONSTANT: case GI_INFO_TYPE_VALUE: case GI_INFO_TYPE_SIGNAL: case GI_INFO_TYPE_PROPERTY: case GI_INFO_TYPE_FIELD: case GI_INFO_TYPE_ARG: case GI_INFO_TYPE_TYPE: case GI_INFO_TYPE_UNRESOLVED: is_simple = false; break; case GI_INFO_TYPE_INVALID_0: g_assert_not_reached(); break; case GI_INFO_TYPE_ENUM: case GI_INFO_TYPE_FLAGS: default: break; } g_base_info_unref(interface); break; } case GI_TYPE_TAG_BOOLEAN: case GI_TYPE_TAG_INT8: case GI_TYPE_TAG_UINT8: case GI_TYPE_TAG_INT16: case GI_TYPE_TAG_UINT16: case GI_TYPE_TAG_INT32: case GI_TYPE_TAG_UINT32: case GI_TYPE_TAG_INT64: case GI_TYPE_TAG_UINT64: case GI_TYPE_TAG_FLOAT: case GI_TYPE_TAG_DOUBLE: case GI_TYPE_TAG_UNICHAR: case GI_TYPE_TAG_VOID: case GI_TYPE_TAG_GTYPE: case GI_TYPE_TAG_ERROR: case GI_TYPE_TAG_UTF8: case GI_TYPE_TAG_FILENAME: case GI_TYPE_TAG_ARRAY: case GI_TYPE_TAG_GLIST: case GI_TYPE_TAG_GSLIST: case GI_TYPE_TAG_GHASH: default: break; } } return is_simple; } /* Check if the type of the boxed is "simple" - every field is a non-pointer * type that we know how to assign to. If so, then we can allocate and free * instances without needing a constructor. */ static bool struct_is_simple(GIStructInfo *info) { int n_fields = g_struct_info_get_n_fields(info); bool is_simple = true; int i; /* If it's opaque, it's not simple */ if (n_fields == 0) return false; for (i = 0; i < n_fields && is_simple; i++) { GIFieldInfo *field_info = g_struct_info_get_field(info, i); GITypeInfo *type_info = g_field_info_get_type(field_info); is_simple = type_can_be_allocated_directly(type_info); g_base_info_unref((GIBaseInfo *)field_info); g_base_info_unref((GIBaseInfo *)type_info); } return is_simple; } static void boxed_fill_prototype_info(JSContext *context, Boxed *priv) { int i, n_methods; int first_constructor = -1; jsid first_constructor_name = JSID_VOID; priv->gtype = g_registered_type_info_get_g_type( (GIRegisteredTypeInfo*) priv->info); priv->zero_args_constructor = -1; priv->zero_args_constructor_name = JSID_VOID; priv->default_constructor = -1; priv->default_constructor_name = JSID_VOID; if (priv->gtype != G_TYPE_NONE) { /* If the structure is registered as a boxed, we can create a new instance by * looking for a zero-args constructor and calling it; constructors don't * really make sense for non-boxed types, since there is no memory management * for the return value. */ n_methods = g_struct_info_get_n_methods(priv->info); for (i = 0; i < n_methods; ++i) { GIFunctionInfo *func_info; GIFunctionInfoFlags flags; func_info = g_struct_info_get_method(priv->info, i); flags = g_function_info_get_flags(func_info); if ((flags & GI_FUNCTION_IS_CONSTRUCTOR) != 0) { if (first_constructor < 0) { const char *name; name = g_base_info_get_name((GIBaseInfo*) func_info); first_constructor = i; first_constructor_name = gjs_intern_string_to_id(context, name); } if (priv->zero_args_constructor < 0 && g_callable_info_get_n_args((GICallableInfo*) func_info) == 0) { const char *name; name = g_base_info_get_name((GIBaseInfo*) func_info); priv->zero_args_constructor = i; priv->zero_args_constructor_name = gjs_intern_string_to_id(context, name); } if (priv->default_constructor < 0 && strcmp(g_base_info_get_name ((GIBaseInfo*) func_info), "new") == 0) { priv->default_constructor = i; priv->default_constructor_name = gjs_context_get_const_string(context, GJS_STRING_NEW); } } g_base_info_unref((GIBaseInfo*) func_info); } if (priv->default_constructor < 0) { priv->default_constructor = priv->zero_args_constructor; priv->default_constructor_name = priv->zero_args_constructor_name; } if (priv->default_constructor < 0) { priv->default_constructor = first_constructor; priv->default_constructor_name = first_constructor_name; } } } void gjs_define_boxed_class(JSContext *context, JS::HandleObject in_object, GIBoxedInfo *info) { const char *constructor_name; JS::RootedObject prototype(context), constructor(context); Boxed *priv; /* See the comment in gjs_define_object_class() for an * explanation of how this all works; Boxed is pretty much the * same as Object. */ constructor_name = g_base_info_get_name( (GIBaseInfo*) info); if (!gjs_init_class_dynamic(context, in_object, nullptr, /* parent prototype */ g_base_info_get_namespace( (GIBaseInfo*) info), constructor_name, &gjs_boxed_class, gjs_boxed_constructor, 1, /* props of prototype */ &gjs_boxed_proto_props[0], /* funcs of prototype */ &gjs_boxed_proto_funcs[0], /* props of constructor, MyConstructor.myprop */ NULL, /* funcs of constructor, MyConstructor.myfunc() */ NULL, &prototype, &constructor)) { gjs_log_exception(context); g_error("Can't init class %s", constructor_name); } GJS_INC_COUNTER(boxed); priv = g_slice_new0(Boxed); new (priv) Boxed(); priv->info = info; boxed_fill_prototype_info(context, priv); g_base_info_ref( (GIBaseInfo*) priv->info); priv->gtype = g_registered_type_info_get_g_type ((GIRegisteredTypeInfo*) priv->info); JS_SetPrivate(prototype, priv); gjs_debug(GJS_DEBUG_GBOXED, "Defined class %s prototype is %p class %p in object %p", constructor_name, prototype.get(), JS_GetClass(prototype), in_object.get()); priv->can_allocate_directly = struct_is_simple (priv->info); define_boxed_class_fields (context, priv, prototype); gjs_define_static_methods (context, constructor, priv->gtype, priv->info); JS::RootedObject gtype_obj(context, gjs_gtype_create_gtype_wrapper(context, priv->gtype)); JS_DefineProperty(context, constructor, "$gtype", gtype_obj, JSPROP_PERMANENT); } JSObject* gjs_boxed_from_c_struct(JSContext *context, GIStructInfo *info, void *gboxed, GjsBoxedCreationFlags flags) { JSObject *obj; Boxed *priv; Boxed *proto_priv; if (gboxed == NULL) return NULL; gjs_debug_marshal(GJS_DEBUG_GBOXED, "Wrapping struct %s %p with JSObject", g_base_info_get_name((GIBaseInfo *)info), gboxed); JS::RootedObject proto(context, gjs_lookup_generic_prototype(context, info)); proto_priv = priv_from_js(context, proto); obj = JS_NewObjectWithGivenProto(context, JS_GetClass(proto), proto); GJS_INC_COUNTER(boxed); priv = g_slice_new0(Boxed); new (priv) Boxed(); *priv = *proto_priv; g_base_info_ref( (GIBaseInfo*) priv->info); JS_SetPrivate(obj, priv); if ((flags & GJS_BOXED_CREATION_NO_COPY) != 0) { /* we need to create a JS Boxed which references the * original C struct, not a copy of it. Used for * G_SIGNAL_TYPE_STATIC_SCOPE */ priv->gboxed = gboxed; priv->not_owning_gboxed = true; } else { if (priv->gtype != G_TYPE_NONE && g_type_is_a (priv->gtype, G_TYPE_BOXED)) { priv->gboxed = g_boxed_copy(priv->gtype, gboxed); } else if (priv->gtype == G_TYPE_VARIANT) { priv->gboxed = g_variant_ref_sink ((GVariant *) gboxed); } else if (priv->can_allocate_directly) { boxed_new_direct(priv); memcpy(priv->gboxed, gboxed, g_struct_info_get_size (priv->info)); } else { gjs_throw(context, "Can't create a Javascript object for %s; no way to copy", g_base_info_get_name( (GIBaseInfo*) priv->info)); } } return obj; } void* gjs_c_struct_from_boxed(JSContext *context, JS::HandleObject obj) { Boxed *priv; if (!obj) return NULL; priv = priv_from_js(context, obj); if (priv == NULL) return NULL; return priv->gboxed; } bool gjs_typecheck_boxed(JSContext *context, JS::HandleObject object, GIStructInfo *expected_info, GType expected_type, bool throw_error) { Boxed *priv; bool result; if (!do_base_typecheck(context, object, throw_error)) return false; priv = priv_from_js(context, object); if (priv->gboxed == NULL) { if (throw_error) { gjs_throw_custom(context, JSProto_TypeError, nullptr, "Object is %s.%s.prototype, not an object instance - cannot convert to a boxed instance", g_base_info_get_namespace( (GIBaseInfo*) priv->info), g_base_info_get_name( (GIBaseInfo*) priv->info)); } return false; } if (expected_type != G_TYPE_NONE) result = g_type_is_a (priv->gtype, expected_type); else if (expected_info != NULL) result = g_base_info_equal((GIBaseInfo*) priv->info, (GIBaseInfo*) expected_info); else result = true; if (!result && throw_error) { if (expected_info != NULL) { gjs_throw_custom(context, JSProto_TypeError, nullptr, "Object is of type %s.%s - cannot convert to %s.%s", g_base_info_get_namespace((GIBaseInfo*) priv->info), g_base_info_get_name((GIBaseInfo*) priv->info), g_base_info_get_namespace((GIBaseInfo*) expected_info), g_base_info_get_name((GIBaseInfo*) expected_info)); } else { gjs_throw_custom(context, JSProto_TypeError, nullptr, "Object is of type %s.%s - cannot convert to %s", g_base_info_get_namespace((GIBaseInfo*) priv->info), g_base_info_get_name((GIBaseInfo*) priv->info), g_type_name(expected_type)); } } return result; } cjs-4.4.0/gi/boxed.h000066400000000000000000000050771356375132300141700ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_BOXED_H__ #define __GJS_BOXED_H__ #include #include #include "cjs/jsapi-util.h" #include G_BEGIN_DECLS typedef enum { GJS_BOXED_CREATION_NONE = 0, GJS_BOXED_CREATION_NO_COPY = (1 << 0) } GjsBoxedCreationFlags; /* Hack for now... why doesn't gobject-introspection have this? */ typedef GIStructInfo GIBoxedInfo; void gjs_define_boxed_class (JSContext *context, JS::HandleObject in_object, GIBoxedInfo *info); void* gjs_c_struct_from_boxed (JSContext *context, JS::HandleObject obj); JSObject* gjs_boxed_from_c_struct (JSContext *context, GIStructInfo *info, void *gboxed, GjsBoxedCreationFlags flags); bool gjs_typecheck_boxed (JSContext *context, JS::HandleObject obj, GIStructInfo *expected_info, GType expected_type, bool throw_error); G_END_DECLS #endif /* __GJS_BOXED_H__ */ cjs-4.4.0/gi/closure.cpp000066400000000000000000000241241356375132300150700ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include "closure.h" #include "cjs/jsapi-util-root.h" #include "cjs/jsapi-wrapper.h" #include "cjs/mem.h" struct Closure { JSContext *context; GjsMaybeOwned obj; }; struct GjsClosure { GClosure base; /* We need a separate object to be able to call the C++ constructor without stomping on the base */ Closure priv; }; /* * Memory management of closures is "interesting" because we're keeping around * a JSContext* and then trying to use it spontaneously from the main loop. * I don't think that's really quite kosher, and perhaps the problem is that * (in xulrunner) we just need to save a different context. * * Or maybe the right fix is to create our own context just for this? * * But for the moment, we save the context that was used to create the closure. * * Here's the problem: this context can be destroyed. AFTER the * context is destroyed, or at least potentially after, the objects in * the context's global object may be garbage collected. Remember that * JSObject* belong to a runtime, not a context. * * There is apparently no robust way to track context destruction in * SpiderMonkey, because the context can be destroyed without running * the garbage collector, and xulrunner takes over the JS_SetContextCallback() * callback. So there's no callback for us. * * So, when we go to use our context, we iterate the contexts in the runtime * and see if ours is still in the valid list, and decide to invalidate * the closure if it isn't. * * The closure can thus be destroyed in several cases: * - invalidation by unref, e.g. when a signal is disconnected, closure is unref'd * - invalidation because we were invoked while the context was dead * - invalidation through finalization (we were garbage collected) * * These don't have to happen in the same order; garbage collection can * be either before, or after, context destruction. * */ static void invalidate_js_pointers(GjsClosure *gc) { Closure *c; c = &gc->priv; if (c->obj == nullptr) return; c->obj.reset(); c->context = NULL; /* Notify any closure reference holders they * may want to drop references. */ g_closure_invalidate(&gc->base); } static void global_context_finalized(JS::HandleObject obj, void *data) { GjsClosure *gc = (GjsClosure*) data; Closure *c = &gc->priv; gjs_debug_closure("Context global object destroy notifier on closure %p " "which calls object %p", c, c->obj.get()); if (c->obj) { g_assert(c->obj == obj.get()); invalidate_js_pointers(gc); } } /* Invalidation is like "dispose" - it is guaranteed to happen at * finalize, but may happen before finalize. Normally, g_closure_invalidate() * is called when the "target" of the closure becomes invalid, so that the * source (the signal connection, say can be removed.) The usage above * in invalidate_js_pointers() is typical. Since the target of the closure * is under our control, it's unlikely that g_closure_invalidate() will ever * be called by anyone else, but in case it ever does, it's slightly better * to remove the "keep alive" here rather than in the finalize notifier. * * Unlike "dispose" invalidation only happens once. */ static void closure_invalidated(gpointer data, GClosure *closure) { Closure *c; c = &((GjsClosure*) closure)->priv; GJS_DEC_COUNTER(closure); gjs_debug_closure("Invalidating closure %p which calls object %p", closure, c->obj.get()); if (c->obj == nullptr) { gjs_debug_closure(" (closure %p already dead, nothing to do)", closure); return; } /* The context still exists, remove our destroy notifier. Otherwise we * would call the destroy notifier on an already-freed closure. * * This happens in the normal case, when the closure is * invalidated for some reason other than destruction of the * JSContext. */ gjs_debug_closure(" (closure %p's context was alive, " "removing our destroy notifier on global object)", closure); c->obj.reset(); c->context = nullptr; } static void closure_set_invalid(gpointer data, GClosure *closure) { Closure *self = &((GjsClosure*) closure)->priv; gjs_debug_closure("Invalidating signal closure %p which calls object %p", closure, self->obj.get()); self->obj.prevent_collection(); self->obj.reset(); self->context = nullptr; GJS_DEC_COUNTER(closure); } static void closure_finalize(gpointer data, GClosure *closure) { Closure *self = &((GjsClosure*) closure)->priv; self->~Closure(); } bool gjs_closure_invoke(GClosure *closure, JS::HandleObject this_obj, const JS::HandleValueArray& args, JS::MutableHandleValue retval, bool return_exception) { Closure *c; JSContext *context; c = &((GjsClosure*) closure)->priv; if (c->obj == nullptr) { /* We were destroyed; become a no-op */ c->context = NULL; return false; } context = c->context; JSAutoRequest ar(context); JSAutoCompartment ac(context, c->obj); if (JS_IsExceptionPending(context)) { gjs_debug_closure("Exception was pending before invoking callback??? " "Not expected - closure %p", closure); gjs_log_exception(context); } JS::RootedValue v_closure(context, JS::ObjectValue(*c->obj)); if (!gjs_call_function_value(context, this_obj, v_closure, args, retval)) { /* Exception thrown... */ gjs_debug_closure("Closure invocation failed (exception should " "have been thrown) closure %p callable %p", closure, c->obj.get()); /* If an exception has been thrown, log it, unless the caller * explicitly wants to handle it manually (for example to turn it * into a GError), in which case it replaces the return value * (which is not valid anyway) */ if (JS_IsExceptionPending(context)) { if (return_exception) JS_GetPendingException(context, retval); else gjs_log_exception(context); } else { retval.setUndefined(); gjs_debug_closure("Closure invocation failed but no exception was set?" "closure %p", closure); } return false; } if (gjs_log_exception(context)) { gjs_debug_closure("Closure invocation succeeded but an exception was set" " - closure %p", closure); } JS_MaybeGC(context); return true; } bool gjs_closure_is_valid(GClosure *closure) { Closure *c; c = &((GjsClosure*) closure)->priv; return c->context != NULL; } JSContext* gjs_closure_get_context(GClosure *closure) { Closure *c; c = &((GjsClosure*) closure)->priv; return c->context; } JSObject* gjs_closure_get_callable(GClosure *closure) { Closure *c; c = &((GjsClosure*) closure)->priv; return c->obj; } void gjs_closure_trace(GClosure *closure, JSTracer *tracer) { Closure *c; c = &((GjsClosure*) closure)->priv; if (c->obj == nullptr) return; c->obj.trace(tracer, "signal connection"); } GClosure* gjs_closure_new(JSContext *context, JSObject *callable, const char *description, bool root_function) { GjsClosure *gc; Closure *c; gc = (GjsClosure*) g_closure_new_simple(sizeof(GjsClosure), NULL); c = new (&gc->priv) Closure(); /* The saved context is used for lifetime management, so that the closure will * be torn down with the context that created it. The context could be attached to * the default context of the runtime using if we wanted the closure to survive * the context that created it. */ c->context = context; JS_BeginRequest(context); GJS_INC_COUNTER(closure); if (root_function) { /* Fully manage closure lifetime if so asked */ c->obj.root(context, callable, global_context_finalized, gc); g_closure_add_invalidate_notifier(&gc->base, NULL, closure_invalidated); } else { c->obj = callable; /* Only mark the closure as invalid if memory is managed outside (i.e. by object.c for signals) */ g_closure_add_invalidate_notifier(&gc->base, NULL, closure_set_invalid); } g_closure_add_finalize_notifier(&gc->base, NULL, closure_finalize); gjs_debug_closure("Create closure %p which calls object %p '%s'", gc, c->obj.get(), description); JS_EndRequest(context); return &gc->base; } cjs-4.4.0/gi/closure.h000066400000000000000000000043141356375132300145340ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_CLOSURE_H__ #define __GJS_CLOSURE_H__ #include #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS GClosure* gjs_closure_new (JSContext *context, JSObject *callable, const char *description, bool root_function); bool gjs_closure_invoke(GClosure *closure, JS::HandleObject this_obj, const JS::HandleValueArray& args, JS::MutableHandleValue retval, bool return_exception); JSContext* gjs_closure_get_context (GClosure *closure); bool gjs_closure_is_valid (GClosure *closure); JSObject* gjs_closure_get_callable (GClosure *closure); void gjs_closure_trace (GClosure *closure, JSTracer *tracer); G_END_DECLS #endif /* __GJS_CLOSURE_H__ */ cjs-4.4.0/gi/enumeration.cpp000066400000000000000000000143701356375132300157440ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include "cjs/jsapi-wrapper.h" #include "repo.h" #include "gtype.h" #include "function.h" #include #include #include "enumeration.h" static bool gjs_define_enum_value(JSContext *context, JS::HandleObject in_object, GIValueInfo *info) { const char *value_name; char *fixed_name; gsize i; gint64 value_val; value_name = g_base_info_get_name( (GIBaseInfo*) info); value_val = g_value_info_get_value(info); /* g-i converts enum members such as GDK_GRAVITY_SOUTH_WEST to * Gdk.GravityType.south-west (where 'south-west' is value_name) * Convert back to all SOUTH_WEST. */ fixed_name = g_ascii_strup(value_name, -1); for (i = 0; fixed_name[i]; ++i) { char c = fixed_name[i]; if (!(('A' <= c && c <= 'Z') || ('0' <= c && c <= '9'))) fixed_name[i] = '_'; } gjs_debug(GJS_DEBUG_GENUM, "Defining enum value %s (fixed from %s) %" G_GINT64_MODIFIER "d", fixed_name, value_name, value_val); if (!JS_DefineProperty(context, in_object, fixed_name, (double) value_val, GJS_MODULE_PROP_FLAGS)) { gjs_throw(context, "Unable to define enumeration value %s %" G_GINT64_FORMAT " (no memory most likely)", fixed_name, value_val); g_free(fixed_name); return false; } g_free(fixed_name); return true; } bool gjs_define_enum_values(JSContext *context, JS::HandleObject in_object, GIEnumInfo *info) { GType gtype; int i, n_values; /* Fill in enum values first, so we don't define the enum itself until we're * sure we can finish successfully. */ n_values = g_enum_info_get_n_values(info); for (i = 0; i < n_values; ++i) { GIValueInfo *value_info = g_enum_info_get_value(info, i); bool failed; failed = !gjs_define_enum_value(context, in_object, value_info); g_base_info_unref( (GIBaseInfo*) value_info); if (failed) { return false; } } gtype = g_registered_type_info_get_g_type((GIRegisteredTypeInfo*)info); JS::RootedObject gtype_obj(context, gjs_gtype_create_gtype_wrapper(context, gtype)); JS_DefineProperty(context, in_object, "$gtype", gtype_obj, JSPROP_PERMANENT); return true; } bool gjs_define_enum_static_methods(JSContext *context, JS::HandleObject constructor, GIEnumInfo *enum_info) { int i, n_methods; n_methods = g_enum_info_get_n_methods(enum_info); for (i = 0; i < n_methods; i++) { GIFunctionInfo *meth_info; GIFunctionInfoFlags flags; meth_info = g_enum_info_get_method(enum_info, i); flags = g_function_info_get_flags(meth_info); g_warn_if_fail(!(flags & GI_FUNCTION_IS_METHOD)); /* Anything that isn't a method we put on the prototype of the * constructor. This includes introspection * methods, as well as the forthcoming "static methods" * support. We may want to change this to use * GI_FUNCTION_IS_CONSTRUCTOR and GI_FUNCTION_IS_STATIC or the * like in the near future. */ if (!(flags & GI_FUNCTION_IS_METHOD)) { gjs_define_function(context, constructor, G_TYPE_NONE, (GICallableInfo *)meth_info); } g_base_info_unref((GIBaseInfo*) meth_info); } return true; } bool gjs_define_enumeration(JSContext *context, JS::HandleObject in_object, GIEnumInfo *info) { const char *enum_name; /* An enumeration is simply an object containing integer attributes for * each enum value. It does not have a special JSClass. * * We could make this more typesafe and also print enum values as strings * if we created a class for each enum and made the enum values instances * of that class. However, it would have a lot more overhead and just * be more complicated in general. I think this is fine. */ enum_name = g_base_info_get_name( (GIBaseInfo*) info); JS::RootedObject enum_obj(context, JS_NewPlainObject(context)); if (!enum_obj) { g_error("Could not create enumeration %s.%s", g_base_info_get_namespace( (GIBaseInfo*) info), enum_name); } if (!gjs_define_enum_values(context, enum_obj, info)) return false; gjs_define_enum_static_methods (context, enum_obj, info); gjs_debug(GJS_DEBUG_GENUM, "Defining %s.%s as %p", g_base_info_get_namespace( (GIBaseInfo*) info), enum_name, enum_obj.get()); if (!JS_DefineProperty(context, in_object, enum_name, enum_obj, GJS_MODULE_PROP_FLAGS)) { gjs_throw(context, "Unable to define enumeration property (no memory most likely)"); return false; } return true; } cjs-4.4.0/gi/enumeration.h000066400000000000000000000036041356375132300154070ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_ENUMERATION_H__ #define __GJS_ENUMERATION_H__ #include #include #include "cjs/jsapi-util.h" #include G_BEGIN_DECLS bool gjs_define_enum_values(JSContext *context, JS::HandleObject in_object, GIEnumInfo *info); bool gjs_define_enum_static_methods(JSContext *context, JS::HandleObject constructor, GIEnumInfo *enum_info); bool gjs_define_enumeration(JSContext *context, JS::HandleObject in_object, GIEnumInfo *info); G_END_DECLS #endif /* __GJS_ENUMERATION_H__ */ cjs-4.4.0/gi/foreign.cpp000066400000000000000000000142571356375132300150530ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2010 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include "arg.h" #include "foreign.h" #include "cjs/jsapi-wrapper.h" static struct { char *gi_namespace; char *module; // relative to "imports." bool loaded; } foreign_modules[] = { { (char*)"cairo", (char*)"cairo", false }, { NULL } }; static GHashTable* foreign_structs_table = NULL; static GHashTable* get_foreign_structs(void) { // FIXME: look into hasing on GITypeInfo instead. if (!foreign_structs_table) { foreign_structs_table = g_hash_table_new_full(g_str_hash, g_str_equal, (GDestroyNotify)g_free, NULL); } return foreign_structs_table; } static bool gjs_foreign_load_foreign_module(JSContext *context, const gchar *gi_namespace) { int i; for (i = 0; foreign_modules[i].gi_namespace; ++i) { char *script; if (strcmp(gi_namespace, foreign_modules[i].gi_namespace) != 0) continue; if (foreign_modules[i].loaded) return true; // FIXME: Find a way to check if a module is imported // and only execute this statement if isn't script = g_strdup_printf("imports.%s;", gi_namespace); JS::RootedValue retval(context); if (!gjs_eval_with_scope(context, nullptr, script, strlen(script), "", &retval)) { g_critical("ERROR importing foreign module %s\n", gi_namespace); g_free(script); return false; } g_free(script); foreign_modules[i].loaded = true; return true; } return false; } bool gjs_struct_foreign_register(const char *gi_namespace, const char *type_name, GjsForeignInfo *info) { char *canonical_name; g_return_val_if_fail(info != NULL, false); g_return_val_if_fail(info->to_func != NULL, false); g_return_val_if_fail(info->from_func != NULL, false); canonical_name = g_strdup_printf("%s.%s", gi_namespace, type_name); g_hash_table_insert(get_foreign_structs(), canonical_name, info); return true; } static GjsForeignInfo * gjs_struct_foreign_lookup(JSContext *context, GIBaseInfo *interface_info) { GjsForeignInfo *retval = NULL; GHashTable *hash_table; char *key; key = g_strdup_printf("%s.%s", g_base_info_get_namespace(interface_info), g_base_info_get_name(interface_info)); hash_table = get_foreign_structs(); retval = (GjsForeignInfo*)g_hash_table_lookup(hash_table, key); if (!retval) { if (gjs_foreign_load_foreign_module(context, g_base_info_get_namespace(interface_info))) { retval = (GjsForeignInfo*)g_hash_table_lookup(hash_table, key); } } if (!retval) { gjs_throw(context, "Unable to find module implementing foreign type %s.%s", g_base_info_get_namespace(interface_info), g_base_info_get_name(interface_info)); } g_free(key); return retval; } bool gjs_struct_foreign_convert_to_g_argument(JSContext *context, JS::Value value, GIBaseInfo *interface_info, const char *arg_name, GjsArgumentType argument_type, GITransfer transfer, bool may_be_null, GArgument *arg) { GjsForeignInfo *foreign; foreign = gjs_struct_foreign_lookup(context, interface_info); if (!foreign) return false; if (!foreign->to_func(context, value, arg_name, argument_type, transfer, may_be_null, arg)) return false; return true; } bool gjs_struct_foreign_convert_from_g_argument(JSContext *context, JS::MutableHandleValue value_p, GIBaseInfo *interface_info, GIArgument *arg) { GjsForeignInfo *foreign; foreign = gjs_struct_foreign_lookup(context, interface_info); if (!foreign) return false; if (!foreign->from_func(context, value_p, arg)) return false; return true; } bool gjs_struct_foreign_release_g_argument(JSContext *context, GITransfer transfer, GIBaseInfo *interface_info, GArgument *arg) { GjsForeignInfo *foreign; foreign = gjs_struct_foreign_lookup(context, interface_info); if (!foreign) return false; if (!foreign->release_func) return true; if (!foreign->release_func(context, transfer, arg)) return false; return true; } cjs-4.4.0/gi/foreign.h000066400000000000000000000076131356375132300145160ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2010 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_OVERRIDE_H__ #define __GJS_OVERRIDE_H__ #include #include #include #include "arg.h" typedef bool (*GjsArgOverrideToGArgumentFunc) (JSContext *context, JS::Value value, const char *arg_name, GjsArgumentType argument_type, GITransfer transfer, bool may_be_null, GArgument *arg); typedef bool (*GjsArgOverrideFromGArgumentFunc) (JSContext *context, JS::MutableHandleValue value_p, GIArgument *arg); typedef bool (*GjsArgOverrideReleaseGArgumentFunc) (JSContext *context, GITransfer transfer, GArgument *arg); typedef struct { GjsArgOverrideToGArgumentFunc to_func; GjsArgOverrideFromGArgumentFunc from_func; GjsArgOverrideReleaseGArgumentFunc release_func; } GjsForeignInfo; bool gjs_struct_foreign_register (const char *gi_namespace, const char *type_name, GjsForeignInfo *info); bool gjs_struct_foreign_convert_to_g_argument (JSContext *context, JS::Value value, GIBaseInfo *interface_info, const char *arg_name, GjsArgumentType argument_type, GITransfer transfer, bool may_be_null, GArgument *arg); bool gjs_struct_foreign_convert_from_g_argument(JSContext *context, JS::MutableHandleValue value_p, GIBaseInfo *interface_info, GIArgument *arg); bool gjs_struct_foreign_release_g_argument (JSContext *context, GITransfer transfer, GIBaseInfo *interface_info, GArgument *arg); #endif /* __GJS_OVERRIDE_H__ */ cjs-4.4.0/gi/function.cpp000066400000000000000000002136031356375132300152430ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "function.h" #include "arg.h" #include "object.h" #include "fundamental.h" #include "boxed.h" #include "union.h" #include "gerror.h" #include "closure.h" #include "gtype.h" #include "param.h" #include "cjs/context-private.h" #include "cjs/jsapi-class.h" #include "cjs/jsapi-wrapper.h" #include "cjs/mem.h" #include #include #include #include /* We use guint8 for arguments; functions can't * have more than this. */ #define GJS_ARG_INDEX_INVALID G_MAXUINT8 typedef struct { GIFunctionInfo *info; GjsParamType *param_types; guint8 expected_js_argc; guint8 js_out_argc; GIFunctionInvoker invoker; } Function; extern struct JSClass gjs_function_class; /* Because we can't free the mmap'd data for a callback * while it's in use, this list keeps track of ones that * will be freed the next time we invoke a C function. */ static GSList *completed_trampolines = NULL; /* GjsCallbackTrampoline */ GJS_DEFINE_PRIV_FROM_JS(Function, gjs_function_class) void gjs_callback_trampoline_ref(GjsCallbackTrampoline *trampoline) { trampoline->ref_count++; } void gjs_callback_trampoline_unref(GjsCallbackTrampoline *trampoline) { /* Not MT-safe, like all the rest of GJS */ trampoline->ref_count--; if (trampoline->ref_count == 0) { g_closure_unref(trampoline->js_function); g_callable_info_free_closure(trampoline->info, trampoline->closure); g_base_info_unref( (GIBaseInfo*) trampoline->info); g_free (trampoline->param_types); g_slice_free(GjsCallbackTrampoline, trampoline); } } static void set_return_ffi_arg_from_giargument (GITypeInfo *ret_type, void *result, GIArgument *return_value) { switch (g_type_info_get_tag(ret_type)) { case GI_TYPE_TAG_VOID: g_assert_not_reached(); case GI_TYPE_TAG_INT8: *(ffi_sarg *) result = return_value->v_int8; break; case GI_TYPE_TAG_UINT8: *(ffi_arg *) result = return_value->v_uint8; break; case GI_TYPE_TAG_INT16: *(ffi_sarg *) result = return_value->v_int16; break; case GI_TYPE_TAG_UINT16: *(ffi_arg *) result = return_value->v_uint16; break; case GI_TYPE_TAG_INT32: *(ffi_sarg *) result = return_value->v_int32; break; case GI_TYPE_TAG_UINT32: case GI_TYPE_TAG_BOOLEAN: case GI_TYPE_TAG_UNICHAR: *(ffi_arg *) result = return_value->v_uint32; break; case GI_TYPE_TAG_INT64: *(ffi_sarg *) result = return_value->v_int64; break; case GI_TYPE_TAG_INTERFACE: { GIBaseInfo* interface_info; GIInfoType interface_type; interface_info = g_type_info_get_interface(ret_type); interface_type = g_base_info_get_type(interface_info); if (interface_type == GI_INFO_TYPE_ENUM || interface_type == GI_INFO_TYPE_FLAGS) *(ffi_sarg *) result = return_value->v_long; else *(ffi_arg *) result = (ffi_arg) return_value->v_pointer; g_base_info_unref(interface_info); } break; case GI_TYPE_TAG_UINT64: /* Other primitive and pointer types need to squeeze into 64-bit ffi_arg too */ case GI_TYPE_TAG_FLOAT: case GI_TYPE_TAG_DOUBLE: case GI_TYPE_TAG_GTYPE: case GI_TYPE_TAG_UTF8: case GI_TYPE_TAG_FILENAME: case GI_TYPE_TAG_ARRAY: case GI_TYPE_TAG_GLIST: case GI_TYPE_TAG_GSLIST: case GI_TYPE_TAG_GHASH: case GI_TYPE_TAG_ERROR: default: *(ffi_arg *) result = (ffi_arg) return_value->v_uint64; break; } } static void warn_about_illegal_js_callback(const GjsCallbackTrampoline *trampoline, const char *when, const char *reason) { g_critical("Attempting to run a JS callback %s. This is most likely caused " "by %s. Because it would crash the application, it has been " "blocked.", when, reason); if (trampoline->info) { const char *name = g_base_info_get_name(trampoline->info); g_critical("The offending callback was %s()%s.", name, trampoline->is_vfunc ? ", a vfunc" : ""); } gjs_dumpstack(); return; } /* This is our main entry point for ffi_closure callbacks. * ffi_prep_closure is doing pure magic and replaces the original * function call with this one which gives us the ffi arguments, * a place to store the return value and our use data. * In other words, everything we need to call the JS function and * getting the return value back. */ static void gjs_callback_closure(ffi_cif *cif, void *result, void **ffi_args, void *data) { JSContext *context; GjsCallbackTrampoline *trampoline; int i, n_args, n_jsargs, n_outargs, c_args_offset = 0; GITypeInfo ret_type; bool success = false; bool ret_type_is_void; auto args = reinterpret_cast(ffi_args); trampoline = (GjsCallbackTrampoline *) data; g_assert(trampoline); gjs_callback_trampoline_ref(trampoline); if (G_UNLIKELY(!gjs_closure_is_valid(trampoline->js_function))) { warn_about_illegal_js_callback(trampoline, "during shutdown", "destroying a Clutter actor or GTK widget with ::destroy signal " "connected, or using the destroy(), dispose(), or remove() vfuncs"); gjs_callback_trampoline_unref(trampoline); return; } context = gjs_closure_get_context(trampoline->js_function); if (G_UNLIKELY(_gjs_context_is_sweeping(context))) { warn_about_illegal_js_callback(trampoline, "during garbage collection", "destroying a Clutter actor or GTK widget with ::destroy signal " "connected, or using the destroy(), dispose(), or remove() vfuncs"); gjs_callback_trampoline_unref(trampoline); return; } auto gjs_cx = static_cast(JS_GetContextPrivate(context)); if (G_UNLIKELY (!_gjs_context_get_is_owner_thread(gjs_cx))) { warn_about_illegal_js_callback(trampoline, "on a different thread", "an API not intended to be used in JS"); gjs_callback_trampoline_unref(trampoline); return; } JS_BeginRequest(context); JSAutoCompartment ac(context, gjs_closure_get_callable(trampoline->js_function)); bool can_throw_gerror = g_callable_info_can_throw_gerror(trampoline->info); n_args = g_callable_info_get_n_args(trampoline->info); g_assert(n_args >= 0); JS::RootedObject this_object(context); if (trampoline->is_vfunc) { auto this_gobject = static_cast(args[0]->v_pointer); this_object = gjs_object_from_g_object(context, this_gobject); /* "this" is not included in the GI signature, but is in the C (and * FFI) signature */ c_args_offset = 1; } n_outargs = 0; JS::AutoValueVector jsargs(context); if (!jsargs.reserve(n_args)) g_error("Unable to reserve space for vector"); JS::RootedValue rval(context); for (i = 0, n_jsargs = 0; i < n_args; i++) { GIArgInfo arg_info; GITypeInfo type_info; GjsParamType param_type; g_callable_info_load_arg(trampoline->info, i, &arg_info); g_arg_info_load_type(&arg_info, &type_info); /* Skip void * arguments */ if (g_type_info_get_tag(&type_info) == GI_TYPE_TAG_VOID) continue; if (g_arg_info_get_direction(&arg_info) == GI_DIRECTION_OUT) { n_outargs++; continue; } if (g_arg_info_get_direction(&arg_info) == GI_DIRECTION_INOUT) n_outargs++; param_type = trampoline->param_types[i]; switch (param_type) { case PARAM_SKIPPED: continue; case PARAM_ARRAY: { gint array_length_pos = g_type_info_get_array_length(&type_info); GIArgInfo array_length_arg; GITypeInfo arg_type_info; JS::RootedValue length(context); g_callable_info_load_arg(trampoline->info, array_length_pos, &array_length_arg); g_arg_info_load_type(&array_length_arg, &arg_type_info); if (!gjs_value_from_g_argument(context, &length, &arg_type_info, args[array_length_pos + c_args_offset], true)) goto out; if (!jsargs.growBy(1)) g_error("Unable to grow vector"); if (!gjs_value_from_explicit_array(context, jsargs[n_jsargs++], &type_info, args[i + c_args_offset], length.toInt32())) goto out; break; } case PARAM_NORMAL: if (!jsargs.growBy(1)) g_error("Unable to grow vector"); if (!gjs_value_from_g_argument(context, jsargs[n_jsargs++], &type_info, args[i + c_args_offset], false)) goto out; break; case PARAM_CALLBACK: /* Callbacks that accept another callback as a parameter are not * supported, see gjs_callback_trampoline_new() */ default: g_assert_not_reached(); } } if (!gjs_closure_invoke(trampoline->js_function, this_object, jsargs, &rval, true)) goto out; g_callable_info_load_return_type(trampoline->info, &ret_type); ret_type_is_void = g_type_info_get_tag (&ret_type) == GI_TYPE_TAG_VOID; if (n_outargs == 0 && ret_type_is_void) { /* void return value, no out args, nothing to do */ } else if (n_outargs == 0) { GIArgument argument; GITransfer transfer; transfer = g_callable_info_get_caller_owns (trampoline->info); /* non-void return value, no out args. Should * be a single return value. */ if (!gjs_value_to_g_argument(context, rval, &ret_type, "callback", GJS_ARGUMENT_RETURN_VALUE, transfer, true, &argument)) goto out; set_return_ffi_arg_from_giargument(&ret_type, result, &argument); } else if (n_outargs == 1 && ret_type_is_void) { /* void return value, one out args. Should * be a single return value. */ for (i = 0; i < n_args; i++) { GIArgInfo arg_info; GITypeInfo type_info; g_callable_info_load_arg(trampoline->info, i, &arg_info); if (g_arg_info_get_direction(&arg_info) == GI_DIRECTION_IN) continue; g_arg_info_load_type(&arg_info, &type_info); if (!gjs_value_to_g_argument(context, rval, &type_info, "callback", GJS_ARGUMENT_ARGUMENT, GI_TRANSFER_NOTHING, true, *(GIArgument **)args[i + c_args_offset])) goto out; break; } } else { JS::RootedValue elem(context); JS::RootedObject out_array(context, rval.toObjectOrNull()); gsize elem_idx = 0; /* more than one of a return value or an out argument. * Should be an array of output values. */ if (!ret_type_is_void) { GIArgument argument; if (!JS_GetElement(context, out_array, elem_idx, &elem)) goto out; if (!gjs_value_to_g_argument(context, elem, &ret_type, "callback", GJS_ARGUMENT_ARGUMENT, GI_TRANSFER_NOTHING, true, &argument)) goto out; set_return_ffi_arg_from_giargument(&ret_type, result, &argument); elem_idx++; } for (i = 0; i < n_args; i++) { GIArgInfo arg_info; GITypeInfo type_info; g_callable_info_load_arg(trampoline->info, i, &arg_info); if (g_arg_info_get_direction(&arg_info) == GI_DIRECTION_IN) continue; g_arg_info_load_type(&arg_info, &type_info); if (!JS_GetElement(context, out_array, elem_idx, &elem)) goto out; if (!gjs_value_to_g_argument(context, elem, &type_info, "callback", GJS_ARGUMENT_ARGUMENT, GI_TRANSFER_NOTHING, true, *(GIArgument **)args[i + c_args_offset])) goto out; elem_idx++; } } success = true; out: if (!success) { if (!JS_IsExceptionPending(context)) { /* "Uncatchable" exception thrown, we have to exit. We may be in a * main loop, or maybe not, but there's no way to tell, so we have * to exit here instead of propagating the exception back to the * original calling JS code. */ auto gcx = static_cast(JS_GetContextPrivate(context)); uint8_t code; if (_gjs_context_should_exit(gcx, &code)) exit(code); /* Some other uncatchable exception, e.g. out of memory */ g_error("Function %s terminated with uncatchable exception", g_base_info_get_name(trampoline->info)); } /* Fill in the result with some hopefully neutral value */ g_callable_info_load_return_type(trampoline->info, &ret_type); gjs_g_argument_init_default (context, &ret_type, (GArgument *) result); /* If the callback has a GError** argument and invoking the closure * returned an error, try to make a GError from it */ if (can_throw_gerror && rval.isObject()) { JS::RootedObject exc_object(context, &rval.toObject()); GError *local_error = gjs_gerror_make_from_error(context, exc_object); if (local_error) { /* the GError ** pointer is the last argument, and is not * included in the n_args */ GIArgument *error_argument = args[n_args + c_args_offset]; auto gerror = static_cast(error_argument->v_pointer); g_propagate_error(gerror, local_error); JS_ClearPendingException(context); /* don't log */ } } else if (!rval.isUndefined()) { JS_SetPendingException(context, rval); } gjs_log_exception(context); } if (trampoline->scope == GI_SCOPE_TYPE_ASYNC) { completed_trampolines = g_slist_prepend(completed_trampolines, trampoline); } gjs_callback_trampoline_unref(trampoline); gjs_schedule_gc_if_needed(context); JS_EndRequest(context); } /* The global entry point for any invocations of GDestroyNotify; * look up the callback through the user_data and then free it. */ static void gjs_destroy_notify_callback(gpointer data) { GjsCallbackTrampoline *trampoline = (GjsCallbackTrampoline *) data; g_assert(trampoline); gjs_callback_trampoline_unref(trampoline); } GjsCallbackTrampoline* gjs_callback_trampoline_new(JSContext *context, JS::HandleValue function, GICallableInfo *callable_info, GIScopeType scope, JS::HandleObject scope_object, bool is_vfunc) { GjsCallbackTrampoline *trampoline; int n_args, i; if (function.isNull()) { return NULL; } g_assert(JS_TypeOfValue(context, function) == JSTYPE_FUNCTION); trampoline = g_slice_new(GjsCallbackTrampoline); new (trampoline) GjsCallbackTrampoline(); trampoline->ref_count = 1; trampoline->info = callable_info; g_base_info_ref((GIBaseInfo*)trampoline->info); /* The rule is: * - async and call callbacks are rooted * - callbacks in GObjects methods are traced from the object * (and same for vfuncs, which are associated with a GObject prototype) */ bool should_root = scope != GI_SCOPE_TYPE_NOTIFIED || !scope_object; trampoline->js_function = gjs_closure_new(context, &function.toObject(), g_base_info_get_name(callable_info), should_root); if (!should_root && scope_object) gjs_object_associate_closure(context, scope_object, trampoline->js_function); /* Analyze param types and directions, similarly to init_cached_function_data */ n_args = g_callable_info_get_n_args(trampoline->info); trampoline->param_types = g_new0(GjsParamType, n_args); for (i = 0; i < n_args; i++) { GIDirection direction; GIArgInfo arg_info; GITypeInfo type_info; GITypeTag type_tag; if (trampoline->param_types[i] == PARAM_SKIPPED) continue; g_callable_info_load_arg(trampoline->info, i, &arg_info); g_arg_info_load_type(&arg_info, &type_info); direction = g_arg_info_get_direction(&arg_info); type_tag = g_type_info_get_tag(&type_info); if (direction != GI_DIRECTION_IN) { /* INOUT and OUT arguments are handled differently. */ continue; } if (type_tag == GI_TYPE_TAG_INTERFACE) { GIBaseInfo* interface_info; GIInfoType interface_type; interface_info = g_type_info_get_interface(&type_info); interface_type = g_base_info_get_type(interface_info); if (interface_type == GI_INFO_TYPE_CALLBACK) { gjs_throw(context, "Callback accepts another callback as a parameter. This is not supported"); g_base_info_unref(interface_info); return NULL; } g_base_info_unref(interface_info); } else if (type_tag == GI_TYPE_TAG_ARRAY) { if (g_type_info_get_array_type(&type_info) == GI_ARRAY_TYPE_C) { int array_length_pos = g_type_info_get_array_length(&type_info); if (array_length_pos >= 0 && array_length_pos < n_args) { GIArgInfo length_arg_info; g_callable_info_load_arg(trampoline->info, array_length_pos, &length_arg_info); if (g_arg_info_get_direction(&length_arg_info) != direction) { gjs_throw(context, "Callback has an array with different-direction length arg, not supported"); return NULL; } trampoline->param_types[array_length_pos] = PARAM_SKIPPED; trampoline->param_types[i] = PARAM_ARRAY; } } } } trampoline->closure = g_callable_info_prepare_closure(callable_info, &trampoline->cif, gjs_callback_closure, trampoline); trampoline->scope = scope; trampoline->is_vfunc = is_vfunc; return trampoline; } /* an helper function to retrieve array lengths from a GArgument (letting the compiler generate good instructions in case of big endian machines) */ static unsigned long get_length_from_arg (GArgument *arg, GITypeTag tag) { if (tag == GI_TYPE_TAG_INT8) return arg->v_int8; if (tag == GI_TYPE_TAG_UINT8) return arg->v_uint8; if (tag == GI_TYPE_TAG_INT16) return arg->v_int16; if (tag == GI_TYPE_TAG_UINT16) return arg->v_uint16; if (tag == GI_TYPE_TAG_INT32) return arg->v_int32; if (tag == GI_TYPE_TAG_UINT32) return arg->v_uint32; if (tag == GI_TYPE_TAG_INT64) return arg->v_int64; if (tag == GI_TYPE_TAG_UINT64) return arg->v_uint64; g_assert_not_reached(); } static bool gjs_fill_method_instance(JSContext *context, JS::HandleObject obj, Function *function, GIArgument *out_arg, bool& is_gobject) { GIBaseInfo *container = g_base_info_get_container((GIBaseInfo *) function->info); GIInfoType type = g_base_info_get_type(container); GType gtype = g_registered_type_info_get_g_type ((GIRegisteredTypeInfo *)container); GITransfer transfer = g_callable_info_get_instance_ownership_transfer (function->info); is_gobject = false; if (type == GI_INFO_TYPE_STRUCT || type == GI_INFO_TYPE_BOXED) { /* GError must be special cased */ if (g_type_is_a(gtype, G_TYPE_ERROR)) { if (!gjs_typecheck_gerror(context, obj, true)) return false; out_arg->v_pointer = gjs_gerror_from_error(context, obj); if (transfer == GI_TRANSFER_EVERYTHING) out_arg->v_pointer = g_error_copy ((GError*) out_arg->v_pointer); } else if (type == GI_INFO_TYPE_STRUCT && g_struct_info_is_gtype_struct((GIStructInfo*) container)) { /* And so do GType structures */ GType actual_gtype; gpointer klass; actual_gtype = gjs_gtype_get_actual_gtype(context, obj); if (actual_gtype == G_TYPE_NONE) { gjs_throw(context, "Invalid GType class passed for instance parameter"); return false; } /* We use peek here to simplify reference counting (we just ignore transfer annotation, as GType classes are never really freed) We know that the GType class is referenced at least once when the JS constructor is initialized. */ if (g_type_is_a(actual_gtype, G_TYPE_INTERFACE)) klass = g_type_default_interface_peek(actual_gtype); else klass = g_type_class_peek(actual_gtype); out_arg->v_pointer = klass; } else { if (!gjs_typecheck_boxed(context, obj, container, gtype, true)) return false; out_arg->v_pointer = gjs_c_struct_from_boxed(context, obj); if (transfer == GI_TRANSFER_EVERYTHING) { if (gtype != G_TYPE_NONE) out_arg->v_pointer = g_boxed_copy (gtype, out_arg->v_pointer); else { gjs_throw (context, "Cannot transfer ownership of instance argument for non boxed structure"); return false; } } } } else if (type == GI_INFO_TYPE_UNION) { if (!gjs_typecheck_union(context, obj, container, gtype, true)) return false; out_arg->v_pointer = gjs_c_union_from_union(context, obj); if (transfer == GI_TRANSFER_EVERYTHING) out_arg->v_pointer = g_boxed_copy (gtype, out_arg->v_pointer); } else if (type == GI_INFO_TYPE_OBJECT || type == GI_INFO_TYPE_INTERFACE) { if (g_type_is_a(gtype, G_TYPE_OBJECT)) { if (!gjs_typecheck_object(context, obj, gtype, true)) return false; out_arg->v_pointer = gjs_g_object_from_object(context, obj); is_gobject = true; if (transfer == GI_TRANSFER_EVERYTHING) g_object_ref (out_arg->v_pointer); } else if (g_type_is_a(gtype, G_TYPE_PARAM)) { if (!gjs_typecheck_param(context, obj, G_TYPE_PARAM, true)) return false; out_arg->v_pointer = gjs_g_param_from_param(context, obj); if (transfer == GI_TRANSFER_EVERYTHING) g_param_spec_ref ((GParamSpec*) out_arg->v_pointer); } else if (G_TYPE_IS_INTERFACE(gtype)) { if (gjs_typecheck_is_object(context, obj, false)) { if (!gjs_typecheck_object(context, obj, gtype, true)) return false; out_arg->v_pointer = gjs_g_object_from_object(context, obj); is_gobject = true; if (transfer == GI_TRANSFER_EVERYTHING) g_object_ref (out_arg->v_pointer); } else { if (!gjs_typecheck_fundamental(context, obj, gtype, true)) return false; out_arg->v_pointer = gjs_g_fundamental_from_object(context, obj); if (transfer == GI_TRANSFER_EVERYTHING) gjs_fundamental_ref (context, out_arg->v_pointer); } } else if (G_TYPE_IS_INSTANTIATABLE(gtype)) { if (!gjs_typecheck_fundamental(context, obj, gtype, true)) return false; out_arg->v_pointer = gjs_g_fundamental_from_object(context, obj); if (transfer == GI_TRANSFER_EVERYTHING) gjs_fundamental_ref (context, out_arg->v_pointer); } else { gjs_throw_custom(context, JSProto_TypeError, nullptr, "%s.%s is not an object instance neither a fundamental instance of a supported type", g_base_info_get_namespace(container), g_base_info_get_name(container)); return false; } } else { g_assert_not_reached(); } return true; } /* Intended for error messages. Return value must be freed */ static char * format_function_name(Function *function, bool is_method) { auto baseinfo = static_cast(function->info); if (is_method) return g_strdup_printf("method %s.%s.%s", g_base_info_get_namespace(baseinfo), g_base_info_get_name(g_base_info_get_container(baseinfo)), g_base_info_get_name(baseinfo)); return g_strdup_printf("function %s.%s", g_base_info_get_namespace(baseinfo), g_base_info_get_name(baseinfo)); } static void complete_async_calls(void) { if (completed_trampolines) { for (GSList *iter = completed_trampolines; iter; iter = iter->next) { auto trampoline = static_cast(iter->data); gjs_callback_trampoline_unref(trampoline); } g_slist_free(completed_trampolines); completed_trampolines = nullptr; } } /* * This function can be called in 2 different ways. You can either use * it to create javascript objects by providing a @js_rval argument or * you can decide to keep the return values in #GArgument format by * providing a @r_value argument. */ static bool gjs_invoke_c_function(JSContext *context, Function *function, JS::HandleObject obj, /* "this" object */ const JS::HandleValueArray& args, mozilla::Maybe js_rval, GIArgument *r_value) { /* These first four are arrays which hold argument pointers. * @in_arg_cvalues: C values which are passed on input (in or inout) * @out_arg_cvalues: C values which are returned as arguments (out or inout) * @inout_original_arg_cvalues: For the special case of (inout) args, we need to * keep track of the original values we passed into the function, in case we * need to free it. * @ffi_arg_pointers: For passing data to FFI, we need to create another layer * of indirection; this array is a pointer to an element in in_arg_cvalues * or out_arg_cvalues. * @return_value: The actual return value of the C function, i.e. not an (out) param */ GArgument *in_arg_cvalues; GArgument *out_arg_cvalues; GArgument *inout_original_arg_cvalues; gpointer *ffi_arg_pointers; GIFFIReturnValue return_value; gpointer return_value_p; /* Will point inside the union return_value */ GArgument return_gargument; guint8 processed_c_args = 0; guint8 gi_argc, gi_arg_pos; guint8 c_argc, c_arg_pos; guint8 js_arg_pos; bool can_throw_gerror; bool did_throw_gerror = false; GError *local_error = NULL; bool failed, postinvoke_release_failed; bool is_method; bool is_object_method = false; GITypeInfo return_info; GITypeTag return_tag; JS::AutoValueVector return_values(context); guint8 next_rval = 0; /* index into return_values */ /* Because we can't free a closure while we're in it, we defer * freeing until the next time a C function is invoked. What * we should really do instead is queue it for a GC thread. */ complete_async_calls(); is_method = g_callable_info_is_method(function->info); can_throw_gerror = g_callable_info_can_throw_gerror(function->info); c_argc = function->invoker.cif.nargs; gi_argc = g_callable_info_get_n_args( (GICallableInfo*) function->info); /* @c_argc is the number of arguments that the underlying C * function takes. @gi_argc is the number of arguments the * GICallableInfo describes (which does not include "this" or * GError**). @function->expected_js_argc is the number of * arguments we expect the JS function to take (which does not * include PARAM_SKIPPED args). * * @args.length() is the number of arguments that were actually passed. */ if (args.length() > function->expected_js_argc) { GjsAutoChar name = format_function_name(function, is_method); JS_ReportWarningUTF8(context, "Too many arguments to %s: expected %d, " "got %" G_GSIZE_FORMAT, name.get(), function->expected_js_argc, args.length()); } else if (args.length() < function->expected_js_argc) { GjsAutoChar name = format_function_name(function, is_method); gjs_throw(context, "Too few arguments to %s: " "expected %d, got %" G_GSIZE_FORMAT, name.get(), function->expected_js_argc, args.length()); return false; } g_callable_info_load_return_type( (GICallableInfo*) function->info, &return_info); return_tag = g_type_info_get_tag(&return_info); in_arg_cvalues = g_newa(GArgument, c_argc); ffi_arg_pointers = g_newa(gpointer, c_argc); out_arg_cvalues = g_newa(GArgument, c_argc); inout_original_arg_cvalues = g_newa(GArgument, c_argc); failed = false; c_arg_pos = 0; /* index into in_arg_cvalues, etc */ js_arg_pos = 0; /* index into argv */ if (is_method) { if (!gjs_fill_method_instance(context, obj, function, &in_arg_cvalues[0], is_object_method)) return false; ffi_arg_pointers[0] = &in_arg_cvalues[0]; ++c_arg_pos; } processed_c_args = c_arg_pos; for (gi_arg_pos = 0; gi_arg_pos < gi_argc; gi_arg_pos++, c_arg_pos++) { GIDirection direction; GIArgInfo arg_info; bool arg_removed = false; /* gjs_debug(GJS_DEBUG_GFUNCTION, "gi_arg_pos: %d c_arg_pos: %d js_arg_pos: %d", gi_arg_pos, c_arg_pos, js_arg_pos); */ g_callable_info_load_arg( (GICallableInfo*) function->info, gi_arg_pos, &arg_info); direction = g_arg_info_get_direction(&arg_info); g_assert_cmpuint(c_arg_pos, <, c_argc); ffi_arg_pointers[c_arg_pos] = &in_arg_cvalues[c_arg_pos]; if (direction == GI_DIRECTION_OUT) { if (g_arg_info_is_caller_allocates(&arg_info)) { GITypeTag type_tag; GITypeInfo ainfo; g_arg_info_load_type(&arg_info, &ainfo); type_tag = g_type_info_get_tag(&ainfo); if (type_tag == GI_TYPE_TAG_INTERFACE) { GIBaseInfo* interface_info; GIInfoType interface_type; gsize size; interface_info = g_type_info_get_interface(&ainfo); g_assert(interface_info != NULL); interface_type = g_base_info_get_type(interface_info); if (interface_type == GI_INFO_TYPE_STRUCT) { size = g_struct_info_get_size((GIStructInfo*)interface_info); } else if (interface_type == GI_INFO_TYPE_UNION) { size = g_union_info_get_size((GIUnionInfo*)interface_info); } else { failed = true; } g_base_info_unref((GIBaseInfo*)interface_info); if (!failed) { in_arg_cvalues[c_arg_pos].v_pointer = g_slice_alloc0(size); out_arg_cvalues[c_arg_pos].v_pointer = in_arg_cvalues[c_arg_pos].v_pointer; } } else { failed = true; } if (failed) gjs_throw(context, "Unsupported type %s for (out caller-allocates)", g_type_tag_to_string(type_tag)); } else { out_arg_cvalues[c_arg_pos].v_pointer = NULL; in_arg_cvalues[c_arg_pos].v_pointer = &out_arg_cvalues[c_arg_pos]; } } else { GArgument *in_value; GITypeInfo ainfo; GjsParamType param_type; g_arg_info_load_type(&arg_info, &ainfo); in_value = &in_arg_cvalues[c_arg_pos]; param_type = function->param_types[gi_arg_pos]; switch (param_type) { case PARAM_CALLBACK: { GICallableInfo *callable_info; GIScopeType scope = g_arg_info_get_scope(&arg_info); GjsCallbackTrampoline *trampoline; ffi_closure *closure; JS::HandleValue current_arg = args[js_arg_pos]; if (current_arg.isNull() && g_arg_info_may_be_null(&arg_info)) { closure = NULL; trampoline = NULL; } else { if (!(JS_TypeOfValue(context, current_arg) == JSTYPE_FUNCTION)) { gjs_throw(context, "Error invoking %s.%s: Expected function for callback argument %s, got %s", g_base_info_get_namespace( (GIBaseInfo*) function->info), g_base_info_get_name( (GIBaseInfo*) function->info), g_base_info_get_name( (GIBaseInfo*) &arg_info), JS::InformalValueTypeName(current_arg)); failed = true; break; } callable_info = (GICallableInfo*) g_type_info_get_interface(&ainfo); trampoline = gjs_callback_trampoline_new(context, current_arg, callable_info, scope, is_object_method ? obj : nullptr, false); closure = trampoline->closure; g_base_info_unref(callable_info); } gint destroy_pos = g_arg_info_get_destroy(&arg_info); gint closure_pos = g_arg_info_get_closure(&arg_info); if (destroy_pos >= 0) { gint c_pos = is_method ? destroy_pos + 1 : destroy_pos; g_assert (function->param_types[destroy_pos] == PARAM_SKIPPED); in_arg_cvalues[c_pos].v_pointer = trampoline ? (gpointer) gjs_destroy_notify_callback : NULL; } if (closure_pos >= 0) { gint c_pos = is_method ? closure_pos + 1 : closure_pos; g_assert (function->param_types[closure_pos] == PARAM_SKIPPED); in_arg_cvalues[c_pos].v_pointer = trampoline; } if (trampoline && scope != GI_SCOPE_TYPE_CALL) { /* Add an extra reference that will be cleared when collecting async calls, or when GDestroyNotify is called */ gjs_callback_trampoline_ref(trampoline); } in_value->v_pointer = closure; break; } case PARAM_SKIPPED: arg_removed = true; break; case PARAM_ARRAY: { GIArgInfo array_length_arg; gint array_length_pos = g_type_info_get_array_length(&ainfo); gsize length; if (!gjs_value_to_explicit_array(context, args[js_arg_pos], &arg_info, in_value, &length)) { failed = true; break; } g_callable_info_load_arg(function->info, array_length_pos, &array_length_arg); array_length_pos += is_method ? 1 : 0; JS::RootedValue v_length(context, JS::Int32Value(length)); if (!gjs_value_to_arg(context, v_length, &array_length_arg, in_arg_cvalues + array_length_pos)) { failed = true; break; } /* Also handle the INOUT for the length here */ if (direction == GI_DIRECTION_INOUT) { if (in_value->v_pointer == NULL) { /* Special case where we were given JS null to * also pass null for length, and not a * pointer to an integer that derefs to 0. */ in_arg_cvalues[array_length_pos].v_pointer = NULL; out_arg_cvalues[array_length_pos].v_pointer = NULL; inout_original_arg_cvalues[array_length_pos].v_pointer = NULL; } else { out_arg_cvalues[array_length_pos] = inout_original_arg_cvalues[array_length_pos] = *(in_arg_cvalues + array_length_pos); in_arg_cvalues[array_length_pos].v_pointer = &out_arg_cvalues[array_length_pos]; } } break; } case PARAM_NORMAL: { /* Ok, now just convert argument normally */ g_assert_cmpuint(js_arg_pos, <, args.length()); if (!gjs_value_to_arg(context, args[js_arg_pos], &arg_info, in_value)) failed = true; break; } default: ; } if (direction == GI_DIRECTION_INOUT && !arg_removed && !failed) { out_arg_cvalues[c_arg_pos] = inout_original_arg_cvalues[c_arg_pos] = in_arg_cvalues[c_arg_pos]; in_arg_cvalues[c_arg_pos].v_pointer = &out_arg_cvalues[c_arg_pos]; } if (failed) { /* Exit from the loop */ break; } if (!failed && !arg_removed) ++js_arg_pos; } if (failed) break; processed_c_args++; } /* Did argument conversion fail? In that case, skip invocation and jump to release * processing. */ if (failed) { did_throw_gerror = false; goto release; } if (can_throw_gerror) { g_assert_cmpuint(c_arg_pos, <, c_argc); in_arg_cvalues[c_arg_pos].v_pointer = &local_error; ffi_arg_pointers[c_arg_pos] = &(in_arg_cvalues[c_arg_pos]); c_arg_pos++; /* don't update processed_c_args as we deal with local_error * separately */ } g_assert_cmpuint(c_arg_pos, ==, c_argc); g_assert_cmpuint(gi_arg_pos, ==, gi_argc); /* See comment for GjsFFIReturnValue above */ if (return_tag == GI_TYPE_TAG_FLOAT) return_value_p = &return_value.v_float; else if (return_tag == GI_TYPE_TAG_DOUBLE) return_value_p = &return_value.v_double; else if (return_tag == GI_TYPE_TAG_INT64 || return_tag == GI_TYPE_TAG_UINT64) return_value_p = &return_value.v_uint64; else return_value_p = &return_value.v_long; ffi_call(&(function->invoker.cif), FFI_FN(function->invoker.native_address), return_value_p, ffi_arg_pointers); /* Return value and out arguments are valid only if invocation doesn't * return error. In arguments need to be released always. */ if (can_throw_gerror) { did_throw_gerror = local_error != NULL; } else { did_throw_gerror = false; } if (js_rval) js_rval.ref().setUndefined(); /* Only process return values if the function didn't throw */ if (function->js_out_argc > 0 && !did_throw_gerror) { for (size_t i = 0; i < function->js_out_argc; i++) if (!return_values.append(JS::UndefinedValue())) g_error("Unable to append to vector"); if (return_tag != GI_TYPE_TAG_VOID) { GITransfer transfer = g_callable_info_get_caller_owns((GICallableInfo*) function->info); bool arg_failed = false; gint array_length_pos; g_assert_cmpuint(next_rval, <, function->js_out_argc); gi_type_info_extract_ffi_return_value(&return_info, &return_value, &return_gargument); array_length_pos = g_type_info_get_array_length(&return_info); if (array_length_pos >= 0) { GIArgInfo array_length_arg; GITypeInfo arg_type_info; JS::RootedValue length(context); g_callable_info_load_arg(function->info, array_length_pos, &array_length_arg); g_arg_info_load_type(&array_length_arg, &arg_type_info); array_length_pos += is_method ? 1 : 0; arg_failed = !gjs_value_from_g_argument(context, &length, &arg_type_info, &out_arg_cvalues[array_length_pos], true); if (!arg_failed && js_rval) { arg_failed = !gjs_value_from_explicit_array(context, return_values[next_rval], &return_info, &return_gargument, length.toInt32()); } if (!arg_failed && !r_value && !gjs_g_argument_release_out_array(context, transfer, &return_info, length.toInt32(), &return_gargument)) failed = true; } else { if (js_rval) arg_failed = !gjs_value_from_g_argument(context, return_values[next_rval], &return_info, &return_gargument, true); /* Free GArgument, the JS::Value should have ref'd or copied it */ if (!arg_failed && !r_value && !gjs_g_argument_release(context, transfer, &return_info, &return_gargument)) failed = true; } if (arg_failed) failed = true; ++next_rval; } } release: /* We walk over all args, release in args (if allocated) and convert * all out args to JS */ c_arg_pos = is_method ? 1 : 0; postinvoke_release_failed = false; for (gi_arg_pos = 0; gi_arg_pos < gi_argc && c_arg_pos < processed_c_args; gi_arg_pos++, c_arg_pos++) { GIDirection direction; GIArgInfo arg_info; GITypeInfo arg_type_info; GjsParamType param_type; g_callable_info_load_arg( (GICallableInfo*) function->info, gi_arg_pos, &arg_info); direction = g_arg_info_get_direction(&arg_info); g_arg_info_load_type(&arg_info, &arg_type_info); param_type = function->param_types[gi_arg_pos]; if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) { GArgument *arg; GITransfer transfer; if (direction == GI_DIRECTION_IN) { arg = &in_arg_cvalues[c_arg_pos]; transfer = g_arg_info_get_ownership_transfer(&arg_info); } else { arg = &inout_original_arg_cvalues[c_arg_pos]; /* For inout, transfer refers to what we get back from the function; for * the temporary C value we allocated, clearly we're responsible for * freeing it. */ transfer = GI_TRANSFER_NOTHING; } if (param_type == PARAM_CALLBACK) { ffi_closure *closure = (ffi_closure *) arg->v_pointer; if (closure) { GjsCallbackTrampoline *trampoline = (GjsCallbackTrampoline *) closure->user_data; /* CallbackTrampolines are refcounted because for notified/async closures it is possible to destroy it while in call, and therefore we cannot check its scope at this point */ gjs_callback_trampoline_unref(trampoline); arg->v_pointer = NULL; } } else if (param_type == PARAM_ARRAY) { gsize length; GIArgInfo array_length_arg; GITypeInfo array_length_type; gint array_length_pos = g_type_info_get_array_length(&arg_type_info); g_assert(array_length_pos >= 0); g_callable_info_load_arg(function->info, array_length_pos, &array_length_arg); g_arg_info_load_type(&array_length_arg, &array_length_type); array_length_pos += is_method ? 1 : 0; length = get_length_from_arg(in_arg_cvalues + array_length_pos, g_type_info_get_tag(&array_length_type)); if (!gjs_g_argument_release_in_array(context, transfer, &arg_type_info, length, arg)) { postinvoke_release_failed = true; } } else if (param_type == PARAM_NORMAL) { if (!gjs_g_argument_release_in_arg(context, transfer, &arg_type_info, arg)) { postinvoke_release_failed = true; } } } /* Don't free out arguments if function threw an exception or we failed * earlier - note "postinvoke_release_failed" is separate from "failed". We * sync them up after this loop. */ if (did_throw_gerror || failed) continue; if ((direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) && param_type != PARAM_SKIPPED) { GArgument *arg; bool arg_failed = false; gint array_length_pos; JS::RootedValue array_length(context, JS::Int32Value(0)); GITransfer transfer; g_assert(next_rval < function->js_out_argc); arg = &out_arg_cvalues[c_arg_pos]; array_length_pos = g_type_info_get_array_length(&arg_type_info); if (js_rval) { if (array_length_pos >= 0) { GIArgInfo array_length_arg; GITypeInfo array_length_type_info; g_callable_info_load_arg(function->info, array_length_pos, &array_length_arg); g_arg_info_load_type(&array_length_arg, &array_length_type_info); array_length_pos += is_method ? 1 : 0; arg_failed = !gjs_value_from_g_argument(context, &array_length, &array_length_type_info, &out_arg_cvalues[array_length_pos], true); if (!arg_failed) { arg_failed = !gjs_value_from_explicit_array(context, return_values[next_rval], &arg_type_info, arg, array_length.toInt32()); } } else { arg_failed = !gjs_value_from_g_argument(context, return_values[next_rval], &arg_type_info, arg, true); } } if (arg_failed) postinvoke_release_failed = true; /* Free GArgument, the JS::Value should have ref'd or copied it */ transfer = g_arg_info_get_ownership_transfer(&arg_info); if (!arg_failed) { if (array_length_pos >= 0) { gjs_g_argument_release_out_array(context, transfer, &arg_type_info, array_length.toInt32(), arg); } else { gjs_g_argument_release(context, transfer, &arg_type_info, arg); } } /* For caller-allocates, what happens here is we allocate * a structure above, then gjs_value_from_g_argument calls * g_boxed_copy on it, and takes ownership of that. So * here we release the memory allocated above. It would be * better to special case this and directly hand JS the boxed * object and tell gjs_boxed it owns the memory, but for now * this works OK. We could also alloca() the structure instead * of slice allocating. */ if (g_arg_info_is_caller_allocates(&arg_info)) { GITypeTag type_tag; GIBaseInfo* interface_info; GIInfoType interface_type; gsize size; type_tag = g_type_info_get_tag(&arg_type_info); g_assert(type_tag == GI_TYPE_TAG_INTERFACE); interface_info = g_type_info_get_interface(&arg_type_info); interface_type = g_base_info_get_type(interface_info); if (interface_type == GI_INFO_TYPE_STRUCT) { size = g_struct_info_get_size((GIStructInfo*)interface_info); } else if (interface_type == GI_INFO_TYPE_UNION) { size = g_union_info_get_size((GIUnionInfo*)interface_info); } else { g_assert_not_reached(); } g_slice_free1(size, out_arg_cvalues[c_arg_pos].v_pointer); g_base_info_unref((GIBaseInfo*)interface_info); } ++next_rval; } } if (postinvoke_release_failed) failed = true; g_assert(failed || did_throw_gerror || next_rval == (guint8)function->js_out_argc); g_assert_cmpuint(c_arg_pos, ==, processed_c_args); if (function->js_out_argc > 0 && (!failed && !did_throw_gerror)) { /* if we have 1 return value or out arg, return that item * on its own, otherwise return a JavaScript array with * [return value, out arg 1, out arg 2, ...] */ if (js_rval) { if (function->js_out_argc == 1) { js_rval.ref().set(return_values[0]); } else { JSObject *array; array = JS_NewArrayObject(context, return_values); if (array == NULL) { failed = true; } else { js_rval.ref().setObject(*array); } } } if (r_value) { *r_value = return_gargument; } } if (!failed && did_throw_gerror) { gjs_throw_g_error(context, local_error); return false; } else if (failed) { return false; } else { return true; } } static bool function_call(JSContext *context, unsigned js_argc, JS::Value *vp) { GJS_GET_THIS(context, js_argc, vp, js_argv, object); JS::RootedObject callee(context, &js_argv.callee()); bool success; Function *priv; JS::RootedValue retval(context); priv = priv_from_js(context, callee); gjs_debug_marshal(GJS_DEBUG_GFUNCTION, "Call callee %p priv %p this obj %p", callee.get(), priv, object.get()); if (priv == NULL) return true; /* we are the prototype, or have the wrong class */ success = gjs_invoke_c_function(context, priv, object, js_argv, mozilla::Some(&retval), NULL); if (success) js_argv.rval().set(retval); return success; } GJS_NATIVE_CONSTRUCTOR_DEFINE_ABSTRACT(function) /* Does not actually free storage for structure, just * reverses init_cached_function_data */ static void uninit_cached_function_data (Function *function) { if (function->info) g_base_info_unref( (GIBaseInfo*) function->info); if (function->param_types) g_free(function->param_types); g_function_invoker_destroy(&function->invoker); } static void function_finalize(JSFreeOp *fop, JSObject *obj) { Function *priv; priv = (Function *) JS_GetPrivate(obj); gjs_debug_lifecycle(GJS_DEBUG_GFUNCTION, "finalize, obj %p priv %p", obj, priv); if (priv == NULL) return; /* we are the prototype, not a real instance, so constructor never called */ uninit_cached_function_data(priv); GJS_DEC_COUNTER(function); g_slice_free(Function, priv); } static bool get_num_arguments (JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_PRIV(context, argc, vp, rec, to, Function, priv); int n_args, n_jsargs, i; if (priv == NULL) return false; n_args = g_callable_info_get_n_args(priv->info); n_jsargs = 0; for (i = 0; i < n_args; i++) { GIArgInfo arg_info; if (priv->param_types[i] == PARAM_SKIPPED) continue; g_callable_info_load_arg(priv->info, i, &arg_info); if (g_arg_info_get_direction(&arg_info) == GI_DIRECTION_OUT) continue; n_jsargs++; } rec.rval().setInt32(n_jsargs); return true; } static bool function_to_string (JSContext *context, guint argc, JS::Value *vp) { GJS_GET_PRIV(context, argc, vp, rec, to, Function, priv); gchar *string; bool free; bool ret = false; int i, n_args, n_jsargs; GString *arg_names_str; gchar *arg_names; if (priv == NULL) { string = (gchar *) "function () {\n}"; free = false; goto out; } free = true; n_args = g_callable_info_get_n_args(priv->info); n_jsargs = 0; arg_names_str = g_string_new(""); for (i = 0; i < n_args; i++) { GIArgInfo arg_info; if (priv->param_types[i] == PARAM_SKIPPED) continue; g_callable_info_load_arg(priv->info, i, &arg_info); if (g_arg_info_get_direction(&arg_info) == GI_DIRECTION_OUT) continue; if (n_jsargs > 0) g_string_append(arg_names_str, ", "); n_jsargs++; g_string_append(arg_names_str, g_base_info_get_name(&arg_info)); } arg_names = g_string_free(arg_names_str, false); if (g_base_info_get_type(priv->info) == GI_INFO_TYPE_FUNCTION) { string = g_strdup_printf("function %s(%s) {\n\t/* proxy for native symbol %s(); */\n}", g_base_info_get_name ((GIBaseInfo *) priv->info), arg_names, g_function_info_get_symbol ((GIFunctionInfo *) priv->info)); } else { string = g_strdup_printf("function %s(%s) {\n\t/* proxy for native symbol */\n}", g_base_info_get_name ((GIBaseInfo *) priv->info), arg_names); } g_free(arg_names); out: if (gjs_string_from_utf8(context, string, rec.rval())) ret = true; if (free) g_free(string); return ret; } /* The bizarre thing about this vtable is that it applies to both * instances of the object, and to the prototype that instances of the * class have. */ static const struct JSClassOps gjs_function_class_ops = { NULL, /* addProperty */ NULL, /* deleteProperty */ NULL, /* getProperty */ NULL, /* setProperty */ NULL, /* enumerate */ NULL, /* resolve */ nullptr, /* mayResolve */ function_finalize, function_call }; struct JSClass gjs_function_class = { "GIRepositoryFunction", /* means "new GIRepositoryFunction()" works */ JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE, &gjs_function_class_ops }; static JSPropertySpec gjs_function_proto_props[] = { JS_PSG("length", get_num_arguments, JSPROP_PERMANENT), JS_PS_END }; /* The original Function.prototype.toString complains when given a GIRepository function as an argument */ static JSFunctionSpec gjs_function_proto_funcs[] = { JS_FN("toString", function_to_string, 0, 0), JS_FS_END }; static JSFunctionSpec *gjs_function_static_funcs = nullptr; static bool init_cached_function_data (JSContext *context, Function *function, GType gtype, GICallableInfo *info) { guint8 i, n_args; int array_length_pos; GError *error = NULL; GITypeInfo return_type; GIInfoType info_type; info_type = g_base_info_get_type((GIBaseInfo *)info); if (info_type == GI_INFO_TYPE_FUNCTION) { if (!g_function_info_prep_invoker((GIFunctionInfo *)info, &(function->invoker), &error)) { gjs_throw_g_error(context, error); return false; } } else if (info_type == GI_INFO_TYPE_VFUNC) { gpointer addr; addr = g_vfunc_info_get_address((GIVFuncInfo *)info, gtype, &error); if (error != NULL) { if (error->code != G_INVOKE_ERROR_SYMBOL_NOT_FOUND) gjs_throw_g_error(context, error); g_clear_error(&error); return false; } if (!g_function_invoker_new_for_address(addr, info, &(function->invoker), &error)) { gjs_throw_g_error(context, error); return false; } } g_callable_info_load_return_type((GICallableInfo*)info, &return_type); if (g_type_info_get_tag(&return_type) != GI_TYPE_TAG_VOID) function->js_out_argc += 1; n_args = g_callable_info_get_n_args((GICallableInfo*) info); function->param_types = g_new0(GjsParamType, n_args); array_length_pos = g_type_info_get_array_length(&return_type); if (array_length_pos >= 0 && array_length_pos < n_args) function->param_types[array_length_pos] = PARAM_SKIPPED; for (i = 0; i < n_args; i++) { GIDirection direction; GIArgInfo arg_info; GITypeInfo type_info; int destroy = -1; int closure = -1; GITypeTag type_tag; if (function->param_types[i] == PARAM_SKIPPED) continue; g_callable_info_load_arg((GICallableInfo*) info, i, &arg_info); g_arg_info_load_type(&arg_info, &type_info); direction = g_arg_info_get_direction(&arg_info); type_tag = g_type_info_get_tag(&type_info); if (type_tag == GI_TYPE_TAG_INTERFACE) { GIBaseInfo* interface_info; GIInfoType interface_type; interface_info = g_type_info_get_interface(&type_info); interface_type = g_base_info_get_type(interface_info); if (interface_type == GI_INFO_TYPE_CALLBACK) { if (strcmp(g_base_info_get_name(interface_info), "DestroyNotify") == 0 && strcmp(g_base_info_get_namespace(interface_info), "GLib") == 0) { /* Skip GDestroyNotify if they appear before the respective callback */ function->param_types[i] = PARAM_SKIPPED; } else { function->param_types[i] = PARAM_CALLBACK; function->expected_js_argc += 1; destroy = g_arg_info_get_destroy(&arg_info); closure = g_arg_info_get_closure(&arg_info); if (destroy >= 0 && destroy < n_args) function->param_types[destroy] = PARAM_SKIPPED; if (closure >= 0 && closure < n_args) function->param_types[closure] = PARAM_SKIPPED; if (destroy >= 0 && closure < 0) { gjs_throw(context, "Function %s.%s has a GDestroyNotify but no user_data, not supported", g_base_info_get_namespace( (GIBaseInfo*) info), g_base_info_get_name( (GIBaseInfo*) info)); g_base_info_unref(interface_info); return false; } } } g_base_info_unref(interface_info); } else if (type_tag == GI_TYPE_TAG_ARRAY) { if (g_type_info_get_array_type(&type_info) == GI_ARRAY_TYPE_C) { array_length_pos = g_type_info_get_array_length(&type_info); if (array_length_pos >= 0 && array_length_pos < n_args) { GIArgInfo length_arg_info; g_callable_info_load_arg((GICallableInfo*) info, array_length_pos, &length_arg_info); if (g_arg_info_get_direction(&length_arg_info) != direction) { gjs_throw(context, "Function %s.%s has an array with different-direction length arg, not supported", g_base_info_get_namespace( (GIBaseInfo*) info), g_base_info_get_name( (GIBaseInfo*) info)); return false; } function->param_types[array_length_pos] = PARAM_SKIPPED; function->param_types[i] = PARAM_ARRAY; if (array_length_pos < i) { /* we already collected array_length_pos, remove it */ if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) function->expected_js_argc -= 1; if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) function->js_out_argc -= 1; } } } } if (function->param_types[i] == PARAM_NORMAL || function->param_types[i] == PARAM_ARRAY) { if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) function->expected_js_argc += 1; if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) function->js_out_argc += 1; } } function->info = info; g_base_info_ref((GIBaseInfo*) function->info); return true; } static inline JSObject * gjs_builtin_function_get_proto(JSContext *cx) { JS::RootedObject global(cx, gjs_get_import_global(cx)); return JS_GetFunctionPrototype(cx, global); } GJS_DEFINE_PROTO_FUNCS_WITH_PARENT(function, builtin_function) static JSObject* function_new(JSContext *context, GType gtype, GICallableInfo *info) { Function *priv; JS::RootedObject proto(context); if (!gjs_function_define_proto(context, nullptr, &proto)) return nullptr; JS::RootedObject function(context, JS_NewObjectWithGivenProto(context, &gjs_function_class, proto)); if (!function) { gjs_debug(GJS_DEBUG_GFUNCTION, "Failed to construct function"); return NULL; } priv = g_slice_new0(Function); GJS_INC_COUNTER(function); g_assert(priv_from_js(context, function) == NULL); JS_SetPrivate(function, priv); gjs_debug_lifecycle(GJS_DEBUG_GFUNCTION, "function constructor, obj %p priv %p", function.get(), priv); if (!init_cached_function_data(context, priv, gtype, (GICallableInfo *)info)) return NULL; return function; } JSObject* gjs_define_function(JSContext *context, JS::HandleObject in_object, GType gtype, GICallableInfo *info) { GIInfoType info_type; gchar *name; bool free_name; info_type = g_base_info_get_type((GIBaseInfo *)info); JSAutoRequest ar(context); JS::RootedObject function(context, function_new(context, gtype, info)); if (!function) return NULL; if (info_type == GI_INFO_TYPE_FUNCTION) { name = (gchar *) g_base_info_get_name((GIBaseInfo*) info); free_name = false; } else if (info_type == GI_INFO_TYPE_VFUNC) { name = g_strdup_printf("vfunc_%s", g_base_info_get_name((GIBaseInfo*) info)); free_name = true; } else { g_assert_not_reached (); } if (!JS_DefineProperty(context, in_object, name, function, GJS_MODULE_PROP_FLAGS)) { gjs_debug(GJS_DEBUG_GFUNCTION, "Failed to define function"); function = NULL; } if (free_name) g_free(name); return function; } bool gjs_invoke_c_function_uncached(JSContext *context, GIFunctionInfo *info, JS::HandleObject obj, const JS::HandleValueArray& args, JS::MutableHandleValue rval) { Function function; bool result; memset (&function, 0, sizeof (Function)); if (!init_cached_function_data (context, &function, 0, info)) return false; result = gjs_invoke_c_function(context, &function, obj, args, mozilla::Some(rval), NULL); uninit_cached_function_data (&function); return result; } bool gjs_invoke_constructor_from_c(JSContext *context, JS::HandleObject constructor, JS::HandleObject obj, const JS::HandleValueArray& args, GIArgument *rvalue) { Function *priv; priv = priv_from_js(context, constructor); mozilla::Maybe m_jsrval; return gjs_invoke_c_function(context, priv, obj, args, m_jsrval, rvalue); } cjs-4.4.0/gi/function.h000066400000000000000000000064021356375132300147050ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_FUNCTION_H__ #define __GJS_FUNCTION_H__ #include #include #include "cjs/jsapi-util.h" #include "cjs/jsapi-util-root.h" #include #include G_BEGIN_DECLS typedef enum { PARAM_NORMAL, PARAM_SKIPPED, PARAM_ARRAY, PARAM_CALLBACK } GjsParamType; struct GjsCallbackTrampoline { gint ref_count; GICallableInfo *info; GClosure *js_function; ffi_cif cif; ffi_closure *closure; GIScopeType scope; bool is_vfunc; GjsParamType *param_types; }; GjsCallbackTrampoline* gjs_callback_trampoline_new(JSContext *context, JS::HandleValue function, GICallableInfo *callable_info, GIScopeType scope, JS::HandleObject scope_object, bool is_vfunc); void gjs_callback_trampoline_unref(GjsCallbackTrampoline *trampoline); void gjs_callback_trampoline_ref(GjsCallbackTrampoline *trampoline); JSObject *gjs_define_function(JSContext *context, JS::HandleObject in_object, GType gtype, GICallableInfo *info); bool gjs_invoke_c_function_uncached(JSContext *context, GIFunctionInfo *info, JS::HandleObject obj, const JS::HandleValueArray& args, JS::MutableHandleValue rval); bool gjs_invoke_constructor_from_c(JSContext *context, JS::HandleObject constructor, JS::HandleObject obj, const JS::HandleValueArray& args, GIArgument *rvalue); G_END_DECLS #endif /* __GJS_FUNCTION_H__ */ cjs-4.4.0/gi/fundamental.cpp000066400000000000000000000746541356375132300157270ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2013 Intel Corporation * Copyright (c) 2008-2010 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "fundamental.h" #include "arg.h" #include "object.h" #include "boxed.h" #include "function.h" #include "gtype.h" #include "proxyutils.h" #include "repo.h" #include "cjs/jsapi-class.h" #include "cjs/jsapi-wrapper.h" #include "cjs/mem.h" #include #include #include /* * Structure allocated for prototypes. */ struct Fundamental { /* instance info */ void *gfundamental; Fundamental *prototype; /* NULL if prototype */ /* prototype info */ GIObjectInfo *info; GType gtype; GIObjectInfoRefFunction ref_function; GIObjectInfoUnrefFunction unref_function; GIObjectInfoGetValueFunction get_value_function; GIObjectInfoSetValueFunction set_value_function; JS::Heap constructor_name; GICallableInfo *constructor_info; }; /* * Structure allocated for instances. */ typedef struct { void *gfundamental; Fundamental *prototype; } FundamentalInstance; extern struct JSClass gjs_fundamental_instance_class; GJS_DEFINE_PRIV_FROM_JS(FundamentalInstance, gjs_fundamental_instance_class) static GQuark gjs_fundamental_table_quark (void) { static GQuark val = 0; if (!val) val = g_quark_from_static_string("gjs::fundamental-table"); return val; } static GHashTable * _ensure_mapping_table(GjsContext *context) { GHashTable *table = (GHashTable *) g_object_get_qdata ((GObject *) context, gjs_fundamental_table_quark()); if (G_UNLIKELY(table == NULL)) { table = g_hash_table_new(NULL, NULL); g_object_set_qdata_full((GObject *) context, gjs_fundamental_table_quark(), table, (GDestroyNotify) g_hash_table_unref); } return table; } static void _fundamental_add_object(void *native_object, JSObject *js_object) { GHashTable *table = _ensure_mapping_table(gjs_context_get_current()); g_hash_table_insert(table, native_object, js_object); } static void _fundamental_remove_object(void *native_object) { GHashTable *table = _ensure_mapping_table(gjs_context_get_current()); g_hash_table_remove(table, native_object); } static JSObject * _fundamental_lookup_object(void *native_object) { GHashTable *table = _ensure_mapping_table(gjs_context_get_current()); return (JSObject *) g_hash_table_lookup(table, native_object); } /**/ static inline bool fundamental_is_prototype(Fundamental *priv) { return (priv->prototype == nullptr); } static inline bool fundamental_is_prototype(FundamentalInstance *priv) { return (priv->prototype == nullptr); } static inline Fundamental * proto_priv_from_js(JSContext *context, JS::HandleObject obj) { JS::RootedObject proto(context); JS_GetPrototype(context, obj, &proto); return (Fundamental*) priv_from_js(context, proto); } static FundamentalInstance * init_fundamental_instance(JSContext *context, JS::HandleObject object) { Fundamental *proto_priv; FundamentalInstance *priv; JS_BeginRequest(context); priv = g_slice_new0(FundamentalInstance); GJS_INC_COUNTER(fundamental); g_assert(priv_from_js(context, object) == NULL); JS_SetPrivate(object, priv); gjs_debug_lifecycle(GJS_DEBUG_GFUNDAMENTAL, "fundamental instance constructor, obj %p priv %p", object.get(), priv); proto_priv = proto_priv_from_js(context, object); g_assert(proto_priv != NULL); priv->prototype = proto_priv; JS_EndRequest(context); return priv; } static void associate_js_instance_to_fundamental(JSContext *context, JS::HandleObject object, void *gfundamental, bool owned_ref) { FundamentalInstance *priv; priv = priv_from_js(context, object); priv->gfundamental = gfundamental; g_assert(_fundamental_lookup_object(gfundamental) == NULL); _fundamental_add_object(gfundamental, object); gjs_debug_lifecycle(GJS_DEBUG_GFUNDAMENTAL, "associated JSObject %p with fundamental %p", object.get(), gfundamental); if (!owned_ref) priv->prototype->ref_function(gfundamental); } /**/ /* Find the first constructor */ static GIFunctionInfo * find_fundamental_constructor(JSContext *context, GIObjectInfo *info, JS::MutableHandleId constructor_name) { int i, n_methods; n_methods = g_object_info_get_n_methods(info); for (i = 0; i < n_methods; ++i) { GIFunctionInfo *func_info; GIFunctionInfoFlags flags; func_info = g_object_info_get_method(info, i); flags = g_function_info_get_flags(func_info); if ((flags & GI_FUNCTION_IS_CONSTRUCTOR) != 0) { const char *name; name = g_base_info_get_name((GIBaseInfo *) func_info); constructor_name.set(gjs_intern_string_to_id(context, name)); return func_info; } g_base_info_unref((GIBaseInfo *) func_info); } return NULL; } /**/ static bool fundamental_instance_resolve_interface(JSContext *context, JS::HandleObject obj, bool *resolved, Fundamental *proto_priv, const char *name) { GIFunctionInfo *method_info; bool ret; GType *interfaces; guint n_interfaces; guint i; ret = true; interfaces = g_type_interfaces(proto_priv->gtype, &n_interfaces); for (i = 0; i < n_interfaces; i++) { GIBaseInfo *base_info; GIInterfaceInfo *iface_info; base_info = g_irepository_find_by_gtype(g_irepository_get_default(), interfaces[i]); if (base_info == NULL) continue; /* An interface GType ought to have interface introspection info */ g_assert(g_base_info_get_type(base_info) == GI_INFO_TYPE_INTERFACE); iface_info = (GIInterfaceInfo *) base_info; method_info = g_interface_info_find_method(iface_info, name); g_base_info_unref(base_info); if (method_info != NULL) { if (g_function_info_get_flags (method_info) & GI_FUNCTION_IS_METHOD) { if (gjs_define_function(context, obj, proto_priv->gtype, (GICallableInfo *) method_info)) { *resolved = true; } else { ret = false; } } g_base_info_unref((GIBaseInfo *) method_info); } } g_free(interfaces); return ret; } /* * The *resolved out parameter, on success, should be false to indicate that id * was not resolved; and true if id was resolved. */ static bool fundamental_instance_resolve(JSContext *context, JS::HandleObject obj, JS::HandleId id, bool *resolved) { FundamentalInstance *priv; priv = priv_from_js(context, obj); gjs_debug_jsprop(GJS_DEBUG_GFUNDAMENTAL, "Resolve prop '%s' hook, obj %s, priv %p", gjs_debug_id(id).c_str(), gjs_debug_object(obj).c_str(), priv); if (priv == nullptr) return false; /* wrong class */ if (!fundamental_is_prototype(priv)) { /* We are an instance, not a prototype, so look for * per-instance props that we want to define on the * JSObject. Generally we do not want to cache these in JS, we * want to always pull them from the C object, or JS would not * see any changes made from C. So we use the get/set prop * hooks, not this resolve hook. */ *resolved = false; return true; } GjsAutoJSChar name; if (!gjs_get_string_id(context, id, &name)) { *resolved = false; return true; /* not resolved, but no error */ } /* We are the prototype, so look for methods and other class properties */ Fundamental *proto_priv = (Fundamental *) priv; GIFunctionInfo *method_info; method_info = g_object_info_find_method((GIStructInfo*) proto_priv->info, name); if (method_info != NULL) { const char *method_name; #if GJS_VERBOSE_ENABLE_GI_USAGE _gjs_log_info_usage((GIBaseInfo *) method_info); #endif if (g_function_info_get_flags (method_info) & GI_FUNCTION_IS_METHOD) { method_name = g_base_info_get_name((GIBaseInfo *) method_info); /* we do not define deprecated methods in the prototype */ if (g_base_info_is_deprecated((GIBaseInfo *) method_info)) { gjs_debug(GJS_DEBUG_GFUNDAMENTAL, "Ignoring definition of deprecated method %s in prototype %s.%s", method_name, g_base_info_get_namespace((GIBaseInfo *) proto_priv->info), g_base_info_get_name((GIBaseInfo *) proto_priv->info)); g_base_info_unref((GIBaseInfo *) method_info); *resolved = false; return true; } gjs_debug(GJS_DEBUG_GFUNDAMENTAL, "Defining method %s in prototype for %s.%s", method_name, g_base_info_get_namespace((GIBaseInfo *) proto_priv->info), g_base_info_get_name((GIBaseInfo *) proto_priv->info)); if (gjs_define_function(context, obj, proto_priv->gtype, method_info) == NULL) { g_base_info_unref((GIBaseInfo *) method_info); return false; } *resolved = true; } g_base_info_unref((GIBaseInfo *) method_info); } else { *resolved = false; } bool status = fundamental_instance_resolve_interface(context, obj, resolved, proto_priv, name); return status; } static bool fundamental_invoke_constructor(FundamentalInstance *priv, JSContext *context, JS::HandleObject obj, const JS::HandleValueArray& args, GIArgument *rvalue) { JS::RootedObject js_constructor(context); if (!gjs_object_require_property(context, obj, NULL, GJS_STRING_CONSTRUCTOR, &js_constructor) || priv->prototype->constructor_name.get() == JSID_VOID) { gjs_throw (context, "Couldn't find a constructor for type %s.%s", g_base_info_get_namespace((GIBaseInfo*) priv->prototype->info), g_base_info_get_name((GIBaseInfo*) priv->prototype->info)); return false; } JS::RootedObject constructor(context); JS::RootedId constructor_name(context, priv->prototype->constructor_name); if (!gjs_object_require_property(context, js_constructor, NULL, constructor_name, &constructor)) { gjs_throw (context, "Couldn't find a constructor for type %s.%s", g_base_info_get_namespace((GIBaseInfo*) priv->prototype->info), g_base_info_get_name((GIBaseInfo*) priv->prototype->info)); return false; } return gjs_invoke_constructor_from_c(context, constructor, obj, args, rvalue); } /* If we set JSCLASS_CONSTRUCT_PROTOTYPE flag, then this is called on * the prototype in addition to on each instance. When called on the * prototype, "obj" is the prototype, and "retval" is the prototype * also, but can be replaced with another object to use instead as the * prototype. If we don't set JSCLASS_CONSTRUCT_PROTOTYPE we can * identify the prototype as an object of our class with NULL private * data. */ GJS_NATIVE_CONSTRUCTOR_DECLARE(fundamental_instance) { GJS_NATIVE_CONSTRUCTOR_VARIABLES(fundamental_instance) FundamentalInstance *priv; GArgument ret_value; GITypeInfo return_info; GJS_NATIVE_CONSTRUCTOR_PRELUDE(fundamental_instance); priv = init_fundamental_instance(context, object); gjs_debug_lifecycle(GJS_DEBUG_GFUNDAMENTAL, "fundamental constructor, obj %p priv %p", object.get(), priv); if (!fundamental_invoke_constructor(priv, context, object, argv, &ret_value)) return false; associate_js_instance_to_fundamental(context, object, ret_value.v_pointer, false); g_callable_info_load_return_type((GICallableInfo*) priv->prototype->constructor_info, &return_info); if (!gjs_g_argument_release (context, g_callable_info_get_caller_owns((GICallableInfo*) priv->prototype->constructor_info), &return_info, &ret_value)) return false; GJS_NATIVE_CONSTRUCTOR_FINISH(fundamental_instance); return true; } static void fundamental_finalize(JSFreeOp *fop, JSObject *obj) { FundamentalInstance *priv; priv = (FundamentalInstance *) JS_GetPrivate(obj); gjs_debug_lifecycle(GJS_DEBUG_GFUNDAMENTAL, "finalize, obj %p priv %p", obj, priv); if (priv == NULL) return; /* wrong class? */ if (!fundamental_is_prototype(priv)) { if (priv->gfundamental) { _fundamental_remove_object(priv->gfundamental); priv->prototype->unref_function(priv->gfundamental); priv->gfundamental = NULL; } g_slice_free(FundamentalInstance, priv); GJS_DEC_COUNTER(fundamental); } else { Fundamental *proto_priv = (Fundamental *) priv; /* Only unref infos when freeing the prototype */ if (proto_priv->constructor_info) g_base_info_unref (proto_priv->constructor_info); proto_priv->constructor_info = NULL; if (proto_priv->info) g_base_info_unref((GIBaseInfo *) proto_priv->info); proto_priv->info = NULL; proto_priv->~Fundamental(); g_slice_free(Fundamental, proto_priv); } } static bool to_string_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_PRIV(context, argc, vp, rec, obj, FundamentalInstance, priv); if (fundamental_is_prototype(priv)) { Fundamental *proto_priv = (Fundamental *) priv; if (!_gjs_proxy_to_string_func(context, obj, "fundamental", (GIBaseInfo *) proto_priv->info, proto_priv->gtype, proto_priv->gfundamental, rec.rval())) return false; } else { if (!_gjs_proxy_to_string_func(context, obj, "fundamental", (GIBaseInfo *) priv->prototype->info, priv->prototype->gtype, priv->gfundamental, rec.rval())) return false; } return true; } static void fundamental_trace(JSTracer *tracer, JSObject *obj) { auto priv = static_cast(JS_GetPrivate(obj)); if (priv == nullptr || !fundamental_is_prototype(priv)) return; /* Only prototypes need tracing */ JS::TraceEdge(tracer, &priv->constructor_name, "Fundamental::constructor_name"); } /* The bizarre thing about this vtable is that it applies to both * instances of the object, and to the prototype that instances of the * class have. * * Also, there's a constructor field in here, but as far as I can * tell, it would only be used if no constructor were provided to * JS_InitClass. The constructor from JS_InitClass is not applied to * the prototype unless JSCLASS_CONSTRUCT_PROTOTYPE is in flags. */ static const struct JSClassOps gjs_fundamental_class_ops = { NULL, /* addProperty */ NULL, /* deleteProperty */ NULL, /* getProperty */ NULL, /* setProperty */ NULL, /* enumerate */ fundamental_instance_resolve, nullptr, /* mayResolve */ fundamental_finalize, NULL, /* call */ NULL, /* hasInstance */ NULL, /* construct */ fundamental_trace }; struct JSClass gjs_fundamental_instance_class = { "GFundamental_Object", JSCLASS_HAS_PRIVATE | JSCLASS_FOREGROUND_FINALIZE, &gjs_fundamental_class_ops }; static JSPropertySpec gjs_fundamental_instance_proto_props[] = { JS_PS_END }; static JSFunctionSpec gjs_fundamental_instance_proto_funcs[] = { JS_FS("toString", to_string_func, 0, 0), JS_FS_END }; static JSObject * gjs_lookup_fundamental_prototype(JSContext *context, GIObjectInfo *info, GType gtype) { JS::RootedObject in_object(context); const char *constructor_name; if (info) { in_object = gjs_lookup_namespace_object(context, (GIBaseInfo*) info); constructor_name = g_base_info_get_name((GIBaseInfo*) info); } else { in_object = gjs_lookup_private_namespace(context); constructor_name = g_type_name(gtype); } if (G_UNLIKELY (!in_object)) return NULL; JS::RootedValue value(context); if (!JS_GetProperty(context, in_object, constructor_name, &value)) return NULL; JS::RootedObject constructor(context); if (value.isUndefined()) { /* In case we're looking for a private type, and we don't find it, we need to define it first. */ JS::RootedObject ignored(context); gjs_define_fundamental_class(context, in_object, info, &constructor, &ignored); } else { if (G_UNLIKELY (!value.isObject())) return NULL; constructor = &value.toObject(); } g_assert(constructor); if (!gjs_object_get_property(context, constructor, GJS_STRING_PROTOTYPE, &value)) return NULL; if (G_UNLIKELY (!value.isObjectOrNull())) return NULL; return value.toObjectOrNull(); } static JSObject* gjs_lookup_fundamental_prototype_from_gtype(JSContext *context, GType gtype) { GIObjectInfo *info; JSObject *proto; /* A given gtype might not have any definition in the introspection * data. If that's the case, try to look for a definition of any of the * parent type. */ while ((info = (GIObjectInfo *) g_irepository_find_by_gtype(g_irepository_get_default(), gtype)) == NULL && gtype != G_TYPE_INVALID) gtype = g_type_parent(gtype); proto = gjs_lookup_fundamental_prototype(context, info, gtype); if (info) g_base_info_unref((GIBaseInfo*)info); return proto; } bool gjs_define_fundamental_class(JSContext *context, JS::HandleObject in_object, GIObjectInfo *info, JS::MutableHandleObject constructor, JS::MutableHandleObject prototype) { const char *constructor_name; JS::RootedId js_constructor_name(context); JS::RootedObject parent_proto(context); Fundamental *priv; GType parent_gtype; GType gtype; GIFunctionInfo *constructor_info; /* See the comment in gjs_define_object_class() for an explanation * of how this all works; Fundamental is pretty much the same as * Object. */ constructor_name = g_base_info_get_name((GIBaseInfo *) info); constructor_info = find_fundamental_constructor(context, info, &js_constructor_name); gtype = g_registered_type_info_get_g_type (info); parent_gtype = g_type_parent(gtype); if (parent_gtype != G_TYPE_INVALID) parent_proto = gjs_lookup_fundamental_prototype_from_gtype(context, parent_gtype); if (!gjs_init_class_dynamic(context, in_object, /* parent prototype JSObject* for * prototype; NULL for * Object.prototype */ parent_proto, g_base_info_get_namespace((GIBaseInfo *) info), constructor_name, &gjs_fundamental_instance_class, gjs_fundamental_instance_constructor, /* number of constructor args (less can be passed) */ constructor_info != NULL ? g_callable_info_get_n_args((GICallableInfo *) constructor_info) : 0, /* props of prototype */ parent_proto ? NULL : &gjs_fundamental_instance_proto_props[0], /* funcs of prototype */ parent_proto ? NULL : &gjs_fundamental_instance_proto_funcs[0], /* props of constructor, MyConstructor.myprop */ NULL, /* funcs of constructor, MyConstructor.myfunc() */ NULL, prototype, constructor)) { gjs_log_exception(context); g_error("Can't init class %s", constructor_name); } /* Put the info in the prototype */ priv = g_slice_new0(Fundamental); new (priv) Fundamental(); g_assert(priv != NULL); g_assert(priv->info == NULL); priv->info = g_base_info_ref((GIBaseInfo *) info); priv->gtype = gtype; priv->constructor_name = js_constructor_name; priv->constructor_info = constructor_info; priv->ref_function = g_object_info_get_ref_function_pointer(info); g_assert(priv->ref_function != NULL); priv->unref_function = g_object_info_get_unref_function_pointer(info); g_assert(priv->unref_function != NULL); priv->set_value_function = g_object_info_get_set_value_function_pointer(info); g_assert(priv->set_value_function != NULL); priv->get_value_function = g_object_info_get_get_value_function_pointer(info); g_assert(priv->get_value_function != NULL); JS_SetPrivate(prototype, priv); gjs_debug(GJS_DEBUG_GFUNDAMENTAL, "Defined class %s prototype is %p class %p in object %p constructor %s.%s.%s", constructor_name, prototype.get(), JS_GetClass(prototype), in_object.get(), constructor_info != NULL ? g_base_info_get_namespace(constructor_info) : "unknown", constructor_info != NULL ? g_base_info_get_name(g_base_info_get_container(constructor_info)) : "unknown", constructor_info != NULL ? g_base_info_get_name(constructor_info) : "unknown"); if (g_object_info_get_n_fields(priv->info) > 0) { gjs_debug(GJS_DEBUG_GFUNDAMENTAL, "Fundamental type '%s.%s' apparently has accessible fields. " "Gjs has no support for this yet, ignoring these.", g_base_info_get_namespace((GIBaseInfo *)priv->info), g_base_info_get_name ((GIBaseInfo *)priv->info)); } gjs_object_define_static_methods(context, constructor, gtype, info); JS::RootedObject gtype_obj(context, gjs_gtype_create_gtype_wrapper(context, gtype)); JS_DefineProperty(context, constructor, "$gtype", gtype_obj, JSPROP_PERMANENT); return true; } JSObject* gjs_object_from_g_fundamental(JSContext *context, GIObjectInfo *info, void *gfundamental) { if (gfundamental == NULL) return NULL; JS::RootedObject object(context, _fundamental_lookup_object(gfundamental)); if (object) return object; gjs_debug_marshal(GJS_DEBUG_GFUNDAMENTAL, "Wrapping fundamental %s.%s %p with JSObject", g_base_info_get_namespace((GIBaseInfo *) info), g_base_info_get_name((GIBaseInfo *) info), gfundamental); JS::RootedObject proto(context, gjs_lookup_fundamental_prototype_from_gtype(context, G_TYPE_FROM_INSTANCE(gfundamental))); if (!proto) return NULL; object = JS_NewObjectWithGivenProto(context, JS_GetClass(proto), proto); if (!object) goto out; init_fundamental_instance(context, object); associate_js_instance_to_fundamental(context, object, gfundamental, false); out: return object; } JSObject * gjs_fundamental_from_g_value(JSContext *context, const GValue *value, GType gtype) { Fundamental *proto_priv; void *fobj; JS::RootedObject proto(context, gjs_lookup_fundamental_prototype_from_gtype(context, gtype)); if (!proto) return NULL; proto_priv = (Fundamental *) priv_from_js(context, proto); fobj = proto_priv->get_value_function(value); if (!fobj) { gjs_throw(context, "Failed to convert GValue to a fundamental instance"); return NULL; } return gjs_object_from_g_fundamental(context, proto_priv->info, fobj); } void* gjs_g_fundamental_from_object(JSContext *context, JS::HandleObject obj) { FundamentalInstance *priv; if (!obj) return NULL; priv = priv_from_js(context, obj); if (priv == NULL) { gjs_throw(context, "No introspection information for %p", obj.get()); return NULL; } if (priv->gfundamental == NULL) { gjs_throw(context, "Object is %s.%s.prototype, not an object instance - cannot convert to a fundamental instance", g_base_info_get_namespace((GIBaseInfo *) priv->prototype->info), g_base_info_get_name((GIBaseInfo *) priv->prototype->info)); return NULL; } return priv->gfundamental; } bool gjs_typecheck_fundamental(JSContext *context, JS::HandleObject object, GType expected_gtype, bool throw_error) { FundamentalInstance *priv; bool result; if (!do_base_typecheck(context, object, throw_error)) return false; priv = priv_from_js(context, object); g_assert(priv != NULL); if (fundamental_is_prototype(priv)) { if (throw_error) { Fundamental *proto_priv = (Fundamental *) priv; gjs_throw(context, "Object is %s.%s.prototype, not an fundamental instance - cannot convert to void*", proto_priv->info ? g_base_info_get_namespace((GIBaseInfo *) proto_priv->info) : "", proto_priv->info ? g_base_info_get_name((GIBaseInfo *) proto_priv->info) : g_type_name(proto_priv->gtype)); } return false; } if (expected_gtype != G_TYPE_NONE) result = g_type_is_a(priv->prototype->gtype, expected_gtype); else result = true; if (!result && throw_error) { if (priv->prototype->info) { gjs_throw_custom(context, JSProto_TypeError, nullptr, "Object is of type %s.%s - cannot convert to %s", g_base_info_get_namespace((GIBaseInfo *) priv->prototype->info), g_base_info_get_name((GIBaseInfo *) priv->prototype->info), g_type_name(expected_gtype)); } else { gjs_throw_custom(context, JSProto_TypeError, nullptr, "Object is of type %s - cannot convert to %s", g_type_name(priv->prototype->gtype), g_type_name(expected_gtype)); } } return result; } void * gjs_fundamental_ref(JSContext *context, void *gfundamental) { Fundamental *proto_priv; JS::RootedObject proto(context, gjs_lookup_fundamental_prototype_from_gtype(context, G_TYPE_FROM_INSTANCE(gfundamental))); proto_priv = (Fundamental *) priv_from_js(context, proto); return proto_priv->ref_function(gfundamental); } void gjs_fundamental_unref(JSContext *context, void *gfundamental) { Fundamental *proto_priv; JS::RootedObject proto(context, gjs_lookup_fundamental_prototype_from_gtype(context, G_TYPE_FROM_INSTANCE(gfundamental))); proto_priv = (Fundamental *) priv_from_js(context, proto); proto_priv->unref_function(gfundamental); } cjs-4.4.0/gi/fundamental.h000066400000000000000000000054741356375132300153660ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2013 Intel Corporation * Copyright (c) 2008-2010 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_FUNDAMENTAL_H__ #define __GJS_FUNDAMENTAL_H__ #include #include #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS bool gjs_define_fundamental_class(JSContext *context, JS::HandleObject in_object, GIObjectInfo *info, JS::MutableHandleObject constructor, JS::MutableHandleObject prototype); JSObject* gjs_object_from_g_fundamental (JSContext *context, GIObjectInfo *info, void *fobj); void *gjs_g_fundamental_from_object(JSContext *context, JS::HandleObject obj); JSObject *gjs_fundamental_from_g_value (JSContext *context, const GValue *value, GType gtype); bool gjs_typecheck_fundamental(JSContext *context, JS::HandleObject object, GType expected_gtype, bool throw_error); void* gjs_fundamental_ref (JSContext *context, void *fobj); void gjs_fundamental_unref (JSContext *context, void *fobj); G_END_DECLS #endif /* __GJS_FUNDAMENTAL_H__ */ cjs-4.4.0/gi/gerror.cpp000066400000000000000000000446161356375132300147240ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include "boxed.h" #include "enumeration.h" #include "cjs/jsapi-class.h" #include "cjs/jsapi-wrapper.h" #include "cjs/mem.h" #include "repo.h" #include "gerror.h" #include "util/error.h" #include #include typedef struct { GIEnumInfo *info; GQuark domain; GError *gerror; /* NULL if we are the prototype and not an instance */ } Error; extern struct JSClass gjs_error_class; static void define_error_properties(JSContext *, JS::HandleObject); GJS_DEFINE_PRIV_FROM_JS(Error, gjs_error_class) GJS_NATIVE_CONSTRUCTOR_DECLARE(error) { GJS_NATIVE_CONSTRUCTOR_VARIABLES(error) Error *priv; Error *proto_priv; int32_t code; /* Check early to avoid allocating memory for nothing */ if (argc != 1 || !argv[0].isObject()) { gjs_throw(context, "Invalid parameters passed to GError constructor, expected one object"); return false; } GJS_NATIVE_CONSTRUCTOR_PRELUDE(error); priv = g_slice_new0(Error); GJS_INC_COUNTER(gerror); g_assert(priv_from_js(context, object) == NULL); JS_SetPrivate(object, priv); gjs_debug_lifecycle(GJS_DEBUG_GERROR, "GError constructor, obj %p priv %p", object.get(), priv); JS::RootedObject proto(context); JS_GetPrototype(context, object, &proto); gjs_debug_lifecycle(GJS_DEBUG_GERROR, "GError instance __proto__ is %p", proto.get()); /* If we're the prototype, then post-construct we'll fill in priv->info. * If we are not the prototype, though, then we'll get ->info from the * prototype and then create a GObject if we don't have one already. */ proto_priv = priv_from_js(context, proto); if (proto_priv == NULL) { gjs_debug(GJS_DEBUG_GERROR, "Bad prototype set on GError? Must match JSClass of object. JS error should have been reported."); return false; } priv->info = proto_priv->info; g_base_info_ref( (GIBaseInfo*) priv->info); priv->domain = proto_priv->domain; JS::RootedObject params_obj(context, &argv[0].toObject()); GjsAutoJSChar message; if (!gjs_object_require_property(context, params_obj, "GError constructor", GJS_STRING_MESSAGE, &message)) return false; if (!gjs_object_require_property(context, params_obj, "GError constructor", GJS_STRING_CODE, &code)) return false; priv->gerror = g_error_new_literal(priv->domain, code, message); /* We assume this error will be thrown in the same line as the constructor */ define_error_properties(context, object); GJS_NATIVE_CONSTRUCTOR_FINISH(boxed); return true; } static void error_finalize(JSFreeOp *fop, JSObject *obj) { Error *priv; priv = (Error*) JS_GetPrivate(obj); gjs_debug_lifecycle(GJS_DEBUG_GERROR, "finalize, obj %p priv %p", obj, priv); if (priv == NULL) return; /* wrong class? */ g_clear_error (&priv->gerror); if (priv->info) { g_base_info_unref( (GIBaseInfo*) priv->info); priv->info = NULL; } GJS_DEC_COUNTER(gerror); g_slice_free(Error, priv); } static bool error_get_domain(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_PRIV(context, argc, vp, args, obj, Error, priv); if (priv == NULL) return false; args.rval().setInt32(priv->domain); return true; } static bool error_get_message(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_PRIV(context, argc, vp, args, obj, Error, priv); if (priv == NULL) return false; if (priv->gerror == NULL) { /* Object is prototype, not instance */ gjs_throw(context, "Can't get a field from a GError prototype"); return false; } return gjs_string_from_utf8(context, priv->gerror->message, args.rval()); } static bool error_get_code(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_PRIV(context, argc, vp, args, obj, Error, priv); if (priv == NULL) return false; if (priv->gerror == NULL) { /* Object is prototype, not instance */ gjs_throw(context, "Can't get a field from a GError prototype"); return false; } args.rval().setInt32(priv->gerror->code); return true; } static bool error_to_string(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_PRIV(context, argc, vp, rec, self, Error, priv); if (priv == NULL) return false; /* We follow the same pattern as standard JS errors, at the expense of hiding some useful information */ GjsAutoChar descr; if (priv->gerror == NULL) { descr = g_strdup_printf("%s.%s", g_base_info_get_namespace(priv->info), g_base_info_get_name(priv->info)); } else { descr = g_strdup_printf("%s.%s: %s", g_base_info_get_namespace(priv->info), g_base_info_get_name(priv->info), priv->gerror->message); } return gjs_string_from_utf8(context, descr, rec.rval()); } static bool error_constructor_value_of(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_THIS(context, argc, vp, rec, self); Error *priv; JS::RootedObject prototype(context); if (!gjs_object_require_property(context, self, "constructor", GJS_STRING_PROTOTYPE, &prototype)) { /* This error message will be more informative */ JS_ClearPendingException(context); gjs_throw(context, "GLib.Error.valueOf() called on something that is not" " a constructor"); return false; } priv = priv_from_js(context, prototype); if (priv == NULL) return false; rec.rval().setInt32(priv->domain); return true; } /* The bizarre thing about this vtable is that it applies to both * instances of the object, and to the prototype that instances of the * class have. */ static const struct JSClassOps gjs_error_class_ops = { NULL, /* addProperty */ NULL, /* deleteProperty */ NULL, /* getProperty */ NULL, /* setProperty */ NULL, /* enumerate */ NULL, /* resolve */ nullptr, /* mayResolve */ error_finalize }; struct JSClass gjs_error_class = { "GLib_Error", JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE, &gjs_error_class_ops }; /* We need to shadow all fields of GError, to prevent calling the getter from GBoxed (which would trash memory accessing the instance private data) */ JSPropertySpec gjs_error_proto_props[] = { JS_PSG("domain", error_get_domain, GJS_MODULE_PROP_FLAGS), JS_PSG("code", error_get_code, GJS_MODULE_PROP_FLAGS), JS_PSG("message", error_get_message, GJS_MODULE_PROP_FLAGS), JS_PS_END }; JSFunctionSpec gjs_error_proto_funcs[] = { JS_FS("toString", error_to_string, 0, GJS_MODULE_PROP_FLAGS), JS_FS_END }; static JSFunctionSpec gjs_error_constructor_funcs[] = { JS_FS("valueOf", error_constructor_value_of, 0, GJS_MODULE_PROP_FLAGS), JS_FS_END }; void gjs_define_error_class(JSContext *context, JS::HandleObject in_object, GIEnumInfo *info) { const char *constructor_name; GIBoxedInfo *glib_error_info; JS::RootedObject prototype(context), constructor(context); Error *priv; /* See the comment in gjs_define_boxed_class() for an * explanation of how this all works; Error is pretty much the * same as Boxed (except that we inherit from GLib.Error). */ constructor_name = g_base_info_get_name( (GIBaseInfo*) info); g_irepository_require(NULL, "GLib", "2.0", (GIRepositoryLoadFlags) 0, NULL); glib_error_info = (GIBoxedInfo*) g_irepository_find_by_name(NULL, "GLib", "Error"); JS::RootedObject parent_proto(context, gjs_lookup_generic_prototype(context, glib_error_info)); g_base_info_unref((GIBaseInfo*)glib_error_info); if (!gjs_init_class_dynamic(context, in_object, parent_proto, g_base_info_get_namespace( (GIBaseInfo*) info), constructor_name, &gjs_error_class, gjs_error_constructor, 1, /* props of prototype */ &gjs_error_proto_props[0], /* funcs of prototype */ &gjs_error_proto_funcs[0], /* props of constructor, MyConstructor.myprop */ NULL, /* funcs of constructor, MyConstructor.myfunc() */ &gjs_error_constructor_funcs[0], &prototype, &constructor)) { gjs_log_exception(context); g_error("Can't init class %s", constructor_name); } GJS_INC_COUNTER(gerror); priv = g_slice_new0(Error); priv->info = info; g_base_info_ref( (GIBaseInfo*) priv->info); priv->domain = g_quark_from_string (g_enum_info_get_error_domain(priv->info)); JS_SetPrivate(prototype, priv); gjs_debug(GJS_DEBUG_GBOXED, "Defined class %s prototype is %p class %p in object %p", constructor_name, prototype.get(), JS_GetClass(prototype), in_object.get()); gjs_define_enum_values(context, constructor, priv->info); gjs_define_enum_static_methods(context, constructor, priv->info); } static GIEnumInfo * find_error_domain_info(GQuark domain) { GIEnumInfo *info; /* first an attempt without loading extra libraries */ info = g_irepository_find_by_error_domain(NULL, domain); if (info) return info; /* load standard stuff */ g_irepository_require(NULL, "GLib", "2.0", (GIRepositoryLoadFlags) 0, NULL); g_irepository_require(NULL, "GObject", "2.0", (GIRepositoryLoadFlags) 0, NULL); g_irepository_require(NULL, "Gio", "2.0", (GIRepositoryLoadFlags) 0, NULL); info = g_irepository_find_by_error_domain(NULL, domain); if (info) return info; /* last attempt: load GIRepository (for invoke errors, rarely needed) */ g_irepository_require(NULL, "GIRepository", "1.0", (GIRepositoryLoadFlags) 0, NULL); info = g_irepository_find_by_error_domain(NULL, domain); return info; } /* define properties that JS Error() expose, such as fileName, lineNumber and stack */ static void define_error_properties(JSContext *cx, JS::HandleObject obj) { JS::RootedObject frame(cx); JS::RootedString stack(cx); JS::RootedString source(cx); uint32_t line, column; JS::AutoSaveExceptionState exc(cx); if (!JS::CaptureCurrentStack(cx, &frame) || !JS::BuildStackString(cx, frame, &stack)) { exc.restore(); return; } JS::SavedFrameResult result; result = JS::GetSavedFrameSource(cx, frame, &source); g_assert(result == JS::SavedFrameResult::Ok); result = JS::GetSavedFrameLine(cx, frame, &line); g_assert(result == JS::SavedFrameResult::Ok); result = JS::GetSavedFrameColumn(cx, frame, &column); g_assert(result == JS::SavedFrameResult::Ok); if (!gjs_object_define_property(cx, obj, GJS_STRING_STACK, stack, JSPROP_ENUMERATE) || !gjs_object_define_property(cx, obj, GJS_STRING_FILENAME, source, JSPROP_ENUMERATE) || !gjs_object_define_property(cx, obj, GJS_STRING_LINE_NUMBER, line, JSPROP_ENUMERATE) || !gjs_object_define_property(cx, obj, GJS_STRING_COLUMN_NUMBER, column, JSPROP_ENUMERATE)) exc.restore(); } static JSProtoKey proto_key_from_error_enum(int val) { switch (val) { case GJS_JS_ERROR_EVAL_ERROR: return JSProto_EvalError; case GJS_JS_ERROR_INTERNAL_ERROR: return JSProto_InternalError; case GJS_JS_ERROR_RANGE_ERROR: return JSProto_RangeError; case GJS_JS_ERROR_REFERENCE_ERROR: return JSProto_ReferenceError; case GJS_JS_ERROR_STOP_ITERATION: return JSProto_StopIteration; case GJS_JS_ERROR_SYNTAX_ERROR: return JSProto_SyntaxError; case GJS_JS_ERROR_TYPE_ERROR: return JSProto_TypeError; case GJS_JS_ERROR_URI_ERROR: return JSProto_URIError; case GJS_JS_ERROR_ERROR: default: return JSProto_Error; } } static JSObject * gjs_error_from_js_gerror(JSContext *cx, GError *gerror) { JS::AutoValueArray<1> error_args(cx); if (!gjs_string_from_utf8(cx, gerror->message, error_args[0])) return nullptr; JSProtoKey error_kind = proto_key_from_error_enum(gerror->code); JS::RootedObject error_constructor(cx); if (!JS_GetClassObject(cx, error_kind, &error_constructor)) return nullptr; return JS_New(cx, error_constructor, error_args); } JSObject* gjs_error_from_gerror(JSContext *context, GError *gerror, bool add_stack) { Error *priv; Error *proto_priv; GIEnumInfo *info; if (gerror == NULL) return NULL; if (gerror->domain == GJS_JS_ERROR) return gjs_error_from_js_gerror(context, gerror); info = find_error_domain_info(gerror->domain); if (!info) { /* We don't have error domain metadata */ /* Marshal the error as a plain GError */ GIBaseInfo *glib_boxed; JSObject *retval; glib_boxed = g_irepository_find_by_name(NULL, "GLib", "Error"); retval = gjs_boxed_from_c_struct(context, glib_boxed, gerror, (GjsBoxedCreationFlags) 0); g_base_info_unref(glib_boxed); return retval; } gjs_debug_marshal(GJS_DEBUG_GBOXED, "Wrapping struct %s with JSObject", g_base_info_get_name((GIBaseInfo *)info)); JS::RootedObject proto(context, gjs_lookup_generic_prototype(context, info)); proto_priv = priv_from_js(context, proto); JS::RootedObject obj(context, JS_NewObjectWithGivenProto(context, JS_GetClass(proto), proto)); GJS_INC_COUNTER(gerror); priv = g_slice_new0(Error); JS_SetPrivate(obj, priv); priv->info = info; priv->domain = proto_priv->domain; g_base_info_ref( (GIBaseInfo*) priv->info); priv->gerror = g_error_copy(gerror); if (add_stack) define_error_properties(context, obj); return obj; } GError* gjs_gerror_from_error(JSContext *context, JS::HandleObject obj) { Error *priv; if (!obj) return NULL; /* If this is a plain GBoxed (i.e. a GError without metadata), delegate marshalling. */ if (gjs_typecheck_boxed (context, obj, NULL, G_TYPE_ERROR, false)) return (GError*) gjs_c_struct_from_boxed (context, obj); priv = priv_from_js(context, obj); if (priv == NULL) return NULL; if (priv->gerror == NULL) { gjs_throw(context, "Object is %s.%s.prototype, not an object instance - cannot convert to a boxed instance", g_base_info_get_namespace( (GIBaseInfo*) priv->info), g_base_info_get_name( (GIBaseInfo*) priv->info)); return NULL; } return priv->gerror; } bool gjs_typecheck_gerror (JSContext *context, JS::HandleObject obj, bool throw_error) { if (gjs_typecheck_boxed (context, obj, NULL, G_TYPE_ERROR, false)) return true; return do_base_typecheck(context, obj, throw_error); } GError * gjs_gerror_make_from_error(JSContext *cx, JS::HandleObject obj) { using AutoEnumClass = std::unique_ptr; if (gjs_typecheck_gerror(cx, obj, false)) { /* This is already a GError, just copy it */ GError *inner = gjs_gerror_from_error(cx, obj); return g_error_copy(inner); } /* Try to make something useful from the error name and message (in case this is a JS error) */ JS::RootedValue v_name(cx); if (!gjs_object_get_property(cx, obj, GJS_STRING_NAME, &v_name)) return nullptr; GjsAutoJSChar name; if (!gjs_string_to_utf8(cx, v_name, &name)) return nullptr; JS::RootedValue v_message(cx); if (!gjs_object_get_property(cx, obj, GJS_STRING_MESSAGE, &v_message)) return nullptr; GjsAutoJSChar message; if (!gjs_string_to_utf8(cx, v_message, &message)) return nullptr; AutoEnumClass klass(static_cast(g_type_class_ref(GJS_TYPE_JS_ERROR)), g_type_class_unref); const GEnumValue *value = g_enum_get_value_by_name(klass.get(), name); int code; if (value) code = value->value; else code = GJS_JS_ERROR_ERROR; return g_error_new_literal(GJS_JS_ERROR, code, message); } cjs-4.4.0/gi/gerror.h000066400000000000000000000043651356375132300143660ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_ERROR_H__ #define __GJS_ERROR_H__ #include #include #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS void gjs_define_error_class (JSContext *context, JS::HandleObject in_object, GIEnumInfo *info); GError* gjs_gerror_from_error (JSContext *context, JS::HandleObject obj); JSObject* gjs_error_from_gerror (JSContext *context, GError *gerror, bool add_stack); bool gjs_typecheck_gerror (JSContext *context, JS::HandleObject obj, bool throw_error); GError *gjs_gerror_make_from_error(JSContext *cx, JS::HandleObject obj); G_END_DECLS #endif /* __GJS_ERROR_H__ */ cjs-4.4.0/gi/gjs_gi_probes.d000066400000000000000000000002111356375132300156600ustar00rootroot00000000000000provider gjs { probe object__proxy__new(void*, void*, char *, char *); probe object__proxy__finalize(void*, void*, char *, char *); }; cjs-4.4.0/gi/gjs_gi_trace.h000066400000000000000000000031671356375132300155050ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2010 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. * * Author: Colin Walters */ #ifndef __GJS_TRACE_H__ #define __GJS_TRACE_H__ #ifndef GETTEXT_PACKAGE #error "config.h must be included prior to gjs_trace.h" #endif #ifdef HAVE_DTRACE /* include the generated probes header and put markers in code */ #include "gjs_gi_probes.h" #define TRACE(probe) probe #else /* Wrap the probe to allow it to be removed when no systemtap available */ #define TRACE(probe) #endif #endif /* __GJS_TRACE_H__ */ cjs-4.4.0/gi/gtype.cpp000066400000000000000000000201351356375132300145420ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * Copyright (c) 2012 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include "gtype.h" #include "cjs/jsapi-class.h" #include "cjs/jsapi-wrapper.h" #include #include static bool weak_pointer_callback = false; static std::set weak_pointer_list; static JSObject *gjs_gtype_get_proto(JSContext *) G_GNUC_UNUSED; static bool gjs_gtype_define_proto(JSContext *, JS::HandleObject, JS::MutableHandleObject); GJS_DEFINE_PROTO_ABSTRACT("GIRepositoryGType", gtype, JSCLASS_FOREGROUND_FINALIZE); /* priv_from_js adds a "*", so this returns "void *" */ GJS_DEFINE_PRIV_FROM_JS(void, gjs_gtype_class); static GQuark gjs_get_gtype_wrapper_quark(void) { static gsize once_init = 0; static GQuark value = 0; if (g_once_init_enter(&once_init)) { value = g_quark_from_string("gjs-gtype-wrapper"); g_once_init_leave(&once_init, 1); } return value; } static void update_gtype_weak_pointers(JSContext *cx, JSCompartment *compartment, void *data) { for (auto iter = weak_pointer_list.begin(); iter != weak_pointer_list.end(); ) { auto heap_wrapper = static_cast *>(g_type_get_qdata(*iter, gjs_get_gtype_wrapper_quark())); JS_UpdateWeakPointerAfterGC(heap_wrapper); /* No read barriers are needed if the only thing we are doing with the * pointer is comparing it to nullptr. */ if (heap_wrapper->unbarrieredGet() == nullptr) iter = weak_pointer_list.erase(iter); else iter++; } } static void ensure_weak_pointer_callback(JSContext *cx) { if (!weak_pointer_callback) { JS_AddWeakPointerCompartmentCallback(cx, update_gtype_weak_pointers, nullptr); weak_pointer_callback = true; } } static void gjs_gtype_finalize(JSFreeOp *fop, JSObject *obj) { GType gtype = GPOINTER_TO_SIZE(JS_GetPrivate(obj)); /* proto doesn't have a private set */ if (G_UNLIKELY(gtype == 0)) return; weak_pointer_list.erase(gtype); g_type_set_qdata(gtype, gjs_get_gtype_wrapper_quark(), NULL); } static bool to_string_func(JSContext *cx, unsigned argc, JS::Value *vp) { GJS_GET_PRIV(cx, argc, vp, rec, obj, void, priv); GType gtype = GPOINTER_TO_SIZE(priv); if (gtype == 0) { JS::RootedString str(cx, JS_NewStringCopyZ(cx, "[object GType prototype]")); if (!str) return false; rec.rval().setString(str); return true; } GjsAutoChar strval = g_strdup_printf("[object GType for '%s']", g_type_name(gtype)); return gjs_string_from_utf8(cx, strval, rec.rval()); } static bool get_name_func (JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_PRIV(context, argc, vp, rec, obj, void, priv); GType gtype; gtype = GPOINTER_TO_SIZE(priv); if (gtype == 0) { rec.rval().setNull(); return true; } return gjs_string_from_utf8(context, g_type_name(gtype), rec.rval()); } /* Properties */ JSPropertySpec gjs_gtype_proto_props[] = { JS_PSG("name", get_name_func, JSPROP_PERMANENT), JS_PS_END, }; /* Functions */ JSFunctionSpec gjs_gtype_proto_funcs[] = { JS_FS("toString", to_string_func, 0, 0), JS_FS_END }; JSFunctionSpec gjs_gtype_static_funcs[] = { JS_FS_END }; JSObject * gjs_gtype_create_gtype_wrapper (JSContext *context, GType gtype) { g_assert(((void) "Attempted to create wrapper object for invalid GType", gtype != 0)); JSAutoRequest ar(context); auto heap_wrapper = static_cast *>(g_type_get_qdata(gtype, gjs_get_gtype_wrapper_quark())); if (heap_wrapper != nullptr) return *heap_wrapper; JS::RootedObject proto(context); if (!gjs_gtype_define_proto(context, nullptr, &proto)) return nullptr; heap_wrapper = new JS::Heap(); *heap_wrapper = JS_NewObjectWithGivenProto(context, &gjs_gtype_class, proto); if (*heap_wrapper == nullptr) return nullptr; JS_SetPrivate(*heap_wrapper, GSIZE_TO_POINTER(gtype)); ensure_weak_pointer_callback(context); g_type_set_qdata(gtype, gjs_get_gtype_wrapper_quark(), heap_wrapper); weak_pointer_list.insert(gtype); return *heap_wrapper; } static GType _gjs_gtype_get_actual_gtype(JSContext *context, JS::HandleObject object, int recurse) { JSAutoRequest ar(context); if (JS_InstanceOf(context, object, &gjs_gtype_class, NULL)) return GPOINTER_TO_SIZE(priv_from_js(context, object)); JS::RootedValue gtype_val(context); /* OK, we don't have a GType wrapper object -- grab the "$gtype" * property on that and hope it's a GType wrapper object */ if (!JS_GetProperty(context, object, "$gtype", >ype_val) || !gtype_val.isObject()) { /* OK, so we're not a class. But maybe we're an instance. Check for "constructor" and recurse on that. */ if (!JS_GetProperty(context, object, "constructor", >ype_val)) return G_TYPE_INVALID; } if (recurse > 0 && gtype_val.isObject()) { JS::RootedObject gtype_obj(context, >ype_val.toObject()); return _gjs_gtype_get_actual_gtype(context, gtype_obj, recurse - 1); } return G_TYPE_INVALID; } GType gjs_gtype_get_actual_gtype(JSContext *context, JS::HandleObject object) { /* 2 means: recurse at most three times (including this call). The levels are calculated considering that, in the worst case we need to go from instance to class, from class to GType object and from GType object to GType value. */ return _gjs_gtype_get_actual_gtype(context, object, 2); } bool gjs_typecheck_gtype (JSContext *context, JS::HandleObject obj, bool throw_error) { return do_base_typecheck(context, obj, throw_error); } const char * gjs_get_names_from_gtype_and_gi_info(GType gtype, GIBaseInfo *info, const char **constructor_name) { const char *ns; /* ns is only used to set the JSClass->name field (exposed by * Object.prototype.toString). * We can safely set "unknown" if there is no info, as in that case * the name is globally unique (it's a GType name). */ if (info) { ns = g_base_info_get_namespace((GIBaseInfo*) info); if (constructor_name) *constructor_name = g_base_info_get_name((GIBaseInfo*) info); } else { ns = "unknown"; if (constructor_name) *constructor_name = g_type_name(gtype); } return ns; } cjs-4.4.0/gi/gtype.h000066400000000000000000000041121356375132300142040ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * Copyright (c) 2012 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_GTYPE_H__ #define __GJS_GTYPE_H__ #include #include #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS JSObject * gjs_gtype_create_gtype_wrapper (JSContext *context, GType gtype); GType gjs_gtype_get_actual_gtype(JSContext *context, JS::HandleObject object); bool gjs_typecheck_gtype (JSContext *context, JS::HandleObject obj, bool throw_error); const char *gjs_get_names_from_gtype_and_gi_info(GType gtype, GIBaseInfo *info, const char **constructor_name); G_END_DECLS #endif /* __GJS_INTERFACE_H__ */ cjs-4.4.0/gi/interface.cpp000066400000000000000000000226531356375132300153610ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * Copyright (c) 2012 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "function.h" #include "gtype.h" #include "interface.h" #include "object.h" #include "repo.h" #include "cjs/jsapi-class.h" #include "cjs/jsapi-wrapper.h" #include "cjs/mem.h" #include #include typedef struct { GIInterfaceInfo *info; GType gtype; /* the GTypeInterface vtable wrapped by this JS Object (only used for prototypes) */ GTypeInterface *vtable; } Interface; extern struct JSClass gjs_interface_class; GJS_DEFINE_PRIV_FROM_JS(Interface, gjs_interface_class) GJS_NATIVE_CONSTRUCTOR_DEFINE_ABSTRACT(interface) static void interface_finalize(JSFreeOp *fop, JSObject *obj) { Interface *priv; priv = (Interface*) JS_GetPrivate(obj); if (priv == NULL) return; if (priv->info != NULL) g_base_info_unref((GIBaseInfo*)priv->info); g_clear_pointer(&priv->vtable, (GDestroyNotify)g_type_default_interface_unref); GJS_DEC_COUNTER(interface); g_slice_free(Interface, priv); } static bool gjs_define_static_methods(JSContext *context, JS::HandleObject constructor, GType gtype, GIInterfaceInfo *info) { int i; int n_methods; n_methods = g_interface_info_get_n_methods(info); for (i = 0; i < n_methods; i++) { GIFunctionInfo *meth_info; GIFunctionInfoFlags flags; meth_info = g_interface_info_get_method (info, i); flags = g_function_info_get_flags (meth_info); /* Anything that isn't a method we put on the prototype of the * constructor. This includes introspection * methods, as well as the forthcoming "static methods" * support. We may want to change this to use * GI_FUNCTION_IS_CONSTRUCTOR and GI_FUNCTION_IS_STATIC or the * like in the near future. */ if (!(flags & GI_FUNCTION_IS_METHOD)) { gjs_define_function(context, constructor, gtype, (GICallableInfo *)meth_info); } g_base_info_unref((GIBaseInfo*) meth_info); } return true; } static bool interface_resolve(JSContext *context, JS::HandleObject obj, JS::HandleId id, bool *resolved) { Interface *priv; GIFunctionInfo *method_info; priv = priv_from_js(context, obj); if (priv == nullptr) return false; /* If we have no GIRepository information then this interface was defined * from within GJS. In that case, it has no properties that need to be * resolved from within C code, as interfaces cannot inherit. */ if (priv->info == NULL) { *resolved = false; return true; } GjsAutoJSChar name; if (!gjs_get_string_id(context, id, &name)) { *resolved = false; return true; } method_info = g_interface_info_find_method((GIInterfaceInfo*) priv->info, name); if (method_info != NULL) { if (g_function_info_get_flags (method_info) & GI_FUNCTION_IS_METHOD) { if (gjs_define_function(context, obj, priv->gtype, (GICallableInfo*)method_info) == NULL) { g_base_info_unref((GIBaseInfo*)method_info); return false; } *resolved = true; } else { *resolved = false; } g_base_info_unref((GIBaseInfo*)method_info); } else { *resolved = false; } return true; } static bool interface_has_instance_func(JSContext *cx, unsigned argc, JS::Value *vp) { JS::CallArgs args = JS::CallArgsFromVp(argc, vp); /* This method is not called directly, so no need for error messages */ JS::RootedValue interface(cx, args.computeThis(cx)); g_assert(interface.isObject()); JS::RootedObject interface_constructor(cx, &interface.toObject()); JS::RootedObject interface_proto(cx); gjs_object_require_property(cx, interface_constructor, "interface constructor", GJS_STRING_PROTOTYPE, &interface_proto); Interface *priv; if (!priv_from_js_with_typecheck(cx, interface_proto, &priv)) return false; g_assert(args.length() == 1); g_assert(args[0].isObject()); JS::RootedObject instance(cx, &args[0].toObject()); bool isinstance = gjs_typecheck_object(cx, instance, priv->gtype, false); args.rval().setBoolean(isinstance); return true; } static const struct JSClassOps gjs_interface_class_ops = { NULL, /* addProperty */ NULL, /* deleteProperty */ NULL, /* getProperty */ NULL, /* setProperty */ NULL, /* enumerate */ interface_resolve, nullptr, /* mayResolve */ interface_finalize }; struct JSClass gjs_interface_class = { "GObject_Interface", JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE, &gjs_interface_class_ops }; JSPropertySpec gjs_interface_proto_props[] = { JS_PS_END }; JSFunctionSpec gjs_interface_proto_funcs[] = { JS_FS_END }; JSFunctionSpec gjs_interface_static_funcs[] = { JS_SYM_FN(hasInstance, interface_has_instance_func, 1, 0), JS_FS_END }; bool gjs_define_interface_class(JSContext *context, JS::HandleObject in_object, GIInterfaceInfo *info, GType gtype, JS::MutableHandleObject constructor) { Interface *priv; const char *constructor_name; const char *ns; JS::RootedObject prototype(context); ns = gjs_get_names_from_gtype_and_gi_info(gtype, (GIBaseInfo *) info, &constructor_name); if (!gjs_init_class_dynamic(context, in_object, nullptr, ns, constructor_name, &gjs_interface_class, gjs_interface_constructor, 0, /* props of prototype */ &gjs_interface_proto_props[0], /* funcs of prototype */ &gjs_interface_proto_funcs[0], /* props of constructor, MyConstructor.myprop */ NULL, /* funcs of constructor, MyConstructor.myfunc() */ gjs_interface_static_funcs, &prototype, constructor)) { g_error("Can't init class %s", constructor_name); } GJS_INC_COUNTER(interface); priv = g_slice_new0(Interface); priv->info = info == NULL ? NULL : g_base_info_ref((GIBaseInfo *) info); priv->gtype = gtype; priv->vtable = (GTypeInterface *) g_type_default_interface_ref(gtype); JS_SetPrivate(prototype, priv); /* If we have no GIRepository information, then this interface was defined * from within GJS and therefore has no C static methods to be defined. */ if (priv->info) gjs_define_static_methods(context, constructor, priv->gtype, priv->info); JS::RootedObject gtype_obj(context, gjs_gtype_create_gtype_wrapper(context, priv->gtype)); JS_DefineProperty(context, constructor, "$gtype", gtype_obj, JSPROP_PERMANENT); return true; } bool gjs_lookup_interface_constructor(JSContext *context, GType gtype, JS::MutableHandleValue value_p) { JSObject *constructor; GIBaseInfo *interface_info; interface_info = g_irepository_find_by_gtype(NULL, gtype); if (interface_info == NULL) { gjs_throw(context, "Cannot expose non introspectable interface %s", g_type_name(gtype)); return false; } g_assert(g_base_info_get_type(interface_info) == GI_INFO_TYPE_INTERFACE); constructor = gjs_lookup_generic_constructor(context, interface_info); if (G_UNLIKELY (constructor == NULL)) return false; g_base_info_unref(interface_info); value_p.setObject(*constructor); return true; } cjs-4.4.0/gi/interface.h000066400000000000000000000036631356375132300150260ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * Copyright (c) 2012 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_INTERFACE_H__ #define __GJS_INTERFACE_H__ #include #include #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS bool gjs_define_interface_class(JSContext *context, JS::HandleObject in_object, GIInterfaceInfo *info, GType gtype, JS::MutableHandleObject constructor); bool gjs_lookup_interface_constructor(JSContext *context, GType gtype, JS::MutableHandleValue value_p); G_END_DECLS #endif /* __GJS_INTERFACE_H__ */ cjs-4.4.0/gi/ns.cpp000066400000000000000000000140011356375132300140250ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "ns.h" #include "repo.h" #include "param.h" #include "cjs/jsapi-class.h" #include "cjs/jsapi-wrapper.h" #include "cjs/mem.h" #include #include #include typedef struct { char *gi_namespace; } Ns; extern struct JSClass gjs_ns_class; GJS_DEFINE_PRIV_FROM_JS(Ns, gjs_ns_class) /* The *resolved out parameter, on success, should be false to indicate that id * was not resolved; and true if id was resolved. */ static bool ns_resolve(JSContext *context, JS::HandleObject obj, JS::HandleId id, bool *resolved) { Ns *priv; GIRepository *repo; GIBaseInfo *info; bool defined; if (!JSID_IS_STRING(id)) { *resolved = false; return true; /* not resolved, but no error */ } /* let Object.prototype resolve these */ JSFlatString *str = JSID_TO_FLAT_STRING(id); if (JS_FlatStringEqualsAscii(str, "valueOf") || JS_FlatStringEqualsAscii(str, "toString")) { *resolved = false; return true; } priv = priv_from_js(context, obj); gjs_debug_jsprop(GJS_DEBUG_GNAMESPACE, "Resolve prop '%s' hook, obj %s, priv %p", gjs_debug_id(id).c_str(), gjs_debug_object(obj).c_str(), priv); if (priv == NULL) { *resolved = false; /* we are the prototype, or have the wrong class */ return true; } GjsAutoJSChar name; if (!gjs_get_string_id(context, id, &name)) { *resolved = false; return true; /* not resolved, but no error */ } repo = g_irepository_get_default(); info = g_irepository_find_by_name(repo, priv->gi_namespace, name); if (info == NULL) { *resolved = false; /* No property defined, but no error either */ return true; } gjs_debug(GJS_DEBUG_GNAMESPACE, "Found info type %s for '%s' in namespace '%s'", gjs_info_type_name(g_base_info_get_type(info)), g_base_info_get_name(info), g_base_info_get_namespace(info)); JSAutoRequest ar(context); if (!gjs_define_info(context, obj, info, &defined)) { gjs_debug(GJS_DEBUG_GNAMESPACE, "Failed to define info '%s'", g_base_info_get_name(info)); g_base_info_unref(info); return false; } /* we defined the property in this object? */ g_base_info_unref(info); *resolved = defined; return true; } static bool get_name (JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_PRIV(context, argc, vp, args, obj, Ns, priv); if (priv == NULL) return false; return gjs_string_from_utf8(context, priv->gi_namespace, args.rval()); } GJS_NATIVE_CONSTRUCTOR_DEFINE_ABSTRACT(ns) static void ns_finalize(JSFreeOp *fop, JSObject *obj) { Ns *priv; priv = (Ns *)JS_GetPrivate(obj); gjs_debug_lifecycle(GJS_DEBUG_GNAMESPACE, "finalize, obj %p priv %p", obj, priv); if (priv == NULL) return; /* we are the prototype, not a real instance */ if (priv->gi_namespace) g_free(priv->gi_namespace); GJS_DEC_COUNTER(ns); g_slice_free(Ns, priv); } /* The bizarre thing about this vtable is that it applies to both * instances of the object, and to the prototype that instances of the * class have. */ static const struct JSClassOps gjs_ns_class_ops = { NULL, /* addProperty */ NULL, /* deleteProperty */ NULL, /* getProperty */ NULL, /* setProperty */ NULL, /* enumerate */ ns_resolve, nullptr, /* mayResolve */ ns_finalize }; struct JSClass gjs_ns_class = { "GIRepositoryNamespace", JSCLASS_HAS_PRIVATE | JSCLASS_FOREGROUND_FINALIZE, &gjs_ns_class_ops }; static JSPropertySpec gjs_ns_proto_props[] = { JS_PSG("__name__", get_name, GJS_MODULE_PROP_FLAGS), JS_PS_END }; static JSFunctionSpec *gjs_ns_proto_funcs = nullptr; static JSFunctionSpec *gjs_ns_static_funcs = nullptr; GJS_DEFINE_PROTO_FUNCS(ns) static JSObject* ns_new(JSContext *context, const char *ns_name) { Ns *priv; JS::RootedObject proto(context); if (!gjs_ns_define_proto(context, nullptr, &proto)) return nullptr; JS::RootedObject ns(context, JS_NewObjectWithGivenProto(context, &gjs_ns_class, proto)); if (!ns) g_error("No memory to create ns object"); priv = g_slice_new0(Ns); GJS_INC_COUNTER(ns); g_assert(priv_from_js(context, ns) == NULL); JS_SetPrivate(ns, priv); gjs_debug_lifecycle(GJS_DEBUG_GNAMESPACE, "ns constructor, obj %p priv %p", ns.get(), priv); priv = priv_from_js(context, ns); priv->gi_namespace = g_strdup(ns_name); return ns; } JSObject* gjs_create_ns(JSContext *context, const char *ns_name) { return ns_new(context, ns_name); } cjs-4.4.0/gi/ns.h000066400000000000000000000026521356375132300135030ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_NS_H__ #define __GJS_NS_H__ #include #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS JSObject* gjs_create_ns(JSContext *context, const char *ns_name); G_END_DECLS #endif /* __GJS_NS_H__ */ cjs-4.4.0/gi/object.cpp000066400000000000000000003213231356375132300146630ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include #include #include #include #include "object.h" #include "gtype.h" #include "interface.h" #include "cjs/jsapi-util-args.h" #include "arg.h" #include "repo.h" #include "gtype.h" #include "function.h" #include "proxyutils.h" #include "param.h" #include "toggle.h" #include "value.h" #include "closure.h" #include "gjs_gi_trace.h" #include "cjs/jsapi-class.h" #include "cjs/jsapi-util-root.h" #include "cjs/jsapi-wrapper.h" #include "cjs/context-private.h" #include "cjs/mem.h" #include #include typedef class GjsListLink GjsListLink; typedef struct ObjectInstance ObjectInstance; static GjsListLink* object_instance_get_link(ObjectInstance *priv); class GjsListLink { private: ObjectInstance *m_prev; ObjectInstance *m_next; public: ObjectInstance* prev() { return m_prev; } ObjectInstance* next() { return m_next; } void prepend(ObjectInstance *this_instance, ObjectInstance *head) { GjsListLink *elem = object_instance_get_link(head); g_assert(object_instance_get_link(this_instance) == this); if (elem->m_prev) { GjsListLink *prev = object_instance_get_link(elem->m_prev); prev->m_next = this_instance; this->m_prev = elem->m_prev; } elem->m_prev = this_instance; this->m_next = head; } void unlink() { if (m_prev) object_instance_get_link(m_prev)->m_next = m_next; if (m_next) object_instance_get_link(m_next)->m_prev = m_prev; m_prev = m_next = NULL; } size_t size() { GjsListLink *elem = this; size_t count = 0; do { count++; if (!elem->m_next) break; elem = object_instance_get_link(elem->m_next); } while (elem); return count; } }; struct ObjectInstance { GIObjectInfo *info; GObject *gobj; /* NULL if we are the prototype and not an instance */ GjsMaybeOwned keep_alive; GType gtype; /* a list of all GClosures installed on this object (from * signals, trampolines and explicit GClosures), used when tracing */ std::set closures; /* the GObjectClass wrapped by this JS Object (only used for prototypes) */ GTypeClass *klass; GjsListLink instance_link; unsigned js_object_finalized : 1; unsigned g_object_finalized : 1; /* True if this object has visible JS state, and thus its lifecycle is * managed using toggle references. False if this object just keeps a * hard ref on the underlying GObject, and may be finalized at will. */ bool uses_toggle_ref : 1; }; static std::stack object_init_list; using ParamRef = std::unique_ptr; using ParamRefArray = std::vector; static std::unordered_map class_init_properties; static bool weak_pointer_callback = false; ObjectInstance *wrapped_gobject_list; extern struct JSClass gjs_object_instance_class; GJS_DEFINE_PRIV_FROM_JS(ObjectInstance, gjs_object_instance_class) static void disassociate_js_gobject(ObjectInstance *priv); static void ensure_uses_toggle_ref(JSContext *cx, ObjectInstance *priv); typedef enum { SOME_ERROR_OCCURRED = false, NO_SUCH_G_PROPERTY, VALUE_WAS_SET } ValueFromPropertyResult; static GQuark gjs_is_custom_type_quark (void) { static GQuark val = 0; if (!val) val = g_quark_from_static_string ("gjs::custom-type"); return val; } static GQuark gjs_is_custom_property_quark (void) { static GQuark val = 0; if (!val) val = g_quark_from_static_string ("gjs::custom-property"); return val; } static GQuark gjs_object_priv_quark (void) { static GQuark val = 0; if (G_UNLIKELY (!val)) val = g_quark_from_static_string ("gjs::private"); return val; } /* Plain g_type_query fails and leaves @query uninitialized for dynamic types. See https://bugzilla.gnome.org/show_bug.cgi?id=687184 and https://bugzilla.gnome.org/show_bug.cgi?id=687211 */ static void g_type_query_dynamic_safe (GType type, GTypeQuery *query) { while (g_type_get_qdata(type, gjs_is_custom_type_quark())) type = g_type_parent(type); g_type_query(type, query); } static void throw_priv_is_null_error(JSContext *context) { gjs_throw(context, "This JS object wrapper isn't wrapping a GObject." " If this is a custom subclass, are you sure you chained" " up to the parent _init properly?"); } static ObjectInstance * get_object_qdata(GObject *gobj) { auto priv = static_cast(g_object_get_qdata(gobj, gjs_object_priv_quark())); if (priv && priv->uses_toggle_ref && G_UNLIKELY(priv->js_object_finalized)) { g_critical("Object %p (a %s) resurfaced after the JS wrapper was finalized. " "This is some library doing dubious memory management inside dispose()", gobj, g_type_name(G_TYPE_FROM_INSTANCE(gobj))); priv->js_object_finalized = false; g_assert(!priv->keep_alive); /* should associate again with a new wrapper */ } return priv; } static void set_object_qdata(GObject *gobj, ObjectInstance *priv) { g_object_set_qdata(gobj, gjs_object_priv_quark(), priv); } static ValueFromPropertyResult init_g_param_from_property(JSContext *context, const char *js_prop_name, JS::HandleValue value, GType gtype, GParameter *parameter, bool constructing) { char *gname; GParamSpec *param_spec; void *klass; gname = gjs_hyphen_from_camel(js_prop_name); gjs_debug_jsprop(GJS_DEBUG_GOBJECT, "Hyphen name %s on %s", gname, g_type_name(gtype)); klass = g_type_class_ref(gtype); param_spec = g_object_class_find_property(G_OBJECT_CLASS(klass), gname); g_type_class_unref(klass); g_free(gname); if (param_spec == NULL) { /* not a GObject prop, so nothing else to do */ return NO_SUCH_G_PROPERTY; } /* Do not set JS overridden properties through GObject, to avoid * infinite recursion (but set them when constructing) */ if (!constructing && g_param_spec_get_qdata(param_spec, gjs_is_custom_property_quark())) return NO_SUCH_G_PROPERTY; if ((param_spec->flags & G_PARAM_WRITABLE) == 0) { /* prevent setting the prop even in JS */ gjs_throw(context, "Property %s (GObject %s) is not writable", js_prop_name, param_spec->name); return SOME_ERROR_OCCURRED; } gjs_debug_jsprop(GJS_DEBUG_GOBJECT, "Syncing %s to GObject prop %s", js_prop_name, param_spec->name); g_value_init(¶meter->value, G_PARAM_SPEC_VALUE_TYPE(param_spec)); if (!gjs_value_to_g_value(context, value, ¶meter->value)) { g_value_unset(¶meter->value); return SOME_ERROR_OCCURRED; } parameter->name = param_spec->name; return VALUE_WAS_SET; } static inline ObjectInstance * proto_priv_from_js(JSContext *context, JS::HandleObject obj) { JS::RootedObject proto(context); JS_GetPrototype(context, obj, &proto); return priv_from_js(context, proto); } static bool get_prop_from_g_param(JSContext *context, JS::HandleObject obj, ObjectInstance *priv, const char *name, JS::MutableHandleValue value_p) { char *gname; GParamSpec *param; GValue gvalue = { 0, }; gname = gjs_hyphen_from_camel(name); param = g_object_class_find_property(G_OBJECT_GET_CLASS(priv->gobj), gname); g_free(gname); if (param == NULL) { /* leave value_p as it was */ return true; } /* Do not fetch JS overridden properties from GObject, to avoid * infinite recursion. */ if (g_param_spec_get_qdata(param, gjs_is_custom_property_quark())) return true; if ((param->flags & G_PARAM_READABLE) == 0) return true; gjs_debug_jsprop(GJS_DEBUG_GOBJECT, "Overriding %s with GObject prop %s", name, param->name); g_value_init(&gvalue, G_PARAM_SPEC_VALUE_TYPE(param)); g_object_get_property(priv->gobj, param->name, &gvalue); if (!gjs_value_from_g_value(context, value_p, &gvalue)) { g_value_unset(&gvalue); return false; } g_value_unset(&gvalue); return true; } static GIFieldInfo * lookup_field_info(GIObjectInfo *info, const char *name) { int n_fields = g_object_info_get_n_fields(info); int ix; GIFieldInfo *retval = NULL; for (ix = 0; ix < n_fields; ix++) { retval = g_object_info_get_field(info, ix); const char *field_name = g_base_info_get_name((GIBaseInfo *) retval); if (strcmp(name, field_name) == 0) break; g_clear_pointer(&retval, g_base_info_unref); } if (!retval) return nullptr; if (!(g_field_info_get_flags(retval) & GI_FIELD_IS_READABLE)) { g_base_info_unref(retval); return nullptr; } return retval; } static bool get_prop_from_field(JSContext *cx, JS::HandleObject obj, ObjectInstance *priv, const char *name, JS::MutableHandleValue value_p) { if (priv->info == NULL) return true; /* Not resolved, but no error; leave value_p untouched */ GIFieldInfo *field = lookup_field_info(priv->info, name); if (field == NULL) return true; bool retval = true; GITypeInfo *type = NULL; GITypeTag tag; GIArgument arg = { 0 }; gjs_debug_jsprop(GJS_DEBUG_GOBJECT, "Overriding %s with GObject field", name); type = g_field_info_get_type(field); tag = g_type_info_get_tag(type); if (tag == GI_TYPE_TAG_ARRAY || tag == GI_TYPE_TAG_INTERFACE || tag == GI_TYPE_TAG_GLIST || tag == GI_TYPE_TAG_GSLIST || tag == GI_TYPE_TAG_GHASH || tag == GI_TYPE_TAG_ERROR) { gjs_throw(cx, "Can't get field %s; GObject introspection supports only " "fields with simple types, not %s", name, g_type_tag_to_string(tag)); retval = false; goto out; } retval = g_field_info_get_field(field, priv->gobj, &arg); if (!retval) { gjs_throw(cx, "Error getting field %s from object", name); goto out; } retval = gjs_value_from_g_argument(cx, value_p, type, &arg, true); /* copy_structs is irrelevant because g_field_info_get_field() doesn't * handle boxed types */ out: if (type != NULL) g_base_info_unref((GIBaseInfo *) type); g_base_info_unref((GIBaseInfo *) field); return retval; } /* a hook on getting a property; set value_p to override property's value. * Return value is false on OOM/exception. */ static bool object_instance_get_prop(JSContext *context, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleValue value_p) { ObjectInstance *priv = priv_from_js(context, obj); gjs_debug_jsprop(GJS_DEBUG_GOBJECT, "Get prop '%s' hook, obj %s, priv %p", gjs_debug_id(id).c_str(), gjs_debug_object(obj).c_str(), priv); if (priv == nullptr) /* If we reach this point, either object_instance_new_resolve * did not throw (so name == "_init"), or the property actually * exists and it's not something we should be concerned with */ return true; if (priv->gobj == NULL) /* prototype, not an instance. */ return true; if (priv->g_object_finalized) { g_critical("Object %s.%s (%p), has been already finalized. " "Impossible to get any property from it.", priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "", priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype), priv->gobj); gjs_dumpstack(); return true; } GjsAutoJSChar name; if (!gjs_get_string_id(context, id, &name)) return true; /* not resolved, but no error */ if (!get_prop_from_g_param(context, obj, priv, name, value_p)) return false; if (!value_p.isUndefined()) return true; /* Fall back to fields */ return get_prop_from_field(context, obj, priv, name, value_p); } static bool set_g_param_from_prop(JSContext *context, ObjectInstance *priv, const char *name, bool& was_set, JS::HandleValue value_p, JS::ObjectOpResult& result) { GParameter param = { NULL, { 0, }}; was_set = false; switch (init_g_param_from_property(context, name, value_p, G_TYPE_FROM_INSTANCE(priv->gobj), ¶m, false /* constructing */)) { case SOME_ERROR_OCCURRED: return false; case NO_SUCH_G_PROPERTY: /* We need to keep the wrapper alive in order not to lose custom * "expando" properties */ ensure_uses_toggle_ref(context, priv); return result.succeed(); case VALUE_WAS_SET: default: break; } g_object_set_property(priv->gobj, param.name, ¶m.value); g_value_unset(¶m.value); was_set = true; return result.succeed(); } static bool check_set_field_from_prop(JSContext *cx, ObjectInstance *priv, const char *name, JS::MutableHandleValue value_p, JS::ObjectOpResult& result) { if (priv->info == NULL) return result.succeed(); GIFieldInfo *field = lookup_field_info(priv->info, name); if (field == NULL) return result.succeed(); bool retval = true; /* As far as I know, GI never exposes GObject instance struct fields as * writable, so no need to implement this for the time being */ if (g_field_info_get_flags(field) & GI_FIELD_IS_WRITABLE) { g_message("Field %s of a GObject is writable, but setting it is not " "implemented", name); result.succeed(); goto out; } result.failReadOnly(); /* still return true; error only in strict mode */ /* We have to update value_p because JS caches it as the property's "stored * value" (https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/JSAPI_reference/Stored_value) * and so subsequent gets would get the stored value instead of accessing * the field */ value_p.setUndefined(); out: g_base_info_unref((GIBaseInfo *) field); return retval; } /* a hook on setting a property; set value_p to override property value to * be set. Return value is false on OOM/exception. */ static bool object_instance_set_prop(JSContext *context, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleValue value_p, JS::ObjectOpResult& result) { ObjectInstance *priv; bool ret = true; bool g_param_was_set = false; priv = priv_from_js(context, obj); gjs_debug_jsprop(GJS_DEBUG_GOBJECT, "Set prop '%s' hook, obj %s, priv %p", gjs_debug_id(id).c_str(), gjs_debug_object(obj).c_str(), priv); if (priv == nullptr) /* see the comment in object_instance_get_prop() on this */ return result.succeed(); if (priv->gobj == NULL) /* prototype, not an instance. */ return result.succeed(); if (priv->g_object_finalized) { g_critical("Object %s.%s (%p), has been already finalized. " "Impossible to set any property to it.", priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "", priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype), priv->gobj); gjs_dumpstack(); return result.succeed(); } GjsAutoJSChar name; if (!gjs_get_string_id(context, id, &name)) { /* We need to keep the wrapper alive in order not to lose custom * "expando" properties. In this case if gjs_get_string_id() is false * then a number or symbol property was probably set. */ ensure_uses_toggle_ref(context, priv); return result.succeed(); /* not resolved, but no error */ } ret = set_g_param_from_prop(context, priv, name, g_param_was_set, value_p, result); if (g_param_was_set || !ret) return ret; /* note that the prop will also have been set in JS, which I think * is OK, since we hook get and set so will always override that * value. We could also use JS_DefineProperty though and specify a * getter/setter maybe, don't know if that is better. */ return check_set_field_from_prop(context, priv, name, value_p, result); } static bool is_vfunc_unchanged(GIVFuncInfo *info, GType gtype) { GType ptype = g_type_parent(gtype); GError *error = NULL; gpointer addr1, addr2; addr1 = g_vfunc_info_get_address(info, gtype, &error); if (error) { g_clear_error(&error); return false; } addr2 = g_vfunc_info_get_address(info, ptype, &error); if (error) { g_clear_error(&error); return false; } return addr1 == addr2; } static GIVFuncInfo * find_vfunc_on_parents(GIObjectInfo *info, const char *name, bool *out_defined_by_parent) { GIVFuncInfo *vfunc = NULL; GIObjectInfo *parent; bool defined_by_parent = false; /* ref the first info so that we don't destroy * it when unrefing parents later */ g_base_info_ref(info); parent = info; /* Since it isn't possible to override a vfunc on * an interface without reimplementing it, we don't need * to search the parent types when looking for a vfunc. */ vfunc = g_object_info_find_vfunc_using_interfaces(parent, name, NULL); while (!vfunc && parent) { GIObjectInfo *tmp = parent; parent = g_object_info_get_parent(tmp); g_base_info_unref(tmp); if (parent) vfunc = g_object_info_find_vfunc(parent, name); defined_by_parent = true; } if (parent) g_base_info_unref(parent); if (out_defined_by_parent) *out_defined_by_parent = defined_by_parent; return vfunc; } static bool object_instance_resolve_no_info(JSContext *context, JS::HandleObject obj, bool *resolved, ObjectInstance *priv, const char *name) { GIFunctionInfo *method_info; guint n_interfaces; guint i; GType *interfaces = g_type_interfaces(priv->gtype, &n_interfaces); for (i = 0; i < n_interfaces; i++) { GIBaseInfo *base_info; GIInterfaceInfo *iface_info; base_info = g_irepository_find_by_gtype(g_irepository_get_default(), interfaces[i]); if (base_info == NULL) continue; /* An interface GType ought to have interface introspection info */ g_assert (g_base_info_get_type(base_info) == GI_INFO_TYPE_INTERFACE); iface_info = (GIInterfaceInfo*) base_info; method_info = g_interface_info_find_method(iface_info, name); g_base_info_unref(base_info); if (method_info != NULL) { if (g_function_info_get_flags (method_info) & GI_FUNCTION_IS_METHOD) { if (!gjs_define_function(context, obj, priv->gtype, (GICallableInfo *)method_info)) { g_base_info_unref((GIBaseInfo*) method_info); g_free(interfaces); return false; } g_base_info_unref((GIBaseInfo*) method_info); *resolved = true; g_free(interfaces); return true; } g_base_info_unref( (GIBaseInfo*) method_info); } } *resolved = false; g_free(interfaces); return true; } /* Taken from GLib */ static void canonicalize_key(char *key) { for (char *p = key; *p != 0; p++) { char c = *p; if (c != '-' && (c < '0' || c > '9') && (c < 'A' || c > 'Z') && (c < 'a' || c > 'z')) *p = '-'; } } static bool is_gobject_property_name(GIObjectInfo *info, const char *name) { int n_props = g_object_info_get_n_properties(info); int ix; GIPropertyInfo *prop_info = nullptr; char *canonical_name = gjs_hyphen_from_camel(name); canonicalize_key(canonical_name); for (ix = 0; ix < n_props; ix++) { prop_info = g_object_info_get_property(info, ix); const char *prop_name = g_base_info_get_name(prop_info); if (strcmp(canonical_name, prop_name) == 0) break; g_clear_pointer(&prop_info, g_base_info_unref); } g_free(canonical_name); if (!prop_info) return false; if (!(g_property_info_get_flags(prop_info) & G_PARAM_READABLE)) { g_base_info_unref(prop_info); return false; } g_base_info_unref(prop_info); return true; } static bool is_gobject_field_name(GIObjectInfo *info, const char *name) { GIFieldInfo *field_info = lookup_field_info(info, name); if (!field_info) return false; g_base_info_unref(field_info); return true; } /* The *resolved out parameter, on success, should be false to indicate that id * was not resolved; and true if id was resolved. */ static bool object_instance_resolve(JSContext *context, JS::HandleObject obj, JS::HandleId id, bool *resolved) { GIFunctionInfo *method_info; ObjectInstance *priv = priv_from_js(context, obj); gjs_debug_jsprop(GJS_DEBUG_GOBJECT, "Resolve prop '%s' hook, obj %s, priv %p (%s.%s), gobj %p %s", gjs_debug_id(id).c_str(), gjs_debug_object(obj).c_str(), priv, priv && priv->info ? g_base_info_get_namespace (priv->info) : "", priv && priv->info ? g_base_info_get_name (priv->info) : "", priv ? priv->gobj : NULL, (priv && priv->gobj) ? g_type_name_from_instance((GTypeInstance*) priv->gobj) : "(type unknown)"); if (priv == NULL) { /* We won't have a private until the initializer is called, so * just defer to prototype chains in this case. * * This isn't too bad: either you get undefined if the field * doesn't exist on any of the prototype chains, or whatever code * will run afterwards will fail because of the "priv == NULL" * check there. */ *resolved = false; return true; } if (priv->gobj != NULL) { *resolved = false; return true; } if (priv->g_object_finalized) { g_critical("Object %s.%s (%p), has been already finalized. " "Impossible to resolve it.", priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "", priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype), priv->gobj); gjs_dumpstack(); *resolved = false; return true; } GjsAutoJSChar name; if (!gjs_get_string_id(context, id, &name)) { *resolved = false; return true; /* not resolved, but no error */ } /* If we have no GIRepository information (we're a JS GObject subclass), * we need to look at exposing interfaces. Look up our interfaces through * GType data, and then hope that *those* are introspectable. */ if (priv->info == NULL) { bool status = object_instance_resolve_no_info(context, obj, resolved, priv, name); return status; } if (g_str_has_prefix (name, "vfunc_")) { /* The only time we find a vfunc info is when we're the base * class that defined the vfunc. If we let regular prototype * chaining resolve this, we'd have the implementation for the base's * vfunc on the base class, without any other "real" implementations * in the way. If we want to expose a "real" vfunc implementation, * we need to go down to the parent infos and look at their VFuncInfos. * * This is good, but it's memory-hungry -- we would define every * possible vfunc on every possible object, even if it's the same * "real" vfunc underneath. Instead, only expose vfuncs that are * different from their parent, and let prototype chaining do the * rest. */ const char *name_without_vfunc_ = &(name[6]); /* lifetime tied to name */ GIVFuncInfo *vfunc; bool defined_by_parent; vfunc = find_vfunc_on_parents(priv->info, name_without_vfunc_, &defined_by_parent); if (vfunc != NULL) { /* In the event that the vfunc is unchanged, let regular * prototypal inheritance take over. */ if (defined_by_parent && is_vfunc_unchanged(vfunc, priv->gtype)) { g_base_info_unref((GIBaseInfo *)vfunc); *resolved = false; return true; } gjs_define_function(context, obj, priv->gtype, vfunc); *resolved = true; g_base_info_unref((GIBaseInfo *)vfunc); return true; } /* If the vfunc wasn't found, fall through, back to normal * method resolution. */ } /* If the name refers to a GObject property or field, don't resolve. * Instead, let the getProperty hook handle fetching the property from * GObject. */ if (is_gobject_property_name(priv->info, name) || is_gobject_field_name(priv->info, name)) { gjs_debug_jsprop(GJS_DEBUG_GOBJECT, "Breaking out of %p resolve, '%s' is a GObject prop", obj.get(), name.get()); *resolved = false; return true; } /* find_method does not look at methods on parent classes, * we rely on javascript to walk up the __proto__ chain * and find those and define them in the right prototype. * * Note that if it isn't a method on the object, since JS * lacks multiple inheritance, we're sticking the iface * methods in the object prototype, which means there are many * copies of the iface methods (one per object class node that * introduces the iface) */ method_info = g_object_info_find_method_using_interfaces(priv->info, name, NULL); /** * Search through any interfaces implemented by the GType; * this could be done better. See * https://bugzilla.gnome.org/show_bug.cgi?id=632922 */ if (method_info == NULL) { bool retval = object_instance_resolve_no_info(context, obj, resolved, priv, name); return retval; } #if GJS_VERBOSE_ENABLE_GI_USAGE _gjs_log_info_usage((GIBaseInfo*) method_info); #endif if (g_function_info_get_flags (method_info) & GI_FUNCTION_IS_METHOD) { gjs_debug(GJS_DEBUG_GOBJECT, "Defining method %s in prototype for %s (%s.%s)", g_base_info_get_name( (GIBaseInfo*) method_info), g_type_name(priv->gtype), g_base_info_get_namespace( (GIBaseInfo*) priv->info), g_base_info_get_name( (GIBaseInfo*) priv->info)); if (gjs_define_function(context, obj, priv->gtype, method_info) == NULL) { g_base_info_unref( (GIBaseInfo*) method_info); return false; } *resolved = true; /* we defined the prop in obj */ } else { *resolved = false; } g_base_info_unref( (GIBaseInfo*) method_info); return true; } /* Set properties from args to constructor (argv[0] is supposed to be * a hash) * The GParameter elements in the passed-in vector must be unset by the caller, * regardless of the return value of this function. */ static bool object_instance_props_to_g_parameters(JSContext *context, JSObject *obj, const JS::HandleValueArray& args, GType gtype, std::vector& gparams) { size_t ix, length; if (args.length() == 0 || args[0].isUndefined()) return true; if (!args[0].isObject()) { gjs_throw(context, "argument should be a hash with props to set"); return false; } JS::RootedObject props(context, &args[0].toObject()); JS::RootedId prop_id(context); JS::RootedValue value(context); JS::Rooted ids(context, context); if (!JS_Enumerate(context, props, &ids)) { gjs_throw(context, "Failed to create property iterator for object props hash"); return false; } for (ix = 0, length = ids.length(); ix < length; ix++) { GjsAutoJSChar name; GParameter gparam = { NULL, { 0, }}; /* ids[ix] is reachable because props is rooted, but require_property * doesn't know that */ prop_id = ids[ix]; if (!gjs_object_require_property(context, props, "property list", prop_id, &value) || !gjs_get_string_id(context, prop_id, &name)) return false; switch (init_g_param_from_property(context, name, value, gtype, &gparam, true /* constructing */)) { case NO_SUCH_G_PROPERTY: gjs_throw(context, "No property %s on this GObject %s", name.get(), g_type_name(gtype)); /* fallthrough */ case SOME_ERROR_OCCURRED: return false; case VALUE_WAS_SET: default: break; } gparams.push_back(gparam); } return true; } static GjsListLink * object_instance_get_link(ObjectInstance *priv) { return &priv->instance_link; } static void object_instance_unlink(ObjectInstance *priv) { if (wrapped_gobject_list == priv) wrapped_gobject_list = priv->instance_link.next(); priv->instance_link.unlink(); } static void object_instance_link(ObjectInstance *priv) { if (wrapped_gobject_list) priv->instance_link.prepend(priv, wrapped_gobject_list); wrapped_gobject_list = priv; } static void wrapped_gobj_dispose_notify(gpointer data, GObject *where_the_object_was) { auto *priv = static_cast(data); priv->g_object_finalized = true; object_instance_unlink(priv); gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Wrapped GObject %p disposed", where_the_object_was); } void gjs_object_context_dispose_notify(void *data, GObject *where_the_object_was) { ObjectInstance *priv = wrapped_gobject_list; while (priv) { ObjectInstance *next = priv->instance_link.next(); if (priv->keep_alive.rooted()) { gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "GObject wrapper %p for GObject " "%p (%s) was rooted but is now unrooted due to " "GjsContext dispose", priv->keep_alive.get(), priv->gobj, G_OBJECT_TYPE_NAME(priv->gobj)); priv->keep_alive.reset(); object_instance_unlink(priv); } priv = next; } } static void handle_toggle_down(GObject *gobj) { ObjectInstance *priv = get_object_qdata(gobj); gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Toggle notify DOWN for GObject " "%p (%s), JS obj %p", gobj, G_OBJECT_TYPE_NAME(gobj), priv->keep_alive.get()); /* Change to weak ref so the wrapper-wrappee pair can be * collected by the GC */ if (priv->keep_alive.rooted()) { GjsContext *context; gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Unrooting object"); priv->keep_alive.switch_to_unrooted(); /* During a GC, the collector asks each object which other * objects that it wants to hold on to so if there's an entire * section of the heap graph that's not connected to anything * else, and not reachable from the root set, then it can be * trashed all at once. * * GObjects, however, don't work like that, there's only a * reference count but no notion of who owns the reference so, * a JS object that's proxying a GObject is unconditionally held * alive as long as the GObject has >1 references. * * Since we cannot know how many more wrapped GObjects are going * be marked for garbage collection after the owner is destroyed, * always queue a garbage collection when a toggle reference goes * down. */ context = gjs_context_get_current(); if (!_gjs_context_destroying(context)) _gjs_context_schedule_gc(context); } } static void handle_toggle_up(GObject *gobj) { ObjectInstance *priv = get_object_qdata(gobj); /* We need to root the JSObject associated with the passed in GObject so it * doesn't get garbage collected (and lose any associated javascript state * such as custom properties). */ if (!priv->keep_alive) /* Object already GC'd */ return; gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Toggle notify UP for GObject " "%p (%s), JS obj %p", gobj, G_OBJECT_TYPE_NAME(gobj), priv->keep_alive.get()); /* Change to strong ref so the wrappee keeps the wrapper alive * in case the wrapper has data in it that the app cares about */ if (!priv->keep_alive.rooted()) { /* FIXME: thread the context through somehow. Maybe by looking up * the compartment that obj belongs to. */ GjsContext *context = gjs_context_get_current(); gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Rooting object"); auto cx = static_cast(gjs_context_get_native_context(context)); priv->keep_alive.switch_to_rooted(cx); } } static void toggle_handler(GObject *gobj, ToggleQueue::Direction direction) { switch (direction) { case ToggleQueue::UP: handle_toggle_up(gobj); break; case ToggleQueue::DOWN: handle_toggle_down(gobj); break; default: g_assert_not_reached(); } } static void wrapped_gobj_toggle_notify(gpointer data, GObject *gobj, gboolean is_last_ref) { bool is_main_thread; bool toggle_up_queued, toggle_down_queued; GjsContext *context; context = gjs_context_get_current(); if (_gjs_context_destroying(context)) { /* Do nothing here - we're in the process of disassociating * the objects. */ return; } /* We only want to touch javascript from one thread. * If we're not in that thread, then we need to defer processing * to it. * In case we're toggling up (and thus rooting the JS object) we * also need to take care if GC is running. The marking side * of it is taken care by JS::Heap, which we use in GjsMaybeOwned, * so we're safe. As for sweeping, it is too late: the JS object * is dead, and attempting to keep it alive would soon crash * the process. Plus, if we touch the JSAPI, libmozjs aborts in * the first BeginRequest. * Thus, we drain the toggle queue when GC starts, in order to * prevent this from happening. * In practice, a toggle up during JS finalize can only happen * for temporary refs/unrefs of objects that are garbage anyway, * because JS code is never invoked while the finalizers run * and C code needs to clean after itself before it returns * from dispose()/finalize(). * On the other hand, toggling down is a lot simpler, because * we're creating more garbage. So we just unroot the object, make it a * weak pointer, and wait for the next GC cycle. * * Note that one would think that toggling up only happens * in the main thread (because toggling up is the result of * the JS object, previously visible only to JS code, becoming * visible to the refcounted C world), but because of weird * weak singletons like g_bus_get_sync() objects can see toggle-ups * from different threads too. */ is_main_thread = _gjs_context_get_is_owner_thread(context); auto& toggle_queue = ToggleQueue::get_default(); std::tie(toggle_down_queued, toggle_up_queued) = toggle_queue.is_queued(gobj); if (is_last_ref) { /* We've transitions from 2 -> 1 references, * The JSObject is rooted and we need to unroot it so it * can be garbage collected */ if (is_main_thread) { if (G_UNLIKELY (toggle_up_queued || toggle_down_queued)) { g_error("toggling down object %s that's already queued to toggle %s\n", G_OBJECT_TYPE_NAME(gobj), toggle_up_queued && toggle_down_queued? "up and down" : toggle_up_queued? "up" : "down"); } handle_toggle_down(gobj); } else { toggle_queue.enqueue(gobj, ToggleQueue::DOWN, toggle_handler); } } else { /* We've transitioned from 1 -> 2 references. * * The JSObject associated with the gobject is not rooted, * but it needs to be. We'll root it. */ if (is_main_thread && !toggle_down_queued) { if (G_UNLIKELY (toggle_up_queued)) { g_error("toggling up object %s that's already queued to toggle up\n", G_OBJECT_TYPE_NAME(gobj)); } handle_toggle_up(gobj); } else { toggle_queue.enqueue(gobj, ToggleQueue::UP, toggle_handler); } } } static void release_native_object (ObjectInstance *priv) { priv->keep_alive.reset(); if (priv->uses_toggle_ref) g_object_remove_toggle_ref(priv->gobj, wrapped_gobj_toggle_notify, nullptr); else g_object_unref(priv->gobj); priv->gobj = NULL; } /* At shutdown, we need to ensure we've cleared the context of any * pending toggle references. */ void gjs_object_clear_toggles(void) { auto& toggle_queue = ToggleQueue::get_default(); while (toggle_queue.handle_toggle(toggle_handler)) ; } void gjs_object_shutdown_toggle_queue(void) { auto& toggle_queue = ToggleQueue::get_default(); toggle_queue.shutdown(); } void gjs_object_prepare_shutdown(void) { /* We iterate over all of the objects, breaking the JS <-> C * association. We avoid the potential recursion implied in: * toggle ref removal -> gobj dispose -> toggle ref notify * by emptying the toggle queue earlier in the shutdown sequence. */ std::vector to_be_released; ObjectInstance *link = wrapped_gobject_list; while (link) { ObjectInstance *next = link->instance_link.next(); if (link->keep_alive.rooted()) { to_be_released.push_back(link); object_instance_unlink(link); } link = next; } for (ObjectInstance *priv : to_be_released) release_native_object(priv); } static ObjectInstance * init_object_private (JSContext *context, JS::HandleObject object) { ObjectInstance *proto_priv; ObjectInstance *priv; JS_BeginRequest(context); priv = g_slice_new0(ObjectInstance); new (priv) ObjectInstance(); GJS_INC_COUNTER(object); g_assert(priv_from_js(context, object) == NULL); JS_SetPrivate(object, priv); proto_priv = proto_priv_from_js(context, object); g_assert(proto_priv != NULL); priv->gtype = proto_priv->gtype; priv->info = proto_priv->info; if (priv->info) g_base_info_ref( (GIBaseInfo*) priv->info); gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Instance constructor of %s, " "JS obj %p, priv %p", g_type_name(priv->gtype), object.get(), priv); JS_EndRequest(context); return priv; } static void update_heap_wrapper_weak_pointers(JSContext *cx, JSCompartment *compartment, gpointer data) { gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Weak pointer update callback, " "%zu wrapped GObject(s) to examine", wrapped_gobject_list->instance_link.size()); std::vector to_be_disassociated; ObjectInstance *priv = wrapped_gobject_list; while (priv) { ObjectInstance *next = priv->instance_link.next(); if (!priv->keep_alive.rooted() && priv->keep_alive != nullptr && priv->keep_alive.update_after_gc()) { /* Ouch, the JS object is dead already. Disassociate the * GObject and hope the GObject dies too. (Remove it from * the weak pointer list first, since the disassociation * may also cause it to be erased.) */ gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Found GObject weak pointer " "whose JS object %p is about to be finalized: " "%p (%s)", priv->keep_alive.get(), priv->gobj, G_OBJECT_TYPE_NAME(priv->gobj)); to_be_disassociated.push_back(priv); object_instance_unlink(priv); } priv = next; } for (ObjectInstance *ex_object : to_be_disassociated) disassociate_js_gobject(ex_object); } static void ensure_weak_pointer_callback(JSContext *cx) { if (!weak_pointer_callback) { JS_AddWeakPointerCompartmentCallback(cx, update_heap_wrapper_weak_pointers, nullptr); weak_pointer_callback = true; } } static void associate_js_gobject (JSContext *context, JS::HandleObject object, GObject *gobj) { ObjectInstance *priv; priv = priv_from_js(context, object); priv->uses_toggle_ref = false; priv->gobj = gobj; g_assert(!priv->keep_alive.rooted()); set_object_qdata(gobj, priv); priv->keep_alive = object; ensure_weak_pointer_callback(context); object_instance_link(priv); g_object_weak_ref(gobj, wrapped_gobj_dispose_notify, priv); } static void ensure_uses_toggle_ref(JSContext *cx, ObjectInstance *priv) { if (priv->uses_toggle_ref) return; g_assert(!priv->keep_alive.rooted()); /* OK, here is where things get complicated. We want the * wrapped gobj to keep the JSObject* wrapper alive, because * people might set properties on the JSObject* that they care * about. Therefore, whenever the refcount on the wrapped gobj * is >1, i.e. whenever something other than the wrapper is * referencing the wrapped gobj, the wrapped gobj has a strong * ref (gc-roots the wrapper). When the refcount on the * wrapped gobj is 1, then we change to a weak ref to allow * the wrapper to be garbage collected (and thus unref the * wrappee). */ priv->uses_toggle_ref = true; priv->keep_alive.switch_to_rooted(cx); g_object_add_toggle_ref(priv->gobj, wrapped_gobj_toggle_notify, nullptr); /* We now have both a ref and a toggle ref, we only want the toggle ref. * This may immediately remove the GC root we just added, since refcount * may drop to 1. */ g_object_unref(priv->gobj); } static void invalidate_all_closures(ObjectInstance *priv) { /* Can't loop directly through the items, since invalidating an item's * closure might have the effect of removing the item from the set in the * invalidate notifier */ while (!priv->closures.empty()) { /* This will also free cd, through the closure invalidation mechanism */ GClosure *closure = *priv->closures.begin(); g_closure_invalidate(closure); /* Erase element if not already erased */ priv->closures.erase(closure); } } static void disassociate_js_gobject(ObjectInstance *priv) { bool had_toggle_down, had_toggle_up; if (!priv->g_object_finalized) g_object_weak_unref(priv->gobj, wrapped_gobj_dispose_notify, priv); auto& toggle_queue = ToggleQueue::get_default(); std::tie(had_toggle_down, had_toggle_up) = toggle_queue.cancel(priv->gobj); if (had_toggle_down != had_toggle_up) { g_error("JS object wrapper for GObject %p (%s) is being released while " "toggle references are still pending.", priv->gobj, G_OBJECT_TYPE_NAME(priv->gobj)); } /* Fist, remove the wrapper pointer from the wrapped GObject */ set_object_qdata(priv->gobj, nullptr); /* Now release all the resources the current wrapper has */ invalidate_all_closures(priv); release_native_object(priv); /* Mark that a JS object once existed, but it doesn't any more */ priv->js_object_finalized = true; priv->keep_alive = nullptr; } static void clear_g_params(std::vector& params) { for (GParameter param : params) g_value_unset(¶m.value); } static bool object_instance_init (JSContext *context, JS::MutableHandleObject object, const JS::HandleValueArray& args) { ObjectInstance *priv; GType gtype; std::vector params; GTypeQuery query; GObject *gobj; priv = (ObjectInstance *) JS_GetPrivate(object); gtype = priv->gtype; g_assert(gtype != G_TYPE_NONE); if (G_TYPE_IS_ABSTRACT(gtype)) { gjs_throw(context, "Cannot instantiate abstract class %s", g_type_name(gtype)); return false; } if (!object_instance_props_to_g_parameters(context, object, args, gtype, params)) { clear_g_params(params); return false; } /* Mark this object in the construction stack, it will be popped in gjs_object_custom_init() later down. */ if (g_type_get_qdata(gtype, gjs_is_custom_type_quark())) { object_init_list.emplace(context, object); } G_GNUC_BEGIN_IGNORE_DEPRECATIONS gobj = (GObject*) g_object_newv(gtype, params.size(), params.data()); G_GNUC_END_IGNORE_DEPRECATIONS clear_g_params(params); ObjectInstance *other_priv = get_object_qdata(gobj); if (other_priv && other_priv->keep_alive != object.get()) { /* g_object_newv returned an object that's already tracked by a JS * object. Let's assume this is a singleton like IBus.IBus and return * the existing JS wrapper object. * * 'object' has a value that was originally created by * JS_NewObjectForConstructor in GJS_NATIVE_CONSTRUCTOR_PRELUDE, but * we're not actually using it, so just let it get collected. Avoiding * this would require a non-trivial amount of work. * */ ensure_uses_toggle_ref(context, other_priv); object.set(other_priv->keep_alive); g_object_unref(gobj); /* We already own a reference */ gobj = NULL; goto out; } g_type_query_dynamic_safe(gtype, &query); if (G_LIKELY (query.type)) JS_updateMallocCounter(context, query.instance_size); if (G_IS_INITIALLY_UNOWNED(gobj) && !g_object_is_floating(gobj)) { /* GtkWindow does not return a ref to caller of g_object_new. * Need a flag in gobject-introspection to tell us this. */ gjs_debug(GJS_DEBUG_GOBJECT, "Newly-created object is initially unowned but we did not get the " "floating ref, probably GtkWindow, using hacky workaround"); g_object_ref(gobj); } else if (g_object_is_floating(gobj)) { g_object_ref_sink(gobj); } else { /* we should already have a ref */ } if (priv->gobj == NULL) associate_js_gobject(context, object, gobj); gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "JSObject created with GObject %p (%s)", priv->gobj, G_OBJECT_TYPE_NAME(priv->gobj)); TRACE(GJS_OBJECT_PROXY_NEW(priv, priv->gobj, priv->info ? g_base_info_get_namespace((GIBaseInfo*) priv->info) : "_gjs_private", priv->info ? g_base_info_get_name((GIBaseInfo*) priv->info) : g_type_name(gtype))); out: return true; } GJS_NATIVE_CONSTRUCTOR_DECLARE(object_instance) { GJS_NATIVE_CONSTRUCTOR_VARIABLES(object_instance) bool ret; JS::RootedValue initer(context); GJS_NATIVE_CONSTRUCTOR_PRELUDE(object_instance); /* Init the private variable before we do anything else. If a garbage * collection happens when calling the init function then this object * might be traced and we will end up dereferencing a null pointer */ init_object_private(context, object); if (!gjs_object_require_property(context, object, "GObject instance", GJS_STRING_GOBJECT_INIT, &initer)) return false; argv.rval().setUndefined(); ret = gjs_call_function_value(context, object, initer, argv, argv.rval()); if (argv.rval().isUndefined()) argv.rval().setObject(*object); return ret; } static void object_instance_trace(JSTracer *tracer, JSObject *obj) { ObjectInstance *priv; priv = (ObjectInstance *) JS_GetPrivate(obj); if (priv == NULL) return; if (priv->g_object_finalized) { g_debug("Object %s.%s (%p), has been already finalized. " "Impossible to trace it.", priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "", priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype), priv->gobj); return; } for (GClosure *closure : priv->closures) gjs_closure_trace(closure, tracer); } static void closure_invalidated(void *data, GClosure *closure) { auto priv = static_cast(data); priv->closures.erase(closure); } static void object_instance_finalize(JSFreeOp *fop, JSObject *obj) { ObjectInstance *priv; priv = (ObjectInstance *) JS_GetPrivate(obj); g_assert (priv != NULL); gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Finalizing %s, JS obj %p, priv %p, GObject %p", g_type_name(priv->gtype), obj, priv, priv->gobj); TRACE(GJS_OBJECT_PROXY_FINALIZE(priv, priv->gobj, priv->info ? g_base_info_get_namespace((GIBaseInfo*) priv->info) : "_gjs_private", priv->info ? g_base_info_get_name((GIBaseInfo*) priv->info) : g_type_name(priv->gtype))); /* This applies only to instances, not prototypes, but it's possible that * an instance's GObject is already freed at this point. */ invalidate_all_closures(priv); /* Object is instance, not prototype, AND GObject is not already freed */ if (priv->gobj) { bool had_toggle_up; bool had_toggle_down; if (G_UNLIKELY (priv->gobj->ref_count <= 0)) { g_error("Finalizing proxy for an already freed object of type: %s.%s\n", priv->info ? g_base_info_get_namespace((GIBaseInfo*) priv->info) : "", priv->info ? g_base_info_get_name((GIBaseInfo*) priv->info) : g_type_name(priv->gtype)); } auto& toggle_queue = ToggleQueue::get_default(); std::tie(had_toggle_down, had_toggle_up) = toggle_queue.cancel(priv->gobj); if (!had_toggle_up && had_toggle_down) { g_error("Finalizing proxy for an object that's scheduled to be unrooted: %s.%s\n", priv->info ? g_base_info_get_namespace((GIBaseInfo*) priv->info) : "", priv->info ? g_base_info_get_name((GIBaseInfo*) priv->info) : g_type_name(priv->gtype)); } if (!priv->g_object_finalized) g_object_weak_unref(priv->gobj, wrapped_gobj_dispose_notify, priv); release_native_object(priv); } if (priv->keep_alive.rooted()) { /* This happens when the refcount on the object is still >1, * for example with global objects GDK never frees like GdkDisplay, * when we close down the JS runtime. */ gjs_debug(GJS_DEBUG_GOBJECT, "Wrapper was finalized despite being kept alive, has refcount >1"); gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Unrooting object"); priv->keep_alive.reset(); } object_instance_unlink(priv); if (priv->info) { g_base_info_unref( (GIBaseInfo*) priv->info); priv->info = NULL; } if (priv->klass) { g_type_class_unref (priv->klass); priv->klass = NULL; } GJS_DEC_COUNTER(object); priv->~ObjectInstance(); g_slice_free(ObjectInstance, priv); /* Remove the ObjectInstance pointer from the JSObject */ JS_SetPrivate(obj, nullptr); } static JSObject * gjs_lookup_object_constructor_from_info(JSContext *context, GIObjectInfo *info, GType gtype) { JS::RootedObject in_object(context); const char *constructor_name; if (info) { in_object = gjs_lookup_namespace_object(context, (GIBaseInfo*) info); constructor_name = g_base_info_get_name((GIBaseInfo*) info); } else { in_object = gjs_lookup_private_namespace(context); constructor_name = g_type_name(gtype); } if (G_UNLIKELY (!in_object)) return NULL; JS::RootedValue value(context); if (!JS_GetProperty(context, in_object, constructor_name, &value)) return NULL; JS::RootedObject constructor(context); if (value.isUndefined()) { /* In case we're looking for a private type, and we don't find it, we need to define it first. */ JS::RootedObject ignored(context); gjs_define_object_class(context, in_object, NULL, gtype, &constructor, &ignored); } else { if (G_UNLIKELY (!value.isObject())) return NULL; constructor = &value.toObject(); } g_assert(constructor); return constructor; } static JSObject * gjs_lookup_object_prototype_from_info(JSContext *context, GIObjectInfo *info, GType gtype) { JS::RootedObject constructor(context, gjs_lookup_object_constructor_from_info(context, info, gtype)); if (G_UNLIKELY(!constructor)) return NULL; JS::RootedValue value(context); if (!gjs_object_get_property(context, constructor, GJS_STRING_PROTOTYPE, &value)) return NULL; if (G_UNLIKELY (!value.isObjectOrNull())) return NULL; return value.toObjectOrNull(); } static JSObject * gjs_lookup_object_prototype(JSContext *context, GType gtype) { GIObjectInfo *info; JSObject *proto; info = (GIObjectInfo*)g_irepository_find_by_gtype(g_irepository_get_default(), gtype); proto = gjs_lookup_object_prototype_from_info(context, info, gtype); if (info) g_base_info_unref((GIBaseInfo*)info); return proto; } static void do_associate_closure(ObjectInstance *priv, GClosure *closure) { /* This is a weak reference, and will be cleared when the closure is * invalidated */ priv->closures.insert(closure); g_closure_add_invalidate_notifier(closure, priv, closure_invalidated); } static bool real_connect_func(JSContext *context, unsigned argc, JS::Value *vp, bool after) { GJS_GET_PRIV(context, argc, vp, argv, obj, ObjectInstance, priv); GClosure *closure; gulong id; guint signal_id; GQuark signal_detail; gjs_debug_gsignal("connect obj %p priv %p argc %d", obj.get(), priv, argc); if (priv == NULL) { throw_priv_is_null_error(context); return false; /* wrong class passed in */ } if (priv->gobj == NULL) { /* prototype, not an instance. */ gjs_throw(context, "Can't connect to signals on %s.%s.prototype; only on instances", priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "", priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype)); return false; } if (priv->g_object_finalized) { g_critical("Object %s.%s (%p), has been already deallocated - impossible to connect to signal. " "This might be caused by the fact that the object has been destroyed from C " "code using something such as destroy(), dispose(), or remove() vfuncs", priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "", priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype), priv->gobj); gjs_dumpstack(); return true; } ensure_uses_toggle_ref(context, priv); if (argc != 2 || !argv[0].isString() || !JS::IsCallable(&argv[1].toObject())) { gjs_throw(context, "connect() takes two args, the signal name and the callback"); return false; } JS::RootedString signal_str(context, argv[0].toString()); GjsAutoJSChar signal_name = JS_EncodeStringToUTF8(context, signal_str); if (!signal_name) return false; if (!g_signal_parse_name(signal_name, G_OBJECT_TYPE(priv->gobj), &signal_id, &signal_detail, true)) { gjs_throw(context, "No signal '%s' on object '%s'", signal_name.get(), g_type_name(G_OBJECT_TYPE(priv->gobj))); return false; } closure = gjs_closure_new_for_signal(context, &argv[1].toObject(), "signal callback", signal_id); if (closure == NULL) return false; do_associate_closure(priv, closure); id = g_signal_connect_closure_by_id(priv->gobj, signal_id, signal_detail, closure, after); argv.rval().setDouble(id); return true; } static bool connect_after_func(JSContext *context, unsigned argc, JS::Value *vp) { return real_connect_func(context, argc, vp, true); } static bool connect_func(JSContext *context, unsigned argc, JS::Value *vp) { return real_connect_func(context, argc, vp, false); } static bool emit_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_PRIV(context, argc, vp, argv, obj, ObjectInstance, priv); guint signal_id; GQuark signal_detail; GSignalQuery signal_query; GValue *instance_and_args; GValue rvalue = G_VALUE_INIT; unsigned int i; bool failed; gjs_debug_gsignal("emit obj %p priv %p argc %d", obj.get(), priv, argc); if (priv == NULL) { throw_priv_is_null_error(context); return false; /* wrong class passed in */ } if (priv->gobj == NULL) { /* prototype, not an instance. */ gjs_throw(context, "Can't emit signal on %s.%s.prototype; only on instances", priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "", priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype)); return false; } if (priv->g_object_finalized) { g_critical("Object %s.%s (%p), has been already deallocated - impossible to emit signal. " "This might be caused by the fact that the object has been destroyed from C " "code using something such as destroy(), dispose(), or remove() vfuncs", priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "", priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype), priv->gobj); gjs_dumpstack(); return true; } if (argc < 1 || !argv[0].isString()) { gjs_throw(context, "emit() first arg is the signal name"); return false; } JS::RootedString signal_str(context, argv[0].toString()); GjsAutoJSChar signal_name = JS_EncodeStringToUTF8(context, signal_str); if (!signal_name) return false; if (!g_signal_parse_name(signal_name, G_OBJECT_TYPE(priv->gobj), &signal_id, &signal_detail, false)) { gjs_throw(context, "No signal '%s' on object '%s'", signal_name.get(), g_type_name(G_OBJECT_TYPE(priv->gobj))); return false; } g_signal_query(signal_id, &signal_query); if ((argc - 1) != signal_query.n_params) { gjs_throw(context, "Signal '%s' on %s requires %d args got %d", signal_name.get(), g_type_name(G_OBJECT_TYPE(priv->gobj)), signal_query.n_params, argc - 1); return false; } if (signal_query.return_type != G_TYPE_NONE) { g_value_init(&rvalue, signal_query.return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE); } instance_and_args = g_newa(GValue, signal_query.n_params + 1); memset(instance_and_args, 0, sizeof(GValue) * (signal_query.n_params + 1)); g_value_init(&instance_and_args[0], G_TYPE_FROM_INSTANCE(priv->gobj)); g_value_set_instance(&instance_and_args[0], priv->gobj); failed = false; for (i = 0; i < signal_query.n_params; ++i) { GValue *value; value = &instance_and_args[i + 1]; g_value_init(value, signal_query.param_types[i] & ~G_SIGNAL_TYPE_STATIC_SCOPE); if ((signal_query.param_types[i] & G_SIGNAL_TYPE_STATIC_SCOPE) != 0) failed = !gjs_value_to_g_value_no_copy(context, argv[i + 1], value); else failed = !gjs_value_to_g_value(context, argv[i + 1], value); if (failed) break; } if (!failed) { g_signal_emitv(instance_and_args, signal_id, signal_detail, &rvalue); } if (signal_query.return_type != G_TYPE_NONE) { if (!gjs_value_from_g_value(context, argv.rval(), &rvalue)) failed = true; g_value_unset(&rvalue); } else { argv.rval().setUndefined(); } for (i = 0; i < (signal_query.n_params + 1); ++i) { g_value_unset(&instance_and_args[i]); } return !failed; } static bool to_string_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_PRIV(context, argc, vp, rec, obj, ObjectInstance, priv); if (priv == NULL) { throw_priv_is_null_error(context); return false; /* wrong class passed in */ } return _gjs_proxy_to_string_func(context, obj, (priv->g_object_finalized) ? "object (FINALIZED)" : "object", (GIBaseInfo*)priv->info, priv->gtype, priv->gobj, rec.rval()); } static const struct JSClassOps gjs_object_class_ops = { NULL, /* addProperty */ NULL, /* deleteProperty */ object_instance_get_prop, object_instance_set_prop, NULL, /* enumerate */ object_instance_resolve, nullptr, /* mayResolve */ object_instance_finalize, NULL, NULL, NULL, object_instance_trace, }; struct JSClass gjs_object_instance_class = { "GObject_Object", JSCLASS_HAS_PRIVATE | JSCLASS_FOREGROUND_FINALIZE, &gjs_object_class_ops }; static bool init_func (JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_THIS(context, argc, vp, argv, obj); bool ret; if (!do_base_typecheck(context, obj, true)) return false; ret = object_instance_init(context, &obj, argv); if (ret) argv.rval().setObject(*obj); return ret; } JSPropertySpec gjs_object_instance_proto_props[] = { JS_PS_END }; JSFunctionSpec gjs_object_instance_proto_funcs[] = { JS_FS("_init", init_func, 0, 0), JS_FS("connect", connect_func, 0, 0), JS_FS("connect_after", connect_after_func, 0, 0), JS_FS("emit", emit_func, 0, 0), JS_FS("toString", to_string_func, 0, 0), JS_FS_END }; void gjs_object_define_static_methods(JSContext *context, JS::HandleObject constructor, GType gtype, GIObjectInfo *object_info) { GIStructInfo *gtype_struct; int i; int n_methods; n_methods = g_object_info_get_n_methods(object_info); for (i = 0; i < n_methods; i++) { GIFunctionInfo *meth_info; GIFunctionInfoFlags flags; meth_info = g_object_info_get_method(object_info, i); flags = g_function_info_get_flags (meth_info); /* Anything that isn't a method we put on the prototype of the * constructor. This includes introspection * methods, as well as the forthcoming "static methods" * support. We may want to change this to use * GI_FUNCTION_IS_CONSTRUCTOR and GI_FUNCTION_IS_STATIC or the * like in the near future. */ if (!(flags & GI_FUNCTION_IS_METHOD)) { if (!gjs_define_function(context, constructor, gtype, (GICallableInfo *) meth_info)) gjs_log_exception(context); } g_base_info_unref((GIBaseInfo*) meth_info); } gtype_struct = g_object_info_get_class_struct(object_info); if (gtype_struct == NULL) return; n_methods = g_struct_info_get_n_methods(gtype_struct); for (i = 0; i < n_methods; i++) { GIFunctionInfo *meth_info; meth_info = g_struct_info_get_method(gtype_struct, i); if (!gjs_define_function(context, constructor, gtype, (GICallableInfo *) meth_info)) gjs_log_exception(context); g_base_info_unref((GIBaseInfo*) meth_info); } g_base_info_unref((GIBaseInfo*) gtype_struct); } void gjs_define_object_class(JSContext *context, JS::HandleObject in_object, GIObjectInfo *info, GType gtype, JS::MutableHandleObject constructor, JS::MutableHandleObject prototype) { const char *constructor_name; JS::RootedObject parent_proto(context); ObjectInstance *priv; const char *ns; GType parent_type; g_assert(in_object); g_assert(gtype != G_TYPE_INVALID); /* http://egachine.berlios.de/embedding-sm-best-practice/apa.html * http://www.sitepoint.com/blogs/2006/01/17/javascript-inheritance/ * http://www.cs.rit.edu/~atk/JavaScript/manuals/jsobj/ * * What we want is: * * repoobj.Gtk.Window is constructor for a GtkWindow wrapper JSObject * (gjs_define_object_constructor() is supposed to define Window in Gtk) * * Window.prototype contains the methods on Window, e.g. set_default_size() * mywindow.__proto__ is Window.prototype * mywindow.__proto__.__proto__ is Bin.prototype * mywindow.__proto__.__proto__.__proto__ is Container.prototype * * Because Window.prototype is an instance of Window in a sense, * Window.prototype.__proto__ is Window.prototype, just as * mywindow.__proto__ is Window.prototype * * If we do "mywindow = new Window()" then we should get: * mywindow.__proto__ == Window.prototype * which means "mywindow instanceof Window" is true. * * Remember "Window.prototype" is "the __proto__ of stuff * constructed with new Window()" * * __proto__ is used to search for properties if you do "this.foo" * while __parent__ defines the scope to search if you just have * "foo". * * __proto__ is used to look up properties, while .prototype is only * relevant for constructors and is used to set __proto__ on new'd * objects. So .prototype only makes sense on constructors. * * JS_SetPrototype() and JS_GetPrototype() are for __proto__. * To set/get .prototype, just use the normal property accessors, * or JS_InitClass() sets it up automatically. * * JavaScript is SO AWESOME */ parent_type = g_type_parent(gtype); if (parent_type != G_TYPE_INVALID) parent_proto = gjs_lookup_object_prototype(context, parent_type); ns = gjs_get_names_from_gtype_and_gi_info(gtype, (GIBaseInfo *) info, &constructor_name); if (!gjs_init_class_dynamic(context, in_object, parent_proto, ns, constructor_name, &gjs_object_instance_class, gjs_object_instance_constructor, 0, /* props of prototype */ parent_proto ? NULL : &gjs_object_instance_proto_props[0], /* funcs of prototype */ parent_proto ? NULL : &gjs_object_instance_proto_funcs[0], /* props of constructor, MyConstructor.myprop */ NULL, /* funcs of constructor, MyConstructor.myfunc() */ NULL, prototype, constructor)) { g_error("Can't init class %s", constructor_name); } GJS_INC_COUNTER(object); priv = g_slice_new0(ObjectInstance); new (priv) ObjectInstance(); priv->info = info; if (info) g_base_info_ref((GIBaseInfo*) info); priv->gtype = gtype; priv->klass = (GTypeClass*) g_type_class_ref (gtype); JS_SetPrivate(prototype, priv); gjs_debug(GJS_DEBUG_GOBJECT, "Defined class for %s (%s), prototype %p, " "JSClass %p, in object %p", constructor_name, g_type_name(gtype), prototype.get(), JS_GetClass(prototype), in_object.get()); if (info) gjs_object_define_static_methods(context, constructor, gtype, info); JS::RootedObject gtype_obj(context, gjs_gtype_create_gtype_wrapper(context, gtype)); JS_DefineProperty(context, constructor, "$gtype", gtype_obj, JSPROP_PERMANENT); } JSObject* gjs_object_from_g_object(JSContext *context, GObject *gobj) { if (gobj == NULL) return NULL; ObjectInstance *priv = get_object_qdata(gobj); if (!priv) { /* We have to create a wrapper */ GType gtype; gjs_debug_marshal(GJS_DEBUG_GOBJECT, "Wrapping %s with JSObject", g_type_name_from_instance((GTypeInstance*) gobj)); gtype = G_TYPE_FROM_INSTANCE(gobj); JS::RootedObject proto(context, gjs_lookup_object_prototype(context, gtype)); if (!proto) return nullptr; JS::RootedObject obj(context, JS_NewObjectWithGivenProto(context, JS_GetClass(proto), proto)); if (!obj) return nullptr; priv = init_object_private(context, obj); g_object_ref_sink(gobj); associate_js_gobject(context, obj, gobj); g_assert(priv->keep_alive == obj.get()); } return priv->keep_alive; } GObject* gjs_g_object_from_object(JSContext *context, JS::HandleObject obj) { ObjectInstance *priv; if (!obj) return NULL; priv = priv_from_js(context, obj); return priv->gobj; } bool gjs_typecheck_is_object(JSContext *context, JS::HandleObject object, bool throw_error) { return do_base_typecheck(context, object, throw_error); } bool gjs_typecheck_object(JSContext *context, JS::HandleObject object, GType expected_type, bool throw_error) { ObjectInstance *priv; bool result; if (!do_base_typecheck(context, object, throw_error)) return false; priv = priv_from_js(context, object); if (priv == NULL) { if (throw_error) { gjs_throw(context, "Object instance or prototype has not been properly initialized yet. " "Did you forget to chain-up from _init()?"); } return false; } if (priv->gobj == NULL) { if (throw_error) { gjs_throw(context, "Object is %s.%s.prototype, not an object instance - cannot convert to GObject*", priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "", priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype)); } return false; } if (priv->g_object_finalized) { g_critical("Object %s.%s (%p), has been already deallocated - impossible to access to it. " "This might be caused by the fact that the object has been destroyed from C " "code using something such as destroy(), dispose(), or remove() vfuncs", priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "", priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype), priv->gobj); gjs_dumpstack(); return true; } g_assert(priv->gtype == G_OBJECT_TYPE(priv->gobj)); if (expected_type != G_TYPE_NONE) result = g_type_is_a (priv->gtype, expected_type); else result = true; if (!result && throw_error) { if (priv->info) { gjs_throw_custom(context, JSProto_TypeError, nullptr, "Object is of type %s.%s - cannot convert to %s", g_base_info_get_namespace((GIBaseInfo*) priv->info), g_base_info_get_name((GIBaseInfo*) priv->info), g_type_name(expected_type)); } else { gjs_throw_custom(context, JSProto_TypeError, nullptr, "Object is of type %s - cannot convert to %s", g_type_name(priv->gtype), g_type_name(expected_type)); } } return result; } static void find_vfunc_info (JSContext *context, GType implementor_gtype, GIBaseInfo *vfunc_info, const char *vfunc_name, gpointer *implementor_vtable_ret, GIFieldInfo **field_info_ret) { GType ancestor_gtype; int length, i; GIBaseInfo *ancestor_info; GIStructInfo *struct_info; gpointer implementor_class; bool is_interface; *field_info_ret = NULL; *implementor_vtable_ret = NULL; ancestor_info = g_base_info_get_container(vfunc_info); ancestor_gtype = g_registered_type_info_get_g_type((GIRegisteredTypeInfo*)ancestor_info); is_interface = g_base_info_get_type(ancestor_info) == GI_INFO_TYPE_INTERFACE; implementor_class = g_type_class_ref(implementor_gtype); if (is_interface) { GTypeInstance *implementor_iface_class; implementor_iface_class = (GTypeInstance*) g_type_interface_peek(implementor_class, ancestor_gtype); if (implementor_iface_class == NULL) { g_type_class_unref(implementor_class); gjs_throw (context, "Couldn't find GType of implementor of interface %s.", g_type_name(ancestor_gtype)); return; } *implementor_vtable_ret = implementor_iface_class; struct_info = g_interface_info_get_iface_struct((GIInterfaceInfo*)ancestor_info); } else { struct_info = g_object_info_get_class_struct((GIObjectInfo*)ancestor_info); *implementor_vtable_ret = implementor_class; } g_type_class_unref(implementor_class); length = g_struct_info_get_n_fields(struct_info); for (i = 0; i < length; i++) { GIFieldInfo *field_info; GITypeInfo *type_info; field_info = g_struct_info_get_field(struct_info, i); if (strcmp(g_base_info_get_name((GIBaseInfo*)field_info), vfunc_name) != 0) { g_base_info_unref(field_info); continue; } type_info = g_field_info_get_type(field_info); if (g_type_info_get_tag(type_info) != GI_TYPE_TAG_INTERFACE) { /* We have a field with the same name, but it's not a callback. * There's no hope of being another field with a correct name, * so just abort early. */ g_base_info_unref(type_info); g_base_info_unref(field_info); break; } else { g_base_info_unref(type_info); *field_info_ret = field_info; break; } } g_base_info_unref(struct_info); } static bool gjs_hook_up_vfunc(JSContext *cx, unsigned argc, JS::Value *vp) { JS::CallArgs argv = JS::CallArgsFromVp (argc, vp); GjsAutoJSChar name; JS::RootedObject object(cx), function(cx); ObjectInstance *priv; GType gtype, info_gtype; GIObjectInfo *info; GIVFuncInfo *vfunc; gpointer implementor_vtable; GIFieldInfo *field_info; if (!gjs_parse_call_args(cx, "hook_up_vfunc", argv, "oso", "object", &object, "name", &name, "function", &function)) return false; if (!do_base_typecheck(cx, object, true)) return false; priv = priv_from_js(cx, object); gtype = priv->gtype; info = priv->info; /* find the first class that actually has repository information */ info_gtype = gtype; while (!info && info_gtype != G_TYPE_OBJECT) { info_gtype = g_type_parent(info_gtype); info = g_irepository_find_by_gtype(g_irepository_get_default(), info_gtype); } /* If we don't have 'info', we don't have the base class (GObject). * This is awful, so abort now. */ g_assert(info != NULL); argv.rval().setUndefined(); vfunc = find_vfunc_on_parents(info, name, NULL); if (!vfunc) { guint i, n_interfaces; GType *interface_list; GIInterfaceInfo *interface; interface_list = g_type_interfaces(gtype, &n_interfaces); for (i = 0; i < n_interfaces; i++) { interface = (GIInterfaceInfo*)g_irepository_find_by_gtype(g_irepository_get_default(), interface_list[i]); /* The interface doesn't have to exist -- it could be private * or dynamic. */ if (interface) { vfunc = g_interface_info_find_vfunc(interface, name); g_base_info_unref((GIBaseInfo*)interface); if (vfunc) break; } } g_free(interface_list); } if (!vfunc) { gjs_throw(cx, "Could not find definition of virtual function %s", name.get()); return false; } find_vfunc_info(cx, gtype, vfunc, name, &implementor_vtable, &field_info); if (field_info != NULL) { gint offset; gpointer method_ptr; GjsCallbackTrampoline *trampoline; offset = g_field_info_get_offset(field_info); method_ptr = G_STRUCT_MEMBER_P(implementor_vtable, offset); JS::RootedValue v_function(cx, JS::ObjectValue(*function)); trampoline = gjs_callback_trampoline_new(cx, v_function, vfunc, GI_SCOPE_TYPE_NOTIFIED, object, true); *((ffi_closure **)method_ptr) = trampoline->closure; g_base_info_unref(field_info); } g_base_info_unref(vfunc); return true; } static gchar * hyphen_to_underscore (gchar *string) { gchar *str, *s; str = s = g_strdup(string); while (*(str++) != '\0') { if (*str == '-') *str = '_'; } return s; } static void gjs_object_get_gproperty (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GjsContext *gjs_context; JSContext *context; gchar *underscore_name; ObjectInstance *priv = get_object_qdata(object); gjs_context = gjs_context_get_current(); context = (JSContext*) gjs_context_get_native_context(gjs_context); JS::RootedObject js_obj(context, priv->keep_alive); JS::RootedValue jsvalue(context); underscore_name = hyphen_to_underscore((gchar *)pspec->name); if (!JS_GetProperty(context, js_obj, underscore_name, &jsvalue) || !gjs_value_to_g_value(context, jsvalue, value)) gjs_log_exception(context); g_free (underscore_name); } static void jsobj_set_gproperty(JSContext *context, JS::HandleObject object, const GValue *value, GParamSpec *pspec) { JS::RootedValue jsvalue(context); gchar *underscore_name; if (!gjs_value_from_g_value(context, &jsvalue, value)) return; underscore_name = hyphen_to_underscore((gchar *)pspec->name); if (!JS_SetProperty(context, object, underscore_name, jsvalue)) gjs_log_exception(context); g_free (underscore_name); } static GObject * gjs_object_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { if (!object_init_list.empty()) { GType parent_type = g_type_parent(type); /* The object is being constructed from JS: * Simply chain up to the first non-gjs constructor */ while (G_OBJECT_CLASS(g_type_class_peek(parent_type))->constructor == gjs_object_constructor) parent_type = g_type_parent(parent_type); return G_OBJECT_CLASS(g_type_class_peek(parent_type))->constructor(type, n_construct_properties, construct_properties); } GjsContext *gjs_context; JSContext *context; JSObject *object; ObjectInstance *priv; /* The object is being constructed from native code (e.g. GtkBuilder): * Construct the JS object from the constructor, then use the GObject * that was associated in gjs_object_custom_init() */ gjs_context = gjs_context_get_current(); context = (JSContext*) gjs_context_get_native_context(gjs_context); JSAutoRequest ar(context); JSAutoCompartment ac(context, gjs_get_import_global(context)); JS::RootedObject constructor(context, gjs_lookup_object_constructor_from_info(context, NULL, type)); if (!constructor) return NULL; if (n_construct_properties) { guint i; JS::RootedObject props_hash(context, JS_NewPlainObject(context)); for (i = 0; i < n_construct_properties; i++) jsobj_set_gproperty(context, props_hash, construct_properties[i].value, construct_properties[i].pspec); JS::AutoValueArray<1> args(context); args[0].set(JS::ObjectValue(*props_hash)); object = JS_New(context, constructor, args); } else { object = JS_New(context, constructor, JS::HandleValueArray::empty()); } if (!object) return NULL; priv = (ObjectInstance*) JS_GetPrivate(object); /* We only hold a toggle ref at this point, add back a ref that the * native code can own. */ return G_OBJECT(g_object_ref(priv->gobj)); } static void gjs_object_set_gproperty (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GjsContext *gjs_context; JSContext *context; ObjectInstance *priv = get_object_qdata(object); gjs_context = gjs_context_get_current(); context = (JSContext*) gjs_context_get_native_context(gjs_context); JS::RootedObject js_obj(context, priv->keep_alive); jsobj_set_gproperty(context, js_obj, value, pspec); } static bool gjs_override_property(JSContext *cx, unsigned argc, JS::Value *vp) { JS::CallArgs args = JS::CallArgsFromVp(argc, vp); GjsAutoJSChar name; JS::RootedObject type(cx); GParamSpec *pspec; GParamSpec *new_pspec; GType gtype; if (!gjs_parse_call_args(cx, "override_property", args, "so", "name", &name, "type", &type)) return false; if ((gtype = gjs_gtype_get_actual_gtype(cx, type)) == G_TYPE_INVALID) { gjs_throw(cx, "Invalid parameter type was not a GType"); return false; } if (g_type_is_a(gtype, G_TYPE_INTERFACE)) { GTypeInterface *interface_type = (GTypeInterface *) g_type_default_interface_ref(gtype); pspec = g_object_interface_find_property(interface_type, name); g_type_default_interface_unref(interface_type); } else { GTypeClass *class_type = (GTypeClass *) g_type_class_ref(gtype); pspec = g_object_class_find_property(G_OBJECT_CLASS(class_type), name); g_type_class_unref(class_type); } if (pspec == NULL) { gjs_throw(cx, "No such property '%s' to override on type '%s'", name.get(), g_type_name(gtype)); return false; } new_pspec = g_param_spec_override(name, pspec); g_param_spec_set_qdata(new_pspec, gjs_is_custom_property_quark(), GINT_TO_POINTER(1)); args.rval().setObject(*gjs_param_from_g_param(cx, new_pspec)); g_param_spec_unref(new_pspec); return true; } static void gjs_interface_init(GTypeInterface *g_iface, gpointer iface_data) { GType gtype = G_TYPE_FROM_INTERFACE(g_iface); auto found = class_init_properties.find(gtype); if (found == class_init_properties.end()) return; ParamRefArray& properties = found->second; for (ParamRef& pspec : properties) { g_param_spec_set_qdata(pspec.get(), gjs_is_custom_property_quark(), GINT_TO_POINTER(1)); g_object_interface_install_property(g_iface, pspec.get()); } class_init_properties.erase(found); } static void gjs_object_class_init(GObjectClass *klass, gpointer user_data) { GType gtype = G_OBJECT_CLASS_TYPE(klass); klass->constructor = gjs_object_constructor; klass->set_property = gjs_object_set_gproperty; klass->get_property = gjs_object_get_gproperty; auto found = class_init_properties.find(gtype); if (found == class_init_properties.end()) return; ParamRefArray& properties = found->second; unsigned i = 0; for (ParamRef& pspec : properties) { g_param_spec_set_qdata(pspec.get(), gjs_is_custom_property_quark(), GINT_TO_POINTER(1)); g_object_class_install_property(klass, ++i, pspec.get()); } class_init_properties.erase(found); } static void gjs_object_custom_init(GTypeInstance *instance, gpointer klass) { GjsContext *gjs_context; JSContext *context; ObjectInstance *priv; if (object_init_list.empty()) return; gjs_context = gjs_context_get_current(); context = (JSContext*) gjs_context_get_native_context(gjs_context); JS::RootedObject object(context, object_init_list.top().get()); priv = (ObjectInstance*) JS_GetPrivate(object); if (priv->gtype != G_TYPE_FROM_INSTANCE (instance)) { /* This is not the most derived instance_init function, do nothing. */ return; } object_init_list.pop(); associate_js_gobject(context, object, G_OBJECT (instance)); /* Custom JS objects will most likely have visible state, so * just do this from the start */ ensure_uses_toggle_ref(context, priv); JS::RootedValue v(context); if (!gjs_object_get_property(context, object, GJS_STRING_INSTANCE_INIT, &v)) { gjs_log_exception(context); return; } if (!v.isObject()) return; JS::RootedValue r(context); if (!JS_CallFunctionValue(context, object, v, JS::HandleValueArray::empty(), &r)) gjs_log_exception(context); } static inline void gjs_add_interface(GType instance_type, GType interface_type) { static GInterfaceInfo interface_vtable = { NULL, NULL, NULL }; g_type_add_interface_static(instance_type, interface_type, &interface_vtable); } static bool validate_interfaces_and_properties_args(JSContext *cx, JS::HandleObject interfaces, JS::HandleObject properties, uint32_t *n_interfaces, uint32_t *n_properties) { guint32 n_int, n_prop; bool is_array; if (!JS_IsArrayObject(cx, interfaces, &is_array)) return false; if (!is_array) { gjs_throw(cx, "Invalid parameter interfaces (expected Array)"); return false; } if (!JS_GetArrayLength(cx, interfaces, &n_int)) return false; if (!JS_IsArrayObject(cx, properties, &is_array)) return false; if (!is_array) { gjs_throw(cx, "Invalid parameter properties (expected Array)"); return false; } if (!JS_GetArrayLength(cx, properties, &n_prop)) return false; if (n_interfaces != NULL) *n_interfaces = n_int; if (n_properties != NULL) *n_properties = n_prop; return true; } static bool get_interface_gtypes(JSContext *cx, JS::HandleObject interfaces, uint32_t n_interfaces, GType *iface_types) { guint32 i; for (i = 0; i < n_interfaces; i++) { JS::RootedValue iface_val(cx); GType iface_type; if (!JS_GetElement(cx, interfaces, i, &iface_val)) return false; if (!iface_val.isObject()) { gjs_throw (cx, "Invalid parameter interfaces (element %d was not a GType)", i); return false; } JS::RootedObject iface(cx, &iface_val.toObject()); iface_type = gjs_gtype_get_actual_gtype(cx, iface); if (iface_type == G_TYPE_INVALID) { gjs_throw (cx, "Invalid parameter interfaces (element %d was not a GType)", i); return false; } iface_types[i] = iface_type; } return true; } static bool save_properties_for_class_init(JSContext *cx, JS::HandleObject properties, uint32_t n_properties, GType gtype) { ParamRefArray properties_native; JS::RootedValue prop_val(cx); JS::RootedObject prop_obj(cx); for (uint32_t i = 0; i < n_properties; i++) { if (!JS_GetElement(cx, properties, i, &prop_val)) return false; if (!prop_val.isObject()) { gjs_throw(cx, "Invalid parameter, expected object"); return false; } prop_obj = &prop_val.toObject(); if (!gjs_typecheck_param(cx, prop_obj, G_TYPE_NONE, true)) return false; properties_native.emplace_back(g_param_spec_ref(gjs_g_param_from_param(cx, prop_obj)), g_param_spec_unref); } class_init_properties[gtype] = std::move(properties_native); return true; } static bool gjs_register_interface(JSContext *cx, unsigned argc, JS::Value *vp) { JS::CallArgs args = JS::CallArgsFromVp(argc, vp); GjsAutoJSChar name; guint32 i, n_interfaces, n_properties; GType *iface_types; GType interface_type; GTypeInfo type_info = { sizeof (GTypeInterface), /* class_size */ (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) gjs_interface_init, (GClassFinalizeFunc) NULL, NULL, /* class_data */ 0, /* instance_size */ 0, /* n_preallocs */ NULL, /* instance_init */ }; JS::RootedObject interfaces(cx), properties(cx); if (!gjs_parse_call_args(cx, "register_interface", args, "soo", "name", &name, "interfaces", &interfaces, "properties", &properties)) return false; if (!validate_interfaces_and_properties_args(cx, interfaces, properties, &n_interfaces, &n_properties)) return false; iface_types = (GType *) g_alloca(sizeof(GType) * n_interfaces); /* We do interface addition in two passes so that any failure is caught early, before registering the GType (which we can't undo) */ if (!get_interface_gtypes(cx, interfaces, n_interfaces, iface_types)) return false; if (g_type_from_name(name) != G_TYPE_INVALID) { gjs_throw(cx, "Type name %s is already registered", name.get()); return false; } interface_type = g_type_register_static(G_TYPE_INTERFACE, name, &type_info, (GTypeFlags) 0); g_type_set_qdata(interface_type, gjs_is_custom_type_quark(), GINT_TO_POINTER(1)); if (!save_properties_for_class_init(cx, properties, n_properties, interface_type)) return false; for (i = 0; i < n_interfaces; i++) g_type_interface_add_prerequisite(interface_type, iface_types[i]); /* create a custom JSClass */ JS::RootedObject module(cx, gjs_lookup_private_namespace(cx)); if (!module) return false; /* error will have been thrown already */ JS::RootedObject constructor(cx); gjs_define_interface_class(cx, module, NULL, interface_type, &constructor); args.rval().setObject(*constructor); return true; } static void gjs_object_base_init(void *klass) { auto priv = static_cast(g_type_get_qdata(G_OBJECT_CLASS_TYPE(klass), gjs_object_priv_quark())); if (priv) { for (GClosure *closure : priv->closures) g_closure_ref(closure); } } static void gjs_object_base_finalize(void *klass) { auto priv = static_cast(g_type_get_qdata(G_OBJECT_CLASS_TYPE(klass), gjs_object_priv_quark())); if (priv) { for (GClosure *closure : priv->closures) g_closure_unref(closure); } } static bool gjs_register_type(JSContext *cx, unsigned argc, JS::Value *vp) { JS::CallArgs argv = JS::CallArgsFromVp (argc, vp); GjsAutoJSChar name; GType instance_type, parent_type; GTypeQuery query; ObjectInstance *parent_priv; GTypeInfo type_info = { 0, /* class_size */ gjs_object_base_init, gjs_object_base_finalize, (GClassInitFunc) gjs_object_class_init, (GClassFinalizeFunc) NULL, NULL, /* class_data */ 0, /* instance_size */ 0, /* n_preallocs */ gjs_object_custom_init, }; guint32 i, n_interfaces, n_properties; GType *iface_types; JSAutoRequest ar(cx); JS::RootedObject parent(cx), interfaces(cx), properties(cx); if (!gjs_parse_call_args(cx, "register_type", argv, "osoo", "parent", &parent, "name", &name, "interfaces", &interfaces, "properties", &properties)) return false; if (!parent) return false; if (!do_base_typecheck(cx, parent, true)) return false; if (!validate_interfaces_and_properties_args(cx, interfaces, properties, &n_interfaces, &n_properties)) return false; iface_types = (GType*) g_alloca(sizeof(GType) * n_interfaces); /* We do interface addition in two passes so that any failure is caught early, before registering the GType (which we can't undo) */ if (!get_interface_gtypes(cx, interfaces, n_interfaces, iface_types)) return false; if (g_type_from_name(name) != G_TYPE_INVALID) { gjs_throw(cx, "Type name %s is already registered", name.get()); return false; } parent_priv = priv_from_js(cx, parent); /* We checked parent above, in do_base_typecheck() */ g_assert(parent_priv != NULL); parent_type = parent_priv->gtype; g_type_query_dynamic_safe(parent_type, &query); if (G_UNLIKELY (query.type == 0)) { gjs_throw (cx, "Cannot inherit from a non-gjs dynamic type [bug 687184]"); return false; } type_info.class_size = query.class_size; type_info.instance_size = query.instance_size; instance_type = g_type_register_static(parent_type, name, &type_info, (GTypeFlags) 0); g_type_set_qdata (instance_type, gjs_is_custom_type_quark(), GINT_TO_POINTER (1)); if (!save_properties_for_class_init(cx, properties, n_properties, instance_type)) return false; for (i = 0; i < n_interfaces; i++) gjs_add_interface(instance_type, iface_types[i]); /* create a custom JSClass */ JS::RootedObject module(cx, gjs_lookup_private_namespace(cx)); JS::RootedObject constructor(cx), prototype(cx); gjs_define_object_class(cx, module, nullptr, instance_type, &constructor, &prototype); ObjectInstance *priv = priv_from_js(cx, prototype); g_type_set_qdata(instance_type, gjs_object_priv_quark(), priv); argv.rval().setObject(*constructor); return true; } static bool gjs_signal_new(JSContext *cx, unsigned argc, JS::Value *vp) { JS::CallArgs argv = JS::CallArgsFromVp (argc, vp); GType gtype; GjsAutoJSChar signal_name; GSignalAccumulator accumulator; gint signal_id; guint i, n_parameters; GType *params, return_type; if (argc != 6) return false; JSAutoRequest ar(cx); if (!gjs_string_to_utf8(cx, argv[1], &signal_name)) return false; JS::RootedObject obj(cx, &argv[0].toObject()); if (!gjs_typecheck_gtype(cx, obj, true)) return false; /* we only support standard accumulators for now */ switch (argv[3].toInt32()) { case 1: accumulator = g_signal_accumulator_first_wins; break; case 2: accumulator = g_signal_accumulator_true_handled; break; case 0: default: accumulator = NULL; } JS::RootedObject gtype_obj(cx, &argv[4].toObject()); return_type = gjs_gtype_get_actual_gtype(cx, gtype_obj); if (accumulator == g_signal_accumulator_true_handled && return_type != G_TYPE_BOOLEAN) { gjs_throw (cx, "GObject.SignalAccumulator.TRUE_HANDLED can only be used with boolean signals"); return false; } JS::RootedObject params_obj(cx, &argv[5].toObject()); if (!JS_GetArrayLength(cx, params_obj, &n_parameters)) return false; params = g_newa(GType, n_parameters); JS::RootedValue gtype_val(cx); for (i = 0; i < n_parameters; i++) { if (!JS_GetElement(cx, params_obj, i, >ype_val) || !gtype_val.isObject()) { gjs_throw(cx, "Invalid signal parameter number %d", i); return false; } JS::RootedObject gjs_gtype(cx, >ype_val.toObject()); params[i] = gjs_gtype_get_actual_gtype(cx, gjs_gtype); } gtype = gjs_gtype_get_actual_gtype(cx, obj); signal_id = g_signal_newv(signal_name, gtype, (GSignalFlags) argv[2].toInt32(), /* signal_flags */ NULL, /* class closure */ accumulator, NULL, /* accu_data */ g_cclosure_marshal_generic, return_type, /* return type */ n_parameters, params); argv.rval().setInt32(signal_id); return true; } static JSFunctionSpec module_funcs[] = { JS_FS("override_property", gjs_override_property, 2, GJS_MODULE_PROP_FLAGS), JS_FS("register_interface", gjs_register_interface, 3, GJS_MODULE_PROP_FLAGS), JS_FS("register_type", gjs_register_type, 4, GJS_MODULE_PROP_FLAGS), JS_FS("hook_up_vfunc", gjs_hook_up_vfunc, 3, GJS_MODULE_PROP_FLAGS), JS_FS("signal_new", gjs_signal_new, 6, GJS_MODULE_PROP_FLAGS), JS_FS_END, }; bool gjs_define_private_gi_stuff(JSContext *cx, JS::MutableHandleObject module) { module.set(JS_NewPlainObject(cx)); return JS_DefineFunctions(cx, module, &module_funcs[0]); } bool gjs_lookup_object_constructor(JSContext *context, GType gtype, JS::MutableHandleValue value_p) { JSObject *constructor; GIObjectInfo *object_info; object_info = (GIObjectInfo*)g_irepository_find_by_gtype(NULL, gtype); g_assert(object_info == NULL || g_base_info_get_type((GIBaseInfo*)object_info) == GI_INFO_TYPE_OBJECT); constructor = gjs_lookup_object_constructor_from_info(context, object_info, gtype); if (G_UNLIKELY (constructor == NULL)) return false; if (object_info) g_base_info_unref((GIBaseInfo*)object_info); value_p.setObject(*constructor); return true; } bool gjs_object_associate_closure(JSContext *cx, JS::HandleObject object, GClosure *closure) { ObjectInstance *priv = priv_from_js(cx, object); if (!priv) return false; if (priv->gobj) ensure_uses_toggle_ref(cx, priv); do_associate_closure(priv, closure); return true; } cjs-4.4.0/gi/object.h000066400000000000000000000065751356375132300143410ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_OBJECT_H__ #define __GJS_OBJECT_H__ #include #include #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS void gjs_define_object_class(JSContext *context, JS::HandleObject in_object, GIObjectInfo *info, GType gtype, JS::MutableHandleObject constructor, JS::MutableHandleObject prototype); bool gjs_lookup_object_constructor(JSContext *context, GType gtype, JS::MutableHandleValue value_p); JSObject* gjs_object_from_g_object (JSContext *context, GObject *gobj); GObject *gjs_g_object_from_object(JSContext *context, JS::HandleObject obj); bool gjs_typecheck_object(JSContext *context, JS::HandleObject obj, GType expected_type, bool throw_error); bool gjs_typecheck_is_object(JSContext *context, JS::HandleObject obj, bool throw_error); void gjs_object_prepare_shutdown(void); void gjs_object_clear_toggles(void); void gjs_object_shutdown_toggle_queue(void); void gjs_object_context_dispose_notify(void *data, GObject *where_the_object_was); void gjs_object_define_static_methods(JSContext *context, JS::HandleObject constructor, GType gtype, GIObjectInfo *object_info); bool gjs_define_private_gi_stuff(JSContext *cx, JS::MutableHandleObject module); bool gjs_object_associate_closure(JSContext *cx, JS::HandleObject obj, GClosure *closure); G_END_DECLS #endif /* __GJS_OBJECT_H__ */ cjs-4.4.0/gi/param.cpp000066400000000000000000000230511356375132300145120ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include "param.h" #include "arg.h" #include "object.h" #include "repo.h" #include "gtype.h" #include "function.h" #include "cjs/jsapi-class.h" #include "cjs/jsapi-wrapper.h" #include "cjs/mem.h" #include typedef struct { GParamSpec *gparam; /* NULL if we are the prototype and not an instance */ } Param; extern struct JSClass gjs_param_class; GJS_DEFINE_PRIV_FROM_JS(Param, gjs_param_class) /* * The *resolved out parameter, on success, should be false to indicate that id * was not resolved; and true if id was resolved. */ static bool param_resolve(JSContext *context, JS::HandleObject obj, JS::HandleId id, bool *resolved) { GIObjectInfo *info = NULL; GIFunctionInfo *method_info; Param *priv; bool ret = false; priv = priv_from_js(context, obj); if (priv != NULL) { /* instance, not prototype */ *resolved = false; return true; } GjsAutoJSChar name; if (!gjs_get_string_id(context, id, &name)) { *resolved = false; return true; /* not resolved, but no error */ } info = (GIObjectInfo*)g_irepository_find_by_gtype(g_irepository_get_default(), G_TYPE_PARAM); method_info = g_object_info_find_method(info, name); if (method_info == NULL) { *resolved = false; ret = true; goto out; } #if GJS_VERBOSE_ENABLE_GI_USAGE _gjs_log_info_usage((GIBaseInfo*) method_info); #endif if (g_function_info_get_flags (method_info) & GI_FUNCTION_IS_METHOD) { gjs_debug(GJS_DEBUG_GOBJECT, "Defining method %s in prototype for GObject.ParamSpec", g_base_info_get_name( (GIBaseInfo*) method_info)); if (gjs_define_function(context, obj, G_TYPE_PARAM, method_info) == NULL) { g_base_info_unref( (GIBaseInfo*) method_info); goto out; } *resolved = true; /* we defined the prop in obj */ } g_base_info_unref( (GIBaseInfo*) method_info); ret = true; out: if (info != NULL) g_base_info_unref( (GIBaseInfo*)info); return ret; } GJS_NATIVE_CONSTRUCTOR_DECLARE(param) { GJS_NATIVE_CONSTRUCTOR_VARIABLES(param) GJS_NATIVE_CONSTRUCTOR_PRELUDE(param); GJS_INC_COUNTER(param); GJS_NATIVE_CONSTRUCTOR_FINISH(param); return true; } static void param_finalize(JSFreeOp *fop, JSObject *obj) { Param *priv; priv = (Param*) JS_GetPrivate(obj); gjs_debug_lifecycle(GJS_DEBUG_GPARAM, "finalize, obj %p priv %p", obj, priv); if (priv == NULL) return; /* wrong class? */ if (priv->gparam) { g_param_spec_unref(priv->gparam); priv->gparam = NULL; } GJS_DEC_COUNTER(param); g_slice_free(Param, priv); } /* The bizarre thing about this vtable is that it applies to both * instances of the object, and to the prototype that instances of the * class have. */ static const struct JSClassOps gjs_param_class_ops = { NULL, /* addProperty */ NULL, /* deleteProperty */ NULL, /* getProperty */ NULL, /* setProperty */ NULL, /* enumerate */ param_resolve, nullptr, /* mayResolve */ param_finalize }; struct JSClass gjs_param_class = { "GObject_ParamSpec", JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE, &gjs_param_class_ops }; JSPropertySpec gjs_param_proto_props[] = { JS_PS_END }; JSFunctionSpec gjs_param_proto_funcs[] = { JS_FS_END }; static JSFunctionSpec gjs_param_constructor_funcs[] = { JS_FS_END }; static JSObject* gjs_lookup_param_prototype(JSContext *context) { JS::RootedId gobject_name(context, gjs_intern_string_to_id(context, "GObject")); JS::RootedObject in_object(context, gjs_lookup_namespace_object_by_name(context, gobject_name)); if (G_UNLIKELY (!in_object)) return NULL; JS::RootedValue value(context); if (!JS_GetProperty(context, in_object, "ParamSpec", &value)) return NULL; if (G_UNLIKELY (!value.isObject())) return NULL; JS::RootedObject constructor(context, &value.toObject()); g_assert(constructor); if (!gjs_object_get_property(context, constructor, GJS_STRING_PROTOTYPE, &value)) return NULL; if (G_UNLIKELY (!value.isObjectOrNull())) return NULL; return value.toObjectOrNull(); } void gjs_define_param_class(JSContext *context, JS::HandleObject in_object) { const char *constructor_name; JS::RootedObject prototype(context), constructor(context); GIObjectInfo *info; constructor_name = "ParamSpec"; if (!gjs_init_class_dynamic(context, in_object, nullptr, "GObject", constructor_name, &gjs_param_class, gjs_param_constructor, 0, /* props of prototype */ &gjs_param_proto_props[0], /* funcs of prototype */ &gjs_param_proto_funcs[0], /* props of constructor, MyConstructor.myprop */ NULL, /* funcs of constructor, MyConstructor.myfunc() */ gjs_param_constructor_funcs, &prototype, &constructor)) { g_error("Can't init class %s", constructor_name); } JS::RootedObject gtype_obj(context, gjs_gtype_create_gtype_wrapper(context, G_TYPE_PARAM)); JS_DefineProperty(context, constructor, "$gtype", gtype_obj, JSPROP_PERMANENT); info = (GIObjectInfo*)g_irepository_find_by_gtype(g_irepository_get_default(), G_TYPE_PARAM); gjs_object_define_static_methods(context, constructor, G_TYPE_PARAM, info); g_base_info_unref( (GIBaseInfo*) info); gjs_debug(GJS_DEBUG_GPARAM, "Defined class %s prototype is %p class %p in object %p", constructor_name, prototype.get(), JS_GetClass(prototype), in_object.get()); } JSObject* gjs_param_from_g_param(JSContext *context, GParamSpec *gparam) { JSObject *obj; Param *priv; if (gparam == NULL) return NULL; gjs_debug(GJS_DEBUG_GPARAM, "Wrapping %s '%s' on %s with JSObject", g_type_name(G_TYPE_FROM_INSTANCE((GTypeInstance*) gparam)), gparam->name, g_type_name(gparam->owner_type)); JS::RootedObject proto(context, gjs_lookup_param_prototype(context)); obj = JS_NewObjectWithGivenProto(context, JS_GetClass(proto), proto); GJS_INC_COUNTER(param); priv = g_slice_new0(Param); JS_SetPrivate(obj, priv); priv->gparam = gparam; g_param_spec_ref (gparam); gjs_debug(GJS_DEBUG_GPARAM, "JSObject created with param instance %p type %s", priv->gparam, g_type_name(G_TYPE_FROM_INSTANCE((GTypeInstance*) priv->gparam))); return obj; } GParamSpec* gjs_g_param_from_param(JSContext *context, JS::HandleObject obj) { Param *priv; if (!obj) return NULL; priv = priv_from_js(context, obj); return priv->gparam; } bool gjs_typecheck_param(JSContext *context, JS::HandleObject object, GType expected_type, bool throw_error) { Param *priv; bool result; if (!do_base_typecheck(context, object, throw_error)) return false; priv = priv_from_js(context, object); if (priv->gparam == NULL) { if (throw_error) { gjs_throw_custom(context, JSProto_TypeError, nullptr, "Object is GObject.ParamSpec.prototype, not an object instance - " "cannot convert to a GObject.ParamSpec instance"); } return false; } if (expected_type != G_TYPE_NONE) result = g_type_is_a (G_TYPE_FROM_INSTANCE (priv->gparam), expected_type); else result = true; if (!result && throw_error) { gjs_throw_custom(context, JSProto_TypeError, nullptr, "Object is of type %s - cannot convert to %s", g_type_name(G_TYPE_FROM_INSTANCE (priv->gparam)), g_type_name(expected_type)); } return result; } cjs-4.4.0/gi/param.h000066400000000000000000000036741356375132300141700ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_PARAM_H__ #define __GJS_PARAM_H__ #include #include #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS void gjs_define_param_class(JSContext *context, JS::HandleObject in_object); GParamSpec *gjs_g_param_from_param (JSContext *context, JS::HandleObject obj); JSObject* gjs_param_from_g_param (JSContext *context, GParamSpec *param); bool gjs_typecheck_param(JSContext *context, JS::HandleObject obj, GType expected_type, bool throw_error); G_END_DECLS #endif /* __GJS_PARAM_H__ */ cjs-4.4.0/gi/proxyutils.cpp000066400000000000000000000052611356375132300156570ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2012 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include "proxyutils.h" /* Default spidermonkey toString is worthless. Replace it * with something that gives us both the introspection name * and a memory address. */ bool _gjs_proxy_to_string_func(JSContext *context, JSObject *this_obj, const char *objtype, GIBaseInfo *info, GType gtype, gpointer native_address, JS::MutableHandleValue rval) { GString *buf; bool ret = false; buf = g_string_new(""); g_string_append_c(buf, '['); g_string_append(buf, objtype); if (native_address == NULL) g_string_append(buf, " prototype of"); else g_string_append(buf, " instance proxy"); if (info != NULL) { g_string_append_printf(buf, " GIName:%s.%s", g_base_info_get_namespace(info), g_base_info_get_name(info)); } else { g_string_append(buf, " GType:"); g_string_append(buf, g_type_name(gtype)); } g_string_append_printf(buf, " jsobj@%p", this_obj); if (native_address != NULL) g_string_append_printf(buf, " native@%p", native_address); g_string_append_c(buf, ']'); if (!gjs_string_from_utf8(context, buf->str, rval)) goto out; ret = true; out: g_string_free (buf, true); return ret; } cjs-4.4.0/gi/proxyutils.h000066400000000000000000000033541356375132300153250ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_PROXYUTILS_H__ #define __GJS_PROXYUTILS_H__ #include "cjs/jsapi-util.h" G_BEGIN_DECLS bool _gjs_proxy_to_string_func(JSContext *context, JSObject *this_obj, const char *objtype, GIBaseInfo *info, GType gtype, gpointer native_address, JS::MutableHandleValue ret); G_END_DECLS #endif /* __GJS_OBJECT_H__ */ cjs-4.4.0/gi/repo.cpp000066400000000000000000000601441356375132300143630ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "repo.h" #include "ns.h" #include "function.h" #include "object.h" #include "param.h" #include "boxed.h" #include "union.h" #include "enumeration.h" #include "arg.h" #include "foreign.h" #include "fundamental.h" #include "interface.h" #include "gerror.h" #include "cjs/jsapi-class.h" #include "cjs/jsapi-wrapper.h" #include "cjs/mem.h" #include #include #include typedef struct { void *dummy; } Repo; extern struct JSClass gjs_repo_class; GJS_DEFINE_PRIV_FROM_JS(Repo, gjs_repo_class) static bool lookup_override_function(JSContext *, JS::HandleId, JS::MutableHandleValue); static bool get_version_for_ns (JSContext *context, JS::HandleObject repo_obj, JS::HandleId ns_id, GjsAutoJSChar *version) { JS::RootedObject versions(context); bool found; if (!gjs_object_require_property(context, repo_obj, "GI repository object", GJS_STRING_GI_VERSIONS, &versions)) return false; if (!JS_AlreadyHasOwnPropertyById(context, versions, ns_id, &found)) return false; if (!found) return true; return gjs_object_require_property(context, versions, NULL, ns_id, version); } static bool resolve_namespace_object(JSContext *context, JS::HandleObject repo_obj, JS::HandleId ns_id, const char *ns_name) { GIRepository *repo; GError *error; JSAutoRequest ar(context); GjsAutoJSChar version; if (!get_version_for_ns(context, repo_obj, ns_id, &version)) return false; repo = g_irepository_get_default(); GList *versions = g_irepository_enumerate_versions(repo, ns_name); unsigned nversions = g_list_length(versions); if (nversions > 1 && !version && !g_irepository_is_registered(repo, ns_name, NULL)) { GjsAutoChar warn_text = g_strdup_printf("Requiring %s but it has %u " "versions available; use " "imports.gi.versions to pick one", ns_name, nversions); JS_ReportWarningUTF8(context, "%s", warn_text.get()); } g_list_free_full(versions, g_free); error = NULL; g_irepository_require(repo, ns_name, version, (GIRepositoryLoadFlags) 0, &error); if (error != NULL) { gjs_throw(context, "Requiring %s, version %s: %s", ns_name, version ? version.get() : "none", error->message); g_error_free(error); return false; } /* Defines a property on "obj" (the javascript repo object) * with the given namespace name, pointing to that namespace * in the repo. */ JS::RootedObject gi_namespace(context, gjs_create_ns(context, ns_name)); /* Define the property early, to avoid reentrancy issues if the override module looks for namespaces that import this */ if (!JS_DefineProperty(context, repo_obj, ns_name, gi_namespace, GJS_MODULE_PROP_FLAGS)) g_error("no memory to define ns property"); JS::RootedValue override(context); if (!lookup_override_function(context, ns_id, &override)) return false; JS::RootedValue result(context); if (!override.isUndefined() && !JS_CallFunctionValue (context, gi_namespace, /* thisp */ override, /* callee */ JS::HandleValueArray::empty(), &result)) return false; gjs_debug(GJS_DEBUG_GNAMESPACE, "Defined namespace '%s' %p in GIRepository %p", ns_name, gi_namespace.get(), repo_obj.get()); gjs_schedule_gc_if_needed(context); return true; } /* * The *resolved out parameter, on success, should be false to indicate that id * was not resolved; and true if id was resolved. */ static bool repo_resolve(JSContext *context, JS::HandleObject obj, JS::HandleId id, bool *resolved) { Repo *priv; if (!JSID_IS_STRING(id)) { *resolved = false; return true; /* not resolved, but no error */ } JSFlatString *str = JSID_TO_FLAT_STRING(id); /* let Object.prototype resolve these */ if (JS_FlatStringEqualsAscii(str, "valueOf") || JS_FlatStringEqualsAscii(str, "toString")) { *resolved = false; return true; } priv = priv_from_js(context, obj); gjs_debug_jsprop(GJS_DEBUG_GREPO, "Resolve prop '%s' hook, obj %s, priv %p", gjs_debug_id(id).c_str(), gjs_debug_object(obj).c_str(), priv); if (priv == NULL) { /* we are the prototype, or have the wrong class */ *resolved = false; return true; } GjsAutoJSChar name; if (!gjs_get_string_id(context, id, &name)) { *resolved = false; return true; } if (!resolve_namespace_object(context, obj, id, name)) { return false; } *resolved = true; return true; } GJS_NATIVE_CONSTRUCTOR_DEFINE_ABSTRACT(repo) static void repo_finalize(JSFreeOp *fop, JSObject *obj) { Repo *priv; priv = (Repo*) JS_GetPrivate(obj); gjs_debug_lifecycle(GJS_DEBUG_GREPO, "finalize, obj %p priv %p", obj, priv); if (priv == NULL) return; /* we are the prototype, not a real instance */ GJS_DEC_COUNTER(repo); g_slice_free(Repo, priv); } /* The bizarre thing about this vtable is that it applies to both * instances of the object, and to the prototype that instances of the * class have. */ static const struct JSClassOps gjs_repo_class_ops = { NULL, /* addProperty */ NULL, /* deleteProperty */ NULL, /* getProperty */ NULL, /* setProperty */ NULL, /* enumerate */ repo_resolve, nullptr, /* mayResolve */ repo_finalize }; struct JSClass gjs_repo_class = { "GIRepository", /* means "new GIRepository()" works */ JSCLASS_HAS_PRIVATE | JSCLASS_FOREGROUND_FINALIZE, &gjs_repo_class_ops, }; static JSPropertySpec *gjs_repo_proto_props = nullptr; static JSFunctionSpec *gjs_repo_proto_funcs = nullptr; static JSFunctionSpec *gjs_repo_static_funcs = nullptr; GJS_DEFINE_PROTO_FUNCS(repo) static JSObject* repo_new(JSContext *context) { Repo *priv; JS::RootedObject proto(context); if (!gjs_repo_define_proto(context, nullptr, &proto)) return nullptr; JS::RootedObject repo(context, JS_NewObjectWithGivenProto(context, &gjs_repo_class, proto)); if (repo == nullptr) g_error("No memory to create repo object"); priv = g_slice_new0(Repo); GJS_INC_COUNTER(repo); g_assert(priv_from_js(context, repo) == NULL); JS_SetPrivate(repo, priv); gjs_debug_lifecycle(GJS_DEBUG_GREPO, "repo constructor, obj %p priv %p", repo.get(), priv); JS::RootedObject versions(context, JS_NewPlainObject(context)); gjs_object_define_property(context, repo, GJS_STRING_GI_VERSIONS, versions, JSPROP_PERMANENT | JSPROP_RESOLVING); /* GLib/GObject/Gio are fixed at 2.0, since we depend on them * internally. */ JS::RootedString two_point_oh(context, JS_NewStringCopyZ(context, "2.0")); if (!JS_DefineProperty(context, versions, "GLib", two_point_oh, JSPROP_PERMANENT)) return nullptr; if (!JS_DefineProperty(context, versions, "GObject", two_point_oh, JSPROP_PERMANENT)) return nullptr; if (!JS_DefineProperty(context, versions, "Gio", two_point_oh, JSPROP_PERMANENT)) return nullptr; JS::RootedObject private_ns(context, JS_NewPlainObject(context)); gjs_object_define_property(context, repo, GJS_STRING_PRIVATE_NS_MARKER, private_ns, JSPROP_PERMANENT | JSPROP_RESOLVING); return repo; } bool gjs_define_repo(JSContext *cx, JS::MutableHandleObject repo) { repo.set(repo_new(cx)); return true; } static bool gjs_define_constant(JSContext *context, JS::HandleObject in_object, GIConstantInfo *info) { JS::RootedValue value(context); GArgument garg = { 0, }; GITypeInfo *type_info; const char *name; bool ret = false; type_info = g_constant_info_get_type(info); g_constant_info_get_value(info, &garg); if (!gjs_value_from_g_argument(context, &value, type_info, &garg, true)) goto out; name = g_base_info_get_name((GIBaseInfo*) info); if (JS_DefineProperty(context, in_object, name, value, GJS_MODULE_PROP_FLAGS)) ret = true; out: g_constant_info_free_value (info, &garg); g_base_info_unref((GIBaseInfo*) type_info); return ret; } #if GJS_VERBOSE_ENABLE_GI_USAGE void _gjs_log_info_usage(GIBaseInfo *info) { #define DIRECTION_STRING(d) ( ((d) == GI_DIRECTION_IN) ? "IN" : ((d) == GI_DIRECTION_OUT) ? "OUT" : "INOUT" ) #define TRANSFER_STRING(t) ( ((t) == GI_TRANSFER_NOTHING) ? "NOTHING" : ((t) == GI_TRANSFER_CONTAINER) ? "CONTAINER" : "EVERYTHING" ) { char *details; GIInfoType info_type; GIBaseInfo *container; info_type = g_base_info_get_type(info); if (info_type == GI_INFO_TYPE_FUNCTION) { GString *args; int n_args; int i; GITransfer retval_transfer; args = g_string_new("{ "); n_args = g_callable_info_get_n_args((GICallableInfo*) info); for (i = 0; i < n_args; ++i) { GIArgInfo *arg; GIDirection direction; GITransfer transfer; arg = g_callable_info_get_arg((GICallableInfo*)info, i); direction = g_arg_info_get_direction(arg); transfer = g_arg_info_get_ownership_transfer(arg); g_string_append_printf(args, "{ GI_DIRECTION_%s, GI_TRANSFER_%s }, ", DIRECTION_STRING(direction), TRANSFER_STRING(transfer)); g_base_info_unref((GIBaseInfo*) arg); } if (args->len > 2) g_string_truncate(args, args->len - 2); /* chop comma */ g_string_append(args, " }"); retval_transfer = g_callable_info_get_caller_owns((GICallableInfo*) info); details = g_strdup_printf(".details = { .func = { .retval_transfer = GI_TRANSFER_%s, .n_args = %d, .args = %s } }", TRANSFER_STRING(retval_transfer), n_args, args->str); g_string_free(args, true); } else { details = g_strdup_printf(".details = { .nothing = {} }"); } container = g_base_info_get_container(info); gjs_debug_gi_usage("{ GI_INFO_TYPE_%s, \"%s\", \"%s\", \"%s\", %s },", gjs_info_type_name(info_type), g_base_info_get_namespace(info), container ? g_base_info_get_name(container) : "", g_base_info_get_name(info), details); g_free(details); } } #endif /* GJS_VERBOSE_ENABLE_GI_USAGE */ bool gjs_define_info(JSContext *context, JS::HandleObject in_object, GIBaseInfo *info, bool *defined) { #if GJS_VERBOSE_ENABLE_GI_USAGE _gjs_log_info_usage(info); #endif *defined = true; switch (g_base_info_get_type(info)) { case GI_INFO_TYPE_FUNCTION: { JSObject *f; f = gjs_define_function(context, in_object, 0, (GICallableInfo*) info); if (f == NULL) return false; } break; case GI_INFO_TYPE_OBJECT: { GType gtype; gtype = g_registered_type_info_get_g_type((GIRegisteredTypeInfo*)info); if (g_type_is_a (gtype, G_TYPE_PARAM)) { gjs_define_param_class(context, in_object); } else if (g_type_is_a (gtype, G_TYPE_OBJECT)) { JS::RootedObject ignored1(context), ignored2(context); gjs_define_object_class(context, in_object, info, gtype, &ignored1, &ignored2); } else if (G_TYPE_IS_INSTANTIATABLE(gtype)) { JS::RootedObject ignored1(context), ignored2(context); if (!gjs_define_fundamental_class(context, in_object, (GIObjectInfo*)info, &ignored1, &ignored2)) { gjs_throw (context, "Unsupported fundamental class creation for type %s", g_type_name(gtype)); return false; } } else { gjs_throw (context, "Unsupported type %s, deriving from fundamental %s", g_type_name(gtype), g_type_name(g_type_fundamental(gtype))); return false; } } break; case GI_INFO_TYPE_STRUCT: /* We don't want GType structures in the namespace, we expose their fields as vfuncs and their methods as static methods */ if (g_struct_info_is_gtype_struct((GIStructInfo*) info)) { *defined = false; break; } /* Fall through */ case GI_INFO_TYPE_BOXED: gjs_define_boxed_class(context, in_object, (GIBoxedInfo*) info); break; case GI_INFO_TYPE_UNION: if (!gjs_define_union_class(context, in_object, (GIUnionInfo*) info)) return false; break; case GI_INFO_TYPE_ENUM: if (g_enum_info_get_error_domain((GIEnumInfo*) info)) { /* define as GError subclass */ gjs_define_error_class(context, in_object, (GIEnumInfo*) info); break; } /* fall through */ case GI_INFO_TYPE_FLAGS: if (!gjs_define_enumeration(context, in_object, (GIEnumInfo*) info)) return false; break; case GI_INFO_TYPE_CONSTANT: if (!gjs_define_constant(context, in_object, (GIConstantInfo*) info)) return false; break; case GI_INFO_TYPE_INTERFACE: { JS::RootedObject ignored(context); gjs_define_interface_class(context, in_object, (GIInterfaceInfo *) info, g_registered_type_info_get_g_type((GIRegisteredTypeInfo *) info), &ignored); } break; case GI_INFO_TYPE_INVALID: case GI_INFO_TYPE_INVALID_0: case GI_INFO_TYPE_CALLBACK: case GI_INFO_TYPE_VALUE: case GI_INFO_TYPE_SIGNAL: case GI_INFO_TYPE_VFUNC: case GI_INFO_TYPE_PROPERTY: case GI_INFO_TYPE_FIELD: case GI_INFO_TYPE_ARG: case GI_INFO_TYPE_TYPE: case GI_INFO_TYPE_UNRESOLVED: default: gjs_throw(context, "API of type %s not implemented, cannot define %s.%s", gjs_info_type_name(g_base_info_get_type(info)), g_base_info_get_namespace(info), g_base_info_get_name(info)); return false; } return true; } /* Get the "unknown namespace", which should be used for unnamespaced types */ JSObject* gjs_lookup_private_namespace(JSContext *context) { JS::HandleId ns_name = gjs_context_get_const_string(context, GJS_STRING_PRIVATE_NS_MARKER); return gjs_lookup_namespace_object_by_name(context, ns_name); } /* Get the namespace object that the GIBaseInfo should be inside */ JSObject* gjs_lookup_namespace_object(JSContext *context, GIBaseInfo *info) { const char *ns; ns = g_base_info_get_namespace(info); if (ns == NULL) { gjs_throw(context, "%s '%s' does not have a namespace", gjs_info_type_name(g_base_info_get_type(info)), g_base_info_get_name(info)); return NULL; } JS::RootedId ns_name(context, gjs_intern_string_to_id(context, ns)); return gjs_lookup_namespace_object_by_name(context, ns_name); } /* Check if an exception's 'name' property is equal to compare_name. Ignores * all errors that might arise. Requires request. */ static bool error_has_name(JSContext *cx, JS::HandleValue thrown_value, JSString *compare_name) { if (!thrown_value.isObject()) return false; JS::AutoSaveExceptionState saved_exc(cx); JS::RootedObject exc(cx, &thrown_value.toObject()); JS::RootedValue exc_name(cx); bool retval = false; if (!gjs_object_get_property(cx, exc, GJS_STRING_NAME, &exc_name)) goto out; int32_t cmp_result; if (!JS_CompareStrings(cx, exc_name.toString(), compare_name, &cmp_result)) goto out; if (cmp_result == 0) retval = true; out: saved_exc.restore(); return retval; } static bool lookup_override_function(JSContext *cx, JS::HandleId ns_name, JS::MutableHandleValue function) { JSAutoRequest ar(cx); JS::AutoSaveExceptionState saved_exc(cx); JS::RootedValue importer(cx, gjs_get_global_slot(cx, GJS_GLOBAL_SLOT_IMPORTS)); g_assert(importer.isObject()); JS::RootedObject overridespkg(cx), module(cx); JS::RootedObject importer_obj(cx, &importer.toObject()); if (!gjs_object_require_property(cx, importer_obj, "importer", GJS_STRING_GI_OVERRIDES, &overridespkg)) goto fail; if (!gjs_object_require_property(cx, overridespkg, "GI repository object", ns_name, &module)) { JS::RootedValue exc(cx); JS_GetPendingException(cx, &exc); /* If the exception was an ImportError (i.e., module not found) then * we simply didn't have an override, don't throw an exception */ if (error_has_name(cx, exc, JS_AtomizeAndPinString(cx, "ImportError"))) { saved_exc.restore(); return true; } goto fail; } if (!gjs_object_require_property(cx, module, "override module", GJS_STRING_GOBJECT_INIT, function) || !function.isObjectOrNull()) { gjs_throw(cx, "Unexpected value for _init in overrides module"); goto fail; } return true; fail: saved_exc.drop(); return false; } JSObject* gjs_lookup_namespace_object_by_name(JSContext *context, JS::HandleId ns_name) { JSAutoRequest ar(context); JS::RootedValue importer(context, gjs_get_global_slot(context, GJS_GLOBAL_SLOT_IMPORTS)); g_assert(importer.isObject()); JS::RootedObject repo(context), importer_obj(context, &importer.toObject()); if (!gjs_object_require_property(context, importer_obj, "importer", GJS_STRING_GI_MODULE, &repo)) { gjs_log_exception(context); gjs_throw(context, "No gi property in importer"); return NULL; } JS::RootedObject retval(context); if (!gjs_object_require_property(context, repo, "GI repository object", ns_name, &retval)) return NULL; return retval; } const char* gjs_info_type_name(GIInfoType type) { switch (type) { case GI_INFO_TYPE_INVALID: return "INVALID"; case GI_INFO_TYPE_FUNCTION: return "FUNCTION"; case GI_INFO_TYPE_CALLBACK: return "CALLBACK"; case GI_INFO_TYPE_STRUCT: return "STRUCT"; case GI_INFO_TYPE_BOXED: return "BOXED"; case GI_INFO_TYPE_ENUM: return "ENUM"; case GI_INFO_TYPE_FLAGS: return "FLAGS"; case GI_INFO_TYPE_OBJECT: return "OBJECT"; case GI_INFO_TYPE_INTERFACE: return "INTERFACE"; case GI_INFO_TYPE_CONSTANT: return "CONSTANT"; case GI_INFO_TYPE_UNION: return "UNION"; case GI_INFO_TYPE_VALUE: return "VALUE"; case GI_INFO_TYPE_SIGNAL: return "SIGNAL"; case GI_INFO_TYPE_VFUNC: return "VFUNC"; case GI_INFO_TYPE_PROPERTY: return "PROPERTY"; case GI_INFO_TYPE_FIELD: return "FIELD"; case GI_INFO_TYPE_ARG: return "ARG"; case GI_INFO_TYPE_TYPE: return "TYPE"; case GI_INFO_TYPE_UNRESOLVED: return "UNRESOLVED"; case GI_INFO_TYPE_INVALID_0: g_assert_not_reached(); break; default: return "???"; } } char* gjs_camel_from_hyphen(const char *hyphen_name) { GString *s; const char *p; bool next_upper; s = g_string_sized_new(strlen(hyphen_name) + 1); next_upper = false; for (p = hyphen_name; *p; ++p) { if (*p == '-' || *p == '_') { next_upper = true; } else { if (next_upper) { g_string_append_c(s, g_ascii_toupper(*p)); next_upper = false; } else { g_string_append_c(s, *p); } } } return g_string_free(s, false); } char* gjs_hyphen_from_camel(const char *camel_name) { GString *s; const char *p; /* four hyphens should be reasonable guess */ s = g_string_sized_new(strlen(camel_name) + 4 + 1); for (p = camel_name; *p; ++p) { if (g_ascii_isupper(*p)) { g_string_append_c(s, '-'); g_string_append_c(s, g_ascii_tolower(*p)); } else { g_string_append_c(s, *p); } } return g_string_free(s, false); } JSObject * gjs_lookup_generic_constructor(JSContext *context, GIBaseInfo *info) { const char *constructor_name; JS::RootedObject in_object(context, gjs_lookup_namespace_object(context, (GIBaseInfo*) info)); constructor_name = g_base_info_get_name((GIBaseInfo*) info); if (G_UNLIKELY (!in_object)) return NULL; JS::RootedValue value(context); if (!JS_GetProperty(context, in_object, constructor_name, &value)) return NULL; if (G_UNLIKELY (!value.isObject())) return NULL; return &value.toObject(); } JSObject * gjs_lookup_generic_prototype(JSContext *context, GIBaseInfo *info) { JS::RootedObject constructor(context, gjs_lookup_generic_constructor(context, info)); if (G_UNLIKELY(!constructor)) return NULL; JS::RootedValue value(context); if (!gjs_object_get_property(context, constructor, GJS_STRING_PROTOTYPE, &value)) return NULL; if (G_UNLIKELY (!value.isObjectOrNull())) return NULL; return value.toObjectOrNull(); } cjs-4.4.0/gi/repo.h000066400000000000000000000051501356375132300140240ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_REPO_H__ #define __GJS_REPO_H__ #include #include #include #include "cjs/jsapi-wrapper.h" #include G_BEGIN_DECLS bool gjs_define_repo(JSContext *cx, JS::MutableHandleObject repo); const char* gjs_info_type_name (GIInfoType type); JSObject* gjs_lookup_private_namespace (JSContext *context); JSObject* gjs_lookup_namespace_object (JSContext *context, GIBaseInfo *info); JSObject *gjs_lookup_namespace_object_by_name(JSContext *context, JS::HandleId name); JSObject * gjs_lookup_generic_constructor (JSContext *context, GIBaseInfo *info); JSObject * gjs_lookup_generic_prototype (JSContext *context, GIBaseInfo *info); bool gjs_define_info(JSContext *context, JS::HandleObject in_object, GIBaseInfo *info, bool *defined); char* gjs_camel_from_hyphen (const char *hyphen_name); char* gjs_hyphen_from_camel (const char *camel_name); #if GJS_VERBOSE_ENABLE_GI_USAGE void _gjs_log_info_usage(GIBaseInfo *info); #endif G_END_DECLS #endif /* __GJS_REPO_H__ */ cjs-4.4.0/gi/toggle.cpp000066400000000000000000000132561356375132300147010ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2017 Endless Mobile, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. * * Authored by: Philip Chimento , */ #include #include #include #include #include "toggle.h" std::deque::iterator ToggleQueue::find_operation_locked(GObject *gobj, ToggleQueue::Direction direction) { return std::find_if(q.begin(), q.end(), [gobj, direction](const Item& item)->bool { return item.gobj == gobj && item.direction == direction; }); } bool ToggleQueue::find_and_erase_operation_locked(GObject *gobj, ToggleQueue::Direction direction) { auto pos = find_operation_locked(gobj, direction); bool had_toggle = (pos != q.end()); if (had_toggle) q.erase(pos); return had_toggle; } gboolean ToggleQueue::idle_handle_toggle(void *data) { auto self = static_cast(data); while (self->handle_toggle(self->m_toggle_handler)) ; return G_SOURCE_REMOVE; } void ToggleQueue::idle_destroy_notify(void *data) { auto self = static_cast(data); std::lock_guard hold(self->lock); self->m_idle_id = 0; self->m_toggle_handler = nullptr; } std::pair ToggleQueue::is_queued(GObject *gobj) { std::lock_guard hold(lock); bool has_toggle_down = find_operation_locked(gobj, DOWN) != q.end(); bool has_toggle_up = find_operation_locked(gobj, UP) != q.end(); return {has_toggle_down, has_toggle_up}; } std::pair ToggleQueue::cancel(GObject *gobj) { debug("cancel", gobj); std::lock_guard hold(lock); bool had_toggle_down = find_and_erase_operation_locked(gobj, DOWN); bool had_toggle_up = find_and_erase_operation_locked(gobj, UP); gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "ToggleQueue: %p (%s) was %s", gobj, G_OBJECT_TYPE_NAME(gobj), had_toggle_down && had_toggle_up ? "queued to toggle BOTH" : had_toggle_down ? "queued to toggle DOWN" : had_toggle_up ? "queued to toggle UP" : "not queued"); return {had_toggle_down, had_toggle_up}; } bool ToggleQueue::handle_toggle(Handler handler) { Item item; { std::lock_guard hold(lock); if (q.empty()) return false; item = q.front(); handler(item.gobj, item.direction); q.pop_front(); } debug("handle", item.gobj); if (item.needs_unref) g_object_unref(item.gobj); return true; } void ToggleQueue::shutdown(void) { debug("shutdown", nullptr); g_assert(((void)"Queue should have been emptied before shutting down", q.empty())); m_shutdown = true; } void ToggleQueue::enqueue(GObject *gobj, ToggleQueue::Direction direction, ToggleQueue::Handler handler) { if (G_UNLIKELY (m_shutdown)) { gjs_debug(GJS_DEBUG_GOBJECT, "Enqueuing GObject %p to toggle %s after " "shutdown, probably from another thread (%p).", gobj, direction == UP ? "UP" : "DOWN", g_thread_self()); return; } Item item{gobj, direction}; /* If we're toggling up we take a reference to the object now, * so it won't toggle down before we process it. This ensures we * only ever have at most two toggle notifications queued. * (either only up, or down-up) */ if (direction == UP) { debug("enqueue UP", gobj); g_object_ref(gobj); item.needs_unref = true; } else { debug("enqueue DOWN", gobj); } /* If we're toggling down, we don't need to take a reference since * the associated JSObject already has one, and that JSObject won't * get finalized until we've completed toggling (since it's rooted, * until we unroot it when we dispatch the toggle down idle). * * Taking a reference now would be bad anyway, since it would force * the object to toggle back up again. */ std::lock_guard hold(lock); q.push_back(item); if (m_idle_id) { g_assert(((void) "Should always enqueue with the same handler", m_toggle_handler == handler)); return; } m_toggle_handler = handler; m_idle_id = g_idle_add_full(G_PRIORITY_HIGH, idle_handle_toggle, this, idle_destroy_notify); } cjs-4.4.0/gi/toggle.h000066400000000000000000000071271356375132300143460ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2017 Endless Mobile, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. * * Authored by: Philip Chimento , */ #ifndef GJS_TOGGLE_H #define GJS_TOGGLE_H #include #include #include #include #include "util/log.h" /* Thread-safe queue for enqueueing toggle-up or toggle-down events on GObjects * from any thread. For more information, see object.cpp, comments near * wrapped_gobj_toggle_notify(). */ class ToggleQueue { public: enum Direction { DOWN, UP }; typedef void (*Handler)(GObject *, Direction); private: struct Item { GObject *gobj; ToggleQueue::Direction direction; unsigned needs_unref : 1; }; std::mutex lock; std::deque q; std::atomic_bool m_shutdown = ATOMIC_VAR_INIT(false); unsigned m_idle_id; Handler m_toggle_handler; /* No-op unless GJS_VERBOSE_ENABLE_LIFECYCLE is defined to 1. */ inline void debug(const char *did, void *what) { gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "ToggleQueue %s %p", did, what); } std::deque::iterator find_operation_locked(GObject *gobj, Direction direction); bool find_and_erase_operation_locked(GObject *gobj, Direction direction); static gboolean idle_handle_toggle(void *data); static void idle_destroy_notify(void *data); public: /* These two functions return a pair DOWN, UP signifying whether toggles * are / were queued. is_queued() just checks and does not modify. */ std::pair is_queued(GObject *gobj); /* Cancels pending toggles and returns whether any were queued. */ std::pair cancel(GObject *gobj); /* Pops a toggle from the queue and processes it. Call this if you don't * want to wait for it to be processed in idle time. Returns false if queue * is empty. */ bool handle_toggle(Handler handler); /* After calling this, the toggle queue won't accept any more toggles. Only * intended for use when destroying the JSContext and breaking the * associations between C and JS objects. */ void shutdown(void); /* Queues a toggle to be processed in idle time. */ void enqueue(GObject *gobj, Direction direction, Handler handler); static ToggleQueue& get_default(void) { static ToggleQueue the_singleton; return the_singleton; } }; #endif /* GJS_TOGGLE_H */ cjs-4.4.0/gi/union.cpp000066400000000000000000000366521356375132300145550ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include /* include first for logging related #define used in repo.h */ #include #include "union.h" #include "arg.h" #include "object.h" #include "cjs/jsapi-class.h" #include "cjs/jsapi-wrapper.h" #include "cjs/mem.h" #include "repo.h" #include "proxyutils.h" #include "function.h" #include "gtype.h" #include typedef struct { GIUnionInfo *info; void *gboxed; /* NULL if we are the prototype and not an instance */ GType gtype; } Union; extern struct JSClass gjs_union_class; GJS_DEFINE_PRIV_FROM_JS(Union, gjs_union_class) /* * The *resolved out parameter, on success, should be false to indicate that id * was not resolved; and true if id was resolved. */ static bool union_resolve(JSContext *context, JS::HandleObject obj, JS::HandleId id, bool *resolved) { Union *priv = priv_from_js(context, obj); gjs_debug_jsprop(GJS_DEBUG_GBOXED, "Resolve prop '%s' hook, obj %s, priv %p", gjs_debug_id(id).c_str(), gjs_debug_object(obj).c_str(), priv); if (priv == nullptr) return false; /* wrong class */ if (priv->gboxed != NULL) { /* We are an instance, not a prototype, so look for * per-instance props that we want to define on the * JSObject. Generally we do not want to cache these in JS, we * want to always pull them from the C object, or JS would not * see any changes made from C. So we use the get/set prop * hooks, not this resolve hook. */ *resolved = false; return true; } GjsAutoJSChar name; if (!gjs_get_string_id(context, id, &name)) { *resolved = false; return true; /* not resolved, but no error */ } /* We are the prototype, so look for methods and other class properties */ GIFunctionInfo *method_info; method_info = g_union_info_find_method((GIUnionInfo*) priv->info, name); if (method_info != NULL) { const char *method_name; #if GJS_VERBOSE_ENABLE_GI_USAGE _gjs_log_info_usage((GIBaseInfo*) method_info); #endif if (g_function_info_get_flags (method_info) & GI_FUNCTION_IS_METHOD) { method_name = g_base_info_get_name( (GIBaseInfo*) method_info); gjs_debug(GJS_DEBUG_GBOXED, "Defining method %s in prototype for %s.%s", method_name, g_base_info_get_namespace( (GIBaseInfo*) priv->info), g_base_info_get_name( (GIBaseInfo*) priv->info)); /* obj is union proto */ if (gjs_define_function(context, obj, g_registered_type_info_get_g_type(priv->info), method_info) == NULL) { g_base_info_unref( (GIBaseInfo*) method_info); return false; } *resolved = true; /* we defined the prop in object_proto */ } else { *resolved = false; } g_base_info_unref( (GIBaseInfo*) method_info); } else { *resolved = false; } return true; } static void* union_new(JSContext *context, JS::HandleObject obj, /* "this" for constructor */ GIUnionInfo *info) { int n_methods; int i; /* Find a zero-args constructor and call it */ n_methods = g_union_info_get_n_methods(info); for (i = 0; i < n_methods; ++i) { GIFunctionInfo *func_info; GIFunctionInfoFlags flags; func_info = g_union_info_get_method(info, i); flags = g_function_info_get_flags(func_info); if ((flags & GI_FUNCTION_IS_CONSTRUCTOR) != 0 && g_callable_info_get_n_args((GICallableInfo*) func_info) == 0) { JS::RootedValue rval(context, JS::NullValue()); gjs_invoke_c_function_uncached(context, func_info, obj, JS::HandleValueArray::empty(), &rval); g_base_info_unref((GIBaseInfo*) func_info); /* We are somewhat wasteful here; invoke_c_function() above * creates a JSObject wrapper for the union that we immediately * discard. */ if (rval.isNull()) { return NULL; } else { JS::RootedObject rval_obj(context, &rval.toObject()); return gjs_c_union_from_union(context, rval_obj); } } g_base_info_unref((GIBaseInfo*) func_info); } gjs_throw(context, "Unable to construct union type %s since it has no zero-args , can only wrap an existing one", g_base_info_get_name((GIBaseInfo*) info)); return NULL; } GJS_NATIVE_CONSTRUCTOR_DECLARE(union) { GJS_NATIVE_CONSTRUCTOR_VARIABLES(union) Union *priv; Union *proto_priv; JS::RootedObject proto(context); void *gboxed; GJS_NATIVE_CONSTRUCTOR_PRELUDE(union); priv = g_slice_new0(Union); GJS_INC_COUNTER(boxed); g_assert(priv_from_js(context, object) == NULL); JS_SetPrivate(object, priv); gjs_debug_lifecycle(GJS_DEBUG_GBOXED, "union constructor, obj %p priv %p", object.get(), priv); JS_GetPrototype(context, object, &proto); gjs_debug_lifecycle(GJS_DEBUG_GBOXED, "union instance __proto__ is %p", proto.get()); /* If we're the prototype, then post-construct we'll fill in priv->info. * If we are not the prototype, though, then we'll get ->info from the * prototype and then create a GObject if we don't have one already. */ proto_priv = priv_from_js(context, proto); if (proto_priv == NULL) { gjs_debug(GJS_DEBUG_GBOXED, "Bad prototype set on union? Must match JSClass of object. JS error should have been reported."); return false; } priv->info = proto_priv->info; g_base_info_ref( (GIBaseInfo*) priv->info); priv->gtype = proto_priv->gtype; /* union_new happens to be implemented by calling * gjs_invoke_c_function(), which returns a JS::Value. * The returned "gboxed" here is owned by that JS::Value, * not by us. */ gboxed = union_new(context, object, priv->info); if (gboxed == NULL) { return false; } /* Because "gboxed" is owned by a JS::Value and will * be garbage collected, we make a copy here to be * owned by us. */ priv->gboxed = g_boxed_copy(priv->gtype, gboxed); gjs_debug_lifecycle(GJS_DEBUG_GBOXED, "JSObject created with union instance %p type %s", priv->gboxed, g_type_name(priv->gtype)); GJS_NATIVE_CONSTRUCTOR_FINISH(union); return true; } static void union_finalize(JSFreeOp *fop, JSObject *obj) { Union *priv; priv = (Union*) JS_GetPrivate(obj); gjs_debug_lifecycle(GJS_DEBUG_GBOXED, "finalize, obj %p priv %p", obj, priv); if (priv == NULL) return; /* wrong class? */ if (priv->gboxed) { g_boxed_free(g_registered_type_info_get_g_type( (GIRegisteredTypeInfo*) priv->info), priv->gboxed); priv->gboxed = NULL; } if (priv->info) { g_base_info_unref( (GIBaseInfo*) priv->info); priv->info = NULL; } GJS_DEC_COUNTER(boxed); g_slice_free(Union, priv); } static bool to_string_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_PRIV(context, argc, vp, rec, obj, Union, priv); return _gjs_proxy_to_string_func(context, obj, "union", (GIBaseInfo*)priv->info, priv->gtype, priv->gboxed, rec.rval()); } /* The bizarre thing about this vtable is that it applies to both * instances of the object, and to the prototype that instances of the * class have. */ static const struct JSClassOps gjs_union_class_ops = { NULL, /* addProperty */ NULL, /* deleteProperty */ NULL, /* getProperty */ NULL, /* setProperty */ NULL, /* enumerate */ union_resolve, nullptr, /* mayResolve */ union_finalize }; struct JSClass gjs_union_class = { "GObject_Union", JSCLASS_HAS_PRIVATE | JSCLASS_FOREGROUND_FINALIZE, &gjs_union_class_ops }; JSPropertySpec gjs_union_proto_props[] = { JS_PS_END }; JSFunctionSpec gjs_union_proto_funcs[] = { JS_FS("toString", to_string_func, 0, 0), JS_FS_END }; bool gjs_define_union_class(JSContext *context, JS::HandleObject in_object, GIUnionInfo *info) { const char *constructor_name; Union *priv; GType gtype; JS::RootedObject prototype(context), constructor(context); /* For certain unions, we may be able to relax this in the future by * directly allocating union memory, as we do for structures in boxed.c */ gtype = g_registered_type_info_get_g_type( (GIRegisteredTypeInfo*) info); if (gtype == G_TYPE_NONE) { gjs_throw(context, "Unions must currently be registered as boxed types"); return false; } /* See the comment in gjs_define_object_class() for an * explanation of how this all works; Union is pretty much the * same as Object. */ constructor_name = g_base_info_get_name( (GIBaseInfo*) info); if (!gjs_init_class_dynamic(context, in_object, nullptr, g_base_info_get_namespace( (GIBaseInfo*) info), constructor_name, &gjs_union_class, gjs_union_constructor, 0, /* props of prototype */ &gjs_union_proto_props[0], /* funcs of prototype */ &gjs_union_proto_funcs[0], /* props of constructor, MyConstructor.myprop */ NULL, /* funcs of constructor, MyConstructor.myfunc() */ NULL, &prototype, &constructor)) { g_error("Can't init class %s", constructor_name); } GJS_INC_COUNTER(boxed); priv = g_slice_new0(Union); priv->info = info; g_base_info_ref( (GIBaseInfo*) priv->info); priv->gtype = gtype; JS_SetPrivate(prototype, priv); gjs_debug(GJS_DEBUG_GBOXED, "Defined class %s prototype is %p class %p in object %p", constructor_name, prototype.get(), JS_GetClass(prototype), in_object.get()); JS::RootedObject gtype_obj(context, gjs_gtype_create_gtype_wrapper(context, gtype)); JS_DefineProperty(context, constructor, "$gtype", gtype_obj, JSPROP_PERMANENT); return true; } JSObject* gjs_union_from_c_union(JSContext *context, GIUnionInfo *info, void *gboxed) { JSObject *obj; Union *priv; GType gtype; if (gboxed == NULL) return NULL; /* For certain unions, we may be able to relax this in the future by * directly allocating union memory, as we do for structures in boxed.c */ gtype = g_registered_type_info_get_g_type( (GIRegisteredTypeInfo*) info); if (gtype == G_TYPE_NONE) { gjs_throw(context, "Unions must currently be registered as boxed types"); return NULL; } gjs_debug_marshal(GJS_DEBUG_GBOXED, "Wrapping union %s %p with JSObject", g_base_info_get_name((GIBaseInfo *)info), gboxed); JS::RootedObject proto(context, gjs_lookup_generic_prototype(context, (GIUnionInfo*) info)); obj = JS_NewObjectWithGivenProto(context, JS_GetClass(proto), proto); GJS_INC_COUNTER(boxed); priv = g_slice_new0(Union); JS_SetPrivate(obj, priv); priv->info = info; g_base_info_ref( (GIBaseInfo *) priv->info); priv->gtype = gtype; priv->gboxed = g_boxed_copy(gtype, gboxed); return obj; } void* gjs_c_union_from_union(JSContext *context, JS::HandleObject obj) { Union *priv; if (!obj) return NULL; priv = priv_from_js(context, obj); return priv->gboxed; } bool gjs_typecheck_union(JSContext *context, JS::HandleObject object, GIStructInfo *expected_info, GType expected_type, bool throw_error) { Union *priv; bool result; if (!do_base_typecheck(context, object, throw_error)) return false; priv = priv_from_js(context, object); if (priv->gboxed == NULL) { if (throw_error) { gjs_throw_custom(context, JSProto_TypeError, nullptr, "Object is %s.%s.prototype, not an object instance - cannot convert to a union instance", g_base_info_get_namespace( (GIBaseInfo*) priv->info), g_base_info_get_name( (GIBaseInfo*) priv->info)); } return false; } if (expected_type != G_TYPE_NONE) result = g_type_is_a (priv->gtype, expected_type); else if (expected_info != NULL) result = g_base_info_equal((GIBaseInfo*) priv->info, (GIBaseInfo*) expected_info); else result = true; if (!result && throw_error) { if (expected_info != NULL) { gjs_throw_custom(context, JSProto_TypeError, nullptr, "Object is of type %s.%s - cannot convert to %s.%s", g_base_info_get_namespace((GIBaseInfo*) priv->info), g_base_info_get_name((GIBaseInfo*) priv->info), g_base_info_get_namespace((GIBaseInfo*) expected_info), g_base_info_get_name((GIBaseInfo*) expected_info)); } else { gjs_throw_custom(context, JSProto_TypeError, nullptr, "Object is of type %s.%s - cannot convert to %s", g_base_info_get_namespace((GIBaseInfo*) priv->info), g_base_info_get_name((GIBaseInfo*) priv->info), g_type_name(expected_type)); } } return result; } cjs-4.4.0/gi/union.h000066400000000000000000000042571356375132300142160ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_UNION_H__ #define __GJS_UNION_H__ #include #include #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS bool gjs_define_union_class(JSContext *context, JS::HandleObject in_object, GIUnionInfo *info); void *gjs_c_union_from_union(JSContext *context, JS::HandleObject obj); JSObject* gjs_union_from_c_union (JSContext *context, GIUnionInfo *info, void *gboxed); bool gjs_typecheck_union (JSContext *context, JS::HandleObject obj, GIStructInfo *expected_info, GType expected_type, bool throw_error); G_END_DECLS #endif /* __GJS_UNION_H__ */ cjs-4.4.0/gi/value.cpp000066400000000000000000001031551356375132300145320ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include "foreign.h" #include "value.h" #include "closure.h" #include "arg.h" #include "param.h" #include "object.h" #include "fundamental.h" #include "boxed.h" #include "union.h" #include "gtype.h" #include "gerror.h" #include "cjs/context-private.h" #include "cjs/jsapi-wrapper.h" #include static bool gjs_value_from_g_value_internal(JSContext *context, JS::MutableHandleValue value_p, const GValue *gvalue, bool no_copy, GSignalQuery *signal_query, int arg_n); /* * Gets signal introspection info about closure, or NULL if not found. Currently * only works for signals on introspected GObjects, not signals on GJS-defined * GObjects nor standalone closures. The return value must be unreffed. */ static GISignalInfo * get_signal_info_if_available(GSignalQuery *signal_query) { GIBaseInfo *obj; GIInfoType info_type; GISignalInfo *signal_info = NULL; if (!signal_query->itype) return NULL; obj = g_irepository_find_by_gtype(NULL, signal_query->itype); if (!obj) return NULL; info_type = g_base_info_get_type (obj); if (info_type == GI_INFO_TYPE_OBJECT) signal_info = g_object_info_find_signal((GIObjectInfo*)obj, signal_query->signal_name); else if (info_type == GI_INFO_TYPE_INTERFACE) signal_info = g_interface_info_find_signal((GIInterfaceInfo*)obj, signal_query->signal_name); g_base_info_unref((GIBaseInfo*)obj); return signal_info; } /* * Fill in value_p with a JS array, converted from a C array stored as a pointer * in array_value, with its length stored in array_length_value. */ static bool gjs_value_from_array_and_length_values(JSContext *context, JS::MutableHandleValue value_p, GITypeInfo *array_type_info, const GValue *array_value, const GValue *array_length_value, bool no_copy, GSignalQuery *signal_query, int array_length_arg_n) { JS::RootedValue array_length(context); GArgument array_arg; g_assert(G_VALUE_HOLDS_POINTER(array_value)); g_assert(G_VALUE_HOLDS_INT(array_length_value)); if (!gjs_value_from_g_value_internal(context, &array_length, array_length_value, no_copy, signal_query, array_length_arg_n)) return false; array_arg.v_pointer = g_value_get_pointer(array_value); return gjs_value_from_explicit_array(context, value_p, array_type_info, &array_arg, array_length.toInt32()); } static void closure_marshal(GClosure *closure, GValue *return_value, guint n_param_values, const GValue *param_values, gpointer invocation_hint, gpointer marshal_data) { JSContext *context; JSObject *obj; unsigned i; GSignalQuery signal_query = { 0, }; GISignalInfo *signal_info; bool *skip; int *array_len_indices_for; GITypeInfo **type_info_for; gjs_debug_marshal(GJS_DEBUG_GCLOSURE, "Marshal closure %p", closure); if (!gjs_closure_is_valid(closure)) { /* We were destroyed; become a no-op */ return; } context = gjs_closure_get_context(closure); if (G_UNLIKELY(_gjs_context_is_sweeping(context))) { GSignalInvocationHint *hint = (GSignalInvocationHint*) invocation_hint; g_critical("Attempting to call back into JSAPI during the sweeping phase of GC. " "This is most likely caused by not destroying a Clutter actor or Gtk+ " "widget with ::destroy signals connected, but can also be caused by " "using the destroy(), dispose(), or remove() vfuncs. " "Because it would crash the application, it has been " "blocked and the JS callback not invoked."); if (hint) { gpointer instance; g_signal_query(hint->signal_id, &signal_query); instance = g_value_peek_pointer(¶m_values[0]); g_critical("The offending signal was %s on %s %p.", signal_query.signal_name, g_type_name(G_TYPE_FROM_INSTANCE(instance)), instance); } gjs_dumpstack(); return; } obj = gjs_closure_get_callable(closure); JSAutoRequest ar(context); JSAutoCompartment ac(context, obj); if (marshal_data) { /* we are used for a signal handler */ guint signal_id; signal_id = GPOINTER_TO_UINT(marshal_data); g_signal_query(signal_id, &signal_query); if (!signal_query.signal_id) { gjs_debug(GJS_DEBUG_GCLOSURE, "Signal handler being called on invalid signal"); return; } if (signal_query.n_params + 1 != n_param_values) { gjs_debug(GJS_DEBUG_GCLOSURE, "Signal handler being called with wrong number of parameters"); return; } } /* Check if any parameters, such as array lengths, need to be eliminated * before we invoke the closure. */ skip = g_newa(bool, n_param_values); memset(skip, 0, sizeof (bool) * n_param_values); array_len_indices_for = g_newa(int, n_param_values); for(i = 0; i < n_param_values; i++) array_len_indices_for[i] = -1; type_info_for = g_newa(GITypeInfo *, n_param_values); memset(type_info_for, 0, sizeof (gpointer) * n_param_values); signal_info = get_signal_info_if_available(&signal_query); if (signal_info) { /* Start at argument 1, skip the instance parameter */ for (i = 1; i < n_param_values; ++i) { GIArgInfo *arg_info; int array_len_pos; arg_info = g_callable_info_get_arg(signal_info, i - 1); type_info_for[i] = g_arg_info_get_type(arg_info); array_len_pos = g_type_info_get_array_length(type_info_for[i]); if (array_len_pos != -1) { skip[array_len_pos + 1] = true; array_len_indices_for[i] = array_len_pos + 1; } g_base_info_unref((GIBaseInfo *)arg_info); } g_base_info_unref((GIBaseInfo *)signal_info); } JS::AutoValueVector argv(context); /* May end up being less */ if (!argv.reserve(n_param_values)) g_error("Unable to reserve space"); JS::RootedValue argv_to_append(context); for (i = 0; i < n_param_values; ++i) { const GValue *gval = ¶m_values[i]; bool no_copy; int array_len_index; bool res; if (skip[i]) continue; no_copy = false; if (i >= 1 && signal_query.signal_id) { no_copy = (signal_query.param_types[i - 1] & G_SIGNAL_TYPE_STATIC_SCOPE) != 0; } array_len_index = array_len_indices_for[i]; if (array_len_index != -1) { const GValue *array_len_gval = ¶m_values[array_len_index]; res = gjs_value_from_array_and_length_values(context, &argv_to_append, type_info_for[i], gval, array_len_gval, no_copy, &signal_query, array_len_index); } else { res = gjs_value_from_g_value_internal(context, &argv_to_append, gval, no_copy, &signal_query, i); } if (!res) { gjs_debug(GJS_DEBUG_GCLOSURE, "Unable to convert arg %d in order to invoke closure", i); gjs_log_exception(context); return; } if (!argv.append(argv_to_append)) g_error("Unable to append to vector"); } for (i = 1; i < n_param_values; i++) if (type_info_for[i]) g_base_info_unref((GIBaseInfo *)type_info_for[i]); JS::RootedValue rval(context); gjs_closure_invoke(closure, nullptr, argv, &rval, false); if (return_value != NULL) { if (rval.isUndefined()) { /* something went wrong invoking, error should be set already */ return; } if (!gjs_value_to_g_value(context, rval, return_value)) { gjs_debug(GJS_DEBUG_GCLOSURE, "Unable to convert return value when invoking closure"); gjs_log_exception(context); return; } } } GClosure* gjs_closure_new_for_signal(JSContext *context, JSObject *callable, const char *description, guint signal_id) { GClosure *closure; closure = gjs_closure_new(context, callable, description, false); g_closure_set_meta_marshal(closure, GUINT_TO_POINTER(signal_id), closure_marshal); return closure; } GClosure* gjs_closure_new_marshaled (JSContext *context, JSObject *callable, const char *description) { GClosure *closure; closure = gjs_closure_new(context, callable, description, true); g_closure_set_marshal(closure, closure_marshal); return closure; } static GType gjs_value_guess_g_type(JSContext *context, JS::Value value) { if (value.isNull()) return G_TYPE_POINTER; if (value.isString()) return G_TYPE_STRING; if (value.isInt32()) return G_TYPE_INT; if (value.isDouble()) return G_TYPE_DOUBLE; if (value.isBoolean()) return G_TYPE_BOOLEAN; if (value.isObject()) { JS::RootedObject obj(context, &value.toObject()); return gjs_gtype_get_actual_gtype(context, obj); } return G_TYPE_INVALID; } static bool throw_expect_type(JSContext *cx, JS::HandleValue value, const char *expected_type, GType gtype = 0) { gjs_throw(cx, "Wrong type %s; %s%s%s expected", JS::InformalValueTypeName(value), expected_type, gtype ? " " : "", gtype ? g_type_name(gtype) : ""); return false; /* for convenience */ } static bool gjs_value_to_g_value_internal(JSContext *context, JS::HandleValue value, GValue *gvalue, bool no_copy) { GType gtype; gtype = G_VALUE_TYPE(gvalue); if (gtype == 0) { gtype = gjs_value_guess_g_type(context, value); if (gtype == G_TYPE_INVALID) { gjs_throw(context, "Could not guess unspecified GValue type"); return false; } gjs_debug_marshal(GJS_DEBUG_GCLOSURE, "Guessed GValue type %s from JS Value", g_type_name(gtype)); g_value_init(gvalue, gtype); } gjs_debug_marshal(GJS_DEBUG_GCLOSURE, "Converting JS::Value to gtype %s", g_type_name(gtype)); if (gtype == G_TYPE_STRING) { /* Don't use ValueToString since we don't want to just toString() * everything automatically */ if (value.isNull()) { g_value_set_string(gvalue, NULL); } else if (value.isString()) { JS::RootedString str(context, value.toString()); GjsAutoJSChar utf8_string = JS_EncodeStringToUTF8(context, str); if (!utf8_string) return false; g_value_take_string(gvalue, utf8_string.copy()); } else { return throw_expect_type(context, value, "string"); } } else if (gtype == G_TYPE_CHAR) { gint32 i; if (JS::ToInt32(context, value, &i) && i >= SCHAR_MIN && i <= SCHAR_MAX) { g_value_set_schar(gvalue, (signed char)i); } else { return throw_expect_type(context, value, "char"); } } else if (gtype == G_TYPE_UCHAR) { guint16 i; if (JS::ToUint16(context, value, &i) && i <= UCHAR_MAX) { g_value_set_uchar(gvalue, (unsigned char)i); } else { return throw_expect_type(context, value, "unsigned char"); } } else if (gtype == G_TYPE_INT) { gint32 i; if (JS::ToInt32(context, value, &i)) { g_value_set_int(gvalue, i); } else { return throw_expect_type(context, value, "integer"); } } else if (gtype == G_TYPE_DOUBLE) { gdouble d; if (JS::ToNumber(context, value, &d)) { g_value_set_double(gvalue, d); } else { return throw_expect_type(context, value, "double"); } } else if (gtype == G_TYPE_FLOAT) { gdouble d; if (JS::ToNumber(context, value, &d)) { g_value_set_float(gvalue, d); } else { return throw_expect_type(context, value, "float"); } } else if (gtype == G_TYPE_UINT) { guint32 i; if (JS::ToUint32(context, value, &i)) { g_value_set_uint(gvalue, i); } else { return throw_expect_type(context, value, "unsigned integer"); } } else if (gtype == G_TYPE_BOOLEAN) { /* JS::ToBoolean() can't fail */ g_value_set_boolean(gvalue, JS::ToBoolean(value)); } else if (g_type_is_a(gtype, G_TYPE_OBJECT) || g_type_is_a(gtype, G_TYPE_INTERFACE)) { GObject *gobj; gobj = NULL; if (value.isNull()) { /* nothing to do */ } else if (value.isObject()) { JS::RootedObject obj(context, &value.toObject()); if (!gjs_typecheck_object(context, obj, gtype, true)) return false; gobj = gjs_g_object_from_object(context, obj); } else { return throw_expect_type(context, value, "object", gtype); } g_value_set_object(gvalue, gobj); } else if (gtype == G_TYPE_STRV) { bool found_length; if (value.isNull()) { /* do nothing */ } else { JS::RootedObject array_obj(context, &value.toObject()); if (gjs_object_has_property(context, array_obj, GJS_STRING_LENGTH, &found_length) && found_length) { guint32 length; if (!gjs_object_require_converted_property(context, array_obj, NULL, GJS_STRING_LENGTH, &length)) { JS_ClearPendingException(context); return throw_expect_type(context, value, "strv"); } else { void *result; char **strv; if (!gjs_array_to_strv (context, value, length, &result)) return false; /* cast to strv in a separate step to avoid type-punning */ strv = (char**) result; g_value_take_boxed (gvalue, strv); } } else { return throw_expect_type(context, value, "strv"); } } } else if (g_type_is_a(gtype, G_TYPE_BOXED)) { void *gboxed; gboxed = NULL; if (value.isNull()) return true; /* special case GValue */ if (g_type_is_a(gtype, G_TYPE_VALUE)) { GValue nested_gvalue = G_VALUE_INIT; if (!gjs_value_to_g_value(context, value, &nested_gvalue)) return false; g_value_set_boxed(gvalue, &nested_gvalue); g_value_unset(&nested_gvalue); return true; } if (value.isObject()) { JS::RootedObject obj(context, &value.toObject()); if (g_type_is_a(gtype, G_TYPE_ERROR)) { /* special case GError */ if (!gjs_typecheck_gerror(context, obj, true)) return false; gboxed = gjs_gerror_from_error(context, obj); } else { GIBaseInfo *registered = g_irepository_find_by_gtype (NULL, gtype); /* We don't necessarily have the typelib loaded when we first see the structure... */ if (registered) { GIInfoType info_type = g_base_info_get_type (registered); if (info_type == GI_INFO_TYPE_STRUCT && g_struct_info_is_foreign ((GIStructInfo*)registered)) { GArgument arg; if (!gjs_struct_foreign_convert_to_g_argument (context, value, registered, NULL, GJS_ARGUMENT_ARGUMENT, GI_TRANSFER_NOTHING, true, &arg)) return false; gboxed = arg.v_pointer; } } /* First try a union, if that fails, assume a boxed struct. Distinguishing which one is expected would require checking the associated GIBaseInfo, which is not necessary possible, if e.g. we see the GType without loading the typelib. */ if (!gboxed) { if (gjs_typecheck_union(context, obj, NULL, gtype, false)) { gboxed = gjs_c_union_from_union(context, obj); } else { if (!gjs_typecheck_boxed(context, obj, NULL, gtype, true)) return false; gboxed = gjs_c_struct_from_boxed(context, obj); } } } } else { return throw_expect_type(context, value, "boxed type", gtype); } if (no_copy) g_value_set_static_boxed(gvalue, gboxed); else g_value_set_boxed(gvalue, gboxed); } else if (g_type_is_a(gtype, G_TYPE_VARIANT)) { GVariant *variant = NULL; if (value.isNull()) { /* nothing to do */ } else if (value.isObject()) { JS::RootedObject obj(context, &value.toObject()); if (!gjs_typecheck_boxed(context, obj, NULL, G_TYPE_VARIANT, true)) return false; variant = (GVariant*) gjs_c_struct_from_boxed(context, obj); } else { return throw_expect_type(context, value, "boxed type", gtype); } g_value_set_variant (gvalue, variant); } else if (g_type_is_a(gtype, G_TYPE_ENUM)) { int64_t value_int64; if (JS::ToInt64(context, value, &value_int64)) { GEnumValue *v; gpointer gtype_class = g_type_class_ref(gtype); /* See arg.c:_gjs_enum_to_int() */ v = g_enum_get_value(G_ENUM_CLASS(gtype_class), (int)value_int64); g_type_class_unref(gtype_class); if (v == NULL) { gjs_throw(context, "%d is not a valid value for enumeration %s", value.toInt32(), g_type_name(gtype)); return false; } g_value_set_enum(gvalue, v->value); } else { return throw_expect_type(context, value, "enum", gtype); } } else if (g_type_is_a(gtype, G_TYPE_FLAGS)) { int64_t value_int64; if (JS::ToInt64(context, value, &value_int64)) { if (!_gjs_flags_value_is_valid(context, gtype, value_int64)) return false; /* See arg.c:_gjs_enum_to_int() */ g_value_set_flags(gvalue, (int)value_int64); } else { return throw_expect_type(context, value, "flags", gtype); } } else if (g_type_is_a(gtype, G_TYPE_PARAM)) { void *gparam; gparam = NULL; if (value.isNull()) { /* nothing to do */ } else if (value.isObject()) { JS::RootedObject obj(context, &value.toObject()); if (!gjs_typecheck_param(context, obj, gtype, true)) return false; gparam = gjs_g_param_from_param(context, obj); } else { return throw_expect_type(context, value, "param type", gtype); } g_value_set_param(gvalue, (GParamSpec*) gparam); } else if (g_type_is_a(gtype, G_TYPE_GTYPE)) { GType type; if (!value.isObject()) return throw_expect_type(context, value, "GType object"); JS::RootedObject obj(context, &value.toObject()); type = gjs_gtype_get_actual_gtype(context, obj); g_value_set_gtype(gvalue, type); } else if (g_type_is_a(gtype, G_TYPE_POINTER)) { if (value.isNull()) { /* Nothing to do */ } else { gjs_throw(context, "Cannot convert non-null JS value to G_POINTER"); return false; } } else if (value.isNumber() && g_value_type_transformable(G_TYPE_INT, gtype)) { /* Only do this crazy gvalue transform stuff after we've * exhausted everything else. Adding this for * e.g. ClutterUnit. */ gint32 i; if (JS::ToInt32(context, value, &i)) { GValue int_value = { 0, }; g_value_init(&int_value, G_TYPE_INT); g_value_set_int(&int_value, i); g_value_transform(&int_value, gvalue); } else { return throw_expect_type(context, value, "integer"); } } else { gjs_debug(GJS_DEBUG_GCLOSURE, "JS::Value is number %d gtype fundamental %d transformable to int %d from int %d", value.isNumber(), G_TYPE_IS_FUNDAMENTAL(gtype), g_value_type_transformable(gtype, G_TYPE_INT), g_value_type_transformable(G_TYPE_INT, gtype)); gjs_throw(context, "Don't know how to convert JavaScript object to GType %s", g_type_name(gtype)); return false; } return true; } bool gjs_value_to_g_value(JSContext *context, JS::HandleValue value, GValue *gvalue) { return gjs_value_to_g_value_internal(context, value, gvalue, false); } bool gjs_value_to_g_value_no_copy(JSContext *context, JS::HandleValue value, GValue *gvalue) { return gjs_value_to_g_value_internal(context, value, gvalue, true); } static JS::Value convert_int_to_enum (GType gtype, int v) { double v_double; if (v > 0 && v < G_MAXINT) { /* Optimize the unambiguous case */ v_double = v; } else { GIBaseInfo *info; /* Need to distinguish between negative integers and unsigned integers */ info = g_irepository_find_by_gtype(g_irepository_get_default(), gtype); g_assert (info); v_double = _gjs_enum_from_int ((GIEnumInfo *)info, v); g_base_info_unref(info); } return JS::NumberValue(v_double); } static bool gjs_value_from_g_value_internal(JSContext *context, JS::MutableHandleValue value_p, const GValue *gvalue, bool no_copy, GSignalQuery *signal_query, int arg_n) { GType gtype; gtype = G_VALUE_TYPE(gvalue); gjs_debug_marshal(GJS_DEBUG_GCLOSURE, "Converting gtype %s to JS::Value", g_type_name(gtype)); if (gtype == G_TYPE_STRING) { const char *v; v = g_value_get_string(gvalue); if (v == NULL) { gjs_debug_marshal(GJS_DEBUG_GCLOSURE, "Converting NULL string to JS::NullValue()"); value_p.setNull(); } else { if (!gjs_string_from_utf8(context, v, value_p)) return false; } } else if (gtype == G_TYPE_CHAR) { char v; v = g_value_get_schar(gvalue); value_p.setInt32(v); } else if (gtype == G_TYPE_UCHAR) { unsigned char v; v = g_value_get_uchar(gvalue); value_p.setInt32(v); } else if (gtype == G_TYPE_INT) { int v; v = g_value_get_int(gvalue); value_p.set(JS::NumberValue(v)); } else if (gtype == G_TYPE_UINT) { guint v; v = g_value_get_uint(gvalue); value_p.setNumber(v); } else if (gtype == G_TYPE_DOUBLE) { double d; d = g_value_get_double(gvalue); value_p.setNumber(d); } else if (gtype == G_TYPE_FLOAT) { double d; d = g_value_get_float(gvalue); value_p.setNumber(d); } else if (gtype == G_TYPE_BOOLEAN) { bool v; v = g_value_get_boolean(gvalue); value_p.setBoolean(!!v); } else if (g_type_is_a(gtype, G_TYPE_OBJECT) || g_type_is_a(gtype, G_TYPE_INTERFACE)) { GObject *gobj; JSObject *obj; gobj = (GObject*) g_value_get_object(gvalue); obj = gjs_object_from_g_object(context, gobj); value_p.setObjectOrNull(obj); } else if (gtype == G_TYPE_STRV) { if (!gjs_array_from_strv (context, value_p, (const char**) g_value_get_boxed (gvalue))) { gjs_throw(context, "Failed to convert strv to array"); return false; } } else if (g_type_is_a(gtype, G_TYPE_HASH_TABLE) || g_type_is_a(gtype, G_TYPE_ARRAY) || g_type_is_a(gtype, G_TYPE_BYTE_ARRAY) || g_type_is_a(gtype, G_TYPE_PTR_ARRAY)) { gjs_throw(context, "Unable to introspect element-type of container in GValue"); return false; } else if (g_type_is_a(gtype, G_TYPE_BOXED) || g_type_is_a(gtype, G_TYPE_VARIANT)) { GjsBoxedCreationFlags boxed_flags; GIBaseInfo *info; void *gboxed; JSObject *obj; if (g_type_is_a(gtype, G_TYPE_BOXED)) gboxed = g_value_get_boxed(gvalue); else gboxed = g_value_get_variant(gvalue); boxed_flags = GJS_BOXED_CREATION_NONE; /* special case GError */ if (g_type_is_a(gtype, G_TYPE_ERROR)) { obj = gjs_error_from_gerror(context, (GError*) gboxed, false); value_p.setObjectOrNull(obj); return true; } /* special case GValue */ if (g_type_is_a(gtype, G_TYPE_VALUE)) { return gjs_value_from_g_value(context, value_p, static_cast(gboxed)); } /* The only way to differentiate unions and structs is from * their g-i info as both GBoxed */ info = g_irepository_find_by_gtype(g_irepository_get_default(), gtype); if (info == NULL) { gjs_throw(context, "No introspection information found for %s", g_type_name(gtype)); return false; } if (g_base_info_get_type(info) == GI_INFO_TYPE_STRUCT && g_struct_info_is_foreign((GIStructInfo*)info)) { bool ret; GIArgument arg; arg.v_pointer = gboxed; ret = gjs_struct_foreign_convert_from_g_argument(context, value_p, info, &arg); g_base_info_unref(info); return ret; } GIInfoType type = g_base_info_get_type(info); if (type == GI_INFO_TYPE_BOXED || type == GI_INFO_TYPE_STRUCT) { if (no_copy) boxed_flags = (GjsBoxedCreationFlags) (boxed_flags | GJS_BOXED_CREATION_NO_COPY); obj = gjs_boxed_from_c_struct(context, (GIStructInfo *)info, gboxed, boxed_flags); } else if (type == GI_INFO_TYPE_UNION) { obj = gjs_union_from_c_union(context, (GIUnionInfo *)info, gboxed); } else { gjs_throw(context, "Unexpected introspection type %d for %s", g_base_info_get_type(info), g_type_name(gtype)); g_base_info_unref(info); return false; } value_p.setObjectOrNull(obj); g_base_info_unref(info); } else if (g_type_is_a(gtype, G_TYPE_ENUM)) { value_p.set(convert_int_to_enum(gtype, g_value_get_enum(gvalue))); } else if (g_type_is_a(gtype, G_TYPE_PARAM)) { GParamSpec *gparam; JSObject *obj; gparam = g_value_get_param(gvalue); obj = gjs_param_from_g_param(context, gparam); value_p.setObjectOrNull(obj); } else if (signal_query && g_type_is_a(gtype, G_TYPE_POINTER)) { bool res; GArgument arg; GIArgInfo *arg_info; GISignalInfo *signal_info; GITypeInfo type_info; signal_info = get_signal_info_if_available(signal_query); if (!signal_info) { gjs_throw(context, "Unknown signal."); return false; } arg_info = g_callable_info_get_arg(signal_info, arg_n - 1); g_arg_info_load_type(arg_info, &type_info); g_assert(((void) "Check gjs_value_from_array_and_length_values() before" " calling gjs_value_from_g_value_internal()", g_type_info_get_array_length(&type_info) == -1)); arg.v_pointer = g_value_get_pointer(gvalue); res = gjs_value_from_g_argument(context, value_p, &type_info, &arg, true); g_base_info_unref((GIBaseInfo*)arg_info); g_base_info_unref((GIBaseInfo*)signal_info); return res; } else if (g_type_is_a(gtype, G_TYPE_POINTER)) { gpointer pointer; pointer = g_value_get_pointer(gvalue); if (pointer == NULL) { value_p.setNull(); } else { gjs_throw(context, "Can't convert non-null pointer to JS value"); return false; } } else if (g_value_type_transformable(gtype, G_TYPE_DOUBLE)) { GValue double_value = { 0, }; double v; g_value_init(&double_value, G_TYPE_DOUBLE); g_value_transform(gvalue, &double_value); v = g_value_get_double(&double_value); value_p.setNumber(v); } else if (g_value_type_transformable(gtype, G_TYPE_INT)) { GValue int_value = { 0, }; int v; g_value_init(&int_value, G_TYPE_INT); g_value_transform(gvalue, &int_value); v = g_value_get_int(&int_value); value_p.set(JS::NumberValue(v)); } else if (G_TYPE_IS_INSTANTIATABLE(gtype)) { /* The gtype is none of the above, it should be a custom fundamental type. */ JSObject *obj; obj = gjs_fundamental_from_g_value(context, (const GValue*)gvalue, gtype); if (obj == NULL) return false; else value_p.setObject(*obj); } else { gjs_throw(context, "Don't know how to convert GType %s to JavaScript object", g_type_name(gtype)); return false; } return true; } bool gjs_value_from_g_value(JSContext *context, JS::MutableHandleValue value_p, const GValue *gvalue) { return gjs_value_from_g_value_internal(context, value_p, gvalue, false, NULL, 0); } cjs-4.4.0/gi/value.h000066400000000000000000000045421356375132300141770ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_VALUE_H__ #define __GJS_VALUE_H__ #include #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS bool gjs_value_to_g_value (JSContext *context, JS::HandleValue value, GValue *gvalue); bool gjs_value_to_g_value_no_copy (JSContext *context, JS::HandleValue value, GValue *gvalue); bool gjs_value_from_g_value(JSContext *context, JS::MutableHandleValue value_p, const GValue *gvalue); GClosure* gjs_closure_new_marshaled (JSContext *context, JSObject *callable, const char *description); GClosure* gjs_closure_new_for_signal (JSContext *context, JSObject *callable, const char *description, guint signal_id); G_END_DECLS #endif /* __GJS_VALUE_H__ */ cjs-4.4.0/git.mk000066400000000000000000000265551356375132300134370ustar00rootroot00000000000000# git.mk, a small Makefile to autogenerate .gitignore files # for autotools-based projects. # # Copyright 2009, Red Hat, Inc. # Copyright 2010,2011,2012,2013 Behdad Esfahbod # Written by Behdad Esfahbod # # Copying and distribution of this file, with or without modification, # is permitted in any medium without royalty provided the copyright # notice and this notice are preserved. # # The latest version of this file can be downloaded from: GIT_MK_URL = https://raw.githubusercontent.com/behdad/git.mk/master/git.mk # # Bugs, etc, should be reported upstream at: # https://github.com/behdad/git.mk # # To use in your project, import this file in your git repo's toplevel, # then do "make -f git.mk". This modifies all Makefile.am files in # your project to -include git.mk. Remember to add that line to new # Makefile.am files you create in your project, or just rerun the # "make -f git.mk". # # This enables automatic .gitignore generation. If you need to ignore # more files, add them to the GITIGNOREFILES variable in your Makefile.am. # But think twice before doing that. If a file has to be in .gitignore, # chances are very high that it's a generated file and should be in one # of MOSTLYCLEANFILES, CLEANFILES, DISTCLEANFILES, or MAINTAINERCLEANFILES. # # The only case that you need to manually add a file to GITIGNOREFILES is # when remove files in one of mostlyclean-local, clean-local, distclean-local, # or maintainer-clean-local make targets. # # Note that for files like editor backup, etc, there are better places to # ignore them. See "man gitignore". # # If "make maintainer-clean" removes the files but they are not recognized # by this script (that is, if "git status" shows untracked files still), send # me the output of "git status" as well as your Makefile.am and Makefile for # the directories involved and I'll diagnose. # # For a list of toplevel files that should be in MAINTAINERCLEANFILES, see # Makefile.am.sample in the git.mk git repo. # # Don't EXTRA_DIST this file. It is supposed to only live in git clones, # not tarballs. It serves no useful purpose in tarballs and clutters the # build dir. # # This file knows how to handle autoconf, automake, libtool, gtk-doc, # gnome-doc-utils, yelp.m4, mallard, intltool, gsettings, dejagnu, appdata, # appstream, hotdoc. # # This makefile provides the following targets: # # - all: "make all" will build all gitignore files. # - gitignore: makes all gitignore files in the current dir and subdirs. # - .gitignore: make gitignore file for the current dir. # - gitignore-recurse: makes all gitignore files in the subdirs. # # KNOWN ISSUES: # # - Recursive configure doesn't work as $(top_srcdir)/git.mk inside the # submodule doesn't find us. If you have configure.{in,ac} files in # subdirs, add a proxy git.mk file in those dirs that simply does: # "include $(top_srcdir)/../git.mk". Add more ..'s to your taste. # And add those files to git. See vte/gnome-pty-helper/git.mk for # example. # ############################################################################### # Variables user modules may want to add to toplevel MAINTAINERCLEANFILES: ############################################################################### # # Most autotools-using modules should be fine including this variable in their # toplevel MAINTAINERCLEANFILES: GITIGNORE_MAINTAINERCLEANFILES_TOPLEVEL = \ $(srcdir)/aclocal.m4 \ $(srcdir)/autoscan.log \ $(srcdir)/configure.scan \ `AUX_DIR=$(srcdir)/$$(cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_AUX_DIR:$$1' ./configure.ac); \ test "x$$AUX_DIR" = "x$(srcdir)/" && AUX_DIR=$(srcdir); \ for x in \ ar-lib \ compile \ config.guess \ config.rpath \ config.sub \ depcomp \ install-sh \ ltmain.sh \ missing \ mkinstalldirs \ test-driver \ ylwrap \ ; do echo "$$AUX_DIR/$$x"; done` \ `cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_HEADERS:$$1' ./configure.ac | \ head -n 1 | while read f; do echo "$(srcdir)/$$f.in"; done` # # All modules should also be fine including the following variable, which # removes automake-generated Makefile.in files: GITIGNORE_MAINTAINERCLEANFILES_MAKEFILE_IN = \ `cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_FILES:$$1' ./configure.ac | \ while read f; do \ case $$f in Makefile|*/Makefile) \ test -f "$(srcdir)/$$f.am" && echo "$(srcdir)/$$f.in";; esac; \ done` # # Modules that use libtool and use AC_CONFIG_MACRO_DIR() may also include this, # though it's harmless to include regardless. GITIGNORE_MAINTAINERCLEANFILES_M4_LIBTOOL = \ `MACRO_DIR=$(srcdir)/$$(cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_MACRO_DIR:$$1' ./configure.ac); \ if test "x$$MACRO_DIR" != "x$(srcdir)/"; then \ for x in \ libtool.m4 \ ltoptions.m4 \ ltsugar.m4 \ ltversion.m4 \ lt~obsolete.m4 \ ; do echo "$$MACRO_DIR/$$x"; done; \ fi` ############################################################################### # Default rule is to install ourselves in all Makefile.am files: ############################################################################### git-all: git-mk-install git-mk-install: @echo "Installing git makefile" @any_failed=; \ find "`test -z "$(top_srcdir)" && echo . || echo "$(top_srcdir)"`" -name Makefile.am | while read x; do \ if grep 'include .*/git.mk' $$x >/dev/null; then \ echo "$$x already includes git.mk"; \ else \ failed=; \ echo "Updating $$x"; \ { cat $$x; \ echo ''; \ echo '-include $$(top_srcdir)/git.mk'; \ } > $$x.tmp || failed=1; \ if test x$$failed = x; then \ mv $$x.tmp $$x || failed=1; \ fi; \ if test x$$failed = x; then : else \ echo "Failed updating $$x"; >&2 \ any_failed=1; \ fi; \ fi; done; test -z "$$any_failed" git-mk-update: wget $(GIT_MK_URL) -O $(top_srcdir)/git.mk .PHONY: git-all git-mk-install git-mk-update ############################################################################### # Actual .gitignore generation: ############################################################################### $(srcdir)/.gitignore: Makefile.am $(top_srcdir)/git.mk @echo "git.mk: Generating $@" @{ \ if test "x$(DOC_MODULE)" = x -o "x$(DOC_MAIN_SGML_FILE)" = x; then :; else \ for x in \ $(DOC_MODULE)-decl-list.txt \ $(DOC_MODULE)-decl.txt \ tmpl/$(DOC_MODULE)-unused.sgml \ "tmpl/*.bak" \ $(REPORT_FILES) \ $(DOC_MODULE).pdf \ xml html \ ; do echo "/$$x"; done; \ FLAVOR=$$(cd $(top_srcdir); $(AUTOCONF) --trace 'GTK_DOC_CHECK:$$2' ./configure.ac); \ case $$FLAVOR in *no-tmpl*) echo /tmpl;; esac; \ if echo "$(SCAN_OPTIONS)" | grep -q "\-\-rebuild-types"; then \ echo "/$(DOC_MODULE).types"; \ fi; \ if echo "$(SCAN_OPTIONS)" | grep -q "\-\-rebuild-sections"; then \ echo "/$(DOC_MODULE)-sections.txt"; \ fi; \ if test "$(abs_srcdir)" != "$(abs_builddir)" ; then \ for x in \ $(SETUP_FILES) \ $(DOC_MODULE).types \ ; do echo "/$$x"; done; \ fi; \ fi; \ if test "x$(DOC_MODULE)$(DOC_ID)" = x -o "x$(DOC_LINGUAS)" = x; then :; else \ for lc in $(DOC_LINGUAS); do \ for x in \ $(if $(DOC_MODULE),$(DOC_MODULE).xml) \ $(DOC_PAGES) \ $(DOC_INCLUDES) \ ; do echo "/$$lc/$$x"; done; \ done; \ for x in \ $(_DOC_OMF_ALL) \ $(_DOC_DSK_ALL) \ $(_DOC_HTML_ALL) \ $(_DOC_MOFILES) \ $(DOC_H_FILE) \ "*/.xml2po.mo" \ "*/*.omf.out" \ ; do echo /$$x; done; \ fi; \ if test "x$(HOTDOC)" = x; then :; else \ $(foreach project, $(HOTDOC_PROJECTS),echo "/$(call HOTDOC_TARGET,$(project))"; \ echo "/$(shell $(call HOTDOC_PROJECT_COMMAND,$(project)) --get-conf-path output)" ; \ echo "/$(shell $(call HOTDOC_PROJECT_COMMAND,$(project)) --get-private-folder)" ; \ ) \ for x in \ .hotdoc.d \ ; do echo "/$$x"; done; \ fi; \ if test "x$(HELP_ID)" = x -o "x$(HELP_LINGUAS)" = x; then :; else \ for lc in $(HELP_LINGUAS); do \ for x in \ $(HELP_FILES) \ "$$lc.stamp" \ "$$lc.mo" \ ; do echo "/$$lc/$$x"; done; \ done; \ fi; \ if test "x$(gsettings_SCHEMAS)" = x; then :; else \ for x in \ $(gsettings_SCHEMAS:.xml=.valid) \ $(gsettings__enum_file) \ ; do echo "/$$x"; done; \ fi; \ if test "x$(appdata_XML)" = x; then :; else \ for x in \ $(appdata_XML:.xml=.valid) \ ; do echo "/$$x"; done; \ fi; \ if test "x$(appstream_XML)" = x; then :; else \ for x in \ $(appstream_XML:.xml=.valid) \ ; do echo "/$$x"; done; \ fi; \ if test -f $(srcdir)/po/Makefile.in.in; then \ for x in \ ABOUT-NLS \ po/Makefile.in.in \ po/Makefile.in.in~ \ po/Makefile.in \ po/Makefile \ po/Makevars.template \ po/POTFILES \ po/Rules-quot \ po/stamp-it \ po/stamp-po \ po/.intltool-merge-cache \ "po/*.gmo" \ "po/*.header" \ "po/*.mo" \ "po/*.sed" \ "po/*.sin" \ po/$(GETTEXT_PACKAGE).pot \ intltool-extract.in \ intltool-merge.in \ intltool-update.in \ ; do echo "/$$x"; done; \ fi; \ if test -f $(srcdir)/configure; then \ for x in \ autom4te.cache \ configure \ config.h \ stamp-h1 \ libtool \ config.lt \ ; do echo "/$$x"; done; \ fi; \ if test "x$(DEJATOOL)" = x; then :; else \ for x in \ $(DEJATOOL) \ ; do echo "/$$x.sum"; echo "/$$x.log"; done; \ echo /site.exp; \ fi; \ if test "x$(am__dirstamp)" = x; then :; else \ echo "$(am__dirstamp)"; \ fi; \ if test "x$(findstring libtool,$(LTCOMPILE))" = x -a "x$(findstring libtool,$(LTCXXCOMPILE))" = x -a "x$(GTKDOC_RUN)" = x; then :; else \ for x in \ "*.lo" \ ".libs" "_libs" \ ; do echo "$$x"; done; \ fi; \ for x in \ .gitignore \ $(GITIGNOREFILES) \ $(CLEANFILES) \ $(PROGRAMS) $(check_PROGRAMS) $(EXTRA_PROGRAMS) \ $(LIBRARIES) $(check_LIBRARIES) $(EXTRA_LIBRARIES) \ $(LTLIBRARIES) $(check_LTLIBRARIES) $(EXTRA_LTLIBRARIES) \ so_locations \ $(MOSTLYCLEANFILES) \ $(TEST_LOGS) \ $(TEST_LOGS:.log=.trs) \ $(TEST_SUITE_LOG) \ $(TESTS:=.test) \ "*.gcda" \ "*.gcno" \ $(DISTCLEANFILES) \ $(am__CONFIG_DISTCLEAN_FILES) \ $(CONFIG_CLEAN_FILES) \ TAGS ID GTAGS GRTAGS GSYMS GPATH tags \ "*.tab.c" \ $(MAINTAINERCLEANFILES) \ $(BUILT_SOURCES) \ $(patsubst %.vala,%.c,$(filter %.vala,$(SOURCES))) \ $(filter %_vala.stamp,$(DIST_COMMON)) \ $(filter %.vapi,$(DIST_COMMON)) \ $(filter $(addprefix %,$(notdir $(patsubst %.vapi,%.h,$(filter %.vapi,$(DIST_COMMON))))),$(DIST_COMMON)) \ Makefile \ Makefile.in \ "*.orig" \ "*.rej" \ "*.bak" \ "*~" \ ".*.sw[nop]" \ ".dirstamp" \ ; do echo "/$$x"; done; \ for x in \ "*.$(OBJEXT)" \ $(DEPDIR) \ ; do echo "$$x"; done; \ } | \ sed "s@^/`echo "$(srcdir)" | sed 's/\(.\)/[\1]/g'`/@/@" | \ sed 's@/[.]/@/@g' | \ LC_ALL=C sort | uniq > $@.tmp && \ mv $@.tmp $@; all: $(srcdir)/.gitignore gitignore-recurse-maybe gitignore: $(srcdir)/.gitignore gitignore-recurse gitignore-recurse-maybe: @for subdir in $(DIST_SUBDIRS); do \ case " $(SUBDIRS) " in \ *" $$subdir "*) :;; \ *) test "$$subdir" = . -o -e "$$subdir/.git" || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) gitignore || echo "Skipping $$subdir");; \ esac; \ done gitignore-recurse: @for subdir in $(DIST_SUBDIRS); do \ test "$$subdir" = . -o -e "$$subdir/.git" || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) gitignore || echo "Skipping $$subdir"); \ done maintainer-clean: gitignore-clean gitignore-clean: -rm -f $(srcdir)/.gitignore .PHONY: gitignore-clean gitignore gitignore-recurse gitignore-recurse-maybe cjs-4.4.0/gjs-modules-srcs.mk000066400000000000000000000014551356375132300160450ustar00rootroot00000000000000module_console_srcs = \ modules/console.h \ modules/console.cpp \ $(NULL) module_resource_srcs = \ modules-resources.c \ modules-resources.h \ $(NULL) module_system_srcs = \ modules/system.h \ modules/system.cpp \ $(NULL) module_cairo_srcs = \ modules/cairo-private.h \ modules/cairo-module.h \ modules/cairo-region.cpp \ modules/cairo-context.cpp \ modules/cairo-path.cpp \ modules/cairo-surface.cpp \ modules/cairo-image-surface.cpp \ modules/cairo-ps-surface.cpp \ modules/cairo-pdf-surface.cpp \ modules/cairo-svg-surface.cpp \ modules/cairo-pattern.cpp \ modules/cairo-gradient.cpp \ modules/cairo-linear-gradient.cpp \ modules/cairo-radial-gradient.cpp \ modules/cairo-surface-pattern.cpp \ modules/cairo-solid-pattern.cpp \ modules/cairo.cpp \ $(NULL) cjs-4.4.0/gjs-srcs.mk000066400000000000000000000043201356375132300143710ustar00rootroot00000000000000gjs_public_headers = \ cjs/context.h \ cjs/coverage.h \ cjs/gjs.h \ cjs/macros.h \ cjs/profiler.h \ util/error.h \ $(NULL) # For historical reasons, some files live in gi/ # Some headers in the following list were formerly # public gjs_srcs = \ gi/arg.cpp \ gi/arg.h \ gi/boxed.cpp \ gi/boxed.h \ gi/closure.cpp \ gi/closure.h \ gi/enumeration.cpp \ gi/enumeration.h \ gi/foreign.cpp \ gi/foreign.h \ gi/fundamental.cpp \ gi/fundamental.h \ gi/function.cpp \ gi/function.h \ gi/gerror.cpp \ gi/gerror.h \ gi/gjs_gi_trace.h \ gi/gtype.cpp \ gi/gtype.h \ gi/interface.cpp \ gi/interface.h \ gi/ns.cpp \ gi/ns.h \ gi/object.cpp \ gi/object.h \ gi/param.cpp \ gi/param.h \ gi/proxyutils.cpp \ gi/proxyutils.h \ gi/repo.cpp \ gi/repo.h \ gi/toggle.cpp \ gi/toggle.h \ gi/union.cpp \ gi/union.h \ gi/value.cpp \ gi/value.h \ cjs/byteArray.cpp \ cjs/byteArray.h \ cjs/context.cpp \ cjs/context-private.h \ cjs/coverage.cpp \ cjs/engine.cpp \ cjs/engine.h \ cjs/global.cpp \ cjs/global.h \ cjs/importer.cpp \ cjs/importer.h \ cjs/jsapi-class.h \ cjs/jsapi-dynamic-class.cpp \ cjs/jsapi-util.cpp \ cjs/jsapi-util.h \ cjs/jsapi-util-args.h \ cjs/jsapi-util-error.cpp \ cjs/jsapi-util-root.h \ cjs/jsapi-util-string.cpp \ cjs/jsapi-wrapper.h \ cjs/mem.h \ cjs/mem.cpp \ cjs/module.h \ cjs/module.cpp \ cjs/native.cpp \ cjs/native.h \ cjs/profiler.cpp \ cjs/profiler-private.h \ cjs/stack.cpp \ modules/modules.cpp \ modules/modules.h \ util/error.cpp \ util/glib.cpp \ util/glib.h \ util/log.cpp \ util/log.h \ util/misc.cpp \ util/misc.h \ $(NULL) # These files were part of a separate library gjs_private_srcs = \ libgjs-private/gjs-gdbus-wrapper.cpp \ libgjs-private/gjs-gdbus-wrapper.h \ libgjs-private/gjs-util.cpp \ libgjs-private/gjs-util.h \ libgjs-private/gjs-gtk-util.h \ $(NULL) gjs_gtk_private_srcs = \ libgjs-private/gjs-gtk-util.c \ $(NULL) gjs_console_srcs = \ cjs/console.cpp \ $(NULL) gjs_sysprof_srcs = \ util/sp-capture-types.h \ util/sp-capture-writer.c \ util/sp-capture-writer.h \ $(NULL) cjs-4.4.0/gjs.doap000066400000000000000000000034471356375132300137460ustar00rootroot00000000000000 gjs gjs GNOME JavaScript bindings GNOME JavaScript bindings C++ Philip Chimento ptomato pchimento Cosimo Cecchi cosimoc Giovanni Campagna gcampagna cjs-4.4.0/installed-tests/000077500000000000000000000000001356375132300154255ustar00rootroot00000000000000cjs-4.4.0/installed-tests/extra/000077500000000000000000000000001356375132300165505ustar00rootroot00000000000000cjs-4.4.0/installed-tests/extra/gjs.supp000066400000000000000000000054671356375132300202600ustar00rootroot00000000000000# Valgrind suppressions file for GJS # This is intended to be used in addition to GLib's glib.supp file. # We leak a small wrapper in GJS for each registered GType. { gtype-wrapper-new Memcheck:Leak match-leak-kinds: definite fun:_Znwm fun:gjs_gtype_create_gtype_wrapper } { gtype-wrapper-qdata Memcheck:Leak match-leak-kinds: possible ... fun:type_set_qdata_W fun:g_type_set_qdata fun:gjs_gtype_create_gtype_wrapper } { g_type_register_fundamental never freed Memcheck:Leak fun:calloc ... fun:g_type_register_fundamental ... } # SpiderMonkey leaks { mozjs-thread-stack-init Memcheck:Leak match-leak-kinds: possible fun:calloc fun:allocate_dtv fun:_dl_allocate_tls fun:allocate_stack fun:pthread_create@@GLIBC_2.2.5 fun:_ZN7mozilla9TimeStamp20ComputeProcessUptimeEv fun:_ZN7mozilla9TimeStamp15ProcessCreationERb fun:_ZN2JS6detail25InitWithFailureDiagnosticEb fun:JS_Init } { mozjs-thread-stack-new-context Memcheck:Leak match-leak-kinds: possible fun:calloc fun:allocate_dtv fun:_dl_allocate_tls fun:allocate_stack fun:pthread_create@@GLIBC_2.2.5 fun:_ZN2js6Thread6createEPFPvS1_ES1_ fun:init fun:_ZN2js23GlobalHelperThreadState17ensureInitializedEv fun:_ZN9JSRuntime4initEjj fun:init fun:NewContext fun:_Z13JS_NewContextjjP9JSContext } { mozjs-gc-helper-thread-mutex-guard Memcheck:Leak match-leak-kinds: definite fun:malloc fun:js_malloc fun:js_new > fun:_ZN2js5Mutex14heldMutexStackEv.part.* fun:heldMutexStack fun:_ZN2js5Mutex4lockEv fun:LockGuard fun:_ZN2js25AutoLockHelperThreadStateC1EON7mozilla6detail19GuardObjectNotifierE fun:_ZN2js12HelperThread10threadLoopEv fun:callMain<0*> fun:_ZN2js6detail16ThreadTrampolineIRFvPvEJPNS_12HelperThreadEEE5StartES2_ fun:start_thread fun:clone } # SpiderMonkey data races # These are in SpiderMonkey's atomics / thread barrier stuff so presumably # locking is working correctly and helgrind just can't detect it? { mozjs-helgrind-atomic-load-1 Helgrind:Race fun:load fun:load fun:operator unsigned int } { mozjs-helgrind-atomic-load-2 Helgrind:Race fun:load fun:load fun:operator bool } { mozjs-helgrind-atomic-store Helgrind:Race fun:store fun:store fun:operator= } # Presumably this one is OK since the function is called "thread safe"?! { mozjs-helgrind-thread-safe-lookup Helgrind:Race ... fun:lookup fun:readonlyThreadsafeLookup fun:readonlyThreadsafeLookup } { mozjs-helgrind-jit-code Helgrind:Race obj:* ... fun:_ZL13EnterBaselineP9JSContextRN2js3jit12EnterJitDataE } { mozjs-helgrind-probably-jit-code Helgrind:Race obj:* obj:* obj:* obj:* obj:* } cjs-4.4.0/installed-tests/extra/lsan.supp000066400000000000000000000002731356375132300204200ustar00rootroot00000000000000# SpiderMonkey leaks a mutex for each GC helper thread. leak:js::HelperThread::threadLoop # We leak a small wrapper in GJS for each registered GType. leak:gjs_gtype_create_gtype_wrapper cjs-4.4.0/installed-tests/extra/unittest.gdb000066400000000000000000000000461356375132300211050ustar00rootroot00000000000000run if ($_exitcode == 0) quit end cjs-4.4.0/installed-tests/js/000077500000000000000000000000001356375132300160415ustar00rootroot00000000000000cjs-4.4.0/installed-tests/js/complex.ui000066400000000000000000000020501356375132300200440ustar00rootroot00000000000000 cjs-4.4.0/installed-tests/js/jasmine.js000066400000000000000000002766651356375132300200530ustar00rootroot00000000000000/* Copyright (c) 2008-2016 Pivotal Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ var getJasmineRequireObj = (function (jasmineGlobal) { var jasmineRequire; if (typeof module !== 'undefined' && module.exports && typeof exports !== 'undefined') { if (typeof global !== 'undefined') { jasmineGlobal = global; } else { jasmineGlobal = {}; } jasmineRequire = exports; } else { if (typeof window !== 'undefined' && typeof window.toString === 'function' && window.toString() === '[object GjsGlobal]') { jasmineGlobal = window; } jasmineRequire = jasmineGlobal.jasmineRequire = jasmineGlobal.jasmineRequire || {}; } function getJasmineRequire() { return jasmineRequire; } getJasmineRequire().core = function(jRequire) { var j$ = {}; jRequire.base(j$, jasmineGlobal); j$.util = jRequire.util(); j$.errors = jRequire.errors(); j$.formatErrorMsg = jRequire.formatErrorMsg(); j$.Any = jRequire.Any(j$); j$.Anything = jRequire.Anything(j$); j$.CallTracker = jRequire.CallTracker(j$); j$.MockDate = jRequire.MockDate(); j$.Clock = jRequire.Clock(); j$.DelayedFunctionScheduler = jRequire.DelayedFunctionScheduler(); j$.Env = jRequire.Env(j$); j$.ExceptionFormatter = jRequire.ExceptionFormatter(); j$.Expectation = jRequire.Expectation(); j$.buildExpectationResult = jRequire.buildExpectationResult(); j$.JsApiReporter = jRequire.JsApiReporter(); j$.matchersUtil = jRequire.matchersUtil(j$); j$.ObjectContaining = jRequire.ObjectContaining(j$); j$.ArrayContaining = jRequire.ArrayContaining(j$); j$.pp = jRequire.pp(j$); j$.QueueRunner = jRequire.QueueRunner(j$); j$.ReportDispatcher = jRequire.ReportDispatcher(); j$.Spec = jRequire.Spec(j$); j$.SpyRegistry = jRequire.SpyRegistry(j$); j$.SpyStrategy = jRequire.SpyStrategy(j$); j$.StringMatching = jRequire.StringMatching(j$); j$.Suite = jRequire.Suite(j$); j$.Timer = jRequire.Timer(); j$.TreeProcessor = jRequire.TreeProcessor(); j$.version = jRequire.version(); j$.Order = jRequire.Order(); j$.matchers = jRequire.requireMatchers(jRequire, j$); return j$; }; return getJasmineRequire; })(this); getJasmineRequireObj().requireMatchers = function(jRequire, j$) { var availableMatchers = [ 'toBe', 'toBeCloseTo', 'toBeDefined', 'toBeFalsy', 'toBeGreaterThan', 'toBeGreaterThanOrEqual', 'toBeLessThanOrEqual', 'toBeLessThan', 'toBeNaN', 'toBeNull', 'toBeTruthy', 'toBeUndefined', 'toContain', 'toEqual', 'toHaveBeenCalled', 'toHaveBeenCalledWith', 'toHaveBeenCalledTimes', 'toMatch', 'toThrow', 'toThrowError' ], matchers = {}; for (var i = 0; i < availableMatchers.length; i++) { var name = availableMatchers[i]; matchers[name] = jRequire[name](j$); } return matchers; }; getJasmineRequireObj().base = function(j$, jasmineGlobal) { j$.unimplementedMethod_ = function() { throw new Error('unimplemented method'); }; j$.MAX_PRETTY_PRINT_DEPTH = 40; j$.MAX_PRETTY_PRINT_ARRAY_LENGTH = 100; j$.DEFAULT_TIMEOUT_INTERVAL = 5000; j$.getGlobal = function() { return jasmineGlobal; }; j$.getEnv = function(options) { var env = j$.currentEnv_ = j$.currentEnv_ || new j$.Env(options); //jasmine. singletons in here (setTimeout blah blah). return env; }; j$.isArray_ = function(value) { return j$.isA_('Array', value); }; j$.isString_ = function(value) { return j$.isA_('String', value); }; j$.isNumber_ = function(value) { return j$.isA_('Number', value); }; j$.isFunction_ = function(value) { return j$.isA_('Function', value); }; j$.isA_ = function(typeName, value) { return Object.prototype.toString.apply(value) === '[object ' + typeName + ']'; }; j$.isDomNode = function(obj) { return obj.nodeType > 0; }; j$.fnNameFor = function(func) { if (func.name) { return func.name; } var matches = func.toString().match(/^\s*function\s*(\w*)\s*\(/); return matches ? matches[1] : ''; }; j$.any = function(clazz) { return new j$.Any(clazz); }; j$.anything = function() { return new j$.Anything(); }; j$.objectContaining = function(sample) { return new j$.ObjectContaining(sample); }; j$.stringMatching = function(expected) { return new j$.StringMatching(expected); }; j$.arrayContaining = function(sample) { return new j$.ArrayContaining(sample); }; j$.createSpy = function(name, originalFn) { var spyStrategy = new j$.SpyStrategy({ name: name, fn: originalFn, getSpy: function() { return spy; } }), callTracker = new j$.CallTracker(), spy = function() { var callData = { object: this, args: Array.prototype.slice.apply(arguments) }; callTracker.track(callData); var returnValue = spyStrategy.exec.apply(this, arguments); callData.returnValue = returnValue; return returnValue; }; for (var prop in originalFn) { if (prop === 'and' || prop === 'calls') { throw new Error('Jasmine spies would overwrite the \'and\' and \'calls\' properties on the object being spied upon'); } spy[prop] = originalFn[prop]; } spy.and = spyStrategy; spy.calls = callTracker; return spy; }; j$.isSpy = function(putativeSpy) { if (!putativeSpy) { return false; } return putativeSpy.and instanceof j$.SpyStrategy && putativeSpy.calls instanceof j$.CallTracker; }; j$.createSpyObj = function(baseName, methodNames) { if (j$.isArray_(baseName) && j$.util.isUndefined(methodNames)) { methodNames = baseName; baseName = 'unknown'; } if (!j$.isArray_(methodNames) || methodNames.length === 0) { throw 'createSpyObj requires a non-empty array of method names to create spies for'; } var obj = {}; for (var i = 0; i < methodNames.length; i++) { obj[methodNames[i]] = j$.createSpy(baseName + '.' + methodNames[i]); } return obj; }; }; getJasmineRequireObj().util = function() { var util = {}; util.inherit = function(childClass, parentClass) { var Subclass = function() { }; Subclass.prototype = parentClass.prototype; childClass.prototype = new Subclass(); }; util.htmlEscape = function(str) { if (!str) { return str; } return str.replace(/&/g, '&') .replace(//g, '>'); }; util.argsToArray = function(args) { var arrayOfArgs = []; for (var i = 0; i < args.length; i++) { arrayOfArgs.push(args[i]); } return arrayOfArgs; }; util.isUndefined = function(obj) { return obj === void 0; }; util.arrayContains = function(array, search) { var i = array.length; while (i--) { if (array[i] === search) { return true; } } return false; }; util.clone = function(obj) { if (Object.prototype.toString.apply(obj) === '[object Array]') { return obj.slice(); } var cloned = {}; for (var prop in obj) { if (obj.hasOwnProperty(prop)) { cloned[prop] = obj[prop]; } } return cloned; }; return util; }; getJasmineRequireObj().Spec = function(j$) { function Spec(attrs) { this.expectationFactory = attrs.expectationFactory; this.resultCallback = attrs.resultCallback || function() {}; this.id = attrs.id; this.description = attrs.description || ''; this.queueableFn = attrs.queueableFn; this.beforeAndAfterFns = attrs.beforeAndAfterFns || function() { return {befores: [], afters: []}; }; this.userContext = attrs.userContext || function() { return {}; }; this.onStart = attrs.onStart || function() {}; this.getSpecName = attrs.getSpecName || function() { return ''; }; this.expectationResultFactory = attrs.expectationResultFactory || function() { }; this.queueRunnerFactory = attrs.queueRunnerFactory || function() {}; this.catchingExceptions = attrs.catchingExceptions || function() { return true; }; this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure; if (!this.queueableFn.fn) { this.pend(); } this.result = { id: this.id, description: this.description, fullName: this.getFullName(), failedExpectations: [], passedExpectations: [], pendingReason: '' }; } Spec.prototype.addExpectationResult = function(passed, data, isError) { var expectationResult = this.expectationResultFactory(data); if (passed) { this.result.passedExpectations.push(expectationResult); } else { this.result.failedExpectations.push(expectationResult); if (this.throwOnExpectationFailure && !isError) { throw new j$.errors.ExpectationFailed(); } } }; Spec.prototype.expect = function(actual) { return this.expectationFactory(actual, this); }; Spec.prototype.execute = function(onComplete, enabled) { var self = this; this.onStart(this); if (!this.isExecutable() || this.markedPending || enabled === false) { complete(enabled); return; } var fns = this.beforeAndAfterFns(); var allFns = fns.befores.concat(this.queueableFn).concat(fns.afters); this.queueRunnerFactory({ queueableFns: allFns, onException: function() { self.onException.apply(self, arguments); }, onComplete: complete, userContext: this.userContext() }); function complete(enabledAgain) { self.result.status = self.status(enabledAgain); self.resultCallback(self.result); if (onComplete) { onComplete(); } } }; Spec.prototype.onException = function onException(e) { if (Spec.isPendingSpecException(e)) { this.pend(extractCustomPendingMessage(e)); return; } if (e instanceof j$.errors.ExpectationFailed) { return; } this.addExpectationResult(false, { matcherName: '', passed: false, expected: '', actual: '', error: e }, true); }; Spec.prototype.disable = function() { this.disabled = true; }; Spec.prototype.pend = function(message) { this.markedPending = true; if (message) { this.result.pendingReason = message; } }; Spec.prototype.getResult = function() { this.result.status = this.status(); return this.result; }; Spec.prototype.status = function(enabled) { if (this.disabled || enabled === false) { return 'disabled'; } if (this.markedPending) { return 'pending'; } if (this.result.failedExpectations.length > 0) { return 'failed'; } else { return 'passed'; } }; Spec.prototype.isExecutable = function() { return !this.disabled; }; Spec.prototype.getFullName = function() { return this.getSpecName(this); }; var extractCustomPendingMessage = function(e) { var fullMessage = e.toString(), boilerplateStart = fullMessage.indexOf(Spec.pendingSpecExceptionMessage), boilerplateEnd = boilerplateStart + Spec.pendingSpecExceptionMessage.length; return fullMessage.substr(boilerplateEnd); }; Spec.pendingSpecExceptionMessage = '=> marked Pending'; Spec.isPendingSpecException = function(e) { return !!(e && e.toString && e.toString().indexOf(Spec.pendingSpecExceptionMessage) !== -1); }; return Spec; }; if (typeof window == void 0 && typeof exports == 'object') { exports.Spec = jasmineRequire.Spec; } /*jshint bitwise: false*/ getJasmineRequireObj().Order = function() { function Order(options) { this.random = 'random' in options ? options.random : true; var seed = this.seed = options.seed || generateSeed(); this.sort = this.random ? randomOrder : naturalOrder; function naturalOrder(items) { return items; } function randomOrder(items) { var copy = items.slice(); copy.sort(function(a, b) { return jenkinsHash(seed + a.id) - jenkinsHash(seed + b.id); }); return copy; } function generateSeed() { return String(Math.random()).slice(-5); } // Bob Jenkins One-at-a-Time Hash algorithm is a non-cryptographic hash function // used to get a different output when the key changes slighly. // We use your return to sort the children randomly in a consistent way when // used in conjunction with a seed function jenkinsHash(key) { var hash, i; for(hash = i = 0; i < key.length; ++i) { hash += key.charCodeAt(i); hash += (hash << 10); hash ^= (hash >> 6); } hash += (hash << 3); hash ^= (hash >> 11); hash += (hash << 15); return hash; } } return Order; }; getJasmineRequireObj().Env = function(j$) { function Env(options) { options = options || {}; var self = this; var global = options.global || j$.getGlobal(); var totalSpecsDefined = 0; var catchExceptions = true; var realSetTimeout = j$.getGlobal().setTimeout; var realClearTimeout = j$.getGlobal().clearTimeout; this.clock = new j$.Clock(global, function () { return new j$.DelayedFunctionScheduler(); }, new j$.MockDate(global)); var runnableResources = {}; var currentSpec = null; var currentlyExecutingSuites = []; var currentDeclarationSuite = null; var throwOnExpectationFailure = false; var random = false; var seed = null; var currentSuite = function() { return currentlyExecutingSuites[currentlyExecutingSuites.length - 1]; }; var currentRunnable = function() { return currentSpec || currentSuite(); }; var reporter = new j$.ReportDispatcher([ 'jasmineStarted', 'jasmineDone', 'suiteStarted', 'suiteDone', 'specStarted', 'specDone' ]); this.specFilter = function() { return true; }; this.addCustomEqualityTester = function(tester) { if(!currentRunnable()) { throw new Error('Custom Equalities must be added in a before function or a spec'); } runnableResources[currentRunnable().id].customEqualityTesters.push(tester); }; this.addMatchers = function(matchersToAdd) { if(!currentRunnable()) { throw new Error('Matchers must be added in a before function or a spec'); } var customMatchers = runnableResources[currentRunnable().id].customMatchers; for (var matcherName in matchersToAdd) { customMatchers[matcherName] = matchersToAdd[matcherName]; } }; j$.Expectation.addCoreMatchers(j$.matchers); var nextSpecId = 0; var getNextSpecId = function() { return 'spec' + nextSpecId++; }; var nextSuiteId = 0; var getNextSuiteId = function() { return 'suite' + nextSuiteId++; }; var expectationFactory = function(actual, spec) { return j$.Expectation.Factory({ util: j$.matchersUtil, customEqualityTesters: runnableResources[spec.id].customEqualityTesters, customMatchers: runnableResources[spec.id].customMatchers, actual: actual, addExpectationResult: addExpectationResult }); function addExpectationResult(passed, result) { return spec.addExpectationResult(passed, result); } }; var defaultResourcesForRunnable = function(id, parentRunnableId) { var resources = {spies: [], customEqualityTesters: [], customMatchers: {}}; if(runnableResources[parentRunnableId]){ resources.customEqualityTesters = j$.util.clone(runnableResources[parentRunnableId].customEqualityTesters); resources.customMatchers = j$.util.clone(runnableResources[parentRunnableId].customMatchers); } runnableResources[id] = resources; }; var clearResourcesForRunnable = function(id) { spyRegistry.clearSpies(); delete runnableResources[id]; }; var beforeAndAfterFns = function(suite) { return function() { var befores = [], afters = []; while(suite) { befores = befores.concat(suite.beforeFns); afters = afters.concat(suite.afterFns); suite = suite.parentSuite; } return { befores: befores.reverse(), afters: afters }; }; }; var getSpecName = function(spec, suite) { var fullName = [spec.description], suiteFullName = suite.getFullName(); if (suiteFullName !== '') { fullName.unshift(suiteFullName); } return fullName.join(' '); }; // TODO: we may just be able to pass in the fn instead of wrapping here var buildExpectationResult = j$.buildExpectationResult, exceptionFormatter = new j$.ExceptionFormatter(), expectationResultFactory = function(attrs) { attrs.messageFormatter = exceptionFormatter.message; attrs.stackFormatter = exceptionFormatter.stack; return buildExpectationResult(attrs); }; // TODO: fix this naming, and here's where the value comes in this.catchExceptions = function(value) { catchExceptions = !!value; return catchExceptions; }; this.catchingExceptions = function() { return catchExceptions; }; var maximumSpecCallbackDepth = 20; var currentSpecCallbackDepth = 0; function clearStack(fn) { currentSpecCallbackDepth++; if (currentSpecCallbackDepth >= maximumSpecCallbackDepth) { currentSpecCallbackDepth = 0; realSetTimeout(fn, 0); } else { fn(); } } var catchException = function(e) { return j$.Spec.isPendingSpecException(e) || catchExceptions; }; this.throwOnExpectationFailure = function(value) { throwOnExpectationFailure = !!value; }; this.throwingExpectationFailures = function() { return throwOnExpectationFailure; }; this.randomizeTests = function(value) { random = !!value; }; this.randomTests = function() { return random; }; this.seed = function(value) { if (value) { seed = value; } return seed; }; var queueRunnerFactory = function(options) { options.catchException = catchException; options.clearStack = options.clearStack || clearStack; options.timeout = {setTimeout: realSetTimeout, clearTimeout: realClearTimeout}; options.fail = self.fail; new j$.QueueRunner(options).execute(); }; var topSuite = new j$.Suite({ env: this, id: getNextSuiteId(), description: 'Jasmine__TopLevel__Suite', expectationFactory: expectationFactory, expectationResultFactory: expectationResultFactory }); defaultResourcesForRunnable(topSuite.id); currentDeclarationSuite = topSuite; this.topSuite = function() { return topSuite; }; this.execute = function(runnablesToRun) { if(!runnablesToRun) { if (focusedRunnables.length) { runnablesToRun = focusedRunnables; } else { runnablesToRun = [topSuite.id]; } } var order = new j$.Order({ random: random, seed: seed }); var processor = new j$.TreeProcessor({ tree: topSuite, runnableIds: runnablesToRun, queueRunnerFactory: queueRunnerFactory, nodeStart: function(suite) { currentlyExecutingSuites.push(suite); defaultResourcesForRunnable(suite.id, suite.parentSuite.id); reporter.suiteStarted(suite.result); }, nodeComplete: function(suite, result) { if (!suite.disabled) { clearResourcesForRunnable(suite.id); } currentlyExecutingSuites.pop(); reporter.suiteDone(result); }, orderChildren: function(node) { return order.sort(node.children); } }); if(!processor.processTree().valid) { throw new Error('Invalid order: would cause a beforeAll or afterAll to be run multiple times'); } reporter.jasmineStarted({ totalSpecsDefined: totalSpecsDefined }); currentlyExecutingSuites.push(topSuite); processor.execute(function() { clearResourcesForRunnable(topSuite.id); currentlyExecutingSuites.pop(); reporter.jasmineDone({ order: order, failedExpectations: topSuite.result.failedExpectations }); }); }; this.addReporter = function(reporterToAdd) { reporter.addReporter(reporterToAdd); }; this.provideFallbackReporter = function(reporterToAdd) { reporter.provideFallbackReporter(reporterToAdd); }; this.clearReporters = function() { reporter.clearReporters(); }; var spyRegistry = new j$.SpyRegistry({currentSpies: function() { if(!currentRunnable()) { throw new Error('Spies must be created in a before function or a spec'); } return runnableResources[currentRunnable().id].spies; }}); this.allowRespy = function(allow){ spyRegistry.allowRespy(allow); }; this.spyOn = function() { return spyRegistry.spyOn.apply(spyRegistry, arguments); }; var suiteFactory = function(description) { var suite = new j$.Suite({ env: self, id: getNextSuiteId(), description: description, parentSuite: currentDeclarationSuite, expectationFactory: expectationFactory, expectationResultFactory: expectationResultFactory, throwOnExpectationFailure: throwOnExpectationFailure }); return suite; }; this.describe = function(description, specDefinitions) { var suite = suiteFactory(description); if (specDefinitions.length > 0) { throw new Error('describe does not expect any arguments'); } if (currentDeclarationSuite.markedPending) { suite.pend(); } addSpecsToSuite(suite, specDefinitions); return suite; }; this.xdescribe = function(description, specDefinitions) { var suite = suiteFactory(description); suite.pend(); addSpecsToSuite(suite, specDefinitions); return suite; }; var focusedRunnables = []; this.fdescribe = function(description, specDefinitions) { var suite = suiteFactory(description); suite.isFocused = true; focusedRunnables.push(suite.id); unfocusAncestor(); addSpecsToSuite(suite, specDefinitions); return suite; }; function addSpecsToSuite(suite, specDefinitions) { var parentSuite = currentDeclarationSuite; parentSuite.addChild(suite); currentDeclarationSuite = suite; var declarationError = null; try { specDefinitions.call(suite); } catch (e) { declarationError = e; } if (declarationError) { self.it('encountered a declaration exception', function() { throw declarationError; }); } currentDeclarationSuite = parentSuite; } function findFocusedAncestor(suite) { while (suite) { if (suite.isFocused) { return suite.id; } suite = suite.parentSuite; } return null; } function unfocusAncestor() { var focusedAncestor = findFocusedAncestor(currentDeclarationSuite); if (focusedAncestor) { for (var i = 0; i < focusedRunnables.length; i++) { if (focusedRunnables[i] === focusedAncestor) { focusedRunnables.splice(i, 1); break; } } } } var specFactory = function(description, fn, suite, timeout) { totalSpecsDefined++; var spec = new j$.Spec({ id: getNextSpecId(), beforeAndAfterFns: beforeAndAfterFns(suite), expectationFactory: expectationFactory, resultCallback: specResultCallback, getSpecName: function(spec) { return getSpecName(spec, suite); }, onStart: specStarted, description: description, expectationResultFactory: expectationResultFactory, queueRunnerFactory: queueRunnerFactory, userContext: function() { return suite.clonedSharedUserContext(); }, queueableFn: { fn: fn, timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; } }, throwOnExpectationFailure: throwOnExpectationFailure }); if (!self.specFilter(spec)) { spec.disable(); } return spec; function specResultCallback(result) { clearResourcesForRunnable(spec.id); currentSpec = null; reporter.specDone(result); } function specStarted(spec) { currentSpec = spec; defaultResourcesForRunnable(spec.id, suite.id); reporter.specStarted(spec.result); } }; this.it = function(description, fn, timeout) { var spec = specFactory(description, fn, currentDeclarationSuite, timeout); if (currentDeclarationSuite.markedPending) { spec.pend(); } currentDeclarationSuite.addChild(spec); return spec; }; this.xit = function() { var spec = this.it.apply(this, arguments); spec.pend('Temporarily disabled with xit'); return spec; }; this.fit = function(description, fn, timeout){ var spec = specFactory(description, fn, currentDeclarationSuite, timeout); currentDeclarationSuite.addChild(spec); focusedRunnables.push(spec.id); unfocusAncestor(); return spec; }; this.expect = function(actual) { if (!currentRunnable()) { throw new Error('\'expect\' was used when there was no current spec, this could be because an asynchronous test timed out'); } return currentRunnable().expect(actual); }; this.beforeEach = function(beforeEachFunction, timeout) { currentDeclarationSuite.beforeEach({ fn: beforeEachFunction, timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; } }); }; this.beforeAll = function(beforeAllFunction, timeout) { currentDeclarationSuite.beforeAll({ fn: beforeAllFunction, timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; } }); }; this.afterEach = function(afterEachFunction, timeout) { currentDeclarationSuite.afterEach({ fn: afterEachFunction, timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; } }); }; this.afterAll = function(afterAllFunction, timeout) { currentDeclarationSuite.afterAll({ fn: afterAllFunction, timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; } }); }; this.pending = function(message) { var fullMessage = j$.Spec.pendingSpecExceptionMessage; if(message) { fullMessage += message; } throw fullMessage; }; this.fail = function(error) { var message = 'Failed'; if (error) { message += ': '; message += error.message || error; } currentRunnable().addExpectationResult(false, { matcherName: '', passed: false, expected: '', actual: '', message: message, error: error && error.message ? error : null }); }; } return Env; }; getJasmineRequireObj().JsApiReporter = function() { var noopTimer = { start: function(){}, elapsed: function(){ return 0; } }; function JsApiReporter(options) { var timer = options.timer || noopTimer, status = 'loaded'; this.started = false; this.finished = false; this.runDetails = {}; this.jasmineStarted = function() { this.started = true; status = 'started'; timer.start(); }; var executionTime; this.jasmineDone = function(runDetails) { this.finished = true; this.runDetails = runDetails; executionTime = timer.elapsed(); status = 'done'; }; this.status = function() { return status; }; var suites = [], suites_hash = {}; this.suiteStarted = function(result) { suites_hash[result.id] = result; }; this.suiteDone = function(result) { storeSuite(result); }; this.suiteResults = function(index, length) { return suites.slice(index, index + length); }; function storeSuite(result) { suites.push(result); suites_hash[result.id] = result; } this.suites = function() { return suites_hash; }; var specs = []; this.specDone = function(result) { specs.push(result); }; this.specResults = function(index, length) { return specs.slice(index, index + length); }; this.specs = function() { return specs; }; this.executionTime = function() { return executionTime; }; } return JsApiReporter; }; getJasmineRequireObj().CallTracker = function(j$) { function CallTracker() { var calls = []; var opts = {}; function argCloner(context) { var clonedArgs = []; var argsAsArray = j$.util.argsToArray(context.args); for(var i = 0; i < argsAsArray.length; i++) { if(Object.prototype.toString.apply(argsAsArray[i]).match(/^\[object/)) { clonedArgs.push(j$.util.clone(argsAsArray[i])); } else { clonedArgs.push(argsAsArray[i]); } } context.args = clonedArgs; } this.track = function(context) { if(opts.cloneArgs) { argCloner(context); } calls.push(context); }; this.any = function() { return !!calls.length; }; this.count = function() { return calls.length; }; this.argsFor = function(index) { var call = calls[index]; return call ? call.args : []; }; this.all = function() { return calls; }; this.allArgs = function() { var callArgs = []; for(var i = 0; i < calls.length; i++){ callArgs.push(calls[i].args); } return callArgs; }; this.first = function() { return calls[0]; }; this.mostRecent = function() { return calls[calls.length - 1]; }; this.reset = function() { calls = []; }; this.saveArgumentsByValue = function() { opts.cloneArgs = true; }; } return CallTracker; }; getJasmineRequireObj().Clock = function() { function Clock(global, delayedFunctionSchedulerFactory, mockDate) { var self = this, realTimingFunctions = { setTimeout: global.setTimeout, clearTimeout: global.clearTimeout, setInterval: global.setInterval, clearInterval: global.clearInterval }, fakeTimingFunctions = { setTimeout: setTimeout, clearTimeout: clearTimeout, setInterval: setInterval, clearInterval: clearInterval }, installed = false, delayedFunctionScheduler, timer; self.install = function() { if(!originalTimingFunctionsIntact()) { throw new Error('Jasmine Clock was unable to install over custom global timer functions. Is the clock already installed?'); } replace(global, fakeTimingFunctions); timer = fakeTimingFunctions; delayedFunctionScheduler = delayedFunctionSchedulerFactory(); installed = true; return self; }; self.uninstall = function() { delayedFunctionScheduler = null; mockDate.uninstall(); replace(global, realTimingFunctions); timer = realTimingFunctions; installed = false; }; self.withMock = function(closure) { this.install(); try { closure(); } finally { this.uninstall(); } }; self.mockDate = function(initialDate) { mockDate.install(initialDate); }; self.setTimeout = function(fn, delay, params) { if (legacyIE()) { if (arguments.length > 2) { throw new Error('IE < 9 cannot support extra params to setTimeout without a polyfill'); } return timer.setTimeout(fn, delay); } return Function.prototype.apply.apply(timer.setTimeout, [global, arguments]); }; self.setInterval = function(fn, delay, params) { if (legacyIE()) { if (arguments.length > 2) { throw new Error('IE < 9 cannot support extra params to setInterval without a polyfill'); } return timer.setInterval(fn, delay); } return Function.prototype.apply.apply(timer.setInterval, [global, arguments]); }; self.clearTimeout = function(id) { return Function.prototype.call.apply(timer.clearTimeout, [global, id]); }; self.clearInterval = function(id) { return Function.prototype.call.apply(timer.clearInterval, [global, id]); }; self.tick = function(millis) { if (installed) { delayedFunctionScheduler.tick(millis, function(millis) { mockDate.tick(millis); }); } else { throw new Error('Mock clock is not installed, use jasmine.clock().install()'); } }; return self; function originalTimingFunctionsIntact() { return global.setTimeout === realTimingFunctions.setTimeout && global.clearTimeout === realTimingFunctions.clearTimeout && global.setInterval === realTimingFunctions.setInterval && global.clearInterval === realTimingFunctions.clearInterval; } function legacyIE() { //if these methods are polyfilled, apply will be present return !(realTimingFunctions.setTimeout || realTimingFunctions.setInterval).apply; } function replace(dest, source) { for (var prop in source) { dest[prop] = source[prop]; } } function setTimeout(fn, delay) { return delayedFunctionScheduler.scheduleFunction(fn, delay, argSlice(arguments, 2)); } function clearTimeout(id) { return delayedFunctionScheduler.removeFunctionWithId(id); } function setInterval(fn, interval) { return delayedFunctionScheduler.scheduleFunction(fn, interval, argSlice(arguments, 2), true); } function clearInterval(id) { return delayedFunctionScheduler.removeFunctionWithId(id); } function argSlice(argsObj, n) { return Array.prototype.slice.call(argsObj, n); } } return Clock; }; getJasmineRequireObj().DelayedFunctionScheduler = function() { function DelayedFunctionScheduler() { var self = this; var scheduledLookup = []; var scheduledFunctions = {}; var currentTime = 0; var delayedFnCount = 0; self.tick = function(millis, tickDate) { millis = millis || 0; var endTime = currentTime + millis; runScheduledFunctions(endTime, tickDate); currentTime = endTime; }; self.scheduleFunction = function(funcToCall, millis, params, recurring, timeoutKey, runAtMillis) { var f; if (typeof(funcToCall) === 'string') { /* jshint evil: true */ f = function() { return eval(funcToCall); }; /* jshint evil: false */ } else { f = funcToCall; } millis = millis || 0; timeoutKey = timeoutKey || ++delayedFnCount; runAtMillis = runAtMillis || (currentTime + millis); var funcToSchedule = { runAtMillis: runAtMillis, funcToCall: f, recurring: recurring, params: params, timeoutKey: timeoutKey, millis: millis }; if (runAtMillis in scheduledFunctions) { scheduledFunctions[runAtMillis].push(funcToSchedule); } else { scheduledFunctions[runAtMillis] = [funcToSchedule]; scheduledLookup.push(runAtMillis); scheduledLookup.sort(function (a, b) { return a - b; }); } return timeoutKey; }; self.removeFunctionWithId = function(timeoutKey) { for (var runAtMillis in scheduledFunctions) { var funcs = scheduledFunctions[runAtMillis]; var i = indexOfFirstToPass(funcs, function (func) { return func.timeoutKey === timeoutKey; }); if (i > -1) { if (funcs.length === 1) { delete scheduledFunctions[runAtMillis]; deleteFromLookup(runAtMillis); } else { funcs.splice(i, 1); } // intervals get rescheduled when executed, so there's never more // than a single scheduled function with a given timeoutKey break; } } }; return self; function indexOfFirstToPass(array, testFn) { var index = -1; for (var i = 0; i < array.length; ++i) { if (testFn(array[i])) { index = i; break; } } return index; } function deleteFromLookup(key) { var value = Number(key); var i = indexOfFirstToPass(scheduledLookup, function (millis) { return millis === value; }); if (i > -1) { scheduledLookup.splice(i, 1); } } function reschedule(scheduledFn) { self.scheduleFunction(scheduledFn.funcToCall, scheduledFn.millis, scheduledFn.params, true, scheduledFn.timeoutKey, scheduledFn.runAtMillis + scheduledFn.millis); } function forEachFunction(funcsToRun, callback) { for (var i = 0; i < funcsToRun.length; ++i) { callback(funcsToRun[i]); } } function runScheduledFunctions(endTime, tickDate) { tickDate = tickDate || function() {}; if (scheduledLookup.length === 0 || scheduledLookup[0] > endTime) { tickDate(endTime - currentTime); return; } do { var newCurrentTime = scheduledLookup.shift(); tickDate(newCurrentTime - currentTime); currentTime = newCurrentTime; var funcsToRun = scheduledFunctions[currentTime]; delete scheduledFunctions[currentTime]; forEachFunction(funcsToRun, function(funcToRun) { if (funcToRun.recurring) { reschedule(funcToRun); } }); forEachFunction(funcsToRun, function(funcToRun) { funcToRun.funcToCall.apply(null, funcToRun.params || []); }); } while (scheduledLookup.length > 0 && // checking first if we're out of time prevents setTimeout(0) // scheduled in a funcToRun from forcing an extra iteration currentTime !== endTime && scheduledLookup[0] <= endTime); // ran out of functions to call, but still time left on the clock if (currentTime !== endTime) { tickDate(endTime - currentTime); } } } return DelayedFunctionScheduler; }; getJasmineRequireObj().ExceptionFormatter = function() { function ExceptionFormatter() { this.message = function(error) { var message = ''; if (error.name && error.message) { message += error.name + ': ' + error.message; } else { message += error.toString() + ' thrown'; } if (error.fileName || error.sourceURL) { message += ' in ' + (error.fileName || error.sourceURL); } if (error.line || error.lineNumber) { message += ' (line ' + (error.line || error.lineNumber) + ')'; } return message; }; this.stack = function(error) { return error ? error.stack : null; }; } return ExceptionFormatter; }; getJasmineRequireObj().Expectation = function() { function Expectation(options) { this.util = options.util || { buildFailureMessage: function() {} }; this.customEqualityTesters = options.customEqualityTesters || []; this.actual = options.actual; this.addExpectationResult = options.addExpectationResult || function(){}; this.isNot = options.isNot; var customMatchers = options.customMatchers || {}; for (var matcherName in customMatchers) { this[matcherName] = Expectation.prototype.wrapCompare(matcherName, customMatchers[matcherName]); } } Expectation.prototype.wrapCompare = function(name, matcherFactory) { return function() { var args = Array.prototype.slice.call(arguments, 0), expected = args.slice(0), message = ''; args.unshift(this.actual); var matcher = matcherFactory(this.util, this.customEqualityTesters), matcherCompare = matcher.compare; function defaultNegativeCompare() { var result = matcher.compare.apply(null, args); result.pass = !result.pass; return result; } if (this.isNot) { matcherCompare = matcher.negativeCompare || defaultNegativeCompare; } var result = matcherCompare.apply(null, args); if (!result.pass) { if (!result.message) { args.unshift(this.isNot); args.unshift(name); message = this.util.buildFailureMessage.apply(null, args); } else { if (Object.prototype.toString.apply(result.message) === '[object Function]') { message = result.message(); } else { message = result.message; } } } if (expected.length == 1) { expected = expected[0]; } // TODO: how many of these params are needed? this.addExpectationResult( result.pass, { matcherName: name, passed: result.pass, message: message, actual: this.actual, expected: expected // TODO: this may need to be arrayified/sliced } ); }; }; Expectation.addCoreMatchers = function(matchers) { var prototype = Expectation.prototype; for (var matcherName in matchers) { var matcher = matchers[matcherName]; prototype[matcherName] = prototype.wrapCompare(matcherName, matcher); } }; Expectation.Factory = function(options) { options = options || {}; var expect = new Expectation(options); // TODO: this would be nice as its own Object - NegativeExpectation // TODO: copy instead of mutate options options.isNot = true; expect.not = new Expectation(options); return expect; }; return Expectation; }; //TODO: expectation result may make more sense as a presentation of an expectation. getJasmineRequireObj().buildExpectationResult = function() { function buildExpectationResult(options) { var messageFormatter = options.messageFormatter || function() {}, stackFormatter = options.stackFormatter || function() {}; var result = { matcherName: options.matcherName, message: message(), stack: stack(), passed: options.passed }; if(!result.passed) { result.expected = options.expected; result.actual = options.actual; } return result; function message() { if (options.passed) { return 'Passed.'; } else if (options.message) { return options.message; } else if (options.error) { return messageFormatter(options.error); } return ''; } function stack() { if (options.passed) { return ''; } var error = options.error; if (!error) { try { throw new Error(message()); } catch (e) { error = e; } } return stackFormatter(error); } } return buildExpectationResult; }; getJasmineRequireObj().MockDate = function() { function MockDate(global) { var self = this; var currentTime = 0; if (!global || !global.Date) { self.install = function() {}; self.tick = function() {}; self.uninstall = function() {}; return self; } var GlobalDate = global.Date; self.install = function(mockDate) { if (mockDate instanceof GlobalDate) { currentTime = mockDate.getTime(); } else { currentTime = new GlobalDate().getTime(); } global.Date = FakeDate; }; self.tick = function(millis) { millis = millis || 0; currentTime = currentTime + millis; }; self.uninstall = function() { currentTime = 0; global.Date = GlobalDate; }; createDateProperties(); return self; function FakeDate() { switch(arguments.length) { case 0: return new GlobalDate(currentTime); case 1: return new GlobalDate(arguments[0]); case 2: return new GlobalDate(arguments[0], arguments[1]); case 3: return new GlobalDate(arguments[0], arguments[1], arguments[2]); case 4: return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3]); case 5: return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]); case 6: return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]); default: return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6]); } } function createDateProperties() { FakeDate.prototype = GlobalDate.prototype; FakeDate.now = function() { if (GlobalDate.now) { return currentTime; } else { throw new Error('Browser does not support Date.now()'); } }; FakeDate.toSource = GlobalDate.toSource; FakeDate.toString = GlobalDate.toString; FakeDate.parse = GlobalDate.parse; FakeDate.UTC = GlobalDate.UTC; } } return MockDate; }; getJasmineRequireObj().pp = function(j$) { function PrettyPrinter() { this.ppNestLevel_ = 0; this.seen = []; } PrettyPrinter.prototype.format = function(value) { this.ppNestLevel_++; try { if (j$.util.isUndefined(value)) { this.emitScalar('undefined'); } else if (value === null) { this.emitScalar('null'); } else if (value === 0 && 1/value === -Infinity) { this.emitScalar('-0'); } else if (value === j$.getGlobal()) { this.emitScalar(''); } else if (value.jasmineToString) { this.emitScalar(value.jasmineToString()); } else if (typeof value === 'string') { this.emitString(value); } else if (j$.isSpy(value)) { this.emitScalar('spy on ' + value.and.identity()); } else if (value instanceof RegExp) { this.emitScalar(value.toString()); } else if (typeof value === 'function') { this.emitScalar('Function'); } else if (typeof value.nodeType === 'number') { this.emitScalar('HTMLNode'); } else if (value instanceof Date) { this.emitScalar('Date(' + value + ')'); } else if (value.toString && typeof value === 'object' && !(value instanceof Array) && value.toString !== Object.prototype.toString) { this.emitScalar(value.toString()); } else if (j$.util.arrayContains(this.seen, value)) { this.emitScalar(''); } else if (j$.isArray_(value) || j$.isA_('Object', value)) { this.seen.push(value); if (j$.isArray_(value)) { this.emitArray(value); } else { this.emitObject(value); } this.seen.pop(); } else { this.emitScalar(value.toString()); } } finally { this.ppNestLevel_--; } }; PrettyPrinter.prototype.iterateObject = function(obj, fn) { for (var property in obj) { if (!Object.prototype.hasOwnProperty.call(obj, property)) { continue; } fn(property, obj.__lookupGetter__ ? (!j$.util.isUndefined(obj.__lookupGetter__(property)) && obj.__lookupGetter__(property) !== null) : false); } }; PrettyPrinter.prototype.emitArray = j$.unimplementedMethod_; PrettyPrinter.prototype.emitObject = j$.unimplementedMethod_; PrettyPrinter.prototype.emitScalar = j$.unimplementedMethod_; PrettyPrinter.prototype.emitString = j$.unimplementedMethod_; function StringPrettyPrinter() { PrettyPrinter.call(this); this.string = ''; } j$.util.inherit(StringPrettyPrinter, PrettyPrinter); StringPrettyPrinter.prototype.emitScalar = function(value) { this.append(value); }; StringPrettyPrinter.prototype.emitString = function(value) { this.append('\'' + value + '\''); }; StringPrettyPrinter.prototype.emitArray = function(array) { if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { this.append('Array'); return; } var length = Math.min(array.length, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); this.append('[ '); for (var i = 0; i < length; i++) { if (i > 0) { this.append(', '); } this.format(array[i]); } if(array.length > length){ this.append(', ...'); } var self = this; var first = array.length === 0; this.iterateObject(array, function(property, isGetter) { if (property.match(/^\d+$/)) { return; } if (first) { first = false; } else { self.append(', '); } self.formatProperty(array, property, isGetter); }); this.append(' ]'); }; StringPrettyPrinter.prototype.emitObject = function(obj) { var constructorName = obj.constructor ? j$.fnNameFor(obj.constructor) : 'null'; this.append(constructorName); if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { return; } var self = this; this.append('({ '); var first = true; this.iterateObject(obj, function(property, isGetter) { if (first) { first = false; } else { self.append(', '); } self.formatProperty(obj, property, isGetter); }); this.append(' })'); }; StringPrettyPrinter.prototype.formatProperty = function(obj, property, isGetter) { this.append(property); this.append(': '); if (isGetter) { this.append(''); } else { this.format(obj[property]); } }; StringPrettyPrinter.prototype.append = function(value) { this.string += value; }; return function(value) { var stringPrettyPrinter = new StringPrettyPrinter(); stringPrettyPrinter.format(value); return stringPrettyPrinter.string; }; }; getJasmineRequireObj().QueueRunner = function(j$) { function once(fn) { var called = false; return function() { if (!called) { called = true; fn(); } return null; }; } function QueueRunner(attrs) { this.queueableFns = attrs.queueableFns || []; this.onComplete = attrs.onComplete || function() {}; this.clearStack = attrs.clearStack || function(fn) {fn();}; this.onException = attrs.onException || function() {}; this.catchException = attrs.catchException || function() { return true; }; this.userContext = attrs.userContext || {}; this.timeout = attrs.timeout || {setTimeout: setTimeout, clearTimeout: clearTimeout}; this.fail = attrs.fail || function() {}; } QueueRunner.prototype.execute = function() { this.run(this.queueableFns, 0); }; QueueRunner.prototype.run = function(queueableFns, recursiveIndex) { var length = queueableFns.length, self = this, iterativeIndex; for(iterativeIndex = recursiveIndex; iterativeIndex < length; iterativeIndex++) { var queueableFn = queueableFns[iterativeIndex]; if (queueableFn.fn.length > 0) { attemptAsync(queueableFn); return; } else { attemptSync(queueableFn); } } var runnerDone = iterativeIndex >= length; if (runnerDone) { this.clearStack(this.onComplete); } function attemptSync(queueableFn) { try { queueableFn.fn.call(self.userContext); } catch (e) { handleException(e, queueableFn); } } function attemptAsync(queueableFn) { var clearTimeout = function () { Function.prototype.apply.apply(self.timeout.clearTimeout, [j$.getGlobal(), [timeoutId]]); }, next = once(function () { clearTimeout(timeoutId); self.run(queueableFns, iterativeIndex + 1); }), timeoutId; next.fail = function() { self.fail.apply(null, arguments); next(); }; if (queueableFn.timeout) { timeoutId = Function.prototype.apply.apply(self.timeout.setTimeout, [j$.getGlobal(), [function() { var error = new Error('Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.'); onException(error); next(); }, queueableFn.timeout()]]); } try { queueableFn.fn.call(self.userContext, next); } catch (e) { handleException(e, queueableFn); next(); } } function onException(e) { self.onException(e); } function handleException(e, queueableFn) { onException(e); if (!self.catchException(e)) { //TODO: set a var when we catch an exception and //use a finally block to close the loop in a nice way.. throw e; } } }; return QueueRunner; }; getJasmineRequireObj().ReportDispatcher = function() { function ReportDispatcher(methods) { var dispatchedMethods = methods || []; for (var i = 0; i < dispatchedMethods.length; i++) { var method = dispatchedMethods[i]; this[method] = (function(m) { return function() { dispatch(m, arguments); }; }(method)); } var reporters = []; var fallbackReporter = null; this.addReporter = function(reporter) { reporters.push(reporter); }; this.provideFallbackReporter = function(reporter) { fallbackReporter = reporter; }; this.clearReporters = function() { reporters = []; }; return this; function dispatch(method, args) { if (reporters.length === 0 && fallbackReporter !== null) { reporters.push(fallbackReporter); } for (var i = 0; i < reporters.length; i++) { var reporter = reporters[i]; if (reporter[method]) { reporter[method].apply(reporter, args); } } } } return ReportDispatcher; }; getJasmineRequireObj().SpyRegistry = function(j$) { var getErrorMsg = j$.formatErrorMsg('', 'spyOn(, )'); function SpyRegistry(options) { options = options || {}; var currentSpies = options.currentSpies || function() { return []; }; this.allowRespy = function(allow){ this.respy = allow; }; this.spyOn = function(obj, methodName) { if (j$.util.isUndefined(obj)) { throw new Error(getErrorMsg('could not find an object to spy upon for ' + methodName + '()')); } if (j$.util.isUndefined(methodName)) { throw new Error(getErrorMsg('No method name supplied')); } if (j$.util.isUndefined(obj[methodName])) { throw new Error(getErrorMsg(methodName + '() method does not exist')); } if (obj[methodName] && j$.isSpy(obj[methodName]) ) { if ( !!this.respy ){ return obj[methodName]; }else { throw new Error(getErrorMsg(methodName + ' has already been spied upon')); } } var descriptor; try { descriptor = Object.getOwnPropertyDescriptor(obj, methodName); } catch(e) { // IE 8 doesn't support `definePropery` on non-DOM nodes } if (descriptor && !(descriptor.writable || descriptor.set)) { throw new Error(getErrorMsg(methodName + ' is not declared writable or has no setter')); } var originalMethod = obj[methodName], spiedMethod = j$.createSpy(methodName, originalMethod), restoreStrategy; if (Object.prototype.hasOwnProperty.call(obj, methodName)) { restoreStrategy = function() { obj[methodName] = originalMethod; }; } else { restoreStrategy = function() { if (!delete obj[methodName]) { obj[methodName] = originalMethod; } }; } currentSpies().push({ restoreObjectToOriginalState: restoreStrategy }); obj[methodName] = spiedMethod; return spiedMethod; }; this.clearSpies = function() { var spies = currentSpies(); for (var i = spies.length - 1; i >= 0; i--) { var spyEntry = spies[i]; spyEntry.restoreObjectToOriginalState(); } }; } return SpyRegistry; }; getJasmineRequireObj().SpyStrategy = function(j$) { function SpyStrategy(options) { options = options || {}; var identity = options.name || 'unknown', originalFn = options.fn || function() {}, getSpy = options.getSpy || function() {}, plan = function() {}; this.identity = function() { return identity; }; this.exec = function() { return plan.apply(this, arguments); }; this.callThrough = function() { plan = originalFn; return getSpy(); }; this.returnValue = function(value) { plan = function() { return value; }; return getSpy(); }; this.returnValues = function() { var values = Array.prototype.slice.call(arguments); plan = function () { return values.shift(); }; return getSpy(); }; this.throwError = function(something) { var error = (something instanceof Error) ? something : new Error(something); plan = function() { throw error; }; return getSpy(); }; this.callFake = function(fn) { if(!j$.isFunction_(fn)) { throw new Error('Argument passed to callFake should be a function, got ' + fn); } plan = fn; return getSpy(); }; this.stub = function(fn) { plan = function() {}; return getSpy(); }; } return SpyStrategy; }; getJasmineRequireObj().Suite = function(j$) { function Suite(attrs) { this.env = attrs.env; this.id = attrs.id; this.parentSuite = attrs.parentSuite; this.description = attrs.description; this.expectationFactory = attrs.expectationFactory; this.expectationResultFactory = attrs.expectationResultFactory; this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure; this.beforeFns = []; this.afterFns = []; this.beforeAllFns = []; this.afterAllFns = []; this.disabled = false; this.children = []; this.result = { id: this.id, description: this.description, fullName: this.getFullName(), failedExpectations: [] }; } Suite.prototype.expect = function(actual) { return this.expectationFactory(actual, this); }; Suite.prototype.getFullName = function() { var fullName = []; for (var parentSuite = this; parentSuite; parentSuite = parentSuite.parentSuite) { if (parentSuite.parentSuite) { fullName.unshift(parentSuite.description); } } return fullName.join(' '); }; Suite.prototype.disable = function() { this.disabled = true; }; Suite.prototype.pend = function(message) { this.markedPending = true; }; Suite.prototype.beforeEach = function(fn) { this.beforeFns.unshift(fn); }; Suite.prototype.beforeAll = function(fn) { this.beforeAllFns.push(fn); }; Suite.prototype.afterEach = function(fn) { this.afterFns.unshift(fn); }; Suite.prototype.afterAll = function(fn) { this.afterAllFns.push(fn); }; Suite.prototype.addChild = function(child) { this.children.push(child); }; Suite.prototype.status = function() { if (this.disabled) { return 'disabled'; } if (this.markedPending) { return 'pending'; } if (this.result.failedExpectations.length > 0) { return 'failed'; } else { return 'finished'; } }; Suite.prototype.isExecutable = function() { return !this.disabled; }; Suite.prototype.canBeReentered = function() { return this.beforeAllFns.length === 0 && this.afterAllFns.length === 0; }; Suite.prototype.getResult = function() { this.result.status = this.status(); return this.result; }; Suite.prototype.sharedUserContext = function() { if (!this.sharedContext) { this.sharedContext = this.parentSuite ? clone(this.parentSuite.sharedUserContext()) : {}; } return this.sharedContext; }; Suite.prototype.clonedSharedUserContext = function() { return clone(this.sharedUserContext()); }; Suite.prototype.onException = function() { if (arguments[0] instanceof j$.errors.ExpectationFailed) { return; } if(isAfterAll(this.children)) { var data = { matcherName: '', passed: false, expected: '', actual: '', error: arguments[0] }; this.result.failedExpectations.push(this.expectationResultFactory(data)); } else { for (var i = 0; i < this.children.length; i++) { var child = this.children[i]; child.onException.apply(child, arguments); } } }; Suite.prototype.addExpectationResult = function () { if(isAfterAll(this.children) && isFailure(arguments)){ var data = arguments[1]; this.result.failedExpectations.push(this.expectationResultFactory(data)); if(this.throwOnExpectationFailure) { throw new j$.errors.ExpectationFailed(); } } else { for (var i = 0; i < this.children.length; i++) { var child = this.children[i]; try { child.addExpectationResult.apply(child, arguments); } catch(e) { // keep going } } } }; function isAfterAll(children) { return children && children[0].result.status; } function isFailure(args) { return !args[0]; } function clone(obj) { var clonedObj = {}; for (var prop in obj) { if (obj.hasOwnProperty(prop)) { clonedObj[prop] = obj[prop]; } } return clonedObj; } return Suite; }; if (typeof window == void 0 && typeof exports == 'object') { exports.Suite = jasmineRequire.Suite; } getJasmineRequireObj().Timer = function() { var defaultNow = (function(Date) { return function() { return new Date().getTime(); }; })(Date); function Timer(options) { options = options || {}; var now = options.now || defaultNow, startTime; this.start = function() { startTime = now(); }; this.elapsed = function() { return now() - startTime; }; } return Timer; }; getJasmineRequireObj().TreeProcessor = function() { function TreeProcessor(attrs) { var tree = attrs.tree, runnableIds = attrs.runnableIds, queueRunnerFactory = attrs.queueRunnerFactory, nodeStart = attrs.nodeStart || function() {}, nodeComplete = attrs.nodeComplete || function() {}, orderChildren = attrs.orderChildren || function(node) { return node.children; }, stats = { valid: true }, processed = false, defaultMin = Infinity, defaultMax = 1 - Infinity; this.processTree = function() { processNode(tree, false); processed = true; return stats; }; this.execute = function(done) { if (!processed) { this.processTree(); } if (!stats.valid) { throw 'invalid order'; } var childFns = wrapChildren(tree, 0); queueRunnerFactory({ queueableFns: childFns, userContext: tree.sharedUserContext(), onException: function() { tree.onException.apply(tree, arguments); }, onComplete: done }); }; function runnableIndex(id) { for (var i = 0; i < runnableIds.length; i++) { if (runnableIds[i] === id) { return i; } } } function processNode(node, parentEnabled) { var executableIndex = runnableIndex(node.id); if (executableIndex !== undefined) { parentEnabled = true; } parentEnabled = parentEnabled && node.isExecutable(); if (!node.children) { stats[node.id] = { executable: parentEnabled && node.isExecutable(), segments: [{ index: 0, owner: node, nodes: [node], min: startingMin(executableIndex), max: startingMax(executableIndex) }] }; } else { var hasExecutableChild = false; var orderedChildren = orderChildren(node); for (var i = 0; i < orderedChildren.length; i++) { var child = orderedChildren[i]; processNode(child, parentEnabled); if (!stats.valid) { return; } var childStats = stats[child.id]; hasExecutableChild = hasExecutableChild || childStats.executable; } stats[node.id] = { executable: hasExecutableChild }; segmentChildren(node, orderedChildren, stats[node.id], executableIndex); if (!node.canBeReentered() && stats[node.id].segments.length > 1) { stats = { valid: false }; } } } function startingMin(executableIndex) { return executableIndex === undefined ? defaultMin : executableIndex; } function startingMax(executableIndex) { return executableIndex === undefined ? defaultMax : executableIndex; } function segmentChildren(node, orderedChildren, nodeStats, executableIndex) { var currentSegment = { index: 0, owner: node, nodes: [], min: startingMin(executableIndex), max: startingMax(executableIndex) }, result = [currentSegment], lastMax = defaultMax, orderedChildSegments = orderChildSegments(orderedChildren); function isSegmentBoundary(minIndex) { return lastMax !== defaultMax && minIndex !== defaultMin && lastMax < minIndex - 1; } for (var i = 0; i < orderedChildSegments.length; i++) { var childSegment = orderedChildSegments[i], maxIndex = childSegment.max, minIndex = childSegment.min; if (isSegmentBoundary(minIndex)) { currentSegment = {index: result.length, owner: node, nodes: [], min: defaultMin, max: defaultMax}; result.push(currentSegment); } currentSegment.nodes.push(childSegment); currentSegment.min = Math.min(currentSegment.min, minIndex); currentSegment.max = Math.max(currentSegment.max, maxIndex); lastMax = maxIndex; } nodeStats.segments = result; } function orderChildSegments(children) { var specifiedOrder = [], unspecifiedOrder = []; for (var i = 0; i < children.length; i++) { var child = children[i], segments = stats[child.id].segments; for (var j = 0; j < segments.length; j++) { var seg = segments[j]; if (seg.min === defaultMin) { unspecifiedOrder.push(seg); } else { specifiedOrder.push(seg); } } } specifiedOrder.sort(function(a, b) { return a.min - b.min; }); return specifiedOrder.concat(unspecifiedOrder); } function executeNode(node, segmentNumber) { if (node.children) { return { fn: function(done) { nodeStart(node); queueRunnerFactory({ onComplete: function() { nodeComplete(node, node.getResult()); done(); }, queueableFns: wrapChildren(node, segmentNumber), userContext: node.sharedUserContext(), onException: function() { node.onException.apply(node, arguments); } }); } }; } else { return { fn: function(done) { node.execute(done, stats[node.id].executable); } }; } } function wrapChildren(node, segmentNumber) { var result = [], segmentChildren = stats[node.id].segments[segmentNumber].nodes; for (var i = 0; i < segmentChildren.length; i++) { result.push(executeNode(segmentChildren[i].owner, segmentChildren[i].index)); } if (!stats[node.id].executable) { return result; } return node.beforeAllFns.concat(result).concat(node.afterAllFns); } } return TreeProcessor; }; getJasmineRequireObj().Any = function(j$) { function Any(expectedObject) { if (typeof expectedObject === 'undefined') { throw new TypeError( 'jasmine.any() expects to be passed a constructor function. ' + 'Please pass one or use jasmine.anything() to match any object.' ); } this.expectedObject = expectedObject; } Any.prototype.asymmetricMatch = function(other) { if (this.expectedObject == String) { return typeof other == 'string' || other instanceof String; } if (this.expectedObject == Number) { return typeof other == 'number' || other instanceof Number; } if (this.expectedObject == Function) { return typeof other == 'function' || other instanceof Function; } if (this.expectedObject == Object) { return typeof other == 'object'; } if (this.expectedObject == Boolean) { return typeof other == 'boolean'; } return other instanceof this.expectedObject; }; Any.prototype.jasmineToString = function() { return ''; }; return Any; }; getJasmineRequireObj().Anything = function(j$) { function Anything() {} Anything.prototype.asymmetricMatch = function(other) { return !j$.util.isUndefined(other) && other !== null; }; Anything.prototype.jasmineToString = function() { return ''; }; return Anything; }; getJasmineRequireObj().ArrayContaining = function(j$) { function ArrayContaining(sample) { this.sample = sample; } ArrayContaining.prototype.asymmetricMatch = function(other) { var className = Object.prototype.toString.call(this.sample); if (className !== '[object Array]') { throw new Error('You must provide an array to arrayContaining, not \'' + this.sample + '\'.'); } for (var i = 0; i < this.sample.length; i++) { var item = this.sample[i]; if (!j$.matchersUtil.contains(other, item)) { return false; } } return true; }; ArrayContaining.prototype.jasmineToString = function () { return ''; }; return ArrayContaining; }; getJasmineRequireObj().ObjectContaining = function(j$) { function ObjectContaining(sample) { this.sample = sample; } function getPrototype(obj) { if (Object.getPrototypeOf) { return Object.getPrototypeOf(obj); } if (obj.constructor.prototype == obj) { return null; } return obj.constructor.prototype; } function hasProperty(obj, property) { if (!obj) { return false; } if (Object.prototype.hasOwnProperty.call(obj, property)) { return true; } return hasProperty(getPrototype(obj), property); } ObjectContaining.prototype.asymmetricMatch = function(other) { if (typeof(this.sample) !== 'object') { throw new Error('You must provide an object to objectContaining, not \''+this.sample+'\'.'); } for (var property in this.sample) { if (!hasProperty(other, property) || !j$.matchersUtil.equals(this.sample[property], other[property])) { return false; } } return true; }; ObjectContaining.prototype.jasmineToString = function() { return ''; }; return ObjectContaining; }; getJasmineRequireObj().StringMatching = function(j$) { function StringMatching(expected) { if (!j$.isString_(expected) && !j$.isA_('RegExp', expected)) { throw new Error('Expected is not a String or a RegExp'); } this.regexp = new RegExp(expected); } StringMatching.prototype.asymmetricMatch = function(other) { return this.regexp.test(other); }; StringMatching.prototype.jasmineToString = function() { return ''; }; return StringMatching; }; getJasmineRequireObj().errors = function() { function ExpectationFailed() {} ExpectationFailed.prototype = new Error(); ExpectationFailed.prototype.constructor = ExpectationFailed; return { ExpectationFailed: ExpectationFailed }; }; getJasmineRequireObj().formatErrorMsg = function() { function generateErrorMsg(domain, usage) { var usageDefinition = usage ? '\nUsage: ' + usage : ''; return function errorMsg(msg) { return domain + ' : ' + msg + usageDefinition; }; } return generateErrorMsg; }; getJasmineRequireObj().matchersUtil = function(j$) { // TODO: what to do about jasmine.pp not being inject? move to JSON.stringify? gut PrettyPrinter? return { equals: function(a, b, customTesters) { customTesters = customTesters || []; return eq(a, b, [], [], customTesters); }, contains: function(haystack, needle, customTesters) { customTesters = customTesters || []; if ((Object.prototype.toString.apply(haystack) === '[object Array]') || (!!haystack && !haystack.indexOf)) { for (var i = 0; i < haystack.length; i++) { if (eq(haystack[i], needle, [], [], customTesters)) { return true; } } return false; } return !!haystack && haystack.indexOf(needle) >= 0; }, buildFailureMessage: function() { var args = Array.prototype.slice.call(arguments, 0), matcherName = args[0], isNot = args[1], actual = args[2], expected = args.slice(3), englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); }); var message = 'Expected ' + j$.pp(actual) + (isNot ? ' not ' : ' ') + englishyPredicate; if (expected.length > 0) { for (var i = 0; i < expected.length; i++) { if (i > 0) { message += ','; } message += ' ' + j$.pp(expected[i]); } } return message + '.'; } }; function isAsymmetric(obj) { return obj && j$.isA_('Function', obj.asymmetricMatch); } function asymmetricMatch(a, b) { var asymmetricA = isAsymmetric(a), asymmetricB = isAsymmetric(b); if (asymmetricA && asymmetricB) { return undefined; } if (asymmetricA) { return a.asymmetricMatch(b); } if (asymmetricB) { return b.asymmetricMatch(a); } } // Equality function lovingly adapted from isEqual in // [Underscore](http://underscorejs.org) function eq(a, b, aStack, bStack, customTesters) { var result = true; var asymmetricResult = asymmetricMatch(a, b); if (!j$.util.isUndefined(asymmetricResult)) { return asymmetricResult; } for (var i = 0; i < customTesters.length; i++) { var customTesterResult = customTesters[i](a, b); if (!j$.util.isUndefined(customTesterResult)) { return customTesterResult; } } if (a instanceof Error && b instanceof Error) { return a.message == b.message; } // Identical objects are equal. `0 === -0`, but they aren't identical. // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). if (a === b) { return a !== 0 || 1 / a == 1 / b; } // A strict comparison is necessary because `null == undefined`. if (a === null || b === null) { return a === b; } var className = Object.prototype.toString.call(a); if (className != Object.prototype.toString.call(b)) { return false; } switch (className) { // Strings, numbers, dates, and booleans are compared by value. case '[object String]': // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is // equivalent to `new String("5")`. return a == String(b); case '[object Number]': // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for // other numeric values. return a != +a ? b != +b : (a === 0 ? 1 / a == 1 / b : a == +b); case '[object Date]': case '[object Boolean]': // Coerce dates and booleans to numeric primitive values. Dates are compared by their // millisecond representations. Note that invalid dates with millisecond representations // of `NaN` are not equivalent. return +a == +b; // RegExps are compared by their source patterns and flags. case '[object RegExp]': return a.source == b.source && a.global == b.global && a.multiline == b.multiline && a.ignoreCase == b.ignoreCase; } if (typeof a != 'object' || typeof b != 'object') { return false; } var aIsDomNode = j$.isDomNode(a); var bIsDomNode = j$.isDomNode(b); if (aIsDomNode && bIsDomNode) { // At first try to use DOM3 method isEqualNode if (a.isEqualNode) { return a.isEqualNode(b); } // IE8 doesn't support isEqualNode, try to use outerHTML && innerText var aIsElement = a instanceof Element; var bIsElement = b instanceof Element; if (aIsElement && bIsElement) { return a.outerHTML == b.outerHTML; } if (aIsElement || bIsElement) { return false; } return a.innerText == b.innerText && a.textContent == b.textContent; } if (aIsDomNode || bIsDomNode) { return false; } // Assume equality for cyclic structures. The algorithm for detecting cyclic // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. var length = aStack.length; while (length--) { // Linear search. Performance is inversely proportional to the number of // unique nested structures. if (aStack[length] == a) { return bStack[length] == b; } } // Add the first object to the stack of traversed objects. aStack.push(a); bStack.push(b); var size = 0; // Recursively compare objects and arrays. // Compare array lengths to determine if a deep comparison is necessary. if (className == '[object Array]') { size = a.length; if (size !== b.length) { return false; } while (size--) { result = eq(a[size], b[size], aStack, bStack, customTesters); if (!result) { return false; } } } else { // Objects with different constructors are not equivalent, but `Object`s // or `Array`s from different frames are. var aCtor = a.constructor, bCtor = b.constructor; if (aCtor !== bCtor && !(isObjectConstructor(aCtor) && isObjectConstructor(bCtor))) { return false; } } // Deep compare objects. var aKeys = keys(a, className == '[object Array]'), key; size = aKeys.length; // Ensure that both objects contain the same number of properties before comparing deep equality. if (keys(b, className == '[object Array]').length !== size) { return false; } while (size--) { key = aKeys[size]; // Deep compare each member result = has(b, key) && eq(a[key], b[key], aStack, bStack, customTesters); if (!result) { return false; } } // Remove the first object from the stack of traversed objects. aStack.pop(); bStack.pop(); return result; function keys(obj, isArray) { var allKeys = Object.keys ? Object.keys(obj) : (function(o) { var keys = []; for (var key in o) { if (has(o, key)) { keys.push(key); } } return keys; })(obj); if (!isArray) { return allKeys; } var extraKeys = []; if (allKeys.length === 0) { return allKeys; } for (var x = 0; x < allKeys.length; x++) { if (!allKeys[x].match(/^[0-9]+$/)) { extraKeys.push(allKeys[x]); } } return extraKeys; } } function has(obj, key) { return Object.prototype.hasOwnProperty.call(obj, key); } function isFunction(obj) { return typeof obj === 'function'; } function isObjectConstructor(ctor) { // aCtor instanceof aCtor is true for the Object and Function // constructors (since a constructor is-a Function and a function is-a // Object). We don't just compare ctor === Object because the constructor // might come from a different frame with different globals. return isFunction(ctor) && ctor instanceof ctor; } }; getJasmineRequireObj().toBe = function() { function toBe() { return { compare: function(actual, expected) { return { pass: actual === expected }; } }; } return toBe; }; getJasmineRequireObj().toBeCloseTo = function() { function toBeCloseTo() { return { compare: function(actual, expected, precision) { if (precision !== 0) { precision = precision || 2; } return { pass: Math.abs(expected - actual) < (Math.pow(10, -precision) / 2) }; } }; } return toBeCloseTo; }; getJasmineRequireObj().toBeDefined = function() { function toBeDefined() { return { compare: function(actual) { return { pass: (void 0 !== actual) }; } }; } return toBeDefined; }; getJasmineRequireObj().toBeFalsy = function() { function toBeFalsy() { return { compare: function(actual) { return { pass: !!!actual }; } }; } return toBeFalsy; }; getJasmineRequireObj().toBeGreaterThan = function() { function toBeGreaterThan() { return { compare: function(actual, expected) { return { pass: actual > expected }; } }; } return toBeGreaterThan; }; getJasmineRequireObj().toBeGreaterThanOrEqual = function() { function toBeGreaterThanOrEqual() { return { compare: function(actual, expected) { return { pass: actual >= expected }; } }; } return toBeGreaterThanOrEqual; }; getJasmineRequireObj().toBeLessThan = function() { function toBeLessThan() { return { compare: function(actual, expected) { return { pass: actual < expected }; } }; } return toBeLessThan; }; getJasmineRequireObj().toBeLessThanOrEqual = function() { function toBeLessThanOrEqual() { return { compare: function(actual, expected) { return { pass: actual <= expected }; } }; } return toBeLessThanOrEqual; }; getJasmineRequireObj().toBeNaN = function(j$) { function toBeNaN() { return { compare: function(actual) { var result = { pass: (actual !== actual) }; if (result.pass) { result.message = 'Expected actual not to be NaN.'; } else { result.message = function() { return 'Expected ' + j$.pp(actual) + ' to be NaN.'; }; } return result; } }; } return toBeNaN; }; getJasmineRequireObj().toBeNull = function() { function toBeNull() { return { compare: function(actual) { return { pass: actual === null }; } }; } return toBeNull; }; getJasmineRequireObj().toBeTruthy = function() { function toBeTruthy() { return { compare: function(actual) { return { pass: !!actual }; } }; } return toBeTruthy; }; getJasmineRequireObj().toBeUndefined = function() { function toBeUndefined() { return { compare: function(actual) { return { pass: void 0 === actual }; } }; } return toBeUndefined; }; getJasmineRequireObj().toContain = function() { function toContain(util, customEqualityTesters) { customEqualityTesters = customEqualityTesters || []; return { compare: function(actual, expected) { return { pass: util.contains(actual, expected, customEqualityTesters) }; } }; } return toContain; }; getJasmineRequireObj().toEqual = function() { function toEqual(util, customEqualityTesters) { customEqualityTesters = customEqualityTesters || []; return { compare: function(actual, expected) { var result = { pass: false }; result.pass = util.equals(actual, expected, customEqualityTesters); return result; } }; } return toEqual; }; getJasmineRequireObj().toHaveBeenCalled = function(j$) { var getErrorMsg = j$.formatErrorMsg('', 'expect().toHaveBeenCalled()'); function toHaveBeenCalled() { return { compare: function(actual) { var result = {}; if (!j$.isSpy(actual)) { throw new Error(getErrorMsg('Expected a spy, but got ' + j$.pp(actual) + '.')); } if (arguments.length > 1) { throw new Error(getErrorMsg('Does not take arguments, use toHaveBeenCalledWith')); } result.pass = actual.calls.any(); result.message = result.pass ? 'Expected spy ' + actual.and.identity() + ' not to have been called.' : 'Expected spy ' + actual.and.identity() + ' to have been called.'; return result; } }; } return toHaveBeenCalled; }; getJasmineRequireObj().toHaveBeenCalledTimes = function(j$) { var getErrorMsg = j$.formatErrorMsg('', 'expect().toHaveBeenCalledTimes()'); function toHaveBeenCalledTimes() { return { compare: function(actual, expected) { if (!j$.isSpy(actual)) { throw new Error(getErrorMsg('Expected a spy, but got ' + j$.pp(actual) + '.')); } var args = Array.prototype.slice.call(arguments, 0), result = { pass: false }; if (!j$.isNumber_(expected)){ throw new Error(getErrorMsg('The expected times failed is a required argument and must be a number.')); } actual = args[0]; var calls = actual.calls.count(); var timesMessage = expected === 1 ? 'once' : expected + ' times'; result.pass = calls === expected; result.message = result.pass ? 'Expected spy ' + actual.and.identity() + ' not to have been called ' + timesMessage + '. It was called ' + calls + ' times.' : 'Expected spy ' + actual.and.identity() + ' to have been called ' + timesMessage + '. It was called ' + calls + ' times.'; return result; } }; } return toHaveBeenCalledTimes; }; getJasmineRequireObj().toHaveBeenCalledWith = function(j$) { var getErrorMsg = j$.formatErrorMsg('', 'expect().toHaveBeenCalledWith(...arguments)'); function toHaveBeenCalledWith(util, customEqualityTesters) { return { compare: function() { var args = Array.prototype.slice.call(arguments, 0), actual = args[0], expectedArgs = args.slice(1), result = { pass: false }; if (!j$.isSpy(actual)) { throw new Error(getErrorMsg('Expected a spy, but got ' + j$.pp(actual) + '.')); } if (!actual.calls.any()) { result.message = function() { return 'Expected spy ' + actual.and.identity() + ' to have been called with ' + j$.pp(expectedArgs) + ' but it was never called.'; }; return result; } if (util.contains(actual.calls.allArgs(), expectedArgs, customEqualityTesters)) { result.pass = true; result.message = function() { return 'Expected spy ' + actual.and.identity() + ' not to have been called with ' + j$.pp(expectedArgs) + ' but it was.'; }; } else { result.message = function() { return 'Expected spy ' + actual.and.identity() + ' to have been called with ' + j$.pp(expectedArgs) + ' but actual calls were ' + j$.pp(actual.calls.allArgs()).replace(/^\[ | \]$/g, '') + '.'; }; } return result; } }; } return toHaveBeenCalledWith; }; getJasmineRequireObj().toMatch = function(j$) { var getErrorMsg = j$.formatErrorMsg('', 'expect().toMatch( || )'); function toMatch() { return { compare: function(actual, expected) { if (!j$.isString_(expected) && !j$.isA_('RegExp', expected)) { throw new Error(getErrorMsg('Expected is not a String or a RegExp')); } var regexp = new RegExp(expected); return { pass: regexp.test(actual) }; } }; } return toMatch; }; getJasmineRequireObj().toThrow = function(j$) { var getErrorMsg = j$.formatErrorMsg('', 'expect(function() {}).toThrow()'); function toThrow(util) { return { compare: function(actual, expected) { var result = { pass: false }, threw = false, thrown; if (typeof actual != 'function') { throw new Error(getErrorMsg('Actual is not a Function')); } try { actual(); } catch (e) { threw = true; thrown = e; } if (!threw) { result.message = 'Expected function to throw an exception.'; return result; } if (arguments.length == 1) { result.pass = true; result.message = function() { return 'Expected function not to throw, but it threw ' + j$.pp(thrown) + '.'; }; return result; } if (util.equals(thrown, expected)) { result.pass = true; result.message = function() { return 'Expected function not to throw ' + j$.pp(expected) + '.'; }; } else { result.message = function() { return 'Expected function to throw ' + j$.pp(expected) + ', but it threw ' + j$.pp(thrown) + '.'; }; } return result; } }; } return toThrow; }; getJasmineRequireObj().toThrowError = function(j$) { var getErrorMsg = j$.formatErrorMsg('', 'expect(function() {}).toThrowError(, )'); function toThrowError () { return { compare: function(actual) { var threw = false, pass = {pass: true}, fail = {pass: false}, thrown; if (typeof actual != 'function') { throw new Error(getErrorMsg('Actual is not a Function')); } var errorMatcher = getMatcher.apply(null, arguments); try { actual(); } catch (e) { threw = true; thrown = e; } if (!threw) { fail.message = 'Expected function to throw an Error.'; return fail; } if (!(thrown instanceof Error)) { fail.message = function() { return 'Expected function to throw an Error, but it threw ' + j$.pp(thrown) + '.'; }; return fail; } if (errorMatcher.hasNoSpecifics()) { pass.message = 'Expected function not to throw an Error, but it threw ' + j$.fnNameFor(thrown) + '.'; return pass; } if (errorMatcher.matches(thrown)) { pass.message = function() { return 'Expected function not to throw ' + errorMatcher.errorTypeDescription + errorMatcher.messageDescription() + '.'; }; return pass; } else { fail.message = function() { return 'Expected function to throw ' + errorMatcher.errorTypeDescription + errorMatcher.messageDescription() + ', but it threw ' + errorMatcher.thrownDescription(thrown) + '.'; }; return fail; } } }; function getMatcher() { var expected = null, errorType = null; if (arguments.length == 2) { expected = arguments[1]; if (isAnErrorType(expected)) { errorType = expected; expected = null; } } else if (arguments.length > 2) { errorType = arguments[1]; expected = arguments[2]; if (!isAnErrorType(errorType)) { throw new Error(getErrorMsg('Expected error type is not an Error.')); } } if (expected && !isStringOrRegExp(expected)) { if (errorType) { throw new Error(getErrorMsg('Expected error message is not a string or RegExp.')); } else { throw new Error(getErrorMsg('Expected is not an Error, string, or RegExp.')); } } function messageMatch(message) { if (typeof expected == 'string') { return expected == message; } else { return expected.test(message); } } return { errorTypeDescription: errorType ? j$.fnNameFor(errorType) : 'an exception', thrownDescription: function(thrown) { var thrownName = errorType ? j$.fnNameFor(thrown.constructor) : 'an exception', thrownMessage = ''; if (expected) { thrownMessage = ' with message ' + j$.pp(thrown.message); } return thrownName + thrownMessage; }, messageDescription: function() { if (expected === null) { return ''; } else if (expected instanceof RegExp) { return ' with a message matching ' + j$.pp(expected); } else { return ' with message ' + j$.pp(expected); } }, hasNoSpecifics: function() { return expected === null && errorType === null; }, matches: function(error) { return (errorType === null || error instanceof errorType) && (expected === null || messageMatch(error.message)); } }; } function isStringOrRegExp(potential) { return potential instanceof RegExp || (typeof potential == 'string'); } function isAnErrorType(type) { if (typeof type !== 'function') { return false; } var Surrogate = function() {}; Surrogate.prototype = type.prototype; return (new Surrogate()) instanceof Error; } } return toThrowError; }; getJasmineRequireObj().interface = function(jasmine, env) { var jasmineInterface = { describe: function(description, specDefinitions) { return env.describe(description, specDefinitions); }, xdescribe: function(description, specDefinitions) { return env.xdescribe(description, specDefinitions); }, fdescribe: function(description, specDefinitions) { return env.fdescribe(description, specDefinitions); }, it: function() { return env.it.apply(env, arguments); }, xit: function() { return env.xit.apply(env, arguments); }, fit: function() { return env.fit.apply(env, arguments); }, beforeEach: function() { return env.beforeEach.apply(env, arguments); }, afterEach: function() { return env.afterEach.apply(env, arguments); }, beforeAll: function() { return env.beforeAll.apply(env, arguments); }, afterAll: function() { return env.afterAll.apply(env, arguments); }, expect: function(actual) { return env.expect(actual); }, pending: function() { return env.pending.apply(env, arguments); }, fail: function() { return env.fail.apply(env, arguments); }, spyOn: function(obj, methodName) { return env.spyOn(obj, methodName); }, jsApiReporter: new jasmine.JsApiReporter({ timer: new jasmine.Timer() }), jasmine: jasmine }; jasmine.addCustomEqualityTester = function(tester) { env.addCustomEqualityTester(tester); }; jasmine.addMatchers = function(matchers) { return env.addMatchers(matchers); }; jasmine.clock = function() { return env.clock; }; return jasmineInterface; }; getJasmineRequireObj().version = function() { return '2.5.2'; }; cjs-4.4.0/installed-tests/js/jsunit.gresources.xml000066400000000000000000000014441356375132300222620ustar00rootroot00000000000000 complex.ui jasmine.js minijasmine.js modules/alwaysThrows.js modules/foobar.js modules/lexicalScope.js modules/modunicode.js modules/mutualImport/a.js modules/mutualImport/b.js modules/subA/subB/__init__.js modules/overrides/GIMarshallingTests.js modules/overrides/Gio.js modules/overrides/Regress.js modules/overrides/WarnLib.js modules/subA/subB/baz.js modules/subA/subB/foobar.js cjs-4.4.0/installed-tests/js/minijasmine.js000066400000000000000000000067251356375132300207140ustar00rootroot00000000000000#!/usr/bin/env gjs const GLib = imports.gi.GLib; const Lang = imports.lang; function _removeNewlines(str) { let allNewlines = /\n/g; return str.replace(allNewlines, '\\n'); } function _filterStack(stack) { if (!stack) return 'No stack'; return stack.split('\n') .filter(stackLine => stackLine.indexOf('resource:///org/gjs/jsunit') === -1) .filter(stackLine => stackLine.indexOf('') === -1) .join('\n'); } function _setTimeoutInternal(continueTimeout, func, time) { return GLib.timeout_add(GLib.PRIORITY_DEFAULT, time, function () { func(); return continueTimeout; }); } function _clearTimeoutInternal(id) { if (id > 0) GLib.source_remove(id); } // Install the browser setTimeout/setInterval API on the global object window.setTimeout = _setTimeoutInternal.bind(undefined, GLib.SOURCE_REMOVE); window.setInterval = _setTimeoutInternal.bind(undefined, GLib.SOURCE_CONTINUE); window.clearTimeout = window.clearInterval = _clearTimeoutInternal; let jasmineRequire = imports.jasmine.getJasmineRequireObj(); let jasmineCore = jasmineRequire.core(jasmineRequire); window._jasmineEnv = jasmineCore.getEnv(); window._jasmineMain = GLib.MainLoop.new(null, false); window._jasmineRetval = 0; // Install Jasmine API on the global object let jasmineInterface = jasmineRequire.interface(jasmineCore, window._jasmineEnv); Lang.copyProperties(jasmineInterface, window); // Reporter that outputs according to the Test Anything Protocol // See http://testanything.org/tap-specification.html class TapReporter { constructor() { this._failedSuites = []; this._specCount = 0; } jasmineStarted(info) { print('1..' + info.totalSpecsDefined); } jasmineDone() { this._failedSuites.forEach(failure => { failure.failedExpectations.forEach(result => { print('not ok - An error was thrown outside a test'); print('# ' + result.message); }); }); window._jasmineMain.quit(); } suiteDone(result) { if (result.failedExpectations && result.failedExpectations.length > 0) { window._jasmineRetval = 1; this._failedSuites.push(result); } if (result.status === 'disabled') { print('# Suite was disabled:', result.fullName); } } specStarted() { this._specCount++; } specDone(result) { let tap_report; if (result.status === 'failed') { window._jasmineRetval = 1; tap_report = 'not ok'; } else { tap_report = 'ok'; } tap_report += ' ' + this._specCount + ' ' + result.fullName; if (result.status === 'pending' || result.status === 'disabled') { let reason = result.pendingReason || result.status; tap_report += ' # SKIP ' + reason; } print(tap_report); // Print additional diagnostic info on failure if (result.status === 'failed' && result.failedExpectations) { result.failedExpectations.forEach((failedExpectation) => { print('# Message:', _removeNewlines(failedExpectation.message)); print('# Stack:'); let stackTrace = _filterStack(failedExpectation.stack).trim(); print(stackTrace.split('\n').map((str) => '# ' + str).join('\n')); }); } } } window._jasmineEnv.addReporter(new TapReporter()); cjs-4.4.0/installed-tests/js/modules/000077500000000000000000000000001356375132300175115ustar00rootroot00000000000000cjs-4.4.0/installed-tests/js/modules/alwaysThrows.js000066400000000000000000000001411356375132300225520ustar00rootroot00000000000000// line 0 // line 1 // line 2 throw new Error("This is an error that always happens on line 3"); cjs-4.4.0/installed-tests/js/modules/foobar.js000066400000000000000000000002611356375132300213160ustar00rootroot00000000000000// simple test module (used by testImporter.js) var foo = "This is foo"; var bar = "This is bar"; var toString = x => x; function testToString(x) { return toString(x); } cjs-4.4.0/installed-tests/js/modules/lexicalScope.js000066400000000000000000000015231356375132300224630ustar00rootroot00000000000000/* exported a, b, c */ // Tests bindings in the global scope (var) and lexical environment (let, const) // This should be exported as a property when importing this module: var a = 1; // These should not be exported, but for compatibility we will pretend they are // for the time being: let b = 2; const c = 3; // It's not clear whether this should be exported in ES6, but for compatibility // it should be: this.d = 4; // Modules should have access to standard properties on the global object. if (typeof imports === 'undefined') throw new Error('fail the import'); // This should probably not be supported in the future, but I'm not sure how // we can phase it out compatibly. The module should also have access to // properties that the importing code defines. if (typeof expectMe === 'undefined') throw new Error('fail the import'); cjs-4.4.0/installed-tests/js/modules/modunicode.js000066400000000000000000000001001356375132300221640ustar00rootroot00000000000000// This file is written in UTF-8. var uval = "const ♥ utf8"; cjs-4.4.0/installed-tests/js/modules/mutualImport/000077500000000000000000000000001356375132300222135ustar00rootroot00000000000000cjs-4.4.0/installed-tests/js/modules/mutualImport/a.js000066400000000000000000000003001356375132300227620ustar00rootroot00000000000000const B = imports.mutualImport.b; let count = 0; function incrementCount() { count++; } function getCount() { return count; } function getCountViaB() { return B.getCount(); } cjs-4.4.0/installed-tests/js/modules/mutualImport/b.js000066400000000000000000000001251356375132300227700ustar00rootroot00000000000000const A = imports.mutualImport.a; function getCount() { return A.getCount(); } cjs-4.4.0/installed-tests/js/modules/overrides/000077500000000000000000000000001356375132300215135ustar00rootroot00000000000000cjs-4.4.0/installed-tests/js/modules/overrides/GIMarshallingTests.js000066400000000000000000000001101356375132300255450ustar00rootroot00000000000000// Sabotage the import of imports.gi.GIMarshallingTests! throw '💩'; cjs-4.4.0/installed-tests/js/modules/overrides/Gio.js000066400000000000000000000000771356375132300225730ustar00rootroot00000000000000// Sabotage the import of imports.gi.Gio! var _init = '💩'; cjs-4.4.0/installed-tests/js/modules/overrides/Regress.js000066400000000000000000000001261356375132300234620ustar00rootroot00000000000000// Sabotage the import of imports.gi.Regress! function _init() { throw '💩'; } cjs-4.4.0/installed-tests/js/modules/overrides/WarnLib.js000066400000000000000000000001101356375132300233770ustar00rootroot00000000000000// Sabotage the import of imports.gi.WarnLib! k$^s^%$#^*($%jdghdsfjkgd cjs-4.4.0/installed-tests/js/modules/subA/000077500000000000000000000000001356375132300204035ustar00rootroot00000000000000cjs-4.4.0/installed-tests/js/modules/subA/subB/000077500000000000000000000000001356375132300212765ustar00rootroot00000000000000cjs-4.4.0/installed-tests/js/modules/subA/subB/__init__.js000066400000000000000000000004431356375132300233740ustar00rootroot00000000000000function testImporterFunction() { return "__init__ function tested"; } function ImporterClass() { this._init(); } ImporterClass.prototype = { _init : function() { this._a = "__init__ class tested"; }, testMethod : function() { return this._a; } }; cjs-4.4.0/installed-tests/js/modules/subA/subB/baz.js000066400000000000000000000000001356375132300223760ustar00rootroot00000000000000cjs-4.4.0/installed-tests/js/modules/subA/subB/foobar.js000066400000000000000000000001431356375132300231020ustar00rootroot00000000000000// simple test module (used by testImporter.js) var foo = "This is foo"; var bar = "This is bar"; cjs-4.4.0/installed-tests/js/testByteArray.js000066400000000000000000000074531356375132300212120ustar00rootroot00000000000000const ByteArray = imports.byteArray; describe('Byte array', function () { it('has length 0 for empty array', function () { let a = new ByteArray.ByteArray(); expect(a.length).toEqual(0); }); describe('initially sized to 10', function () { let a; beforeEach(function () { a = new ByteArray.ByteArray(10); }); it('has length 10', function () { expect(a.length).toEqual(10); }); it('is initialized to zeroes', function () { for (let i = 0; i < a.length; ++i) { expect(a[i]).toEqual(0); } }); }); it('assigns values correctly', function () { let a = new ByteArray.ByteArray(256); for (let i = 0; i < a.length; ++i) { a[i] = 255 - i; } for (let i = 0; i < a.length; ++i) { expect(a[i]).toEqual(255 - i); } }); describe('assignment past end', function () { let a; beforeEach(function () { a = new ByteArray.ByteArray(); a[2] = 5; }); it('implicitly lengthens the array', function () { expect(a.length).toEqual(3); expect(a[2]).toEqual(5); }); it('implicitly creates zero bytes', function () { expect(a[0]).toEqual(0); expect(a[1]).toEqual(0); }); }); it('changes the length when assigning to length property', function () { let a = new ByteArray.ByteArray(20); expect(a.length).toEqual(20); a.length = 5; expect(a.length).toEqual(5); }); describe('conversions', function () { let a; beforeEach(function () { a = new ByteArray.ByteArray(); a[0] = 255; }); it('gives a byte 5 when assigning 5', function () { a[0] = 5; expect(a[0]).toEqual(5); }); it('gives a byte 0 when assigning null', function () { a[0] = null; expect(a[0]).toEqual(0); }); it('gives a byte 0 when assigning undefined', function () { a[0] = undefined; expect(a[0]).toEqual(0); }); it('rounds off when assigning a double', function () { a[0] = 3.14; expect(a[0]).toEqual(3); }); }); it('can be created from a string', function () { let a = ByteArray.fromString('abcd'); expect(a.length).toEqual(4); [97, 98, 99, 100].forEach((val, ix) => expect(a[ix]).toEqual(val)); }); it('can be encoded from a string', function () { // Pick a string likely to be stored internally as Latin1 let a = ByteArray.fromString('äbcd', 'LATIN1'); expect(a.length).toEqual(4); [228, 98, 99, 100].forEach((val, ix) => expect(a[ix]).toEqual(val)); // Try again with a string not likely to be Latin1 internally a = ByteArray.fromString('⅜', 'UTF-8'); expect(a.length).toEqual(3); [0xe2, 0x85, 0x9c].forEach((val, ix) => expect(a[ix]).toEqual(val)); }); it('encodes as UTF-8 by default', function () { let a = ByteArray.fromString('⅜'); expect(a.length).toEqual(3); [0xe2, 0x85, 0x9c].forEach((val, ix) => expect(a[ix]).toEqual(val)); }); it('can be created from an array', function () { let a = ByteArray.fromArray([ 1, 2, 3, 4 ]); expect(a.length).toEqual(4); [1, 2, 3, 4].forEach((val, ix) => expect(a[ix]).toEqual(val)); }); it('can be converted to a string of ASCII characters', function () { let a = new ByteArray.ByteArray(); a[0] = 97; a[1] = 98; a[2] = 99; a[3] = 100; let s = a.toString(); expect(s.length).toEqual(4); expect(s).toEqual('abcd'); }); }); cjs-4.4.0/installed-tests/js/testCairo.js000066400000000000000000000173031356375132300203400ustar00rootroot00000000000000imports.gi.versions.Gdk = '3.0'; imports.gi.versions.Gtk = '3.0'; const Cairo = imports.cairo; const Gdk = imports.gi.Gdk; const Gtk = imports.gi.Gtk; const Regress = imports.gi.Regress; function _ts(obj) { return obj.toString().slice(8, -1); } describe('Cairo', function () { beforeAll(function () { Gtk.init(null); }); let cr, surface; beforeEach(function () { surface = new Cairo.ImageSurface(Cairo.Format.ARGB32, 1, 1); cr = new Cairo.Context(surface); }); describe('context', function () { it('has the right type', function () { expect(cr instanceof Cairo.Context).toBeTruthy(); }); it('reports its target surface', function () { expect(_ts(cr.getTarget())).toEqual('ImageSurface'); }); it('can set its source to a pattern', function () { let pattern = Cairo.SolidPattern.createRGB(1, 2, 3); cr.setSource(pattern); expect(_ts(cr.getSource())).toEqual('SolidPattern'); }); it('can set its antialias', function () { cr.setAntialias(Cairo.Antialias.NONE); expect(cr.getAntialias()).toEqual(Cairo.Antialias.NONE); }); it('can set its fill rule', function () { cr.setFillRule(Cairo.FillRule.EVEN_ODD); expect(cr.getFillRule()).toEqual(Cairo.FillRule.EVEN_ODD); }); it('can set its line cap', function () { cr.setLineCap(Cairo.LineCap.ROUND); expect(cr.getLineCap()).toEqual(Cairo.LineCap.ROUND); }); it('can set its line join', function () { cr.setLineJoin(Cairo.LineJoin.ROUND); expect(cr.getLineJoin()).toEqual(Cairo.LineJoin.ROUND); }); it('can set its line width', function () { cr.setLineWidth(1138); expect(cr.getLineWidth()).toEqual(1138); }); it('can set its miter limit', function () { cr.setMiterLimit(42); expect(cr.getMiterLimit()).toEqual(42); }); it('can set its operator', function () { cr.setOperator(Cairo.Operator.IN); expect(cr.getOperator()).toEqual(Cairo.Operator.IN); }); it('can set its tolerance', function () { cr.setTolerance(144); expect(cr.getTolerance()).toEqual(144); }); it('has a rectangle as clip extents', function () { expect(cr.clipExtents().length).toEqual(4); }); it('has a rectangle as fill extents', function () { expect(cr.fillExtents().length).toEqual(4); }); it('has a rectangle as stroke extents', function () { expect(cr.strokeExtents().length).toEqual(4); }); it('has zero dashes initially', function () { expect(cr.getDashCount()).toEqual(0); }); it('transforms user to device coordinates', function () { expect(cr.userToDevice(0, 0).length).toEqual(2); }); it('transforms user to device distance', function () { expect(cr.userToDeviceDistance(0, 0).length).toEqual(2); }); it('transforms device to user coordinates', function () { expect(cr.deviceToUser(0, 0).length).toEqual(2); }); it('transforms device to user distance', function () { expect(cr.deviceToUserDistance(0, 0).length).toEqual(2); }); it('can call various, otherwise untested, methods without crashing', function () { expect(() => { cr.save(); cr.restore(); cr.setSourceSurface(surface, 0, 0); cr.pushGroup(); cr.popGroup(); cr.pushGroupWithContent(Cairo.Content.COLOR); cr.popGroupToSource(); cr.setSourceRGB(1, 2, 3); cr.setSourceRGBA(1, 2, 3, 4); cr.clip(); cr.clipPreserve(); cr.fill(); cr.fillPreserve(); let pattern = Cairo.SolidPattern.createRGB(1, 2, 3); cr.mask(pattern); cr.maskSurface(surface, 0, 0); cr.paint(); cr.paintWithAlpha(1); cr.stroke(); cr.strokePreserve(); cr.inFill(0, 0); cr.inStroke(0, 0); cr.copyPage(); cr.showPage(); cr.translate(10, 10); cr.scale(10, 10); cr.rotate(180); cr.identityMatrix(); cr.showText("foobar"); cr.moveTo(0, 0); cr.setDash([1, 0.5], 1); cr.lineTo(1, 0); cr.lineTo(1, 1); cr.lineTo(0, 1); cr.closePath(); let path = cr.copyPath(); cr.fill(); cr.appendPath(path); cr.stroke(); }).not.toThrow(); }); it('can be marshalled through a signal handler', function () { let o = new Regress.TestObj(); let foreignSpy = jasmine.createSpy('sig-with-foreign-struct'); o.connect('sig-with-foreign-struct', foreignSpy); o.emit_sig_with_foreign_struct(); expect(foreignSpy).toHaveBeenCalledWith(o, cr); }); it('has methods when created from a C function', function () { let win = new Gtk.OffscreenWindow(); let da = new Gtk.DrawingArea(); win.add(da); da.realize(); cr = Gdk.cairo_create(da.window); expect(cr.save).toBeDefined(); expect(cr.getTarget()).toBeDefined(); }); }); describe('solid pattern', function () { it('can be created from RGB static method', function () { let p1 = Cairo.SolidPattern.createRGB(1, 2, 3); expect(_ts(p1)).toEqual('SolidPattern'); cr.setSource(p1); expect(_ts(cr.getSource())).toEqual('SolidPattern'); }); it('can be created from RGBA static method', function () { let p2 = Cairo.SolidPattern.createRGBA(1, 2, 3, 4); expect(_ts(p2)).toEqual('SolidPattern'); cr.setSource(p2); expect(_ts(cr.getSource())).toEqual('SolidPattern'); }); }); describe('surface pattern', function () { it('can be created and added as a source', function () { let p1 = new Cairo.SurfacePattern(surface); expect(_ts(p1)).toEqual('SurfacePattern'); cr.setSource(p1); expect(_ts(cr.getSource())).toEqual('SurfacePattern'); }); }); describe('linear gradient', function () { it('can be created and added as a source', function () { let p1 = new Cairo.LinearGradient(1, 2, 3, 4); expect(_ts(p1)).toEqual('LinearGradient'); cr.setSource(p1); expect(_ts(cr.getSource())).toEqual('LinearGradient'); }); }); describe('radial gradient', function () { it('can be created and added as a source', function () { let p1 = new Cairo.RadialGradient(1, 2, 3, 4, 5, 6); expect(_ts(p1)).toEqual('RadialGradient'); cr.setSource(p1); expect(_ts(cr.getSource())).toEqual('RadialGradient'); }); }); }); describe('Cairo imported via GI', function () { const giCairo = imports.gi.cairo; it('has the same functionality as imports.cairo', function () { const surface = new giCairo.ImageSurface(Cairo.Format.ARGB32, 1, 1); void new giCairo.Context(surface); }); it('has boxed types from the GIR file', function () { void new giCairo.RectangleInt(); }); }); cjs-4.4.0/installed-tests/js/testEverythingBasic.js000066400000000000000000000657311356375132300224010ustar00rootroot00000000000000const Regress = imports.gi.Regress; const WarnLib = imports.gi.WarnLib; // We use Gio to have some objects that we know exist imports.gi.versions.Gdk = '3.0'; const Gdk = imports.gi.Gdk; const GLib = imports.gi.GLib; const Gio = imports.gi.Gio; const GObject = imports.gi.GObject; describe('Life, the Universe and Everything', function () { it('includes booleans', function () { expect(Regress.test_boolean(false)).toBe(false); expect(Regress.test_boolean(true)).toBe(true); }); [8, 16, 32, 64].forEach(bits => { it('includes ' + bits + '-bit integers', function () { let method = 'test_int' + bits; expect(Regress[method](42)).toBe(42); expect(Regress[method](-42)).toBe(-42); }); it('includes unsigned ' + bits + '-bit integers', function () { let method = 'test_uint' + bits; expect(Regress[method](42)).toBe(42); }); }); ['short', 'int', 'long', 'ssize', 'float', 'double'].forEach(type => { it('includes ' + type + 's', function () { let method = 'test_' + type; expect(Regress[method](42)).toBe(42); expect(Regress[method](-42)).toBe(-42); }); }); ['ushort', 'uint', 'ulong', 'size'].forEach(type => { it('includes ' + type + 's', function () { let method = 'test_' + type; expect(Regress[method](42)).toBe(42); }); }); it('includes wide characters', function () { expect(Regress.test_unichar('c')).toBe('c'); expect(Regress.test_unichar('')).toBe(''); expect(Regress.test_unichar('\u2665')).toBe('\u2665'); }); it('includes time_t', function () { let now = Math.floor(new Date().getTime() / 1000); let bounced = Math.floor(Regress.test_timet(now)); expect(bounced).toEqual(now); }); describe('Limits', function () { const Limits = { '8': { MIN: -128, MAX: 127, UMAX: 255, }, '16': { MIN: -32767 - 1, MAX: 32767, UMAX: 65535, }, '32': { MIN: -2147483647 - 1, MAX: 2147483647, UMAX: 4294967295, }, '64': { MIN: -9223372036854775807 - 1, MAX: 9223372036854775807, UMAX: 18446744073709551615, }, }; const skip = { 'UMAX64': true, // FAIL: expected 18446744073709552000, got 0 'MAX64': true, // FAIL: expected 9223372036854776000, got -9223372036854776000 }; function run_test(bytes, limit, method_stem) { if(skip[limit + bytes]) pending("This test doesn't work"); if (bytes === '64') GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, "*cannot be safely stored*"); let val = Limits[bytes][limit]; expect(Regress[method_stem + bytes](val)).toBe(val); if (bytes === '64') GLib.test_assert_expected_messages_internal('Gjs', 'testEverythingBasic.js', 0, 'Ignore message'); } ['8', '16', '32', '64'].forEach(bytes => { it('marshals max value of unsigned ' + bytes + '-bit integers', function () { run_test(bytes, 'UMAX', 'test_uint'); }); it('marshals min value of signed ' + bytes + '-bit integers', function () { run_test(bytes, 'MIN', 'test_int'); }); it('marshals max value of signed ' + bytes + '-bit integers', function () { run_test(bytes, 'MAX', 'test_int'); }); }); it('warns when conversion is lossy', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, "*cannot be safely stored*"); GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, "*cannot be safely stored*"); void GLib.MAXINT64; void GLib.MAXUINT64; GLib.test_assert_expected_messages_internal('Gjs', 'testEverythingBasic.js', 0, 'Limits warns when conversion is lossy'); }); }); describe('No implicit conversion to unsigned', function () { ['uint8', 'uint16', 'uint32', 'uint64', 'uint', 'size'].forEach(type => { it('for ' + type, function () { expect(() => Regress['test_' + type](-42)).toThrow(); }); }); }); it('throws when constructor called without new', function () { expect(() => Gio.AppLaunchContext()) .toThrowError(/Constructor called as normal method/); }); describe('String arrays', function () { it('marshalling in', function () { expect(Regress.test_strv_in(['1', '2', '3'])).toBeTruthy(); // Second two are deliberately not strings expect(() => Regress.test_strv_in(['1', 2, 3])).toThrow(); }); it('marshalling out', function () { expect(Regress.test_strv_out()) .toEqual(['thanks', 'for', 'all', 'the', 'fish']); }); it('marshalling out with container transfer', function () { expect(Regress.test_strv_out_container()).toEqual(['1', '2', '3']); }); }); it('in after out', function () { const str = "hello"; let len = Regress.test_int_out_utf8(str); expect(len).toEqual(str.length); }); describe('UTF-8 strings', function () { const CONST_STR = "const \u2665 utf8"; const NONCONST_STR = "nonconst \u2665 utf8"; it('as return types', function () { expect(Regress.test_utf8_const_return()).toEqual(CONST_STR); expect(Regress.test_utf8_nonconst_return()).toEqual(NONCONST_STR); }); it('as in parameters', function () { Regress.test_utf8_const_in(CONST_STR); }); it('as out parameters', function () { expect(Regress.test_utf8_out()).toEqual(NONCONST_STR); }); // FIXME: this is broken due to a change in gobject-introspection. xit('as in-out parameters', function () { expect(Regress.test_utf8_inout(CONST_STR)).toEqual(NONCONST_STR); }).pend('https://bugzilla.gnome.org/show_bug.cgi?id=736517'); }); it('return values in filename encoding', function () { let filenames = Regress.test_filename_return(); expect(filenames).toEqual(['\u00e5\u00e4\u00f6', '/etc/fstab']); }); it('static methods', function () { let v = Regress.TestObj.new_from_file("/enoent"); expect(v instanceof Regress.TestObj).toBeTruthy(); }); it('closures', function () { let callback = jasmine.createSpy('callback').and.returnValue(42); expect(Regress.test_closure(callback)).toEqual(42); expect(callback).toHaveBeenCalledWith(); }); it('closures with one argument', function () { let callback = jasmine.createSpy('callback') .and.callFake(someValue => someValue); expect(Regress.test_closure_one_arg(callback, 42)).toEqual(42); expect(callback).toHaveBeenCalledWith(42); }); it('callbacks', function () { let callback = jasmine.createSpy('callback').and.returnValue(42); expect(Regress.test_callback(callback)).toEqual(42); }); it('null / undefined callback', function () { expect(Regress.test_callback(null)).toEqual(0); expect(() => Regress.test_callback(undefined)).toThrow(); }); it('array callbacks', function () { let callback = jasmine.createSpy('callback').and.returnValue(7); expect(Regress.test_array_callback(callback)).toEqual(14); expect(callback).toHaveBeenCalledWith([-1, 0, 1, 2], ["one", "two", "three"]); }); it('null array callback', function () { expect(() => Regress.test_array_callback(null)).toThrow(); }); it('callback with transfer-full return value', function () { function callback() { return Regress.TestObj.new_from_file("/enoent"); } Regress.test_callback_return_full(callback); }); it('callback with destroy-notify', function () { let testObj = { test: function (data) { return data; }, }; spyOn(testObj, 'test').and.callThrough(); expect(Regress.test_callback_destroy_notify(function () { return testObj.test(42); }.bind(testObj))).toEqual(42); expect(testObj.test).toHaveBeenCalledTimes(1); expect(Regress.test_callback_thaw_notifications()).toEqual(42); }); it('async callback', function () { Regress.test_callback_async(() => 44); expect(Regress.test_callback_thaw_async()).toEqual(44); }); describe('GValue boxing and unboxing', function () { it('integer in', function () { expect(Regress.test_int_value_arg(42)).toEqual(42); }); it('integer out', function () { expect(Regress.test_value_return(42)).toEqual(42); }); it('strv in', function () { expect(Regress.test_strv_in_gvalue()).toEqual(['one', 'two', 'three']); }); const GVALUE_HASH_TABLE = { 'integer': 12, 'boolean': true, 'string': 'some text', 'strings': ['first', 'second', 'third'], 'flags': Regress.TestFlags.FLAG1 | Regress.TestFlags.FLAG3, 'enum': Regress.TestEnum.VALUE2, }; it('hashtable with GValue value type out', function () { expect(Regress.test_ghash_gvalue_return()).toEqual(GVALUE_HASH_TABLE); }); }); ['glist', 'gslist'].forEach(list => { describe(list + ' types', function () { const STR_LIST = ['1', '2', '3']; it('return with transfer-none', function () { expect(Regress['test_' + list + '_nothing_return']()).toEqual(STR_LIST); expect(Regress['test_' + list + '_nothing_return2']()).toEqual(STR_LIST); }); it('return with transfer-container', function () { expect(Regress['test_' + list + '_container_return']()).toEqual(STR_LIST); }); it('return with transfer-full', function () { expect(Regress['test_' + list + '_everything_return']()).toEqual(STR_LIST); }); it('in with transfer-none', function () { Regress['test_' + list + '_nothing_in'](STR_LIST); Regress['test_' + list + '_nothing_in2'](STR_LIST); }); xit('in with transfer-container', function () { Regress['test_' + list + '_container_in'](STR_LIST); }).pend('Function not added to gobject-introspection test suite yet'); }); }); ['int', 'gint8', 'gint16', 'gint32', 'gint64'].forEach(inttype => { it('arrays of ' + inttype + ' in', function () { expect(Regress['test_array_' + inttype + '_in']([1, 2, 3, 4])).toEqual(10); }); }); it('implicit conversions from strings to int arrays', function () { expect(Regress.test_array_gint8_in("\x01\x02\x03\x04")).toEqual(10); expect(Regress.test_array_gint16_in("\x01\x02\x03\x04")).toEqual(10); expect(Regress.test_array_gint16_in("\u0100\u0200\u0300\u0400")).toEqual(2560); }); it('GType arrays', function () { expect(Regress.test_array_gtype_in([Gio.SimpleAction, Gio.Icon, GObject.TYPE_BOXED])) .toEqual('[GSimpleAction,GIcon,GBoxed,]'); expect(() => Regress.test_array_gtype_in(42)).toThrow(); expect(() => Regress.test_array_gtype_in([undefined])).toThrow(); // 80 is G_TYPE_OBJECT, but we don't want it to work expect(() => Regress.test_array_gtype_in([80])).toThrow(); }); it('out arrays of integers', function () { expect(Regress.test_array_int_out()).toEqual([0, 1, 2, 3, 4]); let array = Regress.test_array_fixed_size_int_out(); expect(array[0]).toEqual(0); expect(array[4]).toEqual(4); array = Regress.test_array_fixed_size_int_return(); expect(array[0]).toEqual(0); expect(array[4]).toEqual(4); expect(Regress.test_array_int_none_out()).toEqual([1, 2, 3, 4, 5]); expect(Regress.test_array_int_full_out()).toEqual([0, 1, 2, 3, 4]); expect(Regress.test_array_int_null_out()).toEqual([]); }); it('null in-array', function () { Regress.test_array_int_null_in(null); }); it('out arrays of structs', function () { let array = Regress.test_array_struct_out(); let ints = array.map(struct => struct.some_int); expect(ints).toEqual([22, 33, 44]); }); describe('GHash type', function () {; const EXPECTED_HASH = { baz: 'bat', foo: 'bar', qux: 'quux' }; it('null GHash in', function () { Regress.test_ghash_null_in(null); }); it('null GHash out', function () { expect(Regress.test_ghash_null_return()).toBeNull(); }); it('out GHash', function () { expect(Regress.test_ghash_nothing_return()).toEqual(EXPECTED_HASH); expect(Regress.test_ghash_nothing_return2()).toEqual(EXPECTED_HASH); expect(Regress.test_ghash_container_return()).toEqual(EXPECTED_HASH); expect(Regress.test_ghash_everything_return()).toEqual(EXPECTED_HASH); }); it('in GHash', function () { Regress.test_ghash_nothing_in(EXPECTED_HASH); Regress.test_ghash_nothing_in2(EXPECTED_HASH); }); it('nested GHash', function () { const EXPECTED_NESTED_HASH = { wibble: EXPECTED_HASH }; expect(Regress.test_ghash_nested_everything_return()) .toEqual(EXPECTED_NESTED_HASH); expect(Regress.test_ghash_nested_everything_return2()) .toEqual(EXPECTED_NESTED_HASH); }); }); it('enum parameter', function () { expect(Regress.test_enum_param(Regress.TestEnum.VALUE1)).toEqual('value1'); expect(Regress.test_enum_param(Regress.TestEnum.VALUE3)).toEqual('value3'); }); it('unsigned enum parameter', function () { expect(Regress.test_unsigned_enum_param(Regress.TestEnumUnsigned.VALUE1)) .toEqual('value1'); expect(Regress.test_unsigned_enum_param(Regress.TestEnumUnsigned.VALUE2)) .toEqual('value2'); }); it('enum has a $gtype property', function () { expect(Regress.TestEnumUnsigned.$gtype).toBeDefined(); }); it('enum $gtype property is enumerable', function () { expect('$gtype' in Regress.TestEnumUnsigned).toBeTruthy(); }); it('Number converts error to quark', function () { expect(Regress.TestError.quark()).toEqual(Number(Regress.TestError)); }); it('converts enum to string', function () { expect(Regress.TestEnum.param(Regress.TestEnum.VALUE4)).toEqual('value4'); }); describe('Object-valued GProperty', function () { let o1, t1, t2; beforeEach(function () { o1 = new GObject.Object(); t1 = new Regress.TestObj({bare: o1}); t2 = new Regress.TestSubObj(); t2.bare = o1; }); it('marshals correctly in the getter', function () { expect(t1.bare).toBe(o1); }); it('marshals correctly when inherited', function () { expect(t2.bare).toBe(o1); }); it('marshals into setter function', function () { let o2 = new GObject.Object(); t2.set_bare(o2); expect(t2.bare).toBe(o2); }); it('marshals null', function () { t2.unset_bare(); expect(t2.bare).toBeNull(); }); }); describe('Signal connection', function () { let o; beforeEach(function () { o = new Regress.TestObj(); }); it('calls correct handlers with correct arguments', function () { let handler = jasmine.createSpy('handler'); let handlerId = o.connect('test', handler); handler.and.callFake(() => o.disconnect(handlerId)); o.emit('test'); expect(handler).toHaveBeenCalledTimes(1); expect(handler).toHaveBeenCalledWith(o); handler.calls.reset(); o.emit('test'); expect(handler).not.toHaveBeenCalled(); }); it('throws errors for invalid signals', function () { expect(() => o.connect('invalid-signal', o => {})).toThrow(); expect(() => o.emit('invalid-signal')).toThrow(); }); it('signal handler with static scope arg gets arg passed by reference', function () { let b = new Regress.TestSimpleBoxedA({ some_int: 42, some_int8: 43, some_double: 42.5, some_enum: Regress.TestEnum.VALUE3, }); o.connect('test-with-static-scope-arg', (signalObject, signalArg) => { signalArg.some_int = 44; }); o.emit('test-with-static-scope-arg', b); expect(b.some_int).toEqual(44); }); it('signal with array len parameter is not passed correct array and no length arg', function (done) { o.connect('sig-with-array-len-prop', (signalObj, signalArray, shouldBeUndefined) => { expect(shouldBeUndefined).not.toBeDefined(); expect(signalArray).toEqual([0, 1, 2, 3, 4]); done(); }); o.emit_sig_with_array_len_prop(); }); xit('can pass parameter to signal with array len parameter via emit', function (done) { o.connect('sig-with-array-len-prop', (signalObj, signalArray) => { expect(signalArray).toEqual([0, 1, 2, 3, 4]); done(); }); o.emit('sig-with-array-len-prop', [0, 1, 2, 3, 4]); }).pend('Not yet implemented'); xit('can pass null to signal with array len parameter', function () { let handler = jasmine.createSpy('handler'); o.connect('sig-with-array-len-prop', handler); o.emit('sig-with-array-len-prop', null); expect(handler).toHaveBeenCalledWith([jasmine.any(Object), null]); }).pend('Not yet implemented'); }); describe('Signal alternative syntax', function () { let o, handler; beforeEach(function () { handler = jasmine.createSpy('handler'); o = new Regress.TestObj(); let handlerId = GObject.signal_connect(o, 'test', handler); handler.and.callFake(() => GObject.signal_handler_disconnect(o, handlerId)); GObject.signal_emit_by_name(o, 'test'); }); it('handler is called with the right object', function () { expect(handler).toHaveBeenCalledTimes(1); expect(handler).toHaveBeenCalledWith(o); }); it('disconnected handler is not called', function () { handler.calls.reset(); GObject.signal_emit_by_name(o, 'test'); expect(handler).not.toHaveBeenCalled(); }); }); it('torture signature 0', function () { let [y, z, q] = Regress.test_torture_signature_0(42, 'foo', 7); expect(Math.floor(y)).toEqual(42); expect(z).toEqual(84); expect(q).toEqual(10); }); it('torture signature 1 fail', function () { expect(() => Regress.test_torture_signature_1(42, 'foo', 7)).toThrow(); }); it('torture signature 1 success', function () { let [, y, z, q] = Regress.test_torture_signature_1(11, 'barbaz', 8); expect(Math.floor(y)).toEqual(11); expect(z).toEqual(22); expect(q).toEqual(14); }); it('torture signature 2', function () { let [y, z, q] = Regress.test_torture_signature_2(42, () => 0, 'foo', 7); expect(Math.floor(y)).toEqual(42); expect(z).toEqual(84); expect(q).toEqual(10); }); describe('Object torture signature', function () { let o; beforeEach(function () { o = new Regress.TestObj(); }); it('0', function () { let [y, z, q] = o.torture_signature_0(42, 'foo', 7); expect(Math.floor(y)).toEqual(42); expect(z).toEqual(84); expect(q).toEqual(10); }); it('1 fail', function () { expect(() => o.torture_signature_1(42, 'foo', 7)).toThrow(); }); it('1 success', function () { let [, y, z, q] = o.torture_signature_1(11, 'barbaz', 8); expect(Math.floor(y)).toEqual(11); expect(z).toEqual(22); expect(q).toEqual(14); }); }); // Cannot access the variant contents, for now it('integer GVariant', function () { let ivar = Regress.test_gvariant_i(); expect(ivar.get_type_string()).toEqual('i'); expect(ivar.equal(GLib.Variant.new_int32(1))).toBeTruthy(); }); it('string GVariant', function () { let svar = Regress.test_gvariant_s(); expect(String.fromCharCode(svar.classify())).toEqual('s'); expect(svar.get_string()[0]).toEqual('one'); }); it('a{sv} GVariant', function () { let asvvar = Regress.test_gvariant_asv(); expect(asvvar.n_children()).toEqual(2); }); it('as Variant', function () { let asvar = Regress.test_gvariant_as(); expect(asvar.get_strv()).toEqual(['one', 'two', 'three']); }); it('error enum names match error quarks', function () { expect(Number(Gio.IOErrorEnum)).toEqual(Gio.io_error_quark()); }); describe('thrown GError', function () { let err; beforeEach(function () { try { let file = Gio.file_new_for_path("\\/,.^!@&$_don't exist"); file.read(null); } catch (x) { err = x; } }); it('is an instance of error enum type', function () { expect(err instanceof Gio.IOErrorEnum).toBeTruthy(); }); it('matches error domain and code', function () { expect(err.matches(Gio.io_error_quark(), Gio.IOErrorEnum.NOT_FOUND)) .toBeTruthy(); }); it('has properties for domain and code', function () { expect(err.domain).toEqual(Gio.io_error_quark()); expect(err.code).toEqual(Gio.IOErrorEnum.NOT_FOUND); }); }); it('GError callback', function (done) { Regress.test_gerror_callback(e => { expect(e instanceof Gio.IOErrorEnum).toBeTruthy(); expect(e.domain).toEqual(Gio.io_error_quark()); expect(e.code).toEqual(Gio.IOErrorEnum.NOT_SUPPORTED); done(); }); }); it('owned GError callback', function (done) { Regress.test_owned_gerror_callback(e => { expect(e instanceof Gio.IOErrorEnum).toBeTruthy(); expect(e.domain).toEqual(Gio.io_error_quark()); expect(e.code).toEqual(Gio.IOErrorEnum.PERMISSION_DENIED); done(); }); }); // Calling matches() on an unpaired error used to JSUnit.assert: // https://bugzilla.gnome.org/show_bug.cgi?id=689482 it('bug 689482', function () { try { WarnLib.throw_unpaired(); fail(); } catch (e) { expect(e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND)).toBeFalsy(); } }); it('correctly converts a NULL strv in a GValue to an empty array', function() { let v = Regress.test_null_strv_in_gvalue(); expect(v.length).toEqual(0); }); describe('wrong type for GObject', function () { let wrongObject, wrongBoxed, subclassObject; beforeEach(function () { wrongObject = new Gio.SimpleAction(); wrongBoxed = new GLib.KeyFile(); subclassObject = new Regress.TestSubObj(); }); // Everything.func_obj_null_in expects a Everything.TestObj it('function does not accept a GObject of the wrong type', function () { expect(() => Regress.func_obj_null_in(wrongObject)).toThrow(); }); it('function does not accept a GBoxed instead of GObject', function () { expect(() => Regress.func_obj_null_in(wrongBoxed)).toThrow(); }); it('function does not accept returned GObject of the wrong type', function () { let wrongReturnedObject = Gio.File.new_for_path('/'); expect(() => Regress.func_obj_null_in(wrongReturnedObject)).toThrow(); }); it('function accepts GObject of subclass of expected type', function () { expect(() => Regress.func_obj_null_in(subclassObject)).not.toThrow(); }); it('method cannot be called on a GObject of the wrong type', function () { expect(() => Regress.TestObj.prototype.instance_method.call(wrongObject)) .toThrow(); }); it('method cannot be called on a GBoxed', function () { expect(() => Regress.TestObj.prototype.instance_method.call(wrongBoxed)) .toThrow(); }); it('method can be called on a GObject of subclass of expected type', function () { expect(() => Regress.TestObj.prototype.instance_method.call(subclassObject)) .not.toThrow(); }); }); describe('wrong type for GBoxed', function () { let simpleBoxed, wrongObject, wrongBoxed; beforeEach(function () { simpleBoxed = new Regress.TestSimpleBoxedA(); wrongObject = new Gio.SimpleAction(); wrongBoxed = new GLib.KeyFile(); }); // simpleBoxed.equals expects a Everything.TestSimpleBoxedA it('function does not accept a GObject of the wrong type', function () { expect(() => simpleBoxed.equals(wrongObject)).toThrow(); }); it('function does not accept a GBoxed of the wrong type', function () { expect(() => simpleBoxed.equals(wrongBoxed)).toThrow(); }); it('function does accept a GBoxed of the correct type', function () { expect(simpleBoxed.equals(simpleBoxed)).toBeTruthy(); }); it('method cannot be called on a GObject', function () { expect(() => Regress.TestSimpleBoxedA.prototype.copy.call(wrongObject)) .toThrow(); }); it('method cannot be called on a GBoxed of the wrong type', function () { expect(() => Regress.TestSimpleBoxedA.protoype.copy.call(wrongBoxed)) .toThrow(); }); it('method can be called on correct GBoxed type', function () { expect(() => Regress.TestSimpleBoxedA.prototype.copy.call(simpleBoxed)) .not.toThrow(); }); }); it('presents GdkAtom as string', function () { expect(Gdk.Atom.intern('CLIPBOARD', false)).toBe('CLIPBOARD'); expect(Gdk.Atom.intern('NONE', false)).toBe(null); }); }); cjs-4.4.0/installed-tests/js/testEverythingEncapsulated.js000066400000000000000000000243741356375132300237660ustar00rootroot00000000000000const GLib = imports.gi.GLib; const Regress = imports.gi.Regress; const System = imports.system; describe('Introspected structs', function () { let struct; describe('simple', function () { beforeEach(function () { struct = new Regress.TestStructA(); struct.some_int = 42; struct.some_int8 = 43; struct.some_double = 42.5; struct.some_enum = Regress.TestEnum.VALUE3; }); it('sets fields correctly', function () { expect(struct.some_int).toEqual(42); expect(struct.some_int8).toEqual(43); expect(struct.some_double).toEqual(42.5); expect(struct.some_enum).toEqual(Regress.TestEnum.VALUE3); }); it('can clone', function () { let b = struct.clone(); expect(b.some_int).toEqual(42); expect(b.some_int8).toEqual(43); expect(b.some_double).toEqual(42.5); expect(b.some_enum).toEqual(Regress.TestEnum.VALUE3); }); }); describe('nested', function () { beforeEach(function () { struct = new Regress.TestStructB(); struct.some_int8 = 43; struct.nested_a.some_int8 = 66; }); it('sets fields correctly', function () { expect(struct.some_int8).toEqual(43); expect(struct.nested_a.some_int8).toEqual(66); }); it('can clone', function () { let b = struct.clone(); expect(b.some_int8).toEqual(43); expect(b.nested_a.some_int8).toEqual(66); }); }); describe('constructors', function () { beforeEach(function () { struct = new Regress.TestStructA({ some_int: 42, some_int8: 43, some_double: 42.5, some_enum: Regress.TestEnum.VALUE3, }); }); it('"copies" an object from a hash of field values', function () { expect(struct.some_int).toEqual(42); expect(struct.some_int8).toEqual(43); expect(struct.some_double).toEqual(42.5); expect(struct.some_enum).toEqual(Regress.TestEnum.VALUE3); }); it('catches bad field names', function () { expect(() => new Regress.TestStructA({ junk: 42 })).toThrow(); }); it('copies an object from another object of the same type', function () { let copy = new Regress.TestStructA(struct); expect(copy.some_int).toEqual(42); expect(copy.some_int8).toEqual(43); expect(copy.some_double).toEqual(42.5); expect(copy.some_enum).toEqual(Regress.TestEnum.VALUE3); }); }); it('containing fixed array', function () { let struct = new Regress.TestStructFixedArray(); struct.frob(); expect(struct.just_int).toEqual(7); expect(struct.array).toEqual([42, 43, 44, 45, 46, 47, 48, 49, 50, 51]); }); }); describe('Introspected boxed types', function () { let simple_boxed; it('sets fields correctly', function () { simple_boxed = new Regress.TestSimpleBoxedA(); simple_boxed.some_int = 42; simple_boxed.some_int8 = 43; simple_boxed.some_double = 42.5; simple_boxed.some_enum = Regress.TestEnum.VALUE3; expect(simple_boxed.some_int).toEqual(42); expect(simple_boxed.some_int8).toEqual(43); expect(simple_boxed.some_double).toEqual(42.5); expect(simple_boxed.some_enum).toEqual(Regress.TestEnum.VALUE3); let boxed = new Regress.TestBoxed(); boxed.some_int8 = 42; expect(boxed.some_int8).toEqual(42); }); describe('copy constructors', function () { beforeEach(function () { simple_boxed = new Regress.TestSimpleBoxedA({ some_int: 42, some_int8: 43, some_double: 42.5, some_enum: Regress.TestEnum.VALUE3, }); }); it('"copies" an object from a hash of field values', function () { expect(simple_boxed.some_int).toEqual(42); expect(simple_boxed.some_int8).toEqual(43); expect(simple_boxed.some_double).toEqual(42.5); expect(simple_boxed.some_enum).toEqual(Regress.TestEnum.VALUE3); }); it('catches bad field names', function () { expect(() => new Regress.TestSimpleBoxedA({ junk: 42 })).toThrow(); }); it('copies an object from another object of the same type', function () { let copy = new Regress.TestSimpleBoxedA(simple_boxed); expect(copy instanceof Regress.TestSimpleBoxedA).toBeTruthy(); expect(copy.some_int).toEqual(42); expect(copy.some_int8).toEqual(43); expect(copy.some_double).toEqual(42.5); expect(copy.some_enum).toEqual(Regress.TestEnum.VALUE3); }); }); describe('nested', function () { beforeEach(function () { simple_boxed = new Regress.TestSimpleBoxedB(); }); it('reads fields and nested fields', function () { simple_boxed.some_int8 = 42; simple_boxed.nested_a.some_int = 43; expect(simple_boxed.some_int8).toEqual(42); expect(simple_boxed.nested_a.some_int).toEqual(43); }); it('assigns nested struct field from an instance', function () { simple_boxed.nested_a = new Regress.TestSimpleBoxedA({ some_int: 53 }); expect(simple_boxed.nested_a.some_int).toEqual(53); }); it('assigns nested struct field directly from a hash of field values', function () { simple_boxed.nested_a = { some_int: 63 }; expect(simple_boxed.nested_a.some_int).toEqual(63); }); }); it('constructs with a nested hash of field values', function () { let simple2 = new Regress.TestSimpleBoxedB({ some_int8: 42, nested_a: { some_int: 43, some_int8: 44, some_double: 43.5 } }); expect(simple2.some_int8).toEqual(42); expect(simple2.nested_a.some_int).toEqual(43); expect(simple2.nested_a.some_int8).toEqual(44); expect(simple2.nested_a.some_double).toEqual(43.5); }); it('constructs using a custom constructor', function () { let boxed = new Regress.TestBoxedD('abcd', 8); expect(boxed.get_magic()).toEqual(12); }); // RegressTestBoxedB has a constructor that takes multiple // arguments, but since it is directly allocatable, we keep // the old style of passing an hash of fields. // The two real world structs that have this behavior are // Clutter.Color and Clutter.ActorBox. it('constructs using a custom constructor in backwards compatibility mode', function () { let boxed = new Regress.TestBoxedB({ some_int8: 7, some_long: 5 }); expect(boxed.some_int8).toEqual(7); expect(boxed.some_long).toEqual(5); }); }); describe('Introspected GObject', function () { let obj; beforeEach(function () { obj = new Regress.TestObj({ // These properties have backing public fields with different names int: 42, float: 3.1416, double: 2.71828, }); }); it('can access fields with simple types', function () { // Compare the values gotten through the GObject property getters to the // values of the backing fields expect(obj.some_int8).toEqual(obj.int); expect(obj.some_float).toEqual(obj.float); expect(obj.some_double).toEqual(obj.double); }); it('cannot access fields with complex types (GI limitation)', function () { expect(() => obj.parent_instance).toThrow(); expect(() => obj.function_ptr).toThrow(); }); it('silently does not set read-only fields', function () { obj.some_int8 = 41; expect(obj.some_int8).toEqual(42); expect(obj.int).toEqual(42); }); it('throws an error in strict mode when setting a read-only field', function () { 'use strict'; expect(() => obj.some_int8 = 41).toThrow(); }); it('has normal Object methods', function () { obj.ownprop = 'foo'; expect(obj.hasOwnProperty('ownprop')).toBeTruthy(); }); // This test is not meant to be normative; a GObject behaving like this is // doing something unsupported. However, we have been handling this so far // in a certain way, and we don't want to break user code because of badly // behaved libraries. This test ensures that any change to the behaviour // must be intentional. it('resolves properties when they are shadowed by methods', function () { expect(obj.name_conflict).toEqual(42); expect(obj.name_conflict instanceof Function).toBeFalsy(); }); }); describe('Introspected function length', function () { let obj; beforeEach(function () { obj = new Regress.TestObj(); }); it('skips over instance parameters of methods', function () { expect(obj.set_bare.length).toEqual(1); }); it('skips over out and GError parameters', function () { expect(obj.torture_signature_1.length).toEqual(3); }); it('does not skip over inout parameters', function () { expect(obj.skip_return_val.length).toEqual(5); }); xit('skips over parameters annotated with skip', function () { expect(obj.skip_param.length).toEqual(4); }).pend('Not implemented yet'); it('gives number of arguments for static methods', function () { expect(Regress.TestObj.new_from_file.length).toEqual(1); }); it('skips over destroy-notify and user-data parameters', function () { expect(Regress.TestObj.new_callback.length).toEqual(1); }); }); describe('Garbage collection of introspected objects', function () { // This tests a regression that would very rarely crash, but // when run under valgrind this code would show use-after-free. it('collects objects properly with signals connected', function (done) { function orphanObject() { let obj = new Regress.TestObj(); obj.connect('notify', () => {}); } orphanObject(); System.gc(); GLib.idle_add(GLib.PRIORITY_LOW, () => done()); }); }); cjs-4.4.0/installed-tests/js/testExceptions.js000066400000000000000000000145231356375132300214250ustar00rootroot00000000000000const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Foo = GObject.registerClass({ Properties: { 'prop': GObject.ParamSpec.string('prop', '', '', GObject.ParamFlags.READWRITE, '') }, }, class Foo extends GObject.Object { set prop(v) { throw new Error('set'); } get prop() { throw new Error('get'); } }); const Bar = GObject.registerClass({ Properties: { 'prop': GObject.ParamSpec.string('prop', '', '', GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT, ''), } }, class Bar extends GObject.Object {}); describe('Exceptions', function () { it('are thrown from property setter', function () { let foo = new Foo(); expect(() => foo.prop = 'bar').toThrowError(/set/); }); it('are thrown from property getter', function () { let foo = new Foo(); expect(() => foo.prop).toThrowError(/get/); }); // FIXME: In the next cases the errors aren't thrown but logged it('are logged from constructor', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: Error: set*'); new Foo({ prop: 'bar' }); GLib.test_assert_expected_messages_internal('Gjs', 'testExceptions.js', 0, 'testExceptionInPropertySetterFromConstructor'); }); it('are logged from property setter with binding', function () { let foo = new Foo(); let bar = new Bar(); bar.bind_property('prop', foo, 'prop', GObject.BindingFlags.DEFAULT); GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: Error: set*'); // wake up the binding so that g_object_set() is called on foo bar.notify('prop'); GLib.test_assert_expected_messages_internal('Gjs', 'testExceptions.js', 0, 'testExceptionInPropertySetterWithBinding'); }); it('are logged from property getter with binding', function () { let foo = new Foo(); let bar = new Bar(); foo.bind_property('prop', bar, 'prop', GObject.BindingFlags.DEFAULT); GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: Error: get*'); // wake up the binding so that g_object_get() is called on foo foo.notify('prop'); GLib.test_assert_expected_messages_internal('Gjs', 'testExceptions.js', 0, 'testExceptionInPropertyGetterWithBinding'); }); }); describe('logError', function () { afterEach(function () { GLib.test_assert_expected_messages_internal('Gjs', 'testExceptions.js', 0, 'testGErrorMessages'); }); it('logs a warning for a GError', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: Gio.IOErrorEnum: *'); try { let file = Gio.file_new_for_path("\\/,.^!@&$_don't exist"); file.read(null); } catch(e) { logError(e); } }); it('logs a warning with a message if given', function marker() { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: Gio.IOErrorEnum: a message\nmarker@*'); try { throw new Gio.IOErrorEnum({ message: 'a message', code: 0 }); } catch(e) { logError(e); } }); it('also logs an error for a created GError that is not thrown', function marker() { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: Gio.IOErrorEnum: a message\nmarker@*'); logError(new Gio.IOErrorEnum({ message: 'a message', code: 0 })); }); it('logs an error with no stack trace for an error created with the GLib.Error constructor', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: Gio.IOErrorEnum: a message'); logError(new GLib.Error(Gio.IOErrorEnum, 0, 'a message')); }); it('logs the quark for a JS-created GError type', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: GLib.Error my-error: a message'); logError(new GLib.Error(GLib.quark_from_string('my-error'), 0, 'a message')); }); // Now with prefix it('logs an error with a prefix if given', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: prefix: Gio.IOErrorEnum: *'); try { let file = Gio.file_new_for_path("\\/,.^!@&$_don't exist"); file.read(null); } catch(e) { logError(e, 'prefix'); } }); it('logs an error with prefix and message', function marker() { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: prefix: Gio.IOErrorEnum: a message\nmarker@*'); try { throw new Gio.IOErrorEnum({ message: 'a message', code: 0 }); } catch(e) { logError(e, 'prefix'); } }); it('logs a non-thrown error with prefix', function marker() { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: prefix: Gio.IOErrorEnum: a message\nmarker@*'); logError(new Gio.IOErrorEnum({ message: 'a message', code: 0 }), 'prefix'); }); it('logs a GLib.Error with prefix', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: prefix: Gio.IOErrorEnum: a message'); logError(new GLib.Error(Gio.IOErrorEnum, 0, 'a message'), 'prefix'); }); it('logs a JS-created GLib.Error with prefix', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: prefix: GLib.Error my-error: a message'); logError(new GLib.Error(GLib.quark_from_string('my-error'), 0, 'a message'), 'prefix'); }); }); describe('Exception from function with too few arguments', function () { it('contains the full function name', function () { expect(() => GLib.get_locale_variants()) .toThrowError(/GLib\.get_locale_variants/); }); it('contains the full method name', function () { let file = Gio.File.new_for_path('foo'); expect(() => file.read()).toThrowError(/Gio\.File\.read/); }); });cjs-4.4.0/installed-tests/js/testFormat.js000066400000000000000000000037421356375132300205350ustar00rootroot00000000000000const Format = imports.format; String.prototype.format = Format.format; describe('imports.format', function () { it('escapes % with another % character', function () { expect('%d%%'.format(10)).toEqual('10%'); }); it('formats a single string argument', function () { expect('%s'.format('Foo')).toEqual('Foo'); }); it('formats two string arguments', function () { expect('%s %s'.format('Foo', 'Bar')).toEqual('Foo Bar'); }); it('formats two swapped string arguments', function () { expect('%2$s %1$s'.format('Foo', 'Bar')).toEqual('Bar Foo'); }); it('formats a number in base 10', function () { expect('%d'.format(42)).toEqual('42'); }); it('formats a number in base 16', function () { expect('%x'.format(42)).toEqual('2a'); }); it('formats a floating point number with no precision', function () { expect('%f'.format(0.125)).toEqual('0.125'); }); it('formats a floating point number with precision 2', function () { expect('%.2f'.format(0.125)).toEqual('0.13'); }); it('pads with zeroes', function () { let zeroFormat = '%04d'; expect(zeroFormat.format(1)).toEqual('0001'); expect(zeroFormat.format(10)).toEqual('0010'); expect(zeroFormat.format(100)).toEqual('0100'); }); it('pads with spaces', function () { let spaceFormat = '%4d'; expect(spaceFormat.format(1)).toEqual(' 1'); expect(spaceFormat.format(10)).toEqual(' 10'); expect(spaceFormat.format(100)).toEqual(' 100'); }); it('throws an error when given incorrect modifiers for the conversion type', function () { expect(() => '%z'.format(42)).toThrow(); expect(() => '%.2d'.format(42)).toThrow(); expect(() => '%Ix'.format(42)).toThrow(); }); it('throws an error when incorrectly instructed to swap arguments', function () { expect(() => '%2$d %d %1$d'.format(1, 2, 3)).toThrow(); }); }); cjs-4.4.0/installed-tests/js/testFundamental.js000066400000000000000000000007001356375132300215320ustar00rootroot00000000000000const Regress = imports.gi.Regress; describe('Fundamental type support', function () { it('constructs a subtype of a fundamental type', function () { expect(() => new Regress.TestFundamentalSubObject('plop')).not.toThrow(); }); it('constructs a subtype of a hidden (no introspection data) fundamental type', function() { expect(() => Regress.test_create_fundamental_hidden_class_instance()).not.toThrow(); }); }); cjs-4.4.0/installed-tests/js/testGDBus.js000066400000000000000000000346641356375132300202600ustar00rootroot00000000000000const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; /* The methods list with their signatures. * * *** NOTE: If you add stuff here, you need to update testIntrospectReal */ var TestIface = ' \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ '; const PROP_READ_WRITE_INITIAL_VALUE = 58; const PROP_WRITE_ONLY_INITIAL_VALUE = "Initial value"; /* Test is the actual object exporting the dbus methods */ class Test { constructor() { this._propWriteOnly = PROP_WRITE_ONLY_INITIAL_VALUE; this._propReadWrite = PROP_READ_WRITE_INITIAL_VALUE; this._impl = Gio.DBusExportedObject.wrapJSObject(TestIface, this); this._impl.export(Gio.DBus.session, '/org/gnome/gjs/Test'); } frobateStuff(args) { return { hello: new GLib.Variant('s', 'world') }; } nonJsonFrobateStuff(i) { if (i == 42) { return "42 it is!"; } else { return "Oops"; } } alwaysThrowException() { throw Error("Exception!"); } thisDoesNotExist() { /* We'll remove this later! */ } noInParameter() { return "Yes!"; } multipleInArgs(a, b, c, d, e) { return a + " " + b + " " + c + " " + d + " " + e; } emitSignal() { this._impl.emit_signal('signalFoo', GLib.Variant.new('(s)', [ "foobar" ])); } noReturnValue() { /* Empty! */ } /* The following two functions have identical return values * in JS, but the bus message will be different. * multipleOutValues is "sss", while oneArrayOut is "as" */ multipleOutValues() { return [ "Hello", "World", "!" ]; } oneArrayOut() { return [ "Hello", "World", "!" ]; } /* Same thing again. In this case multipleArrayOut is "asas", * while arrayOfArrayOut is "aas". */ multipleArrayOut() { return [[ "Hello", "World" ], [ "World", "Hello" ]]; } arrayOfArrayOut() { return [[ "Hello", "World" ], [ "World", "Hello" ]]; } arrayOutBadSig() { return Symbol('Hello World!'); } byteArrayEcho(binaryString) { return binaryString; } byteEcho(aByte) { return aByte; } dictEcho(dict) { return dict; } /* This one is implemented asynchronously. Returns * the input arguments */ echoAsync(parameters, invocation) { var [someString, someInt] = parameters; GLib.idle_add(GLib.PRIORITY_DEFAULT, function() { invocation.return_value(new GLib.Variant('(si)', [someString, someInt])); return false; }); } // boolean get PropReadOnly() { return true; } // string set PropWriteOnly(value) { this._propWriteOnly = value; } // variant get PropReadWrite() { return new GLib.Variant('s', this._propReadWrite.toString()); } set PropReadWrite(value) { this._propReadWrite = value.deep_unpack(); } structArray() { return [[128, 123456], [42, 654321]]; } } const ProxyClass = Gio.DBusProxy.makeProxyWrapper(TestIface); describe('Exported DBus object', function () { var own_name_id; var test; var proxy; let loop; beforeAll(function () { loop = new GLib.MainLoop(null, false); test = new Test(); own_name_id = Gio.DBus.session.own_name('org.gnome.gjs.Test', Gio.BusNameOwnerFlags.NONE, name => { log("Acquired name " + name); loop.quit(); }, name => { log("Lost name " + name); }); loop.run(); new ProxyClass(Gio.DBus.session, 'org.gnome.gjs.Test', '/org/gnome/gjs/Test', (obj, error) => { expect(error).toBeNull(); proxy = obj; expect(proxy).not.toBeNull(); loop.quit(); }); loop.run(); }); afterAll(function () { // Not really needed, but if we don't cleanup // memory checking will complain Gio.DBus.session.unown_name(own_name_id); }); beforeEach(function () { loop = new GLib.MainLoop(null, false); }); it('can call a remote method', function () { proxy.frobateStuffRemote({}, ([result], excp) => { expect(excp).toBeNull(); expect(result.hello.deep_unpack()).toEqual('world'); loop.quit(); }); loop.run(); }); it('can call a remote method when not using makeProxyWrapper', function () { let info = Gio.DBusNodeInfo.new_for_xml(TestIface); let iface = info.interfaces[0]; let otherProxy = null; Gio.DBusProxy.new_for_bus(Gio.BusType.SESSION, Gio.DBusProxyFlags.DO_NOT_AUTO_START, iface, 'org.gnome.gjs.Test', '/org/gnome/gjs/Test', iface.name, null, (o, res) => { otherProxy = Gio.DBusProxy.new_for_bus_finish(res); loop.quit(); }); loop.run(); otherProxy.frobateStuffRemote({}, ([result], excp) => { expect(excp).toBeNull(); expect(result.hello.deep_unpack()).toEqual('world'); loop.quit(); }); loop.run(); }); /* excp must be exactly the exception thrown by the remote method (more or less) */ it('can handle an exception thrown by a remote method', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: Exception in method call: alwaysThrowException: *'); proxy.alwaysThrowExceptionRemote({}, function(result, excp) { expect(excp).not.toBeNull(); loop.quit(); }); loop.run(); }); it('can still destructure the return value when an exception is thrown', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: Exception in method call: alwaysThrowException: *'); // This test will not fail, but instead if the functionality is not // implemented correctly it will hang. The exception in the function // argument destructuring will not propagate across the FFI boundary // and the main loop will never quit. // https://bugzilla.gnome.org/show_bug.cgi?id=729015 proxy.alwaysThrowExceptionRemote({}, function([a, b, c], excp) { expect(a).not.toBeDefined(); expect(b).not.toBeDefined(); expect(c).not.toBeDefined(); void excp; loop.quit(); }); loop.run(); }); it('throws an exception when trying to call a method that does not exist', function () { /* First remove the method from the object! */ delete Test.prototype.thisDoesNotExist; proxy.thisDoesNotExistRemote(function (result, excp) { expect(excp).not.toBeNull(); loop.quit(); }); loop.run(); }); it('can pass a parameter to a remote method that is not a JSON object', function () { proxy.nonJsonFrobateStuffRemote(42, ([result], excp) => { expect(result).toEqual('42 it is!'); expect(excp).toBeNull(); loop.quit(); }); loop.run(); }); it('can call a remote method with no in parameter', function () { proxy.noInParameterRemote(([result], excp) => { expect(result).toEqual('Yes!'); expect(excp).toBeNull(); loop.quit(); }); loop.run(); }); it('can call a remote method with multiple in parameters', function () { proxy.multipleInArgsRemote(1, 2, 3, 4, 5, ([result], excp) => { expect(result).toEqual('1 2 3 4 5'); expect(excp).toBeNull(); loop.quit(); }); loop.run(); }); it('can call a remote method with no return value', function () { proxy.noReturnValueRemote(([result], excp) => { expect(result).not.toBeDefined(); expect(excp).toBeNull(); loop.quit(); }); loop.run(); }); it('can emit a DBus signal', function () { let handler = jasmine.createSpy('signalFoo'); let id = proxy.connectSignal('signalFoo', handler); handler.and.callFake(() => proxy.disconnectSignal(id)); proxy.emitSignalRemote(([result], excp) => { expect(result).not.toBeDefined(); expect(excp).toBeNull(); expect(handler).toHaveBeenCalledTimes(1); expect(handler).toHaveBeenCalledWith(jasmine.anything(), jasmine.anything(), ['foobar']); loop.quit(); }); loop.run(); }); it('can call a remote method with multiple return values', function () { proxy.multipleOutValuesRemote(function(result, excp) { expect(result).toEqual(['Hello', 'World', '!']); expect(excp).toBeNull(); loop.quit(); }); loop.run(); }); it('does not coalesce one array into the array of return values', function () { proxy.oneArrayOutRemote(([result], excp) => { expect(result).toEqual(['Hello', 'World', '!']); expect(excp).toBeNull(); loop.quit(); }); loop.run(); }); it('does not coalesce an array of arrays into the array of return values', function () { proxy.arrayOfArrayOutRemote(([[a1, a2]], excp) => { expect(a1).toEqual(['Hello', 'World']); expect(a2).toEqual(['World', 'Hello']); expect(excp).toBeNull(); loop.quit(); }); loop.run(); }); it('can return multiple arrays from a remote method', function () { proxy.multipleArrayOutRemote(([a1, a2], excp) => { expect(a1).toEqual(['Hello', 'World']); expect(a2).toEqual(['World', 'Hello']); expect(excp).toBeNull(); loop.quit(); }); loop.run(); }); it('handles a bad signature by throwing an exception', function () { proxy.arrayOutBadSigRemote(function(result, excp) { expect(excp).not.toBeNull(); loop.quit(); }); loop.run(); }); it('can call a remote method that is implemented asynchronously', function () { let someString = "Hello world!"; let someInt = 42; proxy.echoRemote(someString, someInt, function(result, excp) { expect(excp).toBeNull(); expect(result).toEqual([someString, someInt]); loop.quit(); }); loop.run(); }); it('can send and receive bytes from a remote method', function () { let loop = GLib.MainLoop.new(null, false); let someBytes = [ 0, 63, 234 ]; someBytes.forEach(b => { proxy.byteEchoRemote(b, ([result], excp) => { expect(excp).toBeNull(); expect(result).toEqual(b); loop.quit(); }); loop.run(); }); }); it('can call a remote method that returns an array of structs', function () { proxy.structArrayRemote(([result], excp) => { expect(excp).toBeNull(); expect(result).toEqual([[128, 123456], [42, 654321]]); loop.quit(); }); loop.run(); }); it('can send and receive dicts from a remote method', function () { let someDict = { aDouble: new GLib.Variant('d', 10), // should be an integer after round trip anInteger: new GLib.Variant('i', 10.5), // should remain a double aDoubleBeforeAndAfter: new GLib.Variant('d', 10.5), }; proxy.dictEchoRemote(someDict, ([result], excp) => { expect(excp).toBeNull(); expect(result).not.toBeNull(); // verify the fractional part was dropped off int expect(result['anInteger'].deep_unpack()).toEqual(10); // and not dropped off a double expect(result['aDoubleBeforeAndAfter'].deep_unpack()).toEqual(10.5); // check without type conversion expect(result['aDouble'].deep_unpack()).toBe(10.0); loop.quit(); }); loop.run(); }); }); cjs-4.4.0/installed-tests/js/testGIMarshalling.js000066400000000000000000000611441356375132300217660ustar00rootroot00000000000000const ByteArray = imports.byteArray; const GIMarshallingTests = imports.gi.GIMarshallingTests; // We use Gio and GLib to have some objects that we know exist const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; describe('C array', function () { function createStructArray() { return [1, 2, 3].map(num => { let struct = new GIMarshallingTests.BoxedStruct(); struct.long_ = num; return struct; }); } it('can be passed to a function', function () { expect(() => GIMarshallingTests.array_in([-1, 0, 1, 2])).not.toThrow(); }); it('can be passed to a function with its length parameter before it', function () { expect(() => GIMarshallingTests.array_in_len_before([-1, 0, 1, 2])) .not.toThrow(); }); it('can be passed to a function with zero terminator', function () { expect(() => GIMarshallingTests.array_in_len_zero_terminated([-1, 0, 1, 2])) .not.toThrow(); }); it('can be passed to a function in the style of gtk_init()', function () { let [, newArray] = GIMarshallingTests.init_function(null); expect(newArray).toEqual([]); }); it('can be returned with zero terminator', function () { expect(GIMarshallingTests.array_zero_terminated_return()) .toEqual(['0', '1', '2']); }); it('can be returned', function () { expect(GIMarshallingTests.array_return()).toEqual([-1, 0, 1, 2]); }); it('can be returned along with other arguments', function () { let [array, sum] = GIMarshallingTests.array_return_etc(9, 5); expect(sum).toEqual(14); expect(array).toEqual([9, 0, 1, 5]); }); it('can be an out argument', function () { expect(GIMarshallingTests.array_out()).toEqual([-1, 0, 1, 2]); }); it('can be an out argument along with other arguments', function () { let [array, sum] = GIMarshallingTests.array_out_etc(9, 5); expect(sum).toEqual(14); expect(array).toEqual([9, 0, 1, 5]); }); it('can be an in-out argument', function () { expect(GIMarshallingTests.array_inout([-1, 0, 1, 2])) .toEqual([-2, -1, 0, 1, 2]); }); it('can be an in-out argument along with other arguments', function () { let [array, sum] = GIMarshallingTests.array_inout_etc(9, [-1, 0, 1, 2], 5); expect(sum).toEqual(14); expect(array).toEqual([9, -1, 0, 1, 5]); }); // Run twice to ensure that copies are correctly made for (transfer full) it('copies correctly on transfer full', function () { let array = createStructArray(); expect(() => { GIMarshallingTests.array_struct_take_in(array); GIMarshallingTests.array_struct_take_in(array); }).not.toThrow(); }); describe('of structs', function () { it('can be passed to a function', function () { expect(() => GIMarshallingTests.array_struct_in(createStructArray())) .not.toThrow(); }); it('can be returned with zero terminator', function () { let structArray = GIMarshallingTests.array_zero_terminated_return_struct(); expect(structArray.map(e => e.long_)).toEqual([42, 43, 44]); }); }); describe('of booleans', function () { it('is coerced to true/false when passed to a function', function () { expect(() => GIMarshallingTests.array_bool_in([-1, 0, 1, 2])) .not.toThrow(); }); it('can be an out argument', function () { expect(GIMarshallingTests.array_bool_out()) .toEqual([true, false, true, true]); }); }); describe('of unichars', function () { it('can be passed to a function', function () { expect(() => GIMarshallingTests.array_unichar_in('const \u2665 utf8')) .not.toThrow(); }); it('can be an out argument', function () { expect(GIMarshallingTests.array_unichar_out()) .toEqual('const \u2665 utf8'); }); it('can be returned with zero terminator', function () { expect(GIMarshallingTests.array_zero_terminated_return_unichar()) .toEqual('const \u2665 utf8'); }); it('can be implicitly converted from a number array', function () { expect(() => GIMarshallingTests.array_unichar_in([0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x2665, 0x20, 0x75, 0x74, 0x66, 0x38])).not.toThrow(); }); }); describe('of strings', function () { it('can be passed to a function', function () { expect(() => GIMarshallingTests.array_string_in(['foo', 'bar'])) .not.toThrow(); }); }); describe('of enums', function () { it('can be passed to a function', function () { expect(() => GIMarshallingTests.array_enum_in([GIMarshallingTests.Enum.VALUE1, GIMarshallingTests.Enum.VALUE2, GIMarshallingTests.Enum.VALUE3])).not.toThrow(); }); }); describe('of bytes', function () { it('can be an in argument with length', function () { expect(() => GIMarshallingTests.array_in_guint8_len([-1, 0, 1, 2])) .not.toThrow(); }); it('can be implicitly converted from a string', function () { expect(() => GIMarshallingTests.array_uint8_in('abcd')).not.toThrow(); }); }); describe('of 64-bit ints', function () { it('can be passed to a function', function () { expect(() => GIMarshallingTests.array_int64_in([-1, 0, 1, 2])) .not.toThrow(); expect(() => GIMarshallingTests.array_uint64_in([-1, 0, 1, 2])) .not.toThrow(); }); it('can be an in argument with length', function () { expect(() => GIMarshallingTests.array_in_guint64_len([-1, 0, 1, 2])) .not.toThrow(); }); }); }); describe('GArray', function () { describe('of integers', function () { it('can be passed in with transfer none', function () { expect(() => GIMarshallingTests.garray_int_none_in([-1, 0, 1, 2])) .not.toThrow(); }); it('can be returned with transfer none', function () { expect(GIMarshallingTests.garray_int_none_return()) .toEqual([-1, 0, 1, 2]); }); }); describe('of strings', function () { it('can be passed in with transfer none', function () { expect(() => GIMarshallingTests.garray_utf8_none_in(['0', '1', '2'])) .not.toThrow(); }); ['return', 'out'].forEach(method => { ['none', 'container', 'full'].forEach(transfer => { it('can be passed as ' + method + ' with transfer ' + transfer, function () { expect(GIMarshallingTests['garray_utf8_' + transfer + '_' + method]()) .toEqual(['0', '1', '2']); }); }); }); }); describe('of booleans', function () { it('can be passed in with transfer none', function () { expect(() => GIMarshallingTests.garray_bool_none_in([-1, 0, 1, 2])) .not.toThrow(); }); }); describe('of unichars', function () { it('can be passed in with transfer none', function () { expect(() => GIMarshallingTests.garray_unichar_none_in('const \u2665 utf8')) .not.toThrow(); }); it('can be implicitly converted from a number array', function () { expect(() => GIMarshallingTests.garray_unichar_none_in([0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x2665, 0x20, 0x75, 0x74, 0x66, 0x38])).not.toThrow(); }); }); }); describe('GByteArray', function () { const refByteArray = ByteArray.fromArray([0, 49, 0xFF, 51]); it('can be passed in with transfer none', function () { expect(() => GIMarshallingTests.bytearray_none_in(refByteArray)) .not.toThrow(); }); it('can be returned with transfer full', function () { expect(GIMarshallingTests.bytearray_full_return()).toEqual(refByteArray); }); it('can be implicitly converted from a normal array', function () { expect(() => GIMarshallingTests.bytearray_none_in([0, 49, 0xFF, 51])) .not.toThrow(); }); }); describe('GBytes', function () { const refByteArray = ByteArray.fromArray([0, 49, 0xFF, 51]); it('can be created from an array and passed in', function () { let bytes = GLib.Bytes.new([0, 49, 0xFF, 51]); expect(() => GIMarshallingTests.gbytes_none_in(bytes)).not.toThrow(); }); it('can be created by returning from a function and passed in', function () { var bytes = GIMarshallingTests.gbytes_full_return(); expect(() => GIMarshallingTests.gbytes_none_in(bytes)).not.toThrow(); expect(bytes.toArray()).toEqual(refByteArray); }); it('can be implicitly converted from a ByteArray', function () { expect(() => GIMarshallingTests.gbytes_none_in(refByteArray)) .not.toThrow(); }); it('can be created from a string and is encoded in UTF-8', function () { let bytes = GLib.Bytes.new("const \u2665 utf8"); expect(() => GIMarshallingTests.utf8_as_uint8array_in(bytes.toArray())) .not.toThrow(); }); it('turns into a GByteArray on assignment', function () { let bytes = GIMarshallingTests.gbytes_full_return(); let array = bytes.toArray(); // Array should just be holding a ref, not a copy expect(array[1]).toEqual(49); array[1] = 42; // Assignment should force to GByteArray expect(array[1]).toEqual(42); array[1] = 49; // Flip the value back // Now convert back to GBytes expect(() => GIMarshallingTests.gbytes_none_in(array.toGBytes())) .not.toThrow(); }); it('cannot be passed to a function expecting a byte array', function () { let bytes = GLib.Bytes.new([97, 98, 99, 100]); expect(() => GIMarshallingTests.array_uint8_in(bytes.toArray())).not.toThrow(); expect(() => GIMarshallingTests.array_uint8_in(bytes)).toThrow(); }); }); describe('GPtrArray', function () { const refArray = ['0', '1', '2']; it('can be passed to a function with transfer none', function () { expect(() => GIMarshallingTests.gptrarray_utf8_none_in(refArray)) .not.toThrow(); }); ['return', 'out'].forEach(method => { ['none', 'container', 'full'].forEach(transfer => { it('can be passed as ' + method + ' with transfer ' + transfer, function () { expect(GIMarshallingTests['gptrarray_utf8_' + transfer + '_' + method]()) .toEqual(refArray); }); }); }); }); describe('GHashTable', function () { const INT_DICT = { '-1': 1, 0: 0, 1: -1, 2: -2, }; const STRING_DICT = { '-1': '1', 0: '0', 1: '-1', 2: '-2', }; const NUMBER_DICT = { '-1': -0.1, 0: 0, 1: 0.1, 2: 0.2, }; const STRING_DICT_OUT = { '-1': '1', 0: '0', 1: '1', }; it('can be passed in with integer value type', function () { expect(() => GIMarshallingTests.ghashtable_int_none_in(INT_DICT)) .not.toThrow(); }); it('can be passed in with string value type', function () { expect(() => GIMarshallingTests.ghashtable_utf8_none_in(STRING_DICT)) .not.toThrow(); }); it('can be passed in with float value type', function () { expect(() => GIMarshallingTests.ghashtable_float_in(NUMBER_DICT)) .not.toThrow(); }); it('can be passed in with double value type', function () { expect(() => GIMarshallingTests.ghashtable_double_in(NUMBER_DICT)) .not.toThrow(); }); it('can be passed in with int64 value type', function () { const int64Dict = { '-1': -1, 0: 0, 1: 1, 2: 0x100000000, }; expect(() => GIMarshallingTests.ghashtable_int64_in(int64Dict)) .not.toThrow(); }); it('can be passed in with uint64 value type', function () { const uint64Dict = { '-1': 0x100000000, 0: 0, 1: 1, 2: 2, }; expect(() => GIMarshallingTests.ghashtable_uint64_in(uint64Dict)) .not.toThrow(); }); it('can be returned with integer value type', function () { expect(GIMarshallingTests.ghashtable_int_none_return()).toEqual(INT_DICT); }); ['return', 'out'].forEach(method => { ['none', 'container', 'full'].forEach(transfer => { it('can be passed as ' + method + ' with transfer ' + transfer, function () { expect(GIMarshallingTests['ghashtable_utf8_' + transfer + '_' + method]()) .toEqual(STRING_DICT); }); }); }); it('can be passed as inout with transfer none', function () { expect(GIMarshallingTests.ghashtable_utf8_none_inout(STRING_DICT)) .toEqual(STRING_DICT_OUT); }); xit('can be passed as inout with transfer container', function () { expect(GIMarshallingTests.ghashtable_utf8_container_inout(STRING_DICT)) .toEqual(STRING_DICT_OUT); }).pend('Container transfer for in parameters not supported'); xit('can be passed as inout with transfer full', function () { expect(GIMarshallingTests.ghashtable_utf8_full_inout(STRING_DICT)) .toEqual(STRING_DICT_OUT); }).pend('https://bugzilla.gnome.org/show_bug.cgi?id=773763'); }); describe('GValue', function () { it('can be passed into a function and packed', function () { expect(() => GIMarshallingTests.gvalue_in(42)).not.toThrow(); }); it('array can be passed into a function and packed', function () { expect(() => GIMarshallingTests.gvalue_flat_array([42, '42', true])) .not.toThrow(); }); xit('enum can be passed into a function and packed', function () { expect(() => GIMarshallingTests.gvalue_in_enum(GIMarshallingTests.Enum.VALUE3)) .not.toThrow(); }).pend("GJS doesn't support native enum types"); it('can be returned and unpacked', function () { expect(GIMarshallingTests.gvalue_return()).toEqual(42); }); it('can be passed as an out argument and unpacked', function () { expect(GIMarshallingTests.gvalue_out()).toEqual(42); }); it('array can be passed as an out argument and unpacked', function () { expect(GIMarshallingTests.return_gvalue_flat_array()) .toEqual([42, '42', true]); }); it('can have its type inferred from primitive values', function () { expect(() => GIMarshallingTests.gvalue_in_with_type(42, GObject.TYPE_INT)) .not.toThrow(); expect(() => GIMarshallingTests.gvalue_in_with_type(42.5, GObject.TYPE_DOUBLE)) .not.toThrow(); expect(() => GIMarshallingTests.gvalue_in_with_type('42', GObject.TYPE_STRING)) .not.toThrow(); expect(() => GIMarshallingTests.gvalue_in_with_type(GObject.TYPE_GTYPE, GObject.TYPE_GTYPE)) .not.toThrow(); }); it('type objects can be converted from primitive-like types', function () { expect(() => GIMarshallingTests.gvalue_in_with_type(42, GObject.Int)) .not.toThrow(); expect(() => GIMarshallingTests.gvalue_in_with_type(42.5, GObject.Double)) .not.toThrow(); expect(() => GIMarshallingTests.gvalue_in_with_type(42.5, Number)) .not.toThrow(); }); it('can have its type inferred as a GObject type', function () { expect(() => GIMarshallingTests.gvalue_in_with_type(new Gio.SimpleAction(), Gio.SimpleAction)) .not.toThrow(); }); it('can have its type inferred as a superclass', function () { let action = new Gio.SimpleAction(); expect(() => GIMarshallingTests.gvalue_in_with_type(action, GObject.Object)) .not.toThrow(); expect(() => GIMarshallingTests.gvalue_in_with_type(action, GObject.TYPE_OBJECT)) .not.toThrow(); }); it('can have its type inferred as an interface that it implements', function () { expect(() => GIMarshallingTests.gvalue_in_with_type(new Gio.SimpleAction(), Gio.SimpleAction)) .not.toThrow(); }); it('can have its type inferred as a boxed type', function () { let keyfile = new GLib.KeyFile(); expect(() => GIMarshallingTests.gvalue_in_with_type(keyfile, GLib.KeyFile)) .not.toThrow(); expect(() => GIMarshallingTests.gvalue_in_with_type(keyfile, GObject.TYPE_BOXED)) .not.toThrow(); let struct = new GIMarshallingTests.BoxedStruct(); expect(() => GIMarshallingTests.gvalue_in_with_type(struct, GIMarshallingTests.BoxedStruct)) .not.toThrow(); }); it('can have its type inferred as GVariant', function () { let variant = GLib.Variant.new('u', 42); expect(() => GIMarshallingTests.gvalue_in_with_type(variant, GLib.Variant)) .not.toThrow(); expect(() => GIMarshallingTests.gvalue_in_with_type(variant, GObject.TYPE_VARIANT)) .not.toThrow(); }); it('can have its type inferred as a union type', function () { let union = GIMarshallingTests.union_returnv(); expect(() => GIMarshallingTests.gvalue_in_with_type(union, GIMarshallingTests.Union)) .not.toThrow(); }); it('can have its type inferred as a GParamSpec', function () { let paramSpec = GObject.ParamSpec.string('my-param', '', '', GObject.ParamFlags.READABLE, ''); expect(() => GIMarshallingTests.gvalue_in_with_type(paramSpec, GObject.TYPE_PARAM)) .not.toThrow(); }); xit('can have its type inferred as a foreign struct', function () { let Cairo; try { Cairo = imports.cairo; } catch(e) { pending('Compiled without Cairo support'); return; } let surface = new Cairo.ImageSurface(Cairo.Format.ARGB32, 2, 2); let cr = new Cairo.Context(surface); expect(() => GIMarshallingTests.gvalue_in_with_type(cr, Cairo.Context)) .not.toThrow(); expect(() => GIMarshallingTests.gvalue_in_with_type(surface, Cairo.Surface)) .not.toThrow(); }).pend('Errors out with "not a subclass of GObject_Boxed"'); }); describe('GType', function () { it('can be passed into a function', function () { expect(() => GIMarshallingTests.gtype_in(GObject.TYPE_NONE)).not.toThrow(); expect(() => GIMarshallingTests.gtype_in(GObject.VoidType)).not.toThrow(); expect(() => GIMarshallingTests.gtype_string_in(GObject.TYPE_STRING)).not.toThrow(); expect(() => GIMarshallingTests.gtype_string_in(GObject.String)).not.toThrow(); }); it('can be returned', function () { expect(GIMarshallingTests.gtype_return()).toEqual(GObject.TYPE_NONE); expect(GIMarshallingTests.gtype_string_return()) .toEqual(GObject.TYPE_STRING); }); it('can be passed as an out argument', function () { expect(GIMarshallingTests.gtype_out()).toEqual(GObject.TYPE_NONE); expect(GIMarshallingTests.gtype_string_out()).toEqual(GObject.TYPE_STRING); }); it('can be passed as an inout argument', function () { expect(GIMarshallingTests.gtype_inout(GObject.TYPE_NONE)) .toEqual(GObject.TYPE_INT); }); it('can be implicitly converted from a JS type', function () { expect(() => GIMarshallingTests.gtype_string_in(String)).not.toThrow(); }); }); describe('Callback', function () { it('marshals a return value', function () { expect(GIMarshallingTests.callback_return_value_only(() => 42)) .toEqual(42); }); it('marshals one out parameter', function () { expect(GIMarshallingTests.callback_one_out_parameter(() => 43)) .toEqual(43); }); it('marshals multiple out parameters', function () { expect(GIMarshallingTests.callback_multiple_out_parameters(() => [44, 45])) .toEqual([44, 45]); }); it('marshals a return value and one out parameter', function () { expect(GIMarshallingTests.callback_return_value_and_one_out_parameter(() => [46, 47])) .toEqual([46, 47]); }); it('marshals a return value and multiple out parameters', function () { expect(GIMarshallingTests.callback_return_value_and_multiple_out_parameters(() => [48, 49, 50])) .toEqual([48, 49, 50]); }); xit('marshals an array out parameter', function () { expect(GIMarshallingTests.callback_array_out_parameter(() => [50, 51])) .toEqual([50, 51]); }).pend('Function not added to gobject-introspection test suite yet'); }); const VFuncTester = GObject.registerClass(class VFuncTester extends GIMarshallingTests.Object { vfunc_vfunc_return_value_only() { return 42; } vfunc_vfunc_one_out_parameter() { return 43; } vfunc_vfunc_multiple_out_parameters() { return [44, 45]; } vfunc_vfunc_return_value_and_one_out_parameter() { return [46, 47]; } vfunc_vfunc_return_value_and_multiple_out_parameters() { return [48, 49, 50]; } vfunc_vfunc_array_out_parameter() { return [50, 51]; } vfunc_vfunc_meth_with_err(x) { switch (x) { case -1: return true; case 0: undefined.throw_type_error(); break; case 1: void reference_error; // eslint-disable-line no-undef break; case 2: throw new Gio.IOErrorEnum({ code: Gio.IOErrorEnum.FAILED, message: 'I FAILED, but the test passed!', }); case 3: throw new GLib.SpawnError({ code: GLib.SpawnError.TOO_BIG, message: 'This test is Too Big to Fail', }); } } }); describe('Virtual function', function () { let tester; beforeEach(function () { tester = new VFuncTester(); }); it('marshals a return value', function () { expect(tester.vfunc_return_value_only()).toEqual(42); }); it('marshals one out parameter', function () { expect(tester.vfunc_one_out_parameter()).toEqual(43); }); it('marshals multiple out parameters', function () { expect(tester.vfunc_multiple_out_parameters()).toEqual([44, 45]); }); it('marshals a return value and one out parameter', function () { expect(tester.vfunc_return_value_and_one_out_parameter()) .toEqual([46, 47]); }); it('marshals a return value and multiple out parameters', function () { expect(tester.vfunc_return_value_and_multiple_out_parameters()) .toEqual([48, 49, 50]); }); it('marshals an array out parameter', function () { expect(tester.vfunc_array_out_parameter()).toEqual([50, 51]); }); it('marshals an error out parameter when no error', function () { expect(tester.vfunc_meth_with_error(-1)).toBeTruthy(); }); it('marshals an error out parameter with a JavaScript exception', function () { expect(() => tester.vfunc_meth_with_error(0)).toThrowError(TypeError); expect(() => tester.vfunc_meth_with_error(1)).toThrowError(ReferenceError); }); it('marshals an error out parameter with a GError exception', function () { try { tester.vfunc_meth_with_error(2); fail('Exception should be thrown'); } catch (e) { expect(e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED)).toBeTruthy(); expect(e.message).toEqual('I FAILED, but the test passed!'); } try { tester.vfunc_meth_with_error(3); fail('Exception should be thrown'); } catch (e) { expect(e.matches(GLib.SpawnError, GLib.SpawnError.TOO_BIG)).toBeTruthy(); expect(e.message).toEqual('This test is Too Big to Fail'); } }); }); describe('Interface', function () { it('can be returned', function () { let ifaceImpl = new GIMarshallingTests.InterfaceImpl(); let itself = ifaceImpl.get_as_interface(); expect(ifaceImpl).toEqual(itself); }); }); describe('GObject properties', function () { let obj; beforeEach(function () { obj = new GIMarshallingTests.PropertiesObject(); }); it('can handle GValues', function () { obj.some_gvalue = 42; expect(obj.some_gvalue).toEqual(42); obj.some_gvalue = 'foo'; expect(obj.some_gvalue).toEqual('foo'); }); }); cjs-4.4.0/installed-tests/js/testGLib.js000066400000000000000000000032221356375132300201130ustar00rootroot00000000000000const GLib = imports.gi.GLib; describe('GVariant constructor', function () { it('constructs a string variant', function () { let str_variant = new GLib.Variant('s', 'mystring'); expect(str_variant.get_string()[0]).toEqual('mystring'); expect(str_variant.deep_unpack()).toEqual('mystring'); }); it('constructs a string variant (backwards compatible API)', function () { let str_variant = new GLib.Variant('s', 'mystring'); let str_variant_old = GLib.Variant.new('s', 'mystring'); expect(str_variant.equal(str_variant_old)).toBeTruthy(); }); it('constructs a struct variant', function () { let struct_variant = new GLib.Variant('(sogvau)', [ 'a string', '/a/object/path', 'asig', //nature new GLib.Variant('s', 'variant'), [ 7, 3 ] ]); expect(struct_variant.n_children()).toEqual(5); let unpacked = struct_variant.deep_unpack(); expect(unpacked[0]).toEqual('a string'); expect(unpacked[1]).toEqual('/a/object/path'); expect(unpacked[2]).toEqual('asig'); expect(unpacked[3] instanceof GLib.Variant).toBeTruthy(); expect(unpacked[3].deep_unpack()).toEqual('variant'); expect(unpacked[4] instanceof Array).toBeTruthy(); expect(unpacked[4].length).toEqual(2); }); it('constructs a maybe variant', function () { let maybe_variant = new GLib.Variant('ms', null); expect(maybe_variant.deep_unpack()).toBeNull(); maybe_variant = new GLib.Variant('ms', 'string'); expect(maybe_variant.deep_unpack()).toEqual('string'); }); }); cjs-4.4.0/installed-tests/js/testGObjectClass.js000066400000000000000000000250671356375132300216140ustar00rootroot00000000000000// -*- mode: js; indent-tabs-mode: nil -*- imports.gi.versions.Gtk = '3.0'; const Gio = imports.gi.Gio; const GObject = imports.gi.GObject; const Gtk = imports.gi.Gtk; const MyObject = GObject.registerClass({ Properties: { 'readwrite': GObject.ParamSpec.string('readwrite', 'ParamReadwrite', 'A read write parameter', GObject.ParamFlags.READWRITE, ''), 'readonly': GObject.ParamSpec.string('readonly', 'ParamReadonly', 'A readonly parameter', GObject.ParamFlags.READABLE, ''), 'construct': GObject.ParamSpec.string('construct', 'ParamConstructOnly', 'A readwrite construct-only parameter', GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, 'default'), }, Signals: { 'empty': {}, 'minimal': { param_types: [ GObject.TYPE_INT, GObject.TYPE_INT ] }, 'full': { flags: GObject.SignalFlags.RUN_LAST, accumulator: GObject.AccumulatorType.FIRST_WINS, return_type: GObject.TYPE_INT, param_types: [], }, 'run-last': { flags: GObject.SignalFlags.RUN_LAST }, 'detailed': { flags: GObject.SignalFlags.RUN_FIRST | GObject.SignalFlags.DETAILED, param_types: [ GObject.TYPE_STRING ], }, }, }, class MyObject extends GObject.Object { get readwrite() { if (typeof this._readwrite === 'undefined') return 'foo'; return this._readwrite; } set readwrite(val) { if (val == 'ignore') return; this._readwrite = val; } get readonly() { if (typeof this._readonly === 'undefined') return 'bar'; return this._readonly; } set readonly(val) { // this should never be called void val; this._readonly = 'bogus'; } get construct() { if (typeof this._constructProp === 'undefined') return null; return this._constructProp; } set construct(val) { // this should be called at most once if (this._constructCalled) throw Error('Construct-Only property set more than once'); this._constructProp = val; this._constructCalled = true; } notify_prop() { this._readonly = 'changed'; this.notify('readonly'); } emit_empty() { this.emit('empty'); } emit_minimal(one, two) { this.emit('minimal', one, two); } emit_full() { return this.emit('full'); } emit_detailed() { this.emit('detailed::one'); this.emit('detailed::two'); } emit_run_last(callback) { this._run_last_callback = callback; this.emit('run-last'); } on_run_last() { this._run_last_callback(); } on_empty() { this.empty_called = true; } on_full() { this.full_default_handler_called = true; return 79; } }); const MyApplication = GObject.registerClass({ Signals: { 'custom': { param_types: [ GObject.TYPE_INT ] } }, }, class MyApplication extends Gio.Application { emit_custom(n) { this.emit('custom', n); } }); const MyInitable = GObject.registerClass({ Implements: [ Gio.Initable ], }, class MyInitable extends GObject.Object { vfunc_init(cancellable) { if (!(cancellable instanceof Gio.Cancellable)) throw 'Bad argument'; this.inited = true; } }); const Derived = GObject.registerClass(class Derived extends MyObject { _init() { super._init({ readwrite: 'yes' }); } }); const MyCustomInit = GObject.registerClass(class MyCustomInit extends GObject.Object { _instance_init() { this.foo = true; } }); describe('GObject class with decorator', function () { let myInstance; beforeEach(function () { myInstance = new MyObject(); }); it('throws an error when not used with a GObject-derived class', function () { class Foo {} expect (() => GObject.registerClass(class Bar extends Foo {})).toThrow(); }); it('constructs with default values for properties', function () { expect(myInstance.readwrite).toEqual('foo'); expect(myInstance.readonly).toEqual('bar'); expect(myInstance.construct).toEqual('default'); }); it('constructs with a hash of property values', function () { let myInstance2 = new MyObject({ readwrite: 'baz', construct: 'asdf' }); expect(myInstance2.readwrite).toEqual('baz'); expect(myInstance2.readonly).toEqual('bar'); expect(myInstance2.construct).toEqual('asdf'); }); const ui = ` baz quz `; it('constructs with property values from Gtk.Builder', function () { let builder = Gtk.Builder.new_from_string(ui, -1); let myInstance3 = builder.get_object('MyObject'); expect(myInstance3.readwrite).toEqual('baz'); expect(myInstance3.readonly).toEqual('bar'); expect(myInstance3.construct).toEqual('quz'); }); it('has a name', function () { expect(MyObject.name).toEqual('MyObject'); }); // the following would (should) cause a CRITICAL: // myInstance.readonly = 'val'; // myInstance.construct = 'val'; it('has a notify signal', function () { let notifySpy = jasmine.createSpy('notifySpy'); myInstance.connect('notify::readonly', notifySpy); myInstance.notify_prop(); myInstance.notify_prop(); expect(notifySpy).toHaveBeenCalledTimes(2); }); it('can define its own signals', function () { let emptySpy = jasmine.createSpy('emptySpy'); myInstance.connect('empty', emptySpy); myInstance.emit_empty(); expect(emptySpy).toHaveBeenCalled(); expect(myInstance.empty_called).toBeTruthy(); }); it('passes emitted arguments to signal handlers', function () { let minimalSpy = jasmine.createSpy('minimalSpy'); myInstance.connect('minimal', minimalSpy); myInstance.emit_minimal(7, 5); expect(minimalSpy).toHaveBeenCalledWith(myInstance, 7, 5); }); it('can return values from signals', function () { let fullSpy = jasmine.createSpy('fullSpy').and.returnValue(42); myInstance.connect('full', fullSpy); let result = myInstance.emit_full(); expect(fullSpy).toHaveBeenCalled(); expect(result).toEqual(42); }); it('does not call first-wins signal handlers after one returns a value', function () { let neverCalledSpy = jasmine.createSpy('neverCalledSpy'); myInstance.connect('full', () => 42); myInstance.connect('full', neverCalledSpy); myInstance.emit_full(); expect(neverCalledSpy).not.toHaveBeenCalled(); expect(myInstance.full_default_handler_called).toBeFalsy(); }); it('gets the return value of the default handler', function () { let result = myInstance.emit_full(); expect(myInstance.full_default_handler_called).toBeTruthy(); expect(result).toEqual(79); }); it('calls run-last default handler last', function () { let stack = [ ]; let runLastSpy = jasmine.createSpy('runLastSpy') .and.callFake(() => { stack.push(1); }); myInstance.connect('run-last', runLastSpy); myInstance.emit_run_last(() => { stack.push(2); }); expect(stack).toEqual([1, 2]); }); it("can inherit from something that's not GObject.Object", function () { // ...and still get all the goodies of GObject.Class let instance = new MyApplication({ application_id: 'org.gjs.Application' }); let customSpy = jasmine.createSpy('customSpy'); instance.connect('custom', customSpy); instance.emit_custom(73); expect(customSpy).toHaveBeenCalledWith(instance, 73); }); it('can implement an interface', function () { let instance = new MyInitable(); expect(instance instanceof Gio.Initable).toBeTruthy(); expect(instance instanceof Gio.AsyncInitable).toBeFalsy(); // Old syntax, backwards compatible expect(instance.constructor.implements(Gio.Initable)).toBeTruthy(); expect(instance.constructor.implements(Gio.AsyncInitable)).toBeFalsy(); }); it('can implement interface vfuncs', function () { let instance = new MyInitable(); expect(instance.inited).toBeFalsy(); instance.init(new Gio.Cancellable()); expect(instance.inited).toBeTruthy(); }); it('can be a subclass', function () { let derived = new Derived(); expect(derived instanceof Derived).toBeTruthy(); expect(derived instanceof MyObject).toBeTruthy(); expect(derived.readwrite).toEqual('yes'); }); it('calls its _instance_init() function while chaining up in constructor', function () { let instance = new MyCustomInit(); expect(instance.foo).toBeTruthy(); }); it('can have an interface-valued property', function () { const InterfacePropObject = GObject.registerClass({ Properties: { 'file': GObject.ParamSpec.object('file', 'File', 'File', GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, Gio.File.$gtype) }, }, class InterfacePropObject extends GObject.Object {}); let file = Gio.File.new_for_path('dummy'); expect(() => new InterfacePropObject({ file: file })).not.toThrow(); }); it('can override a property from the parent class', function () { const OverrideObject = GObject.registerClass({ Properties: { 'readwrite': GObject.ParamSpec.override('readwrite', MyObject), }, }, class OverrideObject extends MyObject { get readwrite() { return this._subclass_readwrite; } set readwrite(val) { this._subclass_readwrite = 'subclass' + val; } }); let obj = new OverrideObject(); obj.readwrite = 'foo'; expect(obj.readwrite).toEqual('subclassfoo'); }); it('cannot override a non-existent property', function () { expect(() => GObject.registerClass({ Properties: { 'nonexistent': GObject.ParamSpec.override('nonexistent', GObject.Object), }, }, class BadOverride extends GObject.Object {})).toThrow(); }); }); cjs-4.4.0/installed-tests/js/testGObjectDestructionAccess.js000066400000000000000000000071241356375132300241660ustar00rootroot00000000000000// -*- mode: js; indent-tabs-mode: nil -*- imports.gi.versions.Gtk = '3.0'; const GLib = imports.gi.GLib; const Gtk = imports.gi.Gtk; describe('Access to destroyed GObject', () => { let destroyedWindow; beforeAll(() => { Gtk.init(null); }); beforeEach(() => { destroyedWindow = new Gtk.Window({type: Gtk.WindowType.TOPLEVEL}); destroyedWindow.destroy(); }); it('Get property', () => { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'Object Gtk.Window (0x*'); let title = destroyedWindow.title; GLib.test_assert_expected_messages_internal('Gjs', 'testGObjectDestructionAccess.js', 0, 'testExceptionInDestroyedObjectPropertyGet'); }); it('Set property', () => { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'Object Gtk.Window (0x*'); destroyedWindow.title = 'I am dead'; GLib.test_assert_expected_messages_internal('Gjs', 'testGObjectDestructionAccess.js', 0, 'testExceptionInDestroyedObjectPropertySet'); }); it('Access to getter method', () => { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'Object Gtk.Window (0x*'); let title = destroyedWindow.get_title(); GLib.test_assert_expected_messages_internal('Gjs', 'testGObjectDestructionAccess.js', 0, 'testExceptionInDestroyedObjectMethodGet'); }); it('Access to setter method', () => { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'Object Gtk.Window (0x*'); destroyedWindow.set_title('I am dead'); GLib.test_assert_expected_messages_internal('Gjs', 'testGObjectDestructionAccess.js', 0, 'testExceptionInDestroyedObjectMethodSet'); }); it('Proto function connect', () => { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'Object Gtk.Window (0x*'); destroyedWindow.connect('foo-signal', () => {}); GLib.test_assert_expected_messages_internal('Gjs', 'testGObjectDestructionAccess.js', 0, 'testExceptionInDestroyedObjectConnect'); }); it('Proto function connect_after', () => { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'Object Gtk.Window (0x*'); destroyedWindow.connect_after('foo-signal', () => {}); GLib.test_assert_expected_messages_internal('Gjs', 'testGObjectDestructionAccess.js', 0, 'testExceptionInDestroyedObjectConnectAfter'); }); it('Proto function emit', () => { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'Object Gtk.Window (0x*'); destroyedWindow.emit('foo-signal'); GLib.test_assert_expected_messages_internal('Gjs', 'testGObjectDestructionAccess.js', 0, 'testExceptionInDestroyedObjectEmit'); }); it('Proto function toString', () => { expect(destroyedWindow.toString()).toMatch( /\[object \(FINALIZED\) instance proxy GIName:Gtk.Window jsobj@0x[a-f0-9]+ native@0x[a-f0-9]+\]/); }); it('Porto function toString before/after', () => { var validWindow = new Gtk.Window({type: Gtk.WindowType.TOPLEVEL}); expect(validWindow.toString()).toMatch( /\[object instance proxy GIName:Gtk.Window jsobj@0x[a-f0-9]+ native@0x[a-f0-9]+\]/); validWindow.destroy(); expect(validWindow.toString()).toMatch( /\[object \(FINALIZED\) instance proxy GIName:Gtk.Window jsobj@0x[a-f0-9]+ native@0x[a-f0-9]+\]/); }); }); cjs-4.4.0/installed-tests/js/testGObjectInterface.js000066400000000000000000000237211356375132300224420ustar00rootroot00000000000000const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Mainloop = imports.mainloop; const AGObjectInterface = GObject.registerClass({ GTypeName: 'ArbitraryGTypeName', Requires: [ GObject.Object ], Properties: { 'interface-prop': GObject.ParamSpec.string('interface-prop', 'Interface property', 'Must be overridden in implementation', GObject.ParamFlags.READABLE, 'foobar') }, Signals: { 'interface-signal': {} }, }, class AGObjectInterface extends GObject.Interface { requiredG() { throw new GObject.NotImplementedError(); } optionalG() { return 'AGObjectInterface.optionalG()'; } }); const InterfaceRequiringGObjectInterface = GObject.registerClass({ Requires: [ AGObjectInterface ], }, class InterfaceRequiringGObjectInterface extends GObject.Interface { optionalG() { return 'InterfaceRequiringGObjectInterface.optionalG()\n' + AGObjectInterface.optionalG(this); } }); const GObjectImplementingGObjectInterface = GObject.registerClass({ Implements: [ AGObjectInterface ], Properties: { 'interface-prop': GObject.ParamSpec.override('interface-prop', AGObjectInterface), 'class-prop': GObject.ParamSpec.string('class-prop', 'Class property', 'A property that is not on the interface', GObject.ParamFlags.READABLE, 'meh') }, Signals: { 'class-signal': {}, }, }, class GObjectImplementingGObjectInterface extends GObject.Object { get interface_prop() { return 'foobar'; } get class_prop() { return 'meh'; } requiredG() {} optionalG() { return AGObjectInterface.optionalG(this); } }); const MinimalImplementationOfAGObjectInterface = GObject.registerClass({ Implements: [ AGObjectInterface ], Properties: { 'interface-prop': GObject.ParamSpec.override('interface-prop', AGObjectInterface) }, }, class MinimalImplementationOfAGObjectInterface extends GObject.Object { requiredG() {} }); const ImplementationOfTwoInterfaces = GObject.registerClass({ Implements: [ AGObjectInterface, InterfaceRequiringGObjectInterface ], Properties: { 'interface-prop': GObject.ParamSpec.override('interface-prop', AGObjectInterface) }, }, class ImplementationOfTwoInterfaces extends GObject.Object { requiredG() {} optionalG() { return InterfaceRequiringGObjectInterface.optionalG(this); } }); describe('GObject interface', function () { it('cannot be instantiated', function () { expect(() => new AGObjectInterface()).toThrow(); }); it('has a name', function () { expect(AGObjectInterface.name).toEqual('AGObjectInterface'); }); it('reports its type name', function () { expect(AGObjectInterface.$gtype.name).toEqual('ArbitraryGTypeName'); }); it('can be implemented by a GObject class', function () { let obj; expect(() => { obj = new GObjectImplementingGObjectInterface(); }) .not.toThrow(); expect(obj instanceof AGObjectInterface).toBeTruthy(); }); it('is implemented by a GObject class with the correct class object', function () { let obj = new GObjectImplementingGObjectInterface(); expect(obj.constructor).toBe(GObjectImplementingGObjectInterface); expect(obj.constructor.name) .toEqual('GObjectImplementingGObjectInterface'); }); it('can have its required function implemented', function () { expect(() => { let obj = new GObjectImplementingGObjectInterface(); obj.requiredG(); }).not.toThrow(); }); it('must have its required function implemented', function () { const BadObject = GObject.registerClass({ Implements: [ AGObjectInterface ], Properties: { 'interface-prop': GObject.ParamSpec.override('interface-prop', AGObjectInterface) } }, class BadObject extends GObject.Object {}); expect(() => new BadObject().requiredG()) .toThrowError(GObject.NotImplementedError); }); it("doesn't have to have its optional function implemented", function () { let obj; expect(() => { obj = new MinimalImplementationOfAGObjectInterface(); }) .not.toThrow(); expect(obj instanceof AGObjectInterface).toBeTruthy(); }); it('can have its optional function deferred to by the implementation', function () { let obj = new MinimalImplementationOfAGObjectInterface(); expect(obj.optionalG()).toEqual('AGObjectInterface.optionalG()'); }); it('can have its function chained up to', function () { let obj = new GObjectImplementingGObjectInterface(); expect(obj.optionalG()).toEqual('AGObjectInterface.optionalG()'); }); it('can require another interface', function () { let obj; expect(() => { obj = new ImplementationOfTwoInterfaces(); }).not.toThrow(); expect(obj instanceof AGObjectInterface).toBeTruthy(); expect(obj instanceof InterfaceRequiringGObjectInterface).toBeTruthy(); }); it('can chain up to another interface', function () { let obj = new ImplementationOfTwoInterfaces(); expect(obj.optionalG()) .toEqual('InterfaceRequiringGObjectInterface.optionalG()\nAGObjectInterface.optionalG()'); }); it("defers to the last interface's optional function", function () { const MinimalImplementationOfTwoInterfaces = GObject.registerClass({ Implements: [ AGObjectInterface, InterfaceRequiringGObjectInterface ], Properties: { 'interface-prop': GObject.ParamSpec.override('interface-prop', AGObjectInterface) }, }, class MinimalImplementationOfTwoInterfaces extends GObject.Object { requiredG() {} }); let obj = new MinimalImplementationOfTwoInterfaces(); expect(obj.optionalG()) .toEqual('InterfaceRequiringGObjectInterface.optionalG()\nAGObjectInterface.optionalG()'); }); it('must be implemented by a class that implements all required interfaces', function () { expect(() => GObject.registerClass({ Implements: [ InterfaceRequiringGObjectInterface ], }, class BadObject { required() {} })).toThrow(); }); it('must be implemented by a class that implements required interfaces in correct order', function () { expect(() => GObject.registerClass({ Implements: [ InterfaceRequiringGObjectInterface, AGObjectInterface ], }, class BadObject { required() {} })).toThrow(); }); it('can require an interface from C', function () { const InitableInterface = GObject.registerClass({ Requires: [ GObject.Object, Gio.Initable ] }, class InitableInterface extends GObject.Interface {}); expect(() => GObject.registerClass({ Implements: [ InitableInterface ], }, class BadObject {})).toThrow(); }); it('can define signals on the implementing class', function () { function quitLoop() { Mainloop.quit('signal'); } let obj = new GObjectImplementingGObjectInterface(); let interfaceSignalSpy = jasmine.createSpy('interfaceSignalSpy') .and.callFake(quitLoop); let classSignalSpy = jasmine.createSpy('classSignalSpy') .and.callFake(quitLoop); obj.connect('interface-signal', interfaceSignalSpy); obj.connect('class-signal', classSignalSpy); GLib.idle_add(GLib.PRIORITY_DEFAULT, () => { obj.emit('interface-signal'); return GLib.SOURCE_REMOVE; }); Mainloop.run('signal'); GLib.idle_add(GLib.PRIORITY_DEFAULT, () => { obj.emit('class-signal'); return GLib.SOURCE_REMOVE; }); Mainloop.run('signal'); expect(interfaceSignalSpy).toHaveBeenCalled(); expect(classSignalSpy).toHaveBeenCalled(); }); it('can define properties on the implementing class', function () { let obj = new GObjectImplementingGObjectInterface(); expect(obj.interface_prop).toEqual('foobar'); expect(obj.class_prop).toEqual('meh'); }); it('must have its properties overridden', function () { // Failing to override an interface property doesn't raise an error but // instead logs a critical warning. GLib.test_expect_message('GLib-GObject', GLib.LogLevelFlags.LEVEL_CRITICAL, "Object class * doesn't implement property 'interface-prop' from " + "interface 'ArbitraryGTypeName'"); GObject.registerClass({ Implements: [ AGObjectInterface ], }, class MyNaughtyObject extends GObject.Object { requiredG() {} }); // g_test_assert_expected_messages() is a macro, not introspectable GLib.test_assert_expected_messages_internal('Gjs', 'testGObjectInterface.js', 253, 'testGObjectMustOverrideInterfaceProperties'); }); it('can be implemented by a class as well as its parent class', function () { const SubObject = GObject.registerClass( class SubObject extends GObjectImplementingGObjectInterface {}); let obj = new SubObject(); expect(obj instanceof AGObjectInterface).toBeTruthy(); expect(obj.interface_prop).toEqual('foobar'); // override not needed }); it('can be reimplemented by a subclass of a class that already implements it', function () { const SubImplementer = GObject.registerClass({ Implements: [ AGObjectInterface ], }, class SubImplementer extends GObjectImplementingGObjectInterface {}); let obj = new SubImplementer(); expect(obj instanceof AGObjectInterface).toBeTruthy(); expect(obj.interface_prop).toEqual('foobar'); // override not needed }); }); cjs-4.4.0/installed-tests/js/testGTypeClass.js000066400000000000000000000043041356375132300213160ustar00rootroot00000000000000// We use Gio to have some objects that we know exist const Gio = imports.gi.Gio; const GObject = imports.gi.GObject; describe('Looking up param specs', function () { let p1, p2; beforeEach(function () { let find_property = GObject.Object.find_property; p1 = find_property.call(Gio.ThemedIcon, 'name'); p2 = find_property.call(Gio.SimpleAction, 'enabled'); }); it('works', function () { expect(p1 instanceof GObject.ParamSpec).toBeTruthy(); expect(p2 instanceof GObject.ParamSpec).toBeTruthy(); }); it('gives the correct name', function () { expect(p1.name).toEqual('name'); expect(p2.name).toEqual('enabled'); }); it('gives the default value if present', function () { expect(p2.default_value).toBeTruthy(); }); }); describe('GType object', function () { it('has a name', function () { expect(GObject.TYPE_NONE.name).toEqual('void'); expect(GObject.TYPE_STRING.name).toEqual('gchararray'); }); it('has a read-only name', function () { try { GObject.TYPE_STRING.name = 'foo'; } catch (e) { } expect(GObject.TYPE_STRING.name).toEqual('gchararray'); }); it('has an undeletable name', function () { try { delete GObject.TYPE_STRING.name; } catch (e) { } expect(GObject.TYPE_STRING.name).toEqual('gchararray'); }); it('has a string representation', function () { expect(GObject.TYPE_NONE.toString()).toEqual("[object GType for 'void']"); expect(GObject.TYPE_STRING.toString()).toEqual("[object GType for 'gchararray']"); }); }); describe('GType marshalling', function () { it('marshals the invalid GType object into JS null', function () { expect(GObject.type_from_name('NonexistentType')).toBeNull(); expect(GObject.type_parent(GObject.TYPE_STRING)).toBeNull(); }); }); describe('GType prototype object', function () { it('has no name', function () { expect(GIRepositoryGType.name).toBeNull(); }); it('has a string representation', function () { expect(GIRepositoryGType.toString()).toEqual('[object GType prototype]'); }); }); cjs-4.4.0/installed-tests/js/testGettext.js000066400000000000000000000010671356375132300207270ustar00rootroot00000000000000// -*- mode: js; indent-tabs-mode: nil -*- const Gettext = imports.gettext; describe('Gettext module', function () { // We don't actually want to mess with the locale, so just use setlocale's // query mode. We also don't want to make this test locale-dependent, so // just assert that it returns a string with at least length 1 (the shortest // locale is "C".) it('setlocale returns a locale', function () { let locale = Gettext.setlocale(Gettext.LocaleCategory.ALL, null); expect(locale.length).not.toBeLessThan(1); }); }); cjs-4.4.0/installed-tests/js/testGio.js000066400000000000000000000011341356375132300200140ustar00rootroot00000000000000const Gio = imports.gi.Gio; const GObject = imports.gi.GObject; const Foo = GObject.registerClass(class Foo extends GObject.Object { _init(value) { super._init(); this.value = value; } }); describe('ListStore iterator', function () { let list; beforeEach(function () { list = new Gio.ListStore({item_type: Foo}); for (let i = 0; i < 100; i++) { list.append(new Foo(i)); } }); it('ListStore iterates', function () { let i = 0; for (let f of list) { expect(f.value).toBe(i++); } }); });cjs-4.4.0/installed-tests/js/testGtk.js000077500000000000000000000147711356375132300200410ustar00rootroot00000000000000imports.gi.versions.Gtk = '3.0'; const ByteArray = imports.byteArray; const {GLib, GObject, Gtk} = imports.gi; const System = imports.system; // This is ugly here, but usually it would be in a resource const template = ' \ \ \ '; const MyComplexGtkSubclass = GObject.registerClass({ Template: ByteArray.fromString(template), Children: ['label-child', 'label-child2'], InternalChildren: ['internal-label-child'], CssName: 'complex-subclass', }, class MyComplexGtkSubclass extends Gtk.Grid { templateCallback(widget) { this.callbackEmittedBy = widget; } }); // Sadly, putting this in the body of the class will prevent calling // get_template_child, since MyComplexGtkSubclass will be bound to the ES6 // class name without the GObject goodies in it MyComplexGtkSubclass.prototype.testChildrenExist = function() { this._internalLabel = this.get_template_child(MyComplexGtkSubclass, 'label-child'); expect(this._internalLabel).toEqual(jasmine.anything()); expect(this.label_child2).toEqual(jasmine.anything()); expect(this._internal_label_child).toEqual(jasmine.anything()); }; const MyComplexGtkSubclassFromResource = GObject.registerClass({ Template: 'resource:///org/gjs/jsunit/complex.ui', Children: ['label-child', 'label-child2'], InternalChildren: ['internal-label-child'], }, class MyComplexGtkSubclassFromResource extends Gtk.Grid { testChildrenExist() { expect(this.label_child).toEqual(jasmine.anything()); expect(this.label_child2).toEqual(jasmine.anything()); expect(this._internal_label_child).toEqual(jasmine.anything()); } templateCallback(widget) { this.callbackEmittedBy = widget; } }); const SubclassSubclass = GObject.registerClass( class SubclassSubclass extends MyComplexGtkSubclass {}); function validateTemplate(description, ClassName, pending=false) { let suite = pending ? xdescribe : describe; suite(description, function () { let win, content; beforeEach(function () { win = new Gtk.Window({ type: Gtk.WindowType.TOPLEVEL }); content = new ClassName(); content.label_child.emit('grab-focus'); win.add(content); }); it('sets up internal and public template children', function () { content.testChildrenExist(); }); it('sets up public template children with the correct widgets', function () { expect(content.label_child.get_label()).toEqual('Complex!'); expect(content.label_child2.get_label()).toEqual('Complex as well!'); }); it('sets up internal template children with the correct widgets', function () { expect(content._internal_label_child.get_label()) .toEqual('Complex and internal!'); }); it('connects template callbacks to the correct handler', function () { expect(content.callbackEmittedBy).toBe(content.label_child); }); afterEach(function () { win.destroy(); }); }); } describe('Gtk overrides', function () { beforeAll(function () { Gtk.init(null); }); validateTemplate('UI template', MyComplexGtkSubclass); validateTemplate('UI template from resource', MyComplexGtkSubclassFromResource); validateTemplate('Class inheriting from template class', SubclassSubclass, true); it('sets CSS names on classes', function () { expect(Gtk.Widget.get_css_name.call(MyComplexGtkSubclass)).toEqual('complex-subclass'); }); it('avoid crashing when GTK vfuncs are called in garbage collection', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, '*during garbage collection*'); GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, '*destroy*'); let BadLabel = GObject.registerClass(class BadLabel extends Gtk.Label { vfunc_destroy() {} }); let w = new Gtk.Window(); w.add(new BadLabel()); w.destroy(); System.gc(); GLib.test_assert_expected_messages_internal('Gjs', 'testGtk.js', 0, 'Gtk overrides avoid crashing and print a stack trace'); }); it('accepts string in place of GdkAtom', function () { expect(() => Gtk.Clipboard.get(1)).toThrow(); expect(() => Gtk.Clipboard.get(true)).toThrow(); expect(() => Gtk.Clipboard.get(() => undefined)).toThrow(); const clipboard = Gtk.Clipboard.get('CLIPBOARD'); const primary = Gtk.Clipboard.get('PRIMARY'); const anotherClipboard = Gtk.Clipboard.get('CLIPBOARD'); expect(clipboard).toBeTruthy(); expect(primary).toBeTruthy(); expect(clipboard).not.toBe(primary); expect(clipboard).toBe(anotherClipboard); }); it('accepts null in place of GdkAtom as GDK_NONE', function () { /** * When you pass GDK_NONE (an atom, interned from the 'NONE' string) * to Gtk.Clipboard.get(), it throws an error, mentioning null in * its message. */ expect(() => Gtk.Clipboard.get('NONE')).toThrowError(/null/); /** * Null is converted to GDK_NONE, so you get the same message. If you * know an API function that accepts GDK_NONE without throwing, and * returns something different when passed another atom, consider * adding a less confusing example here. */ expect(() => Gtk.Clipboard.get(null)).toThrowError(/null/); }); }); cjs-4.4.0/installed-tests/js/testImporter.js000066400000000000000000000202061356375132300211000ustar00rootroot00000000000000describe('GI importer', function () { it('can import GI modules', function () { var GLib = imports.gi.GLib; expect(GLib.MAJOR_VERSION).toEqual(2); }); describe('on failure', function () { // For these tests, we provide special overrides files to sabotage the // import, at the path resource:///org/gjs/jsunit/modules/overrides. let oldSearchPath; beforeAll(function () { oldSearchPath = imports.overrides.searchPath.slice(); imports.overrides.searchPath = ['resource:///org/gjs/jsunit/modules/overrides']; }); afterAll(function () { imports.overrides.searchPath = oldSearchPath; }); it("throws an exception when the overrides file can't be imported", function () { expect(() => imports.gi.WarnLib).toThrowError(SyntaxError); }); it('throws an exception when the overrides import throws one', function () { expect(() => imports.gi.GIMarshallingTests).toThrow('💩'); }); it('throws an exception when the overrides _init throws one', function () { expect(() => imports.gi.Regress).toThrow('💩'); }); it("throws an exception when the overrides _init isn't a function", function () { expect(() => imports.gi.Gio).toThrowError(/_init/); }); }); }); describe('Importer', function () { let oldSearchPath; let foobar, subA, subB, subFoobar; beforeAll(function () { oldSearchPath = imports.searchPath.slice(); imports.searchPath = ['resource:///org/gjs/jsunit/modules']; foobar = imports.foobar; subA = imports.subA; subB = imports.subA.subB; subFoobar = subB.foobar; }); afterAll(function () { imports.searchPath = oldSearchPath; }); it('exists', function () { expect(imports).toBeDefined(); }); it('has a toString representation', function () { expect(imports.toString()).toEqual('[GjsFileImporter root]'); expect(subA.toString()).toEqual('[GjsFileImporter subA]'); }); it('throws an import error when trying to import a nonexistent module', function () { expect(() => imports.nonexistentModuleName) .toThrow(jasmine.objectContaining({ name: 'ImportError' })); }); it('throws an error when evaluating the module file throws an error', function () { expect(() => imports.alwaysThrows).toThrow(); // Try again to make sure that we properly discarded the module object expect(() => imports.alwaysThrows).toThrow(); }); it('can import a module', function () { expect(foobar).toBeDefined(); expect(foobar.foo).toEqual('This is foo'); expect(foobar.bar).toEqual('This is bar'); }); it('can import a module with a toString property', function () { expect(foobar.testToString('foo')).toEqual('foo'); }); it('makes deleting the import a no-op', function () { expect(delete imports.foobar).toBeFalsy(); expect(imports.foobar).toBe(foobar); }); it('gives the same object when importing a second time', function () { foobar.somethingElse = 'Should remain'; const foobar2 = imports.foobar; expect(foobar2.somethingElse).toEqual('Should remain'); }); it('can import a submodule', function () { expect(subB).toBeDefined(); expect(subFoobar).toBeDefined(); expect(subFoobar.foo).toEqual('This is foo'); expect(subFoobar.bar).toEqual('This is bar'); }); it('imports modules with a toString representation', function () { expect(Object.prototype.toString.call(foobar)) .toEqual('[object GjsModule foobar]'); expect(subFoobar.toString()) .toEqual('[object GjsModule subA.subB.foobar]'); }); it('does not share the same object for a module on a different path', function () { foobar.somethingElse = 'Should remain'; expect(subFoobar.somethingElse).not.toBeDefined(); }); it('gives the same object when importing a submodule a second time', function () { subFoobar.someProp = 'Should be here'; const subFoobar2 = imports.subA.subB.foobar; expect(subFoobar2.someProp).toEqual('Should be here'); }); it('has no meta properties on the toplevel importer', function () { expect(imports.__moduleName__).toBeNull(); expect(imports.__parentModule__).toBeNull(); }); it('sets the names of imported modules', function () { expect(subA.__moduleName__).toEqual('subA'); expect(subB.__moduleName__).toEqual('subB'); }); it('gives a module the importer object as parent module', function () { expect(subA.__parentModule__).toBe(imports); }); it('gives a submodule the module as parent module', function () { expect(subB.__parentModule__).toBe(subA); }); // We want to check that the copy of the 'a' module imported directly // is the same as the copy that 'b' imports, and that we don't have two // copies because of the A imports B imports A loop. it('does not make a separate copy of a module imported in two places', function () { let A = imports.mutualImport.a; A.incrementCount(); expect(A.getCount()).toEqual(1); expect(A.getCountViaB()).toEqual(1); }); it('evaluates an __init__.js file in an imported directory', function () { expect(subB.testImporterFunction()).toEqual('__init__ function tested'); }); it('accesses a class defined in an __init__.js file', function () { let o = new subB.ImporterClass(); expect(o).not.toBeNull(); expect(o.testMethod()).toEqual('__init__ class tested'); }); it('can import a file encoded in UTF-8', function () { const ModUnicode = imports.modunicode; expect(ModUnicode.uval).toEqual('const \u2665 utf8'); }); describe("properties defined in the module's lexical scope", function () { let LexicalScope; beforeAll(function () { window.expectMe = true; LexicalScope = imports.lexicalScope; }); /*it('will log a compatibility warning when accessed', function () { const GLib = imports.gi.GLib; GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, "Some code accessed the property 'b' on the module " + "'lexicalScope'.*"); GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, "Some code accessed the property 'c' on the module " + "'lexicalScope'.*"); void LexicalScope.b; void LexicalScope.c; // g_test_assert_expected_messages() is a macro, not introspectable GLib.test_assert_expected_messages_internal('Gjs', 'testImporter.js', 179, ''); });*/ it('can be accessed', function () { expect(LexicalScope.a).toEqual(1); expect(LexicalScope.b).toEqual(2); expect(LexicalScope.c).toEqual(3); expect(LexicalScope.d).toEqual(4); }); it('does not leak module properties into the global scope', function () { expect(window.d).not.toBeDefined(); }); }); describe('enumerating modules', function () { let keys; beforeEach(function () { keys = []; for (let key in imports) keys.push(key); }); it('gets all of them', function () { expect(keys).toContain('foobar', 'subA', 'mutualImport', 'modunicode'); }); it('includes modules that throw on import', function () { expect(keys).toContain('alwaysThrows'); }); it('does not include meta properties', function () { expect(keys).not.toContain('__parentModule__'); expect(keys).not.toContain('__moduleName__'); expect(keys).not.toContain('searchPath'); }); }); it("doesn't crash when resolving a non-string property", function () { expect(imports[0]).not.toBeDefined(); expect(imports.foobar[0]).not.toBeDefined(); }); }); cjs-4.4.0/installed-tests/js/testLang.js000066400000000000000000000055431356375132300201670ustar00rootroot00000000000000// tests for imports.lang module // except for Lang.Class and Lang.Interface, which are tested in testLegacyClass const Lang = imports.lang; describe('Lang module', function () { it('counts properties with Lang.countProperties()', function () { var foo = { 'a' : 10, 'b' : 11 }; expect(Lang.countProperties(foo)).toEqual(2); }); it('copies properties from one object to another with Lang.copyProperties()', function () { var foo = { 'a' : 10, 'b' : 11 }; var bar = {}; Lang.copyProperties(foo, bar); expect(bar).toEqual(foo); }); it('copies properties without an underscore with Lang.copyPublicProperties()', function () { var foo = { 'a' : 10, 'b' : 11, '_c' : 12 }; var bar = {}; Lang.copyPublicProperties(foo, bar); expect(bar).toEqual({ 'a': 10, 'b': 11 }); }); it('copies property getters and setters', function () { var foo = { 'a' : 10, 'b' : 11, get c() { return this.a; }, set c(n) { this.a = n; } }; var bar = {}; Lang.copyProperties(foo, bar); expect(bar.__lookupGetter__('c')).not.toBeNull(); expect(bar.__lookupSetter__('c')).not.toBeNull(); // this should return the value of 'a' expect(bar.c).toEqual(10); // this should set 'a' value bar.c = 13; expect(bar.a).toEqual(13); }); describe('bind()', function () { let o; beforeEach(function () { o = { callback() { return true; } }; spyOn(o, 'callback').and.callThrough(); }); it('calls the bound function with the supplied this-object', function () { let callback = Lang.bind(o, o.callback); callback(); expect(o.callback.calls.mostRecent()).toEqual({ object: o, args: [], returnValue: true, }); }); it('throws an error when no function supplied', function () { expect(() => Lang.bind(o, undefined)).toThrow(); }); it('throws an error when this-object undefined', function () { expect(() => Lang.bind(undefined, function () {})).toThrow(); }); it('supplies extra arguments to the function', function () { let callback = Lang.bind(o, o.callback, 42, 1138); callback(); expect(o.callback).toHaveBeenCalledWith(42, 1138); }); it('appends the extra arguments to any arguments passed', function () { let callback = Lang.bind(o, o.callback, 42, 1138); callback(1, 2, 3); expect(o.callback).toHaveBeenCalledWith(1, 2, 3, 42, 1138); }); }); }); cjs-4.4.0/installed-tests/js/testLegacyClass.js000066400000000000000000000556351356375132300215070ustar00rootroot00000000000000// -*- mode: js; indent-tabs-mode: nil -*- const Lang = imports.lang; const NormalClass = new Lang.Class({ Name: 'NormalClass', _init: function() { this.one = 1; } }); let Subclassed = []; const MetaClass = new Lang.Class({ Name: 'MetaClass', Extends: Lang.Class, _init: function(params) { Subclassed.push(params.Name); this.parent(params); if (params.Extended) { this.prototype.dynamic_method = this.wrapFunction('dynamic_method', function() { return 73; }); this.DYNAMIC_CONSTANT = 2; } } }); const CustomMetaOne = new MetaClass({ Name: 'CustomMetaOne', Extends: NormalClass, Extended: false, _init: function() { this.parent(); this.two = 2; } }); const CustomMetaTwo = new MetaClass({ Name: 'CustomMetaTwo', Extends: NormalClass, Extended: true, _init: function() { this.parent(); this.two = 2; } }); // This should inherit CustomMeta, even though // we use Lang.Class const CustomMetaSubclass = new Lang.Class({ Name: 'CustomMetaSubclass', Extends: CustomMetaOne, Extended: true, _init: function() { this.parent(); this.three = 3; } }); describe('A metaclass', function () { it('has its constructor called each time a class is created with it', function () { expect(Subclassed).toEqual(['CustomMetaOne', 'CustomMetaTwo', 'CustomMetaSubclass']); }); it('is an instance of Lang.Class', function () { expect(NormalClass instanceof Lang.Class).toBeTruthy(); expect(MetaClass instanceof Lang.Class).toBeTruthy(); }); it('produces instances that are instances of itself and Lang.Class', function () { expect(CustomMetaOne instanceof Lang.Class).toBeTruthy(); expect(CustomMetaOne instanceof MetaClass).toBeTruthy(); }); it('can dynamically define properties in its constructor', function () { expect(CustomMetaTwo.DYNAMIC_CONSTANT).toEqual(2); expect(CustomMetaOne.DYNAMIC_CONSTANT).not.toBeDefined(); }); describe('instance', function () { let instanceOne, instanceTwo; beforeEach(function () { instanceOne = new CustomMetaOne(); instanceTwo = new CustomMetaTwo(); }); it('gets all the properties from its class and metaclass', function () { expect(instanceOne).toEqual(jasmine.objectContaining({ one: 1, two: 2 })); expect(instanceTwo).toEqual(jasmine.objectContaining({ one: 1, two: 2 })); }); it('gets dynamically defined properties from metaclass', function () { expect(() => instanceOne.dynamic_method()).toThrow(); expect(instanceTwo.dynamic_method()).toEqual(73); }); }); it('can be instantiated with Lang.Class but still get the appropriate metaclass', function () { expect(CustomMetaSubclass instanceof MetaClass).toBeTruthy(); expect(CustomMetaSubclass.DYNAMIC_CONSTANT).toEqual(2); let instance = new CustomMetaSubclass(); expect(instance).toEqual(jasmine.objectContaining({ one: 1, two: 2, three: 3 })); expect(instance.dynamic_method()).toEqual(73); }); it('can be detected with Lang.getMetaClass', function () { expect(Lang.getMetaClass({ Extends: CustomMetaOne, })).toBe(MetaClass); }); }); const MagicBase = new Lang.Class({ Name: 'MagicBase', _init: function(a, buffer) { if (buffer) buffer.push(a); this.a = a; }, foo: function(a, buffer) { buffer.push(a); return a * 3; }, bar: function(a) { return a * 5; } }); const Magic = new Lang.Class({ Name: 'Magic', Extends: MagicBase, _init: function(a, b, buffer) { this.parent(a, buffer); if (buffer) buffer.push(b); this.b = b; }, foo: function(a, b, buffer) { let val = this.parent(a, buffer); buffer.push(b); return val * 2; }, bar: function(a, buffer) { this.foo(a, 2*a, buffer); return this.parent(a); } }); const Accessor = new Lang.Class({ Name: 'AccessorMagic', _init: function(val) { this._val = val; }, get value() { return this._val; }, set value(val) { if (val != 42) throw TypeError('Value is not a magic number'); this._val = val; } }); const AbstractBase = new Lang.Class({ Name: 'AbstractBase', Abstract: true, _init: function() { this.foo = 42; } }); describe('Class framework', function () { it('calls _init constructors', function () { let newMagic = new MagicBase('A'); expect(newMagic.a).toEqual('A'); }); it('calls parent constructors', function () { let buffer = []; let newMagic = new Magic('a', 'b', buffer); expect(buffer).toEqual(['a', 'b']); buffer = []; let val = newMagic.foo(10, 20, buffer); expect(buffer).toEqual([10, 20]); expect(val).toEqual(10 * 6); }); it('sets the right constructor properties', function () { expect(Magic.prototype.constructor).toBe(Magic); let newMagic = new Magic(); expect(newMagic.constructor).toBe(Magic); }); it('sets up instanceof correctly', function () { let newMagic = new Magic(); expect(newMagic instanceof Magic).toBeTruthy(); expect(newMagic instanceof MagicBase).toBeTruthy(); }); it('has a name', function () { expect(Magic.name).toEqual('Magic'); }); it('reports a sensible value for toString()', function () { let newMagic = new MagicBase(); expect(newMagic.toString()).toEqual('[object MagicBase]'); }); it('allows overriding toString()', function () { const ToStringOverride = new Lang.Class({ Name: 'ToStringOverride', toString: function() { let oldToString = this.parent(); return oldToString + '; hello'; } }); let override = new ToStringOverride(); expect(override.toString()).toEqual('[object ToStringOverride]; hello'); }); it('is not configurable', function () { let newMagic = new MagicBase(); delete newMagic.foo; expect(newMagic.foo).toBeDefined(); }); it('allows accessors for properties', function () { let newAccessor = new Accessor(11); expect(newAccessor.value).toEqual(11); expect(() => newAccessor.value = 12).toThrow(); newAccessor.value = 42; expect(newAccessor.value).toEqual(42); }); it('raises an exception when creating an abstract class', function () { expect(() => new AbstractBase()).toThrow(); }); it('inherits properties from abstract base classes', function () { const AbstractImpl = new Lang.Class({ Name: 'AbstractImpl', Extends: AbstractBase, _init: function() { this.parent(); this.bar = 42; } }); let newAbstract = new AbstractImpl(); expect(newAbstract.foo).toEqual(42); expect(newAbstract.bar).toEqual(42); }); it('inherits constructors from abstract base classes', function () { const AbstractImpl = new Lang.Class({ Name: 'AbstractImpl', Extends: AbstractBase, }); let newAbstract = new AbstractImpl(); expect(newAbstract.foo).toEqual(42); }); it('allows ES6 classes to inherit from abstract base classes', function() { class AbstractImpl extends AbstractBase {}; let newAbstract = new AbstractImpl(); expect(newAbstract.foo).toEqual(42); }); it('lets methods call other methods without clobbering __caller__', function () { let newMagic = new Magic(); let buffer = []; let res = newMagic.bar(10, buffer); expect(buffer).toEqual([10, 20]); expect(res).toEqual(50); }); it('allows custom return values from constructors', function () { const CustomConstruct = new Lang.Class({ Name: 'CustomConstruct', _construct: function(one, two) { return [one, two]; } }); let instance = new CustomConstruct(1, 2); expect(instance instanceof Array).toBeTruthy(); expect(instance instanceof CustomConstruct).toBeFalsy(); expect(instance).toEqual([1, 2]); }); it('allows symbol-named methods', function () { const SymbolClass = new Lang.Class({ Name: 'SymbolClass', *[Symbol.iterator]() { yield* [1, 2, 3]; }, }); let instance = new SymbolClass(); expect([...instance]).toEqual([1, 2, 3]); }); }); const AnInterface = new Lang.Interface({ Name: 'AnInterface', required: Lang.Interface.UNIMPLEMENTED, optional: function () { return 'AnInterface.optional()'; }, optionalGeneric: function () { return 'AnInterface.optionalGeneric()'; }, argumentGeneric: function (arg) { return 'AnInterface.argumentGeneric(' + arg + ')'; }, usesThis: function () { return this._interfacePrivateMethod(); }, _interfacePrivateMethod: function () { return 'interface private method'; }, get some_prop() { return 'AnInterface.some_prop getter'; }, set some_prop(value) { this.some_prop_setter_called = true; } }); const InterfaceRequiringOtherInterface = new Lang.Interface({ Name: 'InterfaceRequiringOtherInterface', Requires: [ AnInterface ], optional: function () { return 'InterfaceRequiringOtherInterface.optional()\n' + AnInterface.prototype.optional.apply(this, arguments); }, optionalGeneric: function () { return 'InterfaceRequiringOtherInterface.optionalGeneric()\n' + AnInterface.optionalGeneric(this); } }); const ObjectImplementingAnInterface = new Lang.Class({ Name: 'ObjectImplementingAnInterface', Implements: [ AnInterface ], _init: function () { this.parent(); }, required: function () {}, optional: function () { return AnInterface.prototype.optional.apply(this, arguments); }, optionalGeneric: function () { return AnInterface.optionalGeneric(this); }, argumentGeneric: function (arg) { return AnInterface.argumentGeneric(this, arg + ' (hello from class)'); } }); const InterfaceRequiringClassAndInterface = new Lang.Interface({ Name: 'InterfaceRequiringClassAndInterface', Requires: [ ObjectImplementingAnInterface, InterfaceRequiringOtherInterface ], }); const MinimalImplementationOfAnInterface = new Lang.Class({ Name: 'MinimalImplementationOfAnInterface', Implements: [ AnInterface ], required: function () {} }); const ImplementationOfTwoInterfaces = new Lang.Class({ Name: 'ImplementationOfTwoInterfaces', Implements: [ AnInterface, InterfaceRequiringOtherInterface ], required: function () {}, optional: function () { return InterfaceRequiringOtherInterface.prototype.optional.apply(this, arguments); }, optionalGeneric: function () { return InterfaceRequiringOtherInterface.optionalGeneric(this); } }); describe('An interface', function () { it('is an instance of Lang.Interface', function () { expect(AnInterface instanceof Lang.Interface).toBeTruthy(); expect(InterfaceRequiringOtherInterface instanceof Lang.Interface).toBeTruthy(); }); it('has a name', function () { expect(AnInterface.name).toEqual('AnInterface'); }); it('cannot be instantiated', function () { expect(() => new AnInterface()).toThrow(); }); it('can be implemented by a class', function () { let obj; expect(() => { obj = new ObjectImplementingAnInterface(); }).not.toThrow(); expect(obj.constructor.implements(AnInterface)).toBeTruthy(); }); it("can be implemented by a class's superclass", function () { const ChildWhoseParentImplementsAnInterface = new Lang.Class({ Name: 'ChildWhoseParentImplementsAnInterface', Extends: ObjectImplementingAnInterface }); let obj = new ChildWhoseParentImplementsAnInterface(); expect(obj.constructor.implements(AnInterface)).toBeTruthy(); }); it("doesn't disturb a class's constructor", function () { let obj = new ObjectImplementingAnInterface(); expect(obj.constructor).toEqual(ObjectImplementingAnInterface); }); it('can have its required method implemented', function () { let implementer = new ObjectImplementingAnInterface(); expect(() => implementer.required()).not.toThrow(); }); it('must have a name', function () { expect(() => new Lang.Interface({ required: Lang.Interface.UNIMPLEMENTED, })).toThrow(); }); it('must have its required methods implemented', function () { expect(() => new Lang.Class({ Name: 'MyBadObject', Implements: [AnInterface], })).toThrow(); }); it('does not have to have its optional methods implemented', function () { let obj; expect(() => obj = new MinimalImplementationOfAnInterface()).not.toThrow(); expect(obj.constructor.implements(AnInterface)).toBeTruthy(); }); it('can have its optional method deferred to by the implementation', function () { let obj = new MinimalImplementationOfAnInterface(); expect(obj.optional()).toEqual('AnInterface.optional()'); }); it('can be chained up to by a class', function () { let obj = new ObjectImplementingAnInterface(); expect(obj.optional()).toEqual('AnInterface.optional()'); }); it('can include arguments when being chained up to by a class', function () { let obj = new ObjectImplementingAnInterface(); expect(obj.argumentGeneric('arg')) .toEqual('AnInterface.argumentGeneric(arg (hello from class))'); }); it('can have its property getter deferred to', function () { let obj = new ObjectImplementingAnInterface(); expect(obj.some_prop).toEqual('AnInterface.some_prop getter'); }); it('can have its property setter deferred to', function () { let obj = new ObjectImplementingAnInterface(); obj.some_prop = 'foobar'; expect(obj.some_prop_setter_called).toBeTruthy(); }); it('can have its property getter overridden', function () { const ObjectWithGetter = new Lang.Class({ Name: 'ObjectWithGetter', Implements: [ AnInterface ], required: function () {}, get some_prop() { return 'ObjectWithGetter.some_prop getter'; } }); let obj = new ObjectWithGetter(); expect(obj.some_prop).toEqual('ObjectWithGetter.some_prop getter'); }); it('can have its property setter overridden', function () { const ObjectWithSetter = new Lang.Class({ Name: 'ObjectWithSetter', Implements: [ AnInterface ], required: function () {}, set some_prop(value) { /* setter without getter */// jshint ignore:line this.overridden_some_prop_setter_called = true; } }); let obj = new ObjectWithSetter(); obj.some_prop = 'foobar'; expect(obj.overridden_some_prop_setter_called).toBeTruthy(); expect(obj.some_prop_setter_called).not.toBeDefined(); }); it('can require another interface', function () { let obj; expect(() => { obj = new ImplementationOfTwoInterfaces(); }).not.toThrow(); expect(obj.constructor.implements(AnInterface)).toBeTruthy(); expect(obj.constructor.implements(InterfaceRequiringOtherInterface)).toBeTruthy(); }); it('can have empty requires', function () { expect(() => new Lang.Interface({ Name: 'InterfaceWithEmptyRequires', Requires: [] })).not.toThrow(); }); it('can chain up to another interface', function () { let obj = new ImplementationOfTwoInterfaces(); expect(obj.optional()) .toEqual('InterfaceRequiringOtherInterface.optional()\nAnInterface.optional()'); }); it('can be chained up to with a generic', function () { let obj = new ObjectImplementingAnInterface(); expect(obj.optionalGeneric()).toEqual('AnInterface.optionalGeneric()'); }); it('can chain up to another interface with a generic', function () { let obj = new ImplementationOfTwoInterfaces(); expect(obj.optionalGeneric()) .toEqual('InterfaceRequiringOtherInterface.optionalGeneric()\nAnInterface.optionalGeneric()'); }); it('has its optional function defer to that of the last interface', function () { const MinimalImplementationOfTwoInterfaces = new Lang.Class({ Name: 'MinimalImplementationOfTwoInterfaces', Implements: [ AnInterface, InterfaceRequiringOtherInterface ], required: function () {} }); let obj = new MinimalImplementationOfTwoInterfaces(); expect(obj.optionalGeneric()) .toEqual('InterfaceRequiringOtherInterface.optionalGeneric()\nAnInterface.optionalGeneric()'); }); it('must have all its required interfaces implemented', function () { expect(() => new Lang.Class({ Name: 'ObjectWithNotEnoughInterfaces', Implements: [ InterfaceRequiringOtherInterface ], required: function () {} })).toThrow(); }); it('must have all its required interfaces implemented in the correct order', function () { expect(() => new Lang.Class({ Name: 'ObjectWithInterfacesInWrongOrder', Implements: [ InterfaceRequiringOtherInterface, AnInterface ], required: function () {} })).toThrow(); }); it('can have its implementation on a parent class', function () { let obj; expect(() => { const ObjectInheritingFromInterfaceImplementation = new Lang.Class({ Name: 'ObjectInheritingFromInterfaceImplementation', Extends: ObjectImplementingAnInterface, Implements: [ InterfaceRequiringOtherInterface ], }); obj = new ObjectInheritingFromInterfaceImplementation(); }).not.toThrow(); expect(obj.constructor.implements(AnInterface)).toBeTruthy(); expect(obj.constructor.implements(InterfaceRequiringOtherInterface)).toBeTruthy(); }); it('can require its implementor to be a subclass of some class', function () { let obj; expect(() => { const ObjectImplementingInterfaceRequiringParentObject = new Lang.Class({ Name: 'ObjectImplementingInterfaceRequiringParentObject', Extends: ObjectImplementingAnInterface, Implements: [ InterfaceRequiringOtherInterface, InterfaceRequiringClassAndInterface ] }); obj = new ObjectImplementingInterfaceRequiringParentObject(); }).not.toThrow(); expect(obj.constructor.implements(AnInterface)).toBeTruthy(); expect(obj.constructor.implements(InterfaceRequiringOtherInterface)).toBeTruthy(); expect(obj.constructor.implements(InterfaceRequiringClassAndInterface)).toBeTruthy(); }); it('must be implemented by an object which subclasses the required class', function () { expect(() => new Lang.Class({ Name: 'ObjectWithoutRequiredParent', Implements: [ AnInterface, InterfaceRequiringOtherInterface, InterfaceRequiringClassAndInterface ], required: function () {}, })).toThrow(); }); it('can have methods that call others of its methods', function () { let obj = new ObjectImplementingAnInterface(); expect(obj.usesThis()).toEqual('interface private method'); }); it('is implemented by a subclass of a class that implements it', function () { const SubObject = new Lang.Class({ Name: 'SubObject', Extends: ObjectImplementingAnInterface }); let obj = new SubObject(); expect(obj.constructor.implements(AnInterface)).toBeTruthy(); }); it('can be reimplemented by a subclass of a class that implements it', function () { const SubImplementer = new Lang.Class({ Name: 'SubImplementer', Extends: ObjectImplementingAnInterface, Implements: [ AnInterface ] }); let obj = new SubImplementer(); expect(obj.constructor.implements(AnInterface)).toBeTruthy(); expect(() => obj.required()).not.toThrow(); }); it('tells what it is with toString()', function () { expect(AnInterface.toString()).toEqual('[interface Interface for AnInterface]'); }); }); describe('ES6 class inheriting from Lang.Class', function () { let Shiny, Legacy; beforeEach(function () { Legacy = new Lang.Class({ Name: 'Legacy', _init(someval) { this.constructorCalledWith = someval; }, instanceMethod() {}, chainUpToMe() {}, overrideMe() {}, get property() { return this._property + 1; }, set property(value) { this._property = value - 2; }, }); Legacy.staticMethod = function () {}; spyOn(Legacy, 'staticMethod'); spyOn(Legacy.prototype, 'instanceMethod'); spyOn(Legacy.prototype, 'chainUpToMe'); spyOn(Legacy.prototype, 'overrideMe'); Shiny = class Shiny extends Legacy { constructor(someval) { super(someval); } chainUpToMe() { super.chainUpToMe(); } overrideMe() {} }; }); it('calls a static method on the parent class', function () { Shiny.staticMethod(); expect(Legacy.staticMethod).toHaveBeenCalled(); }); it('calls a method on the parent class', function () { let instance = new Shiny(); instance.instanceMethod(); expect(Legacy.prototype.instanceMethod).toHaveBeenCalled(); }); it("passes arguments to the parent class's constructor", function () { let instance = new Shiny(42); expect(instance.constructorCalledWith).toEqual(42); }); it('chains up to a method on the parent class', function () { let instance = new Shiny(); instance.chainUpToMe(); expect(Legacy.prototype.chainUpToMe).toHaveBeenCalled(); }); it('overrides a method on the parent class', function () { let instance = new Shiny(); instance.overrideMe(); expect(Legacy.prototype.overrideMe).not.toHaveBeenCalled(); }); it('sets and gets a property from the parent class', function () { let instance = new Shiny(); instance.property = 42; expect(instance.property).toEqual(41); }); }); cjs-4.4.0/installed-tests/js/testLegacyGObject.js000066400000000000000000000704501356375132300217470ustar00rootroot00000000000000// -*- mode: js; indent-tabs-mode: nil -*- imports.gi.versions.Gtk = '3.0'; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Gtk = imports.gi.Gtk; const Lang = imports.lang; const Mainloop = imports.mainloop; const MyObject = new GObject.Class({ Name: 'MyObject', Properties: { 'readwrite': GObject.ParamSpec.string('readwrite', 'ParamReadwrite', 'A read write parameter', GObject.ParamFlags.READWRITE, ''), 'readonly': GObject.ParamSpec.string('readonly', 'ParamReadonly', 'A readonly parameter', GObject.ParamFlags.READABLE, ''), 'construct': GObject.ParamSpec.string('construct', 'ParamConstructOnly', 'A readwrite construct-only parameter', GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, 'default'), }, Signals: { 'empty': { }, 'minimal': { param_types: [ GObject.TYPE_INT, GObject.TYPE_INT ] }, 'full': { flags: GObject.SignalFlags.RUN_LAST, accumulator: GObject.AccumulatorType.FIRST_WINS, return_type: GObject.TYPE_INT, param_types: [], }, 'run-last': { flags: GObject.SignalFlags.RUN_LAST }, 'detailed': { flags: GObject.SignalFlags.RUN_FIRST | GObject.SignalFlags.DETAILED, param_types: [ GObject.TYPE_STRING ], }, }, _init: function(props) { // check that it's safe to set properties before // chaining up (priv is NULL at this point, remember) this._readwrite = 'foo'; this._readonly = 'bar'; this._constructProp = null; this._constructCalled = false; this.parent(props); }, get readwrite() { return this._readwrite; }, set readwrite(val) { if (val == 'ignore') return; this._readwrite = val; }, get readonly() { return this._readonly; }, set readonly(val) { // this should never be called this._readonly = 'bogus'; }, get construct() { return this._constructProp; }, set construct(val) { // this should be called at most once if (this._constructCalled) throw Error('Construct-Only property set more than once'); this._constructProp = val; this._constructCalled = true; }, notify_prop: function() { this._readonly = 'changed'; this.notify('readonly'); }, emit_empty: function() { this.emit('empty'); }, emit_minimal: function(one, two) { this.emit('minimal', one, two); }, emit_full: function() { return this.emit('full'); }, emit_detailed: function() { this.emit('detailed::one'); this.emit('detailed::two'); }, emit_run_last: function(callback) { this._run_last_callback = callback; this.emit('run-last'); }, on_run_last: function() { this._run_last_callback(); }, on_empty: function() { this.empty_called = true; }, on_full: function() { this.full_default_handler_called = true; return 79; } }); const MyApplication = new Lang.Class({ Name: 'MyApplication', Extends: Gio.Application, Signals: { 'custom': { param_types: [ GObject.TYPE_INT ] } }, _init: function(params) { this.parent(params); }, emit_custom: function(n) { this.emit('custom', n); } }); const MyInitable = new Lang.Class({ Name: 'MyInitable', Extends: GObject.Object, Implements: [ Gio.Initable ], _init: function(params) { this.parent(params); this.inited = false; }, vfunc_init: function(cancellable) { // error? if (!(cancellable instanceof Gio.Cancellable)) throw 'Bad argument'; this.inited = true; } }); const Derived = new Lang.Class({ Name: 'Derived', Extends: MyObject, _init: function() { this.parent({ readwrite: 'yes' }); } }); const MyCustomInit = new Lang.Class({ Name: 'MyCustomInit', Extends: GObject.Object, _init: function() { this.foo = false; this.parent(); }, _instance_init: function() { this.foo = true; } }); describe('GObject class', function () { let myInstance; beforeEach(function () { myInstance = new MyObject(); }); it('constructs with default values for properties', function () { expect(myInstance.readwrite).toEqual('foo'); expect(myInstance.readonly).toEqual('bar'); expect(myInstance.construct).toEqual('default'); }); it('constructs with a hash of property values', function () { let myInstance2 = new MyObject({ readwrite: 'baz', construct: 'asdf' }); expect(myInstance2.readwrite).toEqual('baz'); expect(myInstance2.readonly).toEqual('bar'); expect(myInstance2.construct).toEqual('asdf'); }); const ui = ` baz quz `; it('constructs with property values from Gtk.Builder', function () { let builder = Gtk.Builder.new_from_string(ui, -1); let myInstance3 = builder.get_object('MyObject'); expect(myInstance3.readwrite).toEqual('baz'); expect(myInstance3.readonly).toEqual('bar'); expect(myInstance3.construct).toEqual('quz'); }); it('has a name', function () { expect(MyObject.name).toEqual('MyObject'); }); // the following would (should) cause a CRITICAL: // myInstance.readonly = 'val'; // myInstance.construct = 'val'; it('has a notify signal', function () { let notifySpy = jasmine.createSpy('notifySpy'); myInstance.connect('notify::readonly', notifySpy); myInstance.notify_prop(); myInstance.notify_prop(); expect(notifySpy).toHaveBeenCalledTimes(2); }); it('can define its own signals', function () { let emptySpy = jasmine.createSpy('emptySpy'); myInstance.connect('empty', emptySpy); myInstance.emit_empty(); expect(emptySpy).toHaveBeenCalled(); expect(myInstance.empty_called).toBeTruthy(); }); it('passes emitted arguments to signal handlers', function () { let minimalSpy = jasmine.createSpy('minimalSpy'); myInstance.connect('minimal', minimalSpy); myInstance.emit_minimal(7, 5); expect(minimalSpy).toHaveBeenCalledWith(myInstance, 7, 5); }); it('can return values from signals', function () { let fullSpy = jasmine.createSpy('fullSpy').and.returnValue(42); myInstance.connect('full', fullSpy); let result = myInstance.emit_full(); expect(fullSpy).toHaveBeenCalled(); expect(result).toEqual(42); }); it('does not call first-wins signal handlers after one returns a value', function () { let neverCalledSpy = jasmine.createSpy('neverCalledSpy'); myInstance.connect('full', () => 42); myInstance.connect('full', neverCalledSpy); myInstance.emit_full(); expect(neverCalledSpy).not.toHaveBeenCalled(); expect(myInstance.full_default_handler_called).toBeFalsy(); }); it('gets the return value of the default handler', function () { let result = myInstance.emit_full(); expect(myInstance.full_default_handler_called).toBeTruthy(); expect(result).toEqual(79); }); it('calls run-last default handler last', function () { let stack = [ ]; let runLastSpy = jasmine.createSpy('runLastSpy') .and.callFake(() => { stack.push(1); }); myInstance.connect('run-last', runLastSpy); myInstance.emit_run_last(() => { stack.push(2); }); expect(stack).toEqual([1, 2]); }); it("can inherit from something that's not GObject.Object", function () { // ...and still get all the goodies of GObject.Class let instance = new MyApplication({ application_id: 'org.gjs.Application' }); let customSpy = jasmine.createSpy('customSpy'); instance.connect('custom', customSpy); instance.emit_custom(73); expect(customSpy).toHaveBeenCalledWith(instance, 73); }); it('can implement an interface', function () { let instance = new MyInitable(); expect(instance.constructor.implements(Gio.Initable)).toBeTruthy(); }); it('can implement interface vfuncs', function () { let instance = new MyInitable(); expect(instance.inited).toBeFalsy(); instance.init(new Gio.Cancellable()); expect(instance.inited).toBeTruthy(); }); it('can be a subclass', function () { let derived = new Derived(); expect(derived instanceof Derived).toBeTruthy(); expect(derived instanceof MyObject).toBeTruthy(); expect(derived.readwrite).toEqual('yes'); }); it('calls its _instance_init() function while chaining up in constructor', function () { let instance = new MyCustomInit(); expect(instance.foo).toBeTruthy(); }); it('can have an interface-valued property', function () { const InterfacePropObject = new Lang.Class({ Name: 'InterfacePropObject', Extends: GObject.Object, Properties: { 'file': GObject.ParamSpec.object('file', 'File', 'File', GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, Gio.File.$gtype) }, }); let file = Gio.File.new_for_path('dummy'); expect(() => new InterfacePropObject({ file: file })).not.toThrow(); }); it('can override a property from the parent class', function () { const OverrideObject = new Lang.Class({ Name: 'OverrideObject', Extends: MyObject, Properties: { 'readwrite': GObject.ParamSpec.override('readwrite', MyObject), }, get readwrite() { return this._subclass_readwrite; }, set readwrite(val) { this._subclass_readwrite = 'subclass' + val; }, }); let obj = new OverrideObject(); obj.readwrite = 'foo'; expect(obj.readwrite).toEqual('subclassfoo'); }); it('cannot override a non-existent property', function () { expect(() => new Lang.Class({ Name: 'BadOverride', Extends: GObject.Object, Properties: { 'nonexistent': GObject.ParamSpec.override('nonexistent', GObject.Object), }, })).toThrow(); }); }); const AnInterface = new Lang.Interface({ Name: 'AnInterface', }); const GObjectImplementingLangInterface = new Lang.Class({ Name: 'GObjectImplementingLangInterface', Extends: GObject.Object, Implements: [ AnInterface ], _init: function (props={}) { this.parent(props); } }); const AGObjectInterface = new Lang.Interface({ Name: 'AGObjectInterface', GTypeName: 'ArbitraryGTypeName', Requires: [ GObject.Object ], Properties: { 'interface-prop': GObject.ParamSpec.string('interface-prop', 'Interface property', 'Must be overridden in implementation', GObject.ParamFlags.READABLE, 'foobar') }, Signals: { 'interface-signal': {} }, requiredG: Lang.Interface.UNIMPLEMENTED, optionalG: function () { return 'AGObjectInterface.optionalG()'; } }); const InterfaceRequiringGObjectInterface = new Lang.Interface({ Name: 'InterfaceRequiringGObjectInterface', Requires: [ AGObjectInterface ], optionalG: function () { return 'InterfaceRequiringGObjectInterface.optionalG()\n' + AGObjectInterface.optionalG(this); } }); const GObjectImplementingGObjectInterface = new Lang.Class({ Name: 'GObjectImplementingGObjectInterface', Extends: GObject.Object, Implements: [ AGObjectInterface ], Properties: { 'interface-prop': GObject.ParamSpec.override('interface-prop', AGObjectInterface), 'class-prop': GObject.ParamSpec.string('class-prop', 'Class property', 'A property that is not on the interface', GObject.ParamFlags.READABLE, 'meh') }, Signals: { 'class-signal': {}, }, get interface_prop() { return 'foobar'; }, get class_prop() { return 'meh'; }, _init: function (props={}) { this.parent(props); }, requiredG: function () {}, optionalG: function () { return AGObjectInterface.optionalG(this); } }); const MinimalImplementationOfAGObjectInterface = new Lang.Class({ Name: 'MinimalImplementationOfAGObjectInterface', Extends: GObject.Object, Implements: [ AGObjectInterface ], Properties: { 'interface-prop': GObject.ParamSpec.override('interface-prop', AGObjectInterface) }, _init: function (props={}) { this.parent(props); }, requiredG: function () {} }); const ImplementationOfTwoInterfaces = new Lang.Class({ Name: 'ImplementationOfTwoInterfaces', Extends: GObject.Object, Implements: [ AGObjectInterface, InterfaceRequiringGObjectInterface ], Properties: { 'interface-prop': GObject.ParamSpec.override('interface-prop', AGObjectInterface) }, _init: function (props={}) { this.parent(props); }, requiredG: function () {}, optionalG: function () { return InterfaceRequiringGObjectInterface.optionalG(this); } }); describe('GObject interface', function () { it('class can implement a Lang.Interface', function () { let obj; expect(() => { obj = new GObjectImplementingLangInterface(); }) .not.toThrow(); expect(obj.constructor.implements(AnInterface)).toBeTruthy(); }); it('throws when an interface requires a GObject interface but not GObject.Object', function () { expect(() => new Lang.Interface({ Name: 'GObjectInterfaceNotRequiringGObject', GTypeName: 'GTypeNameNotRequiringGObject', Requires: [ Gio.Initable ] })).toThrow(); }); it('can be implemented by a GObject class along with a JS interface', function () { const ObjectImplementingLangInterfaceAndCInterface = new Lang.Class({ Name: 'ObjectImplementingLangInterfaceAndCInterface', Extends: GObject.Object, Implements: [ AnInterface, Gio.Initable ], _init: function (props={}) { this.parent(props); } }); let obj; expect(() => { obj = new ObjectImplementingLangInterfaceAndCInterface(); }) .not.toThrow(); expect(obj.constructor.implements(AnInterface)).toBeTruthy(); expect(obj.constructor.implements(Gio.Initable)).toBeTruthy(); }); it('is an instance of the interface classes', function () { expect(AGObjectInterface instanceof Lang.Interface).toBeTruthy(); expect(AGObjectInterface instanceof GObject.Interface).toBeTruthy(); }); it('cannot be instantiated', function () { expect(() => new AGObjectInterface()).toThrow(); }); it('has a name', function () { expect(AGObjectInterface.name).toEqual('AGObjectInterface'); }); it('reports its type name', function () { expect(AGObjectInterface.$gtype.name).toEqual('ArbitraryGTypeName'); }); it('can be implemented by a GObject class', function () { let obj; expect(() => { obj = new GObjectImplementingGObjectInterface(); }) .not.toThrow(); expect(obj.constructor.implements(AGObjectInterface)).toBeTruthy(); }); it('is implemented by a GObject class with the correct class object', function () { let obj = new GObjectImplementingGObjectInterface(); expect(obj.constructor).toEqual(GObjectImplementingGObjectInterface); expect(obj.constructor.name) .toEqual('GObjectImplementingGObjectInterface'); }); it('can be implemented by a class also implementing a Lang.Interface', function () { const GObjectImplementingBothKindsOfInterface = new Lang.Class({ Name: 'GObjectImplementingBothKindsOfInterface', Extends: GObject.Object, Implements: [ AnInterface, AGObjectInterface ], Properties: { 'interface-prop': GObject.ParamSpec.override('interface-prop', AGObjectInterface) }, _init: function (props={}) { this.parent(props); }, required: function () {}, requiredG: function () {} }); let obj; expect(() => { obj = new GObjectImplementingBothKindsOfInterface(); }) .not.toThrow(); expect(obj.constructor.implements(AnInterface)).toBeTruthy(); expect(obj.constructor.implements(AGObjectInterface)).toBeTruthy(); }); it('can have its required function implemented', function () { expect(() => { let obj = new GObjectImplementingGObjectInterface(); obj.requiredG(); }).not.toThrow(); }); it('must have its required function implemented', function () { expect(() => new Lang.Class({ Name: 'BadObject', Extends: GObject.Object, Implements: [ AGObjectInterface ], Properties: { 'interface-prop': GObject.ParamSpec.override('interface-prop', AGObjectInterface) } })).toThrow(); }); it("doesn't have to have its optional function implemented", function () { let obj; expect(() => { obj = new MinimalImplementationOfAGObjectInterface(); }) .not.toThrow(); expect(obj.constructor.implements(AGObjectInterface)).toBeTruthy(); }); it('can have its optional function deferred to by the implementation', function () { let obj = new MinimalImplementationOfAGObjectInterface(); expect(obj.optionalG()).toEqual('AGObjectInterface.optionalG()'); }); it('can have its function chained up to', function () { let obj = new GObjectImplementingGObjectInterface(); expect(obj.optionalG()).toEqual('AGObjectInterface.optionalG()'); }); it('can require another interface', function () { let obj; expect(() => { obj = new ImplementationOfTwoInterfaces(); }).not.toThrow(); expect(obj.constructor.implements(AGObjectInterface)).toBeTruthy(); expect(obj.constructor.implements(InterfaceRequiringGObjectInterface)) .toBeTruthy(); }); it('can chain up to another interface', function () { let obj = new ImplementationOfTwoInterfaces(); expect(obj.optionalG()) .toEqual('InterfaceRequiringGObjectInterface.optionalG()\nAGObjectInterface.optionalG()'); }); it("defers to the last interface's optional function", function () { const MinimalImplementationOfTwoInterfaces = new Lang.Class({ Name: 'MinimalImplementationOfTwoInterfaces', Extends: GObject.Object, Implements: [ AGObjectInterface, InterfaceRequiringGObjectInterface ], Properties: { 'interface-prop': GObject.ParamSpec.override('interface-prop', AGObjectInterface) }, _init: function (props={}) { this.parent(props); }, requiredG: function () {} }); let obj = new MinimalImplementationOfTwoInterfaces(); expect(obj.optionalG()) .toEqual('InterfaceRequiringGObjectInterface.optionalG()\nAGObjectInterface.optionalG()'); }); it('must be implemented by a class that implements all required interfaces', function () { expect(() => new Lang.Class({ Name: 'BadObject', Implements: [ InterfaceRequiringGObjectInterface ], required: function () {} })).toThrow(); }); it('must be implemented by a class that implements required interfaces in correct order', function () { expect(() => new Lang.Class({ Name: 'BadObject', Implements: [ InterfaceRequiringGObjectInterface, AGObjectInterface ], required: function () {} })).toThrow(); }); it('can require an interface from C', function () { const InitableInterface = new Lang.Interface({ Name: 'InitableInterface', Requires: [ GObject.Object, Gio.Initable ] }); expect(() => new Lang.Class({ Name: 'BadObject', Implements: [ InitableInterface ] })).toThrow(); }); it('can define signals on the implementing class', function () { function quitLoop() { Mainloop.quit('signal'); } let obj = new GObjectImplementingGObjectInterface(); let interfaceSignalSpy = jasmine.createSpy('interfaceSignalSpy') .and.callFake(quitLoop); let classSignalSpy = jasmine.createSpy('classSignalSpy') .and.callFake(quitLoop); obj.connect('interface-signal', interfaceSignalSpy); obj.connect('class-signal', classSignalSpy); GLib.idle_add(GLib.PRIORITY_DEFAULT, () => { obj.emit('interface-signal'); return GLib.SOURCE_REMOVE; }); Mainloop.run('signal'); GLib.idle_add(GLib.PRIORITY_DEFAULT, () => { obj.emit('class-signal'); return GLib.SOURCE_REMOVE; }); Mainloop.run('signal'); expect(interfaceSignalSpy).toHaveBeenCalled(); expect(classSignalSpy).toHaveBeenCalled(); }); it('can define properties on the implementing class', function () { let obj = new GObjectImplementingGObjectInterface(); expect(obj.interface_prop).toEqual('foobar'); expect(obj.class_prop).toEqual('meh'); }); it('must have its properties overridden', function () { // Failing to override an interface property doesn't raise an error but // instead logs a critical warning. GLib.test_expect_message('GLib-GObject', GLib.LogLevelFlags.LEVEL_CRITICAL, "Object class * doesn't implement property 'interface-prop' from " + "interface 'ArbitraryGTypeName'"); new Lang.Class({ Name: 'MyNaughtyObject', Extends: GObject.Object, Implements: [ AGObjectInterface ], _init: function (props={}) { this.parent(props); }, requiredG: function () {} }); // g_test_assert_expected_messages() is a macro, not introspectable GLib.test_assert_expected_messages_internal('Gjs', 'testGObjectInterface.js', 416, 'testGObjectMustOverrideInterfaceProperties'); }); // This makes sure that we catch the case where the metaclass (e.g. // GtkWidgetClass) doesn't specify a meta-interface. In that case we get the // meta-interface from the metaclass's parent. it('gets the correct type for its metaclass', function () { const MyMeta = new Lang.Class({ Name: 'MyMeta', Extends: GObject.Class }); const MyMetaObject = new MyMeta({ Name: 'MyMetaObject' }); const MyMetaInterface = new Lang.Interface({ Name: 'MyMetaInterface', Requires: [ MyMetaObject ] }); expect(MyMetaInterface instanceof GObject.Interface).toBeTruthy(); }); it('can be implemented by a class as well as its parent class', function () { const SubObject = new Lang.Class({ Name: 'SubObject', Extends: GObjectImplementingGObjectInterface }); let obj = new SubObject(); expect(obj.constructor.implements(AGObjectInterface)).toBeTruthy(); expect(obj.interface_prop).toEqual('foobar'); // override not needed }); it('can be reimplemented by a subclass of a class that already implements it', function () { const SubImplementer = new Lang.Class({ Name: 'SubImplementer', Extends: GObjectImplementingGObjectInterface, Implements: [ AGObjectInterface ] }); let obj = new SubImplementer(); expect(obj.constructor.implements(AGObjectInterface)).toBeTruthy(); expect(obj.interface_prop).toEqual('foobar'); // override not needed }); }); const LegacyInterface1 = new Lang.Interface({ Name: 'LegacyInterface1', Requires: [GObject.Object], Signals: { 'legacy-iface1-signal': {} }, }); const LegacyInterface2 = new Lang.Interface({ Name: 'LegacyInterface2', Requires: [GObject.Object], Signals: { 'legacy-iface2-signal': {} }, }); const Legacy = new Lang.Class({ Name: 'Legacy', Extends: GObject.Object, Implements: [LegacyInterface1], Properties: { 'property': GObject.ParamSpec.int('property', 'Property', 'A magic property', GObject.ParamFlags.READWRITE, 0, 100, 0), 'override-property': GObject.ParamSpec.int('override-property', 'Override property', 'Another magic property', GObject.ParamFlags.READWRITE, 0, 100, 0), }, Signals: { 'signal': {}, }, _init(someval) { this.constructorCalledWith = someval; this.parent(); }, instanceMethod() {}, chainUpToMe() {}, overrideMe() {}, get property() { return this._property + 1; }, set property(value) { this._property = value - 2; }, get override_property() { return this._override_property + 1; }, set override_property(value) { this._override_property = value - 2; }, }); Legacy.staticMethod = function () {}; const Shiny = GObject.registerClass({ Implements: [LegacyInterface2], Properties: { 'override-property': GObject.ParamSpec.override('override-property', Legacy), }, }, class Shiny extends Legacy { chainUpToMe() { super.chainUpToMe(); } overrideMe() {} get override_property() { return this._override_property + 2; } set override_property(value) { this._override_property = value - 1; } }); describe('ES6 GObject class inheriting from GObject.Class', function () { let instance; beforeEach(function () { spyOn(Legacy, 'staticMethod'); spyOn(Legacy.prototype, 'instanceMethod'); spyOn(Legacy.prototype, 'chainUpToMe'); spyOn(Legacy.prototype, 'overrideMe'); instance = new Shiny(); }); it('calls a static method on the parent class', function () { Shiny.staticMethod(); expect(Legacy.staticMethod).toHaveBeenCalled(); }); it('calls a method on the parent class', function () { instance.instanceMethod(); expect(Legacy.prototype.instanceMethod).toHaveBeenCalled(); }); it("passes arguments to the parent class's constructor", function () { let instance = new Shiny(42); expect(instance.constructorCalledWith).toEqual(42); }); it('chains up to a method on the parent class', function () { instance.chainUpToMe(); expect(Legacy.prototype.chainUpToMe).toHaveBeenCalled(); }); it('overrides a method on the parent class', function () { instance.overrideMe(); expect(Legacy.prototype.overrideMe).not.toHaveBeenCalled(); }); it('sets and gets a property from the parent class', function () { instance.property = 42; expect(instance.property).toEqual(41); }); it('overrides a property from the parent class', function () { instance.override_property = 42; expect(instance.override_property).toEqual(43); }); it('inherits a signal from the parent class', function () { let signalSpy = jasmine.createSpy('signalSpy'); expect(() => { instance.connect('signal', signalSpy); instance.emit('signal'); }).not.toThrow(); expect(signalSpy).toHaveBeenCalled(); }); it('inherits legacy interfaces from the parent', function () { expect(() => instance.emit('legacy-iface1-signal')).not.toThrow(); expect(instance instanceof LegacyInterface1).toBeTruthy(); }); it('can implement a legacy interface itself', function () { expect(() => instance.emit('legacy-iface2-signal')).not.toThrow(); expect(instance instanceof LegacyInterface2).toBeTruthy(); }); }); cjs-4.4.0/installed-tests/js/testLegacyGtk.js000066400000000000000000000071161356375132300211560ustar00rootroot00000000000000// -*- mode: js; indent-tabs-mode: nil -*- imports.gi.versions.Gtk = '3.0'; const ByteArray = imports.byteArray; const Gtk = imports.gi.Gtk; const Lang = imports.lang; const template = ` `; const MyComplexGtkSubclass = new Lang.Class({ Name: 'MyComplexGtkSubclass', Extends: Gtk.Grid, Template: ByteArray.fromString(template), Children: ['label-child', 'label-child2'], InternalChildren: ['internal-label-child'], CssName: 'complex-subclass', testChildrenExist: function () { this._internalLabel = this.get_template_child(MyComplexGtkSubclass, 'label-child'); expect(this._internalLabel).toEqual(jasmine.anything()); expect(this.label_child2).toEqual(jasmine.anything()); expect(this._internal_label_child).toEqual(jasmine.anything()); } }); const MyComplexGtkSubclassFromResource = new Lang.Class({ Name: 'MyComplexGtkSubclassFromResource', Extends: Gtk.Grid, Template: 'resource:///org/gjs/jsunit/complex.ui', Children: ['label-child', 'label-child2'], InternalChildren: ['internal-label-child'], testChildrenExist: function () { expect(this.label_child).toEqual(jasmine.anything()); expect(this.label_child2).toEqual(jasmine.anything()); expect(this._internal_label_child).toEqual(jasmine.anything()); } }); function validateTemplate(description, ClassName) { describe(description, function () { let win, content; beforeEach(function () { win = new Gtk.Window({ type: Gtk.WindowType.TOPLEVEL }); content = new ClassName(); win.add(content); }); it('sets up internal and public template children', function () { content.testChildrenExist(); }); it('sets up public template children with the correct widgets', function () { expect(content.label_child.get_label()).toEqual('Complex!'); expect(content.label_child2.get_label()).toEqual('Complex as well!'); }); it('sets up internal template children with the correct widgets', function () { expect(content._internal_label_child.get_label()) .toEqual('Complex and internal!'); }); afterEach(function () { win.destroy(); }); }); } describe('Legacy Gtk overrides', function () { beforeAll(function () { Gtk.init(null); }); validateTemplate('UI template', MyComplexGtkSubclass); validateTemplate('UI template from resource', MyComplexGtkSubclassFromResource); it('sets CSS names on classes', function () { expect(Gtk.Widget.get_css_name.call(MyComplexGtkSubclass)).toEqual('complex-subclass'); }); }); cjs-4.4.0/installed-tests/js/testLocale.js000066400000000000000000000030311356375132300204730ustar00rootroot00000000000000describe('JS_SetLocaleCallbacks', function () { it('Intl API was compiled into SpiderMonkey', function () { expect(Intl).toBeDefined(); }); // Requesting the weekday name tests locale_to_unicode it('toLocaleDateString() works', function () { let date = new Date('12/15/1981'); let datestr = date.toLocaleDateString('pt-BR', { weekday: 'long' }); expect(datestr).toEqual('terça-feira'); }); it('toLocaleLowerCase() works', function () { expect('AAA'.toLocaleLowerCase()).toEqual('aaa'); }); // String conversion is implemented internally to GLib, // and is more-or-less independent of locale. (A few // characters are handled specially for a few locales, // like i in Turkish. But not A WITH ACUTE) it('toLocaleLowerCase() works for Unicode', function () { expect('\u00c1'.toLocaleLowerCase()).toEqual('\u00e1'); }); it('toLocaleUpperCase() works', function () { expect('aaa'.toLocaleUpperCase()).toEqual('AAA'); }); it('toLocaleUpperCase() works for Unicode', function () { expect('\u00e1'.toLocaleUpperCase()).toEqual('\u00c1'); }); // GLib calls out to libc for collation, so we can't really // assume anything - we could even be running in the // C locale. The below is pretty safe. it('localeCompare() works', function () { expect('a'.localeCompare('b')).toBeLessThan(0); expect('a'.localeCompare('a')).toEqual(0); expect('b'.localeCompare('a')).toBeGreaterThan(0); }); }); cjs-4.4.0/installed-tests/js/testMainloop.js000066400000000000000000000060061356375132300210570ustar00rootroot00000000000000const Mainloop = imports.mainloop; describe('Mainloop.timeout_add()', function () { let runTenTimes, runOnlyOnce, neverRun; beforeAll(function (done) { let count = 0; runTenTimes = jasmine.createSpy('runTenTimes').and.callFake(() => { if (count === 10) { done(); return false; } count += 1; return true; }); runOnlyOnce = jasmine.createSpy('runOnlyOnce').and.returnValue(false); neverRun = jasmine.createSpy('neverRun').and.throwError(); Mainloop.timeout_add(10, runTenTimes); Mainloop.timeout_add(10, runOnlyOnce); Mainloop.timeout_add(15000, neverRun); }); it('runs a timeout function', function () { expect(runOnlyOnce).toHaveBeenCalledTimes(1); }); it('runs a timeout function until it returns false', function () { expect(runTenTimes).toHaveBeenCalledTimes(11); }); it('runs a timeout function after an initial timeout', function () { expect(neverRun).not.toHaveBeenCalled(); }); }); describe('Mainloop.idle_add()', function () { let runOnce, runTwice, neverRuns, quitAfterManyRuns; beforeAll(function (done) { runOnce = jasmine.createSpy('runOnce').and.returnValue(false); runTwice = jasmine.createSpy('runTwice').and.returnValues([true, false]); neverRuns = jasmine.createSpy('neverRuns').and.throwError(); let count = 0; quitAfterManyRuns = jasmine.createSpy('quitAfterManyRuns').and.callFake(() => { count += 1; if (count > 10) { done(); return false; } return true; }); Mainloop.idle_add(runOnce); Mainloop.idle_add(runTwice); let neverRunsId = Mainloop.idle_add(neverRuns); Mainloop.idle_add(quitAfterManyRuns); Mainloop.source_remove(neverRunsId); }); it('runs an idle function', function () { expect(runOnce).toHaveBeenCalledTimes(1); }); it('continues to run idle functions that return true', function () { expect(runTwice).toHaveBeenCalledTimes(2); expect(quitAfterManyRuns).toHaveBeenCalledTimes(11); }); it('does not run idle functions if removed', function () { expect(neverRuns).not.toHaveBeenCalled(); }); it('can remove idle functions while they are being invoked', function (done) { let removeId = Mainloop.idle_add(() => { Mainloop.source_remove(removeId); done(); return false; }); }); // Add an idle before exit, then never run main loop again. // This is to test that we remove idle callbacks when the associated // JSContext is blown away. The leak check in minijasmine will // fail if the idle function is not garbage collected. it('does not leak idle callbacks', function () { Mainloop.idle_add(() => { fail('This should never have been called'); return true; }); }); }); cjs-4.4.0/installed-tests/js/testNamespace.js000066400000000000000000000003001356375132300211640ustar00rootroot00000000000000const Regress = imports.gi.Regress; describe('GI repository namespace', function () { it('supplies a name', function () { expect(Regress.__name__).toEqual('Regress'); }); }); cjs-4.4.0/installed-tests/js/testPackage.js000066400000000000000000000057131356375132300206400ustar00rootroot00000000000000const Pkg = imports.package; describe('Package module', function () { it('finds an existing library', function () { expect(Pkg.checkSymbol('Regress', '1.0')).toEqual(true); }); it('doesn\'t find a non-existent library', function () { expect(Pkg.checkSymbol('Rägräss', '1.0')).toEqual(false); }); it('finds a function', function () { expect(Pkg.checkSymbol('Regress', '1.0', 'get_variant')).toEqual(true); }); it('doesn\'t find a non-existent function', function () { expect(Pkg.checkSymbol('Regress', '1.0', 'get_väriänt')).toEqual(false); }); it('finds a class', function () { expect(Pkg.checkSymbol('Regress', '1.0', 'TestObj')).toEqual(true); }); it('doesn\'t find a non-existent class', function () { expect(Pkg.checkSymbol('Regress', '1.0', 'TestNoObj')).toEqual(false); }); it('finds a property', function () { expect(Pkg.checkSymbol('Regress', '1.0', 'TestObj.bare')).toEqual(true); }); it('doesn\'t find a non-existent property', function () { expect(Pkg.checkSymbol('Regress', '1.0', 'TestObj.bäre')).toEqual(false); }); it('finds a static function', function () { expect(Pkg.checkSymbol('Regress', '1.0', 'TestObj.static_method')).toEqual(true); }); it('doesn\'t find a non-existent static function', function () { expect(Pkg.checkSymbol('Regress', '1.0', 'TestObj.stätic_methöd')).toEqual(false); }); it('finds a method', function () { expect(Pkg.checkSymbol('Regress', '1.0', 'TestObj.null_out')).toEqual(true); }); it('doesn\'t find a non-existent method', function () { expect(Pkg.checkSymbol('Regress', '1.0', 'TestObj.nüll_out')).toEqual(false); }); it('finds an interface', function () { expect(Pkg.checkSymbol('GIMarshallingTests', '1.0', 'Interface')).toEqual(true); }); it('doesn\'t find a non-existent interface', function () { expect(Pkg.checkSymbol('GIMarshallingTests', '1.0', 'Interfäce')).toEqual(false); }); it('finds an interface method', function () { expect(Pkg.checkSymbol('GIMarshallingTests', '1.0', 'Interface.test_int8_in')).toEqual(true); }); it('doesn\'t find a non-existent interface method', function () { expect(Pkg.checkSymbol('GIMarshallingTests', '1.0', 'Interface.test_int42_291in342')).toEqual(false); }); it('finds an enum value', function () { expect(Pkg.checkSymbol('Regress', '1.0', 'TestEnum.VALUE1')).toEqual(true); }); it('doesn\'t find a non-existent enum value', function () { expect(Pkg.checkSymbol('Regress', '1.0', 'TestEnum.value1')).toEqual(false); }); it('finds a constant', function () { expect(Pkg.checkSymbol('Regress', '1.0', 'BOOL_CONSTANT')).toEqual(true); }); it('doesn\'t find a non-existent constant', function () { expect(Pkg.checkSymbol('Regress', '1.0', 'BööL_CONSTANT')).toEqual(false); }); }); cjs-4.4.0/installed-tests/js/testParamSpec.js000066400000000000000000000034071356375132300211560ustar00rootroot00000000000000const Regress = imports.gi.Regress; const GObject = imports.gi.GObject; let name = 'foo-property'; let nick = 'Foo property'; let blurb = 'This is the foo property'; let flags = GObject.ParamFlags.READABLE; function testParamSpec(type, params, defaultValue) { describe('GObject.ParamSpec.' + type, function () { let paramSpec; beforeEach(function () { paramSpec = GObject.ParamSpec[type].apply(GObject.ParamSpec, [name, nick, blurb, flags, ...params]); }); it('has the correct name strings', function () { expect(paramSpec.name).toEqual(name); expect(paramSpec._nick).toEqual(nick); expect(paramSpec._blurb).toEqual(blurb); }); it('has the correct flags', function () { expect(paramSpec.flags).toEqual(flags); }); it('has the correct default value', function () { expect(paramSpec.default_value).toEqual(defaultValue); }); }); } testParamSpec('string', ['Default Value'], 'Default Value'); testParamSpec('int', [-100, 100, -42], -42); testParamSpec('uint', [20, 100, 42], 42); testParamSpec('int64', [0x4000, 0xffffffff, 0x2266bbff], 0x2266bbff); testParamSpec('uint64', [0, 0xffffffff, 0x2266bbff], 0x2266bbff); testParamSpec('enum', [Regress.TestEnum, Regress.TestEnum.VALUE2], Regress.TestEnum.VALUE2); testParamSpec('flags', [Regress.TestFlags, Regress.TestFlags.FLAG2], Regress.TestFlags.FLAG2); testParamSpec('object', [GObject.Object], null); describe('GObject.ParamSpec object', function () { it("doesn't crash when resolving a non-string property", function () { let paramSpec = GObject.ParamSpec.string(name, nick, blurb, flags, ''); expect(paramSpec[0]).not.toBeDefined(); }); }); cjs-4.4.0/installed-tests/js/testSignals.js000066400000000000000000000075331356375132300207070ustar00rootroot00000000000000const GLib = imports.gi.GLib; const Lang = imports.lang; const Signals = imports.signals; const Foo = new Lang.Class({ Name: 'Foo', Implements: [Signals.WithSignals], _init: function () {}, }); describe('Legacy object with signals', function () { testSignals(Foo); }); class FooWithoutSignals {} Signals.addSignalMethods(FooWithoutSignals.prototype); describe('Object with signals added', function () { testSignals(FooWithoutSignals); }); function testSignals(klass) { let foo, bar; beforeEach(function () { foo = new klass(); bar = jasmine.createSpy('bar'); }); it('calls a signal handler when a signal is emitted', function () { foo.connect('bar', bar); foo.emit('bar', "This is a", "This is b"); expect(bar).toHaveBeenCalledWith(foo, 'This is a', 'This is b'); }); it('does not call a signal handler after the signal is disconnected', function () { let id = foo.connect('bar', bar); foo.emit('bar', "This is a", "This is b"); bar.calls.reset(); foo.disconnect(id); // this emission should do nothing foo.emit('bar', "Another a", "Another b"); expect(bar).not.toHaveBeenCalled(); }); it('can disconnect a signal handler during signal emission', function () { var toRemove = []; let firstId = foo.connect('bar', function (theFoo) { theFoo.disconnect(toRemove[0]); theFoo.disconnect(toRemove[1]); }); toRemove.push(foo.connect('bar', bar)); toRemove.push(foo.connect('bar', bar)); // emit signal; what should happen is that the second two handlers are // disconnected before they get invoked foo.emit('bar'); expect(bar).not.toHaveBeenCalled(); // clean up the last handler foo.disconnect(firstId); // poke in private implementation to sanity-check no handlers left expect(foo._signalConnections.length).toEqual(0); }); it('distinguishes multiple signals', function () { let bonk = jasmine.createSpy('bonk'); foo.connect('bar', bar); foo.connect('bonk', bonk); foo.connect('bar', bar); foo.emit('bar'); expect(bar).toHaveBeenCalledTimes(2); expect(bonk).not.toHaveBeenCalled(); foo.emit('bonk'); expect(bar).toHaveBeenCalledTimes(2); expect(bonk).toHaveBeenCalledTimes(1); foo.emit('bar'); expect(bar).toHaveBeenCalledTimes(4); expect(bonk).toHaveBeenCalledTimes(1); foo.disconnectAll(); bar.calls.reset(); bonk.calls.reset(); // these post-disconnect emissions should do nothing foo.emit('bar'); foo.emit('bonk'); expect(bar).not.toHaveBeenCalled(); expect(bonk).not.toHaveBeenCalled(); }); describe('with exception in signal handler', function () { let bar2; beforeEach(function () { bar.and.throwError('Exception we are throwing on purpose'); bar2 = jasmine.createSpy('bar'); foo.connect('bar', bar); foo.connect('bar', bar2); GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: Exception in callback for signal: *'); foo.emit('bar'); }); it('does not affect other callbacks', function () { expect(bar).toHaveBeenCalledTimes(1); expect(bar2).toHaveBeenCalledTimes(1); }); it('does not disconnect the callback', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: Exception in callback for signal: *'); foo.emit('bar'); expect(bar).toHaveBeenCalledTimes(2); expect(bar2).toHaveBeenCalledTimes(2); }); }); } cjs-4.4.0/installed-tests/js/testSystem.js000066400000000000000000000015711356375132300205670ustar00rootroot00000000000000const System = imports.system; const GObject = imports.gi.GObject; describe('System.addressOf()', function () { it('gives different results for different objects', function () { let a = {some: 'object'}; let b = {different: 'object'}; expect(System.addressOf(a)).not.toEqual(System.addressOf(b)); }); }); describe('System.version', function () { it('gives a plausible number', function () { expect(System.version).not.toBeLessThan(14700); expect(System.version).toBeLessThan(40000); }); }); describe('System.refcount()', function () { it('gives the correct number', function () { let o = new GObject.Object({}); expect(System.refcount(o)).toEqual(1); }); }); describe('System.gc()', function () { it('does not crash the application', function () { expect(System.gc).not.toThrow(); }); }); cjs-4.4.0/installed-tests/js/testTweener.js000066400000000000000000000274671356375132300207300ustar00rootroot00000000000000const Tweener = imports.tweener.tweener; function installFrameTicker() { // Set up Tweener to have a "frame pulse" that the Jasmine clock functions // can influence let ticker = { FRAME_RATE: 50, _init : function() { }, start : function() { this._currentTime = 0; this._timeoutID = setInterval(() => { this._currentTime += 1000 / this.FRAME_RATE; this.emit('prepare-frame'); }, Math.floor(1000 / this.FRAME_RATE)); }, stop : function() { if ('_timeoutID' in this) { clearInterval(this._timeoutID); delete this._timeoutID; } this._currentTime = 0; }, getTime : function() { return this._currentTime; } }; imports.signals.addSignalMethods(ticker); Tweener.setFrameTicker(ticker); } describe('Tweener', function () { beforeAll(function () { jasmine.clock().install(); installFrameTicker(); }); afterAll(function () { jasmine.clock().uninstall(); }); let start, update, overwrite, complete; beforeEach(function () { start = jasmine.createSpy('start'); update = jasmine.createSpy('update'); overwrite = jasmine.createSpy('overwrite'); complete = jasmine.createSpy('complete'); }); it('runs a simple tween', function () { var objectA = { x: 0, y: 0 }; var objectB = { x: 0, y: 0 }; Tweener.addTween(objectA, { x: 10, y: 10, time: 1, transition: "linear" }); Tweener.addTween(objectB, { x: 10, y: 10, time: 1, delay: 0.5, transition: "linear" }); jasmine.clock().tick(1001); expect(objectA.x).toEqual(10); expect(objectA.y).toEqual(10); expect(objectB.x).toEqual(5); expect(objectB.y).toEqual(5); }); it('calls callbacks during the tween', function () { Tweener.addTween({}, { time: 0.1, onStart: start, onUpdate: update, onComplete: complete, }); jasmine.clock().tick(101); expect(start).toHaveBeenCalled(); expect(update).toHaveBeenCalled(); expect(complete).toHaveBeenCalled(); }); it('can pause tweens', function () { var objectA = { foo: 0 }; var objectB = { bar: 0 }; var objectC = { baaz: 0 }; Tweener.addTween(objectA, { foo: 100, time: 0.1 }); Tweener.addTween(objectC, { baaz: 100, time: 0.1 }); Tweener.addTween(objectB, { bar: 100, time: 0.1 }); Tweener.pauseTweens(objectA); // This should do nothing expect(Tweener.pauseTweens(objectB, 'quux')).toBeFalsy(); /* Pause and resume should be equal to doing nothing */ Tweener.pauseTweens(objectC, "baaz"); Tweener.resumeTweens(objectC, "baaz"); jasmine.clock().tick(101); expect(objectA.foo).toEqual(0); expect(objectB.bar).toEqual(100); expect(objectC.baaz).toEqual(100); }); it('can remove tweens', function () { var object = { foo: 0, bar: 0, baaz: 0 }; Tweener.addTween(object, { foo: 50, time: 0.1 }); Tweener.addTween(object, { bar: 50, time: 0.1 }); Tweener.addTween(object, { baaz: 50, time: 0.1}); /* The Tween on property foo should still be run after removing the other two */ Tweener.removeTweens(object, "bar", "baaz"); jasmine.clock().tick(101); expect(object.foo).toEqual(50); expect(object.bar).toEqual(0); expect(object.baaz).toEqual(0); }); it('overrides a tween with another one acting on the same object and property at the same time', function () { var objectA = { foo: 0 }; Tweener.addTween(objectA, { foo: 100, time: 0.1 }); Tweener.addTween(objectA, { foo: 0, time: 0.1 }); jasmine.clock().tick(101); expect(objectA.foo).toEqual(0); }); it('does not override a tween with another one acting not at the same time', function () { var objectB = { bar: 0 }; /* In this case both tweens should be executed, as they don't * act on the object at the same time (the second one has a * delay equal to the running time of the first one) */ Tweener.addTween(objectB, { bar: 100, time: 0.1 }); Tweener.addTween(objectB, { bar: 150, time: 0.1, delay: 0.1 }); jasmine.clock(0).tick(201); expect(objectB.bar).toEqual(150); }); it('can pause and resume all tweens', function () { var objectA = { foo: 0 }; var objectB = { bar: 0 }; Tweener.addTween(objectA, { foo: 100, time: 0.1 }); Tweener.addTween(objectB, { bar: 100, time: 0.1 }); Tweener.pauseAllTweens(); jasmine.clock().tick(10); Tweener.resumeAllTweens(); jasmine.clock().tick(101); expect(objectA.foo).toEqual(100); expect(objectB.bar).toEqual(100); }); it('can remove all tweens', function () { var objectA = { foo: 0 }; var objectB = { bar: 0 }; Tweener.addTween(objectA, { foo: 100, time: 0.1 }); Tweener.addTween(objectB, { bar: 100, time: 0.1 }); Tweener.removeAllTweens(); jasmine.clock().tick(200); expect(objectA.foo).toEqual(0); expect(objectB.bar).toEqual(0); }); it('runs a tween with a time of 0 immediately', function () { var object = { foo: 100 }; Tweener.addTween(object, { foo: 50, time: 0, delay: 0 }); Tweener.addTween(object, { foo: 200, time: 0.1, onStart: () => { /* The immediate tween should set it to 50 before we run */ expect(object.foo).toEqual(50); }, }); jasmine.clock().tick(101); expect(object.foo).toEqual(200); }); it('can call a callback a certain number of times', function () { var object = { foo: 0 }; Tweener.addCaller(object, { onUpdate: () => { object.foo += 1; }, count: 10, time: 0.1, }); jasmine.clock().tick(101); expect(object.foo).toEqual(10); }); it('can count the number of tweens on an object', function () { var object = { foo: 0, bar: 0, baaz: 0, quux: 0 }; expect(Tweener.getTweenCount(object)).toEqual(0); Tweener.addTween(object, { foo: 100, time: 0.1 }); expect(Tweener.getTweenCount(object)).toEqual(1); Tweener.addTween(object, { bar: 100, time: 0.1 }); expect(Tweener.getTweenCount(object)).toEqual(2); Tweener.addTween(object, { baaz: 100, time: 0.1 }); expect(Tweener.getTweenCount(object)).toEqual(3); Tweener.addTween(object, { quux: 100, time: 0.1 }); expect(Tweener.getTweenCount(object)).toEqual(4); Tweener.removeTweens(object, 'bar', 'baaz'); expect(Tweener.getTweenCount(object)).toEqual(2); }); it('can register special properties', function () { Tweener.registerSpecialProperty( 'negative_x', function(obj) { return -obj.x; }, function(obj, val) { obj.x = -val; } ); var objectA = { x: 0, y: 0 }; Tweener.addTween(objectA, { negative_x: 10, y: 10, time: 1, transition: "linear" }); jasmine.clock().tick(1001); expect(objectA.x).toEqual(-10); expect(objectA.y).toEqual(10); }); it('can register special modifiers for properties', function () { Tweener.registerSpecialPropertyModifier('discrete', discrete_modifier, discrete_get); function discrete_modifier(props) { return props.map(function (prop) { return { name: prop, parameters: null }; }); } function discrete_get(begin, end, time, params) { return Math.floor(begin + time * (end - begin)); } var objectA = { x: 0, y: 0, xFraction: false, yFraction: false }; Tweener.addTween(objectA, { x: 10, y: 10, time: 1, discrete: ["x"], transition: "linear", onUpdate: function() { if (objectA.x != Math.floor(objectA.x)) objectA.xFraction = true; if (objectA.y != Math.floor(objectA.y)) objectA.yFraction = true; }, }); jasmine.clock().tick(1001); expect(objectA.x).toEqual(10); expect(objectA.y).toEqual(10); expect(objectA.xFraction).toBeFalsy(); expect(objectA.yFraction).toBeTruthy(); }); it('can split properties into more than one special property', function () { Tweener.registerSpecialPropertySplitter( 'xnegy', function(val) { return [ { name: "x", value: val }, { name: "y", value: -val } ]; } ); var objectA = { x: 0, y: 0 }; Tweener.addTween(objectA, { xnegy: 10, time: 1, transition: "linear" }); jasmine.clock().tick(1001); expect(objectA.x).toEqual(10); expect(objectA.y).toEqual(-10); }); it('calls an overwrite callback when a tween is replaced', function () { var object = { a: 0, b: 0, c: 0, d: 0 }; var tweenA = { a: 10, b: 10, c: 10, d: 10, time: 0.1, onStart: start, onOverwrite: overwrite, onComplete: complete, }; var tweenB = { a: 20, b: 20, c: 20, d: 20, time: 0.1, onStart: start, onOverwrite: overwrite, onComplete: complete, }; Tweener.addTween(object, tweenA); Tweener.addTween(object, tweenB); jasmine.clock().tick(101); expect(start).toHaveBeenCalledTimes(1); expect(overwrite).toHaveBeenCalledTimes(1); expect(complete).toHaveBeenCalledTimes(1); }); it('can still overwrite a tween after it has started', function () { var object = { a: 0, b: 0, c: 0, d: 0 }; var tweenA = { a: 10, b: 10, c: 10, d: 10, time: 0.1, onStart: () => { start(); Tweener.addTween(object, tweenB); }, onOverwrite: overwrite, onComplete: complete, }; var tweenB = { a: 20, b: 20, c: 20, d: 20, time: 0.1, onStart: start, onOverwrite: overwrite, onComplete: complete, }; Tweener.addTween(object, tweenA); jasmine.clock().tick(121); expect(start).toHaveBeenCalledTimes(2); expect(overwrite).toHaveBeenCalledTimes(1); expect(complete).toHaveBeenCalledTimes(1); }); it('stays within min and max values', function () { var objectA = { x: 0, y: 0 }; var objectB = { x: 0, y: 0 }; Tweener.addTween(objectA, { x: 300, y: 300, time: 1, max: 255, transition: "linear" }); Tweener.addTween(objectB, { x: -200, y: -200, time: 1, delay: 0.5, min: 0, transition: "linear" }); jasmine.clock().tick(1001); expect(objectA.x).toEqual(255); expect(objectA.y).toEqual(255); expect(objectB.x).toEqual(0); expect(objectB.y).toEqual(0); }); }); cjs-4.4.0/installed-tests/js/testself.js000066400000000000000000000016441356375132300202350ustar00rootroot00000000000000describe('Test harness internal consistency', function () { it('', function () { var someUndefined; var someNumber = 1; var someOtherNumber = 42; var someString = "hello"; var someOtherString = "world"; expect(true).toBeTruthy(); expect(false).toBeFalsy(); expect(someNumber).toEqual(someNumber); expect(someString).toEqual(someString); expect(someNumber).not.toEqual(someOtherNumber); expect(someString).not.toEqual(someOtherString); expect(null).toBeNull(); expect(someNumber).not.toBeNull(); expect(someNumber).toBeDefined(); expect(someUndefined).not.toBeDefined(); expect(0 / 0).toBeNaN(); expect(someNumber).not.toBeNaN(); expect(() => { throw {}; }).toThrow(); expect(() => expect(true).toThrow()).toThrow(); expect(() => true).not.toThrow(); }); }); cjs-4.4.0/installed-tests/minijasmine.cpp000066400000000000000000000114761356375132300204450ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2016 Philip Chimento * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "config.h" #include #include #include #include #include #include "cjs/gjs.h" #include "cjs/mem.h" G_GNUC_NORETURN static void bail_out(GjsContext *gjs_context, const char *msg) { g_object_unref(gjs_context); g_print("Bail out! %s\n", msg); exit(1); } int main(int argc, char **argv) { if (argc < 2) g_error("Need a test file"); /* The fact that this isn't the default is kind of lame... */ g_setenv("GJS_DEBUG_OUTPUT", "stderr", false); /* Jasmine library has some code style nits that trip this */ g_setenv("GJS_DISABLE_EXTRA_WARNINGS", "1", false); setlocale(LC_ALL, ""); if (g_getenv("GJS_USE_UNINSTALLED_FILES") != NULL) { g_irepository_prepend_search_path(g_getenv("TOP_BUILDDIR")); } else { g_irepository_prepend_search_path(INSTTESTDIR); g_irepository_prepend_library_path(PKGLIBDIR); } const char *coverage_prefix = g_getenv("GJS_UNIT_COVERAGE_PREFIX"); const char *coverage_output_path = g_getenv("GJS_UNIT_COVERAGE_OUTPUT"); const char *search_path[] = { "resource:///org/gjs/jsunit", NULL }; GjsContext *cx = gjs_context_new_with_search_path((char **)search_path); GjsCoverage *coverage = NULL; if (coverage_prefix) { const char *coverage_prefixes[2] = { coverage_prefix, NULL }; if (!coverage_output_path) { bail_out(cx, "GJS_UNIT_COVERAGE_OUTPUT is required when using GJS_UNIT_COVERAGE_PREFIX"); } GFile *output = g_file_new_for_commandline_arg(coverage_output_path); coverage = gjs_coverage_new(coverage_prefixes, cx, output); g_object_unref(output); } GError *error = NULL; bool success; int code; success = gjs_context_eval(cx, "imports.minijasmine;", -1, "", &code, &error); if (!success) bail_out(cx, error->message); success = gjs_context_eval_file(cx, argv[1], &code, &error); if (!success) bail_out(cx, error->message); /* jasmineEnv.execute() queues up all the tests and runs them * asynchronously. This should start after the main loop starts, otherwise * we will hit the main loop only after several tests have already run. For * consistency we should guarantee that there is a main loop running during * all tests. */ const char *start_suite_script = "const GLib = imports.gi.GLib;\n" "GLib.idle_add(GLib.PRIORITY_DEFAULT, function () {\n" " try {\n" " window._jasmineEnv.execute();\n" " } catch (e) {\n" " print('Bail out! Exception occurred inside Jasmine:', e);\n" " window._jasmineRetval = 1;\n" " window._jasmineMain.quit();\n" " }\n" " return GLib.SOURCE_REMOVE;\n" "});\n" "window._jasmineMain.run();\n" "window._jasmineRetval;"; success = gjs_context_eval(cx, start_suite_script, -1, "", &code, &error); if (!success) bail_out(cx, error->message); if (code != 0) g_print("# Test script failed; see test log for assertions\n"); if (coverage) { gjs_coverage_write_statistics(coverage); g_clear_object(&coverage); } gjs_memory_report("before destroying context", false); g_object_unref(cx); gjs_memory_report("after destroying context", true); /* For TAP, should actually be return 0; as a nonzero return code would * indicate an error in the test harness. But that would be quite silly * when running the tests outside of the TAP driver. */ return code; } cjs-4.4.0/installed-tests/minijasmine.test.in000066400000000000000000000001721356375132300212360ustar00rootroot00000000000000[Test] Type=session Exec=@pkglibexecdir@/installed-tests/minijasmine @pkglibexecdir@/installed-tests/js/@name@ Output=TAP cjs-4.4.0/installed-tests/script.test.in000066400000000000000000000001261356375132300202360ustar00rootroot00000000000000[Test] Type=session Exec=sh @pkglibexecdir@/installed-tests/scripts/@name@ Output=TAP cjs-4.4.0/installed-tests/scripts/000077500000000000000000000000001356375132300171145ustar00rootroot00000000000000cjs-4.4.0/installed-tests/scripts/testCommandLine.sh000077500000000000000000000176501356375132300225520ustar00rootroot00000000000000#!/bin/sh if test "$GJS_USE_UNINSTALLED_FILES" = "1"; then gjs="$LOG_COMPILER $LOG_FLAGS $TOP_BUILDDIR/cjs-console" else gjs="$LOG_COMPILER $LOG_FLAGS cjs-console" fi # Avoid interference in the profiler tests from stray environment variable unset GJS_ENABLE_PROFILER # This JS script should exit immediately with code 42. If that is not working, # then it will exit after 3 seconds as a fallback, with code 0. cat <exit.js const GLib = imports.gi.GLib; let loop = GLib.MainLoop.new(null, false); GLib.idle_add(GLib.PRIORITY_LOW, () => imports.system.exit(42)); GLib.timeout_add_seconds(GLib.PRIORITY_HIGH, 3, () => loop.quit()); loop.run(); EOF # this JS script fails if either 1) --help is not passed to it, or 2) the string # "sentinel" is not in its search path cat <help.js const System = imports.system; if (imports.searchPath.indexOf('sentinel') == -1) System.exit(1); if (ARGV.indexOf('--help') == -1) System.exit(1); System.exit(0); EOF # this JS script should print one string (jobs are run before the interpreter # finishes) and should not print the other (jobs should not be run after the # interpreter is instructed to quit) cat <promise.js const System = imports.system; Promise.resolve().then(() => { print('Should be printed'); System.exit(42); }); Promise.resolve().then(() => print('Should not be printed')); EOF # this JS script should not cause an unhandled promise rejection cat <awaitcatch.js async function foo() { throw new Error('foo'); } async function bar() { try { await foo(); } catch (e) {} } bar(); EOF total=0 report () { exit_code=$? total=$((total + 1)) if test $exit_code -eq 0; then echo "ok $total - $1" else echo "not ok $total - $1" fi } report_xfail () { exit_code=$? total=$((total + 1)) if test $exit_code -ne 0; then echo "ok $total - $1" else echo "not ok $total - $1" fi } skip () { total=$((total + 1)) echo "ok $total - $1 # SKIP $2" } # Test that System.exit() works in gjs-console $gjs -c 'imports.system.exit(0)' report "System.exit(0) should exit successfully" $gjs -c 'imports.system.exit(42)' test $? -eq 42 report "System.exit(42) should exit with the correct exit code" # FIXME: should check -eq 42 specifically, but in debug mode we will be # hitting an assertion $gjs exit.js test $? -ne 0 report "System.exit() should still exit across an FFI boundary" # ensure the encoding of argv is being properly handled $gjs -c 'imports.system.exit((ARGV[0] !== "Valentín") ? 1 : 0)' "Valentín" report "Basic unicode encoding (accents, etc) should be functioning properly for ARGV and imports." $gjs -c 'imports.system.exit((ARGV[0] !== "☭") ? 1 : 0)' "☭" report "Unicode encoding for symbols should be functioning properly for ARGV and imports." # gjs --help prints GJS help $gjs --help >/dev/null report "--help should succeed" test -n "$($gjs --help)" report "--help should print something" # print GJS help even if it's not the first argument $gjs -I . --help >/dev/null report "should succeed when --help is not first arg" test -n "$($gjs -I . --help)" report "should print something when --help is not first arg" # --help before a script file name prints GJS help $gjs --help help.js >/dev/null report "--help should succeed before a script file" test -n "$($gjs --help help.js)" report "--help should print something before a script file" # --help before a -c argument prints GJS help script='imports.system.exit(1)' $gjs --help -c "$script" >/dev/null report "--help should succeed before -c" test -n "$($gjs --help -c "$script")" report "--help should print something before -c" # --help after a script file name is passed to the script $gjs -I sentinel help.js --help report "--help after script file should be passed to script" test -z "$("$gjs" -I sentinel help.js --help)" report "--help after script file should not print anything" # --help after a -c argument is passed to the script script='if(ARGV[0] !== "--help") imports.system.exit(1)' $gjs -c "$script" --help report "--help after -c should be passed to script" test -z "$("$gjs" -c "$script" --help)" report "--help after -c should not print anything" # -I after a program is not consumed by GJS # Temporary behaviour: still consume the argument, but give a warning # "$gjs" help.js --help -I sentinel # report_xfail "-I after script file should not be added to search path" # fi $gjs help.js --help -I sentinel 2>&1 | grep -q 'Cjs-WARNING.*--include-path' report "-I after script should succeed but give a warning" $gjs -c 'imports.system.exit(0)' --coverage-prefix=foo --coverage-output=foo 2>&1 | grep -q 'Cjs-WARNING.*--coverage-prefix' report "--coverage-prefix after script should succeed but give a warning" $gjs -c 'imports.system.exit(0)' --coverage-prefix=foo --coverage-output=foo 2>&1 | grep -q 'Cjs-WARNING.*--coverage-output' report "--coverage-output after script should succeed but give a warning" rm -f foo/coverage.lcov $gjs -c 'imports.system.exit(0)' --profile=foo 2>&1 | grep -q 'Gjs-WARNING.*--profile' report "--profile after script should succeed but give a warning" rm -f foo for version_arg in --version --jsversion; do # --version and --jsversion work $gjs $version_arg >/dev/null report "$version_arg should work" test -n "$($gjs $version_arg)" report "$version_arg should print something" # --version and --jsversion after a script go to the script script="if(ARGV[0] !== '$version_arg') imports.system.exit(1)" $gjs -c "$script" $version_arg report "$version_arg after -c should be passed to script" test -z "$($gjs -c "$script" $version_arg)" report "$version_arg after -c should not print anything" done # --profile rm -f gjs-*.syscap foo.syscap $gjs -c 'imports.system.exit(0)' && ! stat gjs-*.syscap &> /dev/null report "no profiling data should be dumped without --profile" # Skip some tests if built without profiler support if gjs --profile -c 1 2>&1 | grep -q 'Cjs-Message.*Profiler is disabled'; then reason="profiler is disabled" skip "--profile should dump profiling data to the default file name" "$reason" skip "--profile with argument should dump profiling data to the named file" "$reason" skip "GJS_ENABLE_PROFILER=1 should enable the profiler" "$reason" else rm -f gjs-*.syscap $gjs --profile -c 'imports.system.exit(0)' && stat gjs-*.syscap &> /dev/null report "--profile should dump profiling data to the default file name" $gjs --profile=foo.syscap -c 'imports.system.exit(0)' && test -f foo.syscap report "--profile with argument should dump profiling data to the named file" rm -f gjs-*.syscap foo.syscap GJS_ENABLE_PROFILER=1 $gjs -c 'imports.system.exit(0)' && test -f gjs-*.syscap report "GJS_ENABLE_PROFILER=1 should enable the profiler" rm -f gjs-*.syscap fi # interpreter handles queued promise jobs correctly output=$($gjs promise.js) test $? -eq 42 report "interpreter should exit with the correct exit code from a queued promise job" test -n "$output" -a -z "${output##*Should be printed*}" report "interpreter should run queued promise jobs before finishing" test -n "${output##*Should not be printed*}" report "interpreter should stop running jobs when one calls System.exit()" $gjs -c "Promise.resolve().then(() => { throw new Error(); });" 2>&1 | grep -q 'Cjs-WARNING.*Unhandled promise rejection.*[sS]tack trace' report "unhandled promise rejection should be reported" test -z $($gjs awaitcatch.js) report "catching an await expression should not cause unhandled rejection" # https://gitlab.gnome.org/GNOME/gjs/issues/18 $gjs -c "(async () => await true)(); void foobar;" 2>&1 | grep -q 'Script .* threw an exception' report "main program exceptions are not swallowed by queued promise jobs" # https://gitlab.gnome.org/GNOME/gjs/issues/26 $gjs -c 'new imports.gi.Gio.Subprocess({argv: ["true"]}).init(null);' report "object unref from other thread after shutdown should not race" rm -f exit.js help.js promise.js awaitcatch.js echo "1..$total" cjs-4.4.0/installed-tests/scripts/testWarnings.sh000077500000000000000000000016031356375132300221430ustar00rootroot00000000000000#!/bin/sh if test "$GJS_USE_UNINSTALLED_FILES" = "1"; then gjs="$LOG_COMPILER $LOG_FLAGS $TOP_BUILDDIR/cjs-console" else gjs="$LOG_COMPILER $LOG_FLAGS cjs-console" fi total=0 report () { exit_code=$? total=$((total + 1)) if test $exit_code -eq 0; then echo "ok $total - $1" else echo "not ok $total - $1" fi } $gjs -c 'imports.signals.addSignalMethods({connect: "foo"})' 2>&1 | \ grep -q 'addSignalMethods is replacing existing .* connect method' report "overwriting method with Signals.addSignalMethods() should warn" $gjs -c 'imports.gi.GLib.get_home_dir("foobar")' 2>&1 | \ grep -q 'Too many arguments to .*: expected 0, got 1' report "passing too many arguments to a GI function should warn" $gjs -c '**' 2>&1 | \ grep -q 'SyntaxError.*@ :1' report "file and line number are logged for syntax errors" echo "1..$total" cjs-4.4.0/libgjs-private/000077500000000000000000000000001356375132300152305ustar00rootroot00000000000000cjs-4.4.0/libgjs-private/gjs-gdbus-wrapper.cpp000066400000000000000000000325601356375132300213050ustar00rootroot00000000000000/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2011 Giovanni Campagna. All Rights Reserved. */ #include #include #include "gjs-gdbus-wrapper.h" enum { PROP_0, PROP_G_INTERFACE_INFO, PROP_LAST }; enum { SIGNAL_HANDLE_METHOD, SIGNAL_HANDLE_PROPERTY_GET, SIGNAL_HANDLE_PROPERTY_SET, SIGNAL_LAST, }; static guint signals[SIGNAL_LAST]; struct _GjsDBusImplementationPrivate { GDBusInterfaceVTable vtable; GDBusInterfaceInfo *ifaceinfo; // from gchar* to GVariant* GHashTable *outstanding_properties; guint idle_id; }; /* Temporary workaround for https://bugzilla.gnome.org/show_bug.cgi?id=793175 */ #if __GNUC__ >= 8 _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wcast-function-type\"") #endif G_DEFINE_TYPE_WITH_PRIVATE(GjsDBusImplementation, gjs_dbus_implementation, G_TYPE_DBUS_INTERFACE_SKELETON); #if __GNUC__ >= 8 _Pragma("GCC diagnostic pop") #endif static void gjs_dbus_implementation_method_call(GDBusConnection *connection, const char *sender, const char *object_path, const char *interface_name, const char *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) { GjsDBusImplementation *self = GJS_DBUS_IMPLEMENTATION (user_data); g_signal_emit(self, signals[SIGNAL_HANDLE_METHOD], 0, method_name, parameters, invocation); g_object_unref (invocation); } static GVariant * gjs_dbus_implementation_property_get(GDBusConnection *connection, const char *sender, const char *object_path, const char *interface_name, const char *property_name, GError **error, gpointer user_data) { GjsDBusImplementation *self = GJS_DBUS_IMPLEMENTATION (user_data); GVariant *value; g_signal_emit(self, signals[SIGNAL_HANDLE_PROPERTY_GET], 0, property_name, &value); /* Marshaling GErrors is not supported, so this is the best we can do (GIO will assert if value is NULL and error is not set) */ if (!value) g_set_error(error, g_quark_from_static_string("gjs-error-domain"), 0, "Property retrieval failed"); return value; } static gboolean gjs_dbus_implementation_property_set(GDBusConnection *connection, const char *sender, const char *object_path, const char *interface_name, const char *property_name, GVariant *value, GError **error, gpointer user_data) { GjsDBusImplementation *self = GJS_DBUS_IMPLEMENTATION (user_data); g_signal_emit(self, signals[SIGNAL_HANDLE_PROPERTY_SET], 0, property_name, value); return true; } static void gjs_dbus_implementation_init(GjsDBusImplementation *self) { GjsDBusImplementationPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GJS_TYPE_DBUS_IMPLEMENTATION, GjsDBusImplementationPrivate); self->priv = priv; priv->vtable.method_call = gjs_dbus_implementation_method_call; priv->vtable.get_property = gjs_dbus_implementation_property_get; priv->vtable.set_property = gjs_dbus_implementation_property_set; priv->outstanding_properties = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_variant_unref); } static void gjs_dbus_implementation_finalize(GObject *object) { GjsDBusImplementation *self = GJS_DBUS_IMPLEMENTATION (object); g_dbus_interface_info_unref (self->priv->ifaceinfo); g_hash_table_unref (self->priv->outstanding_properties); G_OBJECT_CLASS(gjs_dbus_implementation_parent_class)->finalize(object); } static void gjs_dbus_implementation_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GjsDBusImplementation *self = GJS_DBUS_IMPLEMENTATION (object); switch (property_id) { case PROP_G_INTERFACE_INFO: self->priv->ifaceinfo = (GDBusInterfaceInfo*) g_value_dup_boxed (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static GDBusInterfaceInfo * gjs_dbus_implementation_get_info (GDBusInterfaceSkeleton *skeleton) { GjsDBusImplementation *self = GJS_DBUS_IMPLEMENTATION (skeleton); return self->priv->ifaceinfo; } static GDBusInterfaceVTable * gjs_dbus_implementation_get_vtable (GDBusInterfaceSkeleton *skeleton) { GjsDBusImplementation *self = GJS_DBUS_IMPLEMENTATION (skeleton); return &(self->priv->vtable); } static GVariant * gjs_dbus_implementation_get_properties (GDBusInterfaceSkeleton *skeleton) { GjsDBusImplementation *self = GJS_DBUS_IMPLEMENTATION (skeleton); GDBusInterfaceInfo *info = self->priv->ifaceinfo; GDBusPropertyInfo **props; GVariantBuilder builder; g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); for (props = info->properties; *props; ++props) { GDBusPropertyInfo *prop = *props; GVariant *value; /* If we have a cached value, we use that instead of querying again */ if ((value = (GVariant*) g_hash_table_lookup(self->priv->outstanding_properties, prop->name))) { g_variant_builder_add(&builder, "{sv}", prop->name, value); continue; } g_signal_emit(self, signals[SIGNAL_HANDLE_PROPERTY_GET], 0, prop->name, &value); g_variant_builder_add(&builder, "{sv}", prop->name, value); } return g_variant_builder_end(&builder); } static void gjs_dbus_implementation_flush (GDBusInterfaceSkeleton *skeleton) { GjsDBusImplementation *self = GJS_DBUS_IMPLEMENTATION (skeleton); GVariantBuilder changed_props; GVariantBuilder invalidated_props; GHashTableIter iter; GVariant *val; gchar *prop_name; g_variant_builder_init(&changed_props, G_VARIANT_TYPE_VARDICT); g_variant_builder_init(&invalidated_props, G_VARIANT_TYPE_STRING_ARRAY); g_hash_table_iter_init(&iter, self->priv->outstanding_properties); while (g_hash_table_iter_next(&iter, (void**) &prop_name, (void**) &val)) { if (val) g_variant_builder_add(&changed_props, "{sv}", prop_name, val); else g_variant_builder_add(&invalidated_props, "s", prop_name); } g_dbus_connection_emit_signal(g_dbus_interface_skeleton_get_connection(skeleton), NULL, /* bus name */ g_dbus_interface_skeleton_get_object_path(skeleton), "org.freedesktop.DBus.Properties", "PropertiesChanged", g_variant_new("(s@a{sv}@as)", self->priv->ifaceinfo->name, g_variant_builder_end(&changed_props), g_variant_builder_end(&invalidated_props)), NULL /* error */); g_hash_table_remove_all(self->priv->outstanding_properties); if (self->priv->idle_id) { g_source_remove(self->priv->idle_id); self->priv->idle_id = 0; } } void gjs_dbus_implementation_class_init(GjsDBusImplementationClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS(klass); GDBusInterfaceSkeletonClass *skeleton_class = G_DBUS_INTERFACE_SKELETON_CLASS(klass); gobject_class->finalize = gjs_dbus_implementation_finalize; gobject_class->set_property = gjs_dbus_implementation_set_property; skeleton_class->get_info = gjs_dbus_implementation_get_info; skeleton_class->get_vtable = gjs_dbus_implementation_get_vtable; skeleton_class->get_properties = gjs_dbus_implementation_get_properties; skeleton_class->flush = gjs_dbus_implementation_flush; g_object_class_install_property(gobject_class, PROP_G_INTERFACE_INFO, g_param_spec_boxed("g-interface-info", "Interface Info", "A DBusInterfaceInfo representing the exported object", G_TYPE_DBUS_INTERFACE_INFO, (GParamFlags) (G_PARAM_STATIC_STRINGS | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY))); signals[SIGNAL_HANDLE_METHOD] = g_signal_new("handle-method-call", G_TYPE_FROM_CLASS(klass), (GSignalFlags) 0, /* flags */ 0, /* closure */ NULL, /* accumulator */ NULL, /* accumulator data */ NULL, /* C marshal */ G_TYPE_NONE, 3, G_TYPE_STRING, /* method name */ G_TYPE_VARIANT, /* parameters */ G_TYPE_DBUS_METHOD_INVOCATION); signals[SIGNAL_HANDLE_PROPERTY_GET] = g_signal_new("handle-property-get", G_TYPE_FROM_CLASS(klass), (GSignalFlags) 0, /* flags */ 0, /* closure */ g_signal_accumulator_first_wins, NULL, /* accumulator data */ NULL, /* C marshal */ G_TYPE_VARIANT, 1, G_TYPE_STRING /* property name */); signals[SIGNAL_HANDLE_PROPERTY_SET] = g_signal_new("handle-property-set", G_TYPE_FROM_CLASS(klass), (GSignalFlags) 0, /* flags */ 0, /* closure */ NULL, /* accumulator */ NULL, /* accumulator data */ NULL, /* C marshal */ G_TYPE_NONE, 2, G_TYPE_STRING, /* property name */ G_TYPE_VARIANT /* parameters */); } static gboolean idle_cb (gpointer data) { GDBusInterfaceSkeleton *skeleton = G_DBUS_INTERFACE_SKELETON (data); g_dbus_interface_skeleton_flush(skeleton); return G_SOURCE_REMOVE; } /** * gjs_dbus_implementation_emit_property_changed: * @self: a #GjsDBusImplementation * @property: the name of the property that changed * @newvalue: (allow-none): the new value, or %NULL to just invalidate it * * Queue a PropertyChanged signal for emission, or update the one queued * adding @property */ void gjs_dbus_implementation_emit_property_changed (GjsDBusImplementation *self, gchar *property, GVariant *newvalue) { g_hash_table_replace (self->priv->outstanding_properties, g_strdup (property), g_variant_ref (newvalue)); if (!self->priv->idle_id) self->priv->idle_id = g_idle_add(idle_cb, self); } /** * gjs_dbus_implementation_emit_signal: * @self: a #GjsDBusImplementation * @signal_name: the name of the signal * @parameters: (allow-none): signal parameters, or %NULL for none * * Emits a signal named @signal_name from the object and interface represented * by @self. This signal has no destination. */ void gjs_dbus_implementation_emit_signal (GjsDBusImplementation *self, gchar *signal_name, GVariant *parameters) { GDBusInterfaceSkeleton *skeleton = G_DBUS_INTERFACE_SKELETON (self); g_dbus_connection_emit_signal(g_dbus_interface_skeleton_get_connection(skeleton), NULL, g_dbus_interface_skeleton_get_object_path(skeleton), self->priv->ifaceinfo->name, signal_name, parameters, NULL); } cjs-4.4.0/libgjs-private/gjs-gdbus-wrapper.h000066400000000000000000000055461356375132300207560ustar00rootroot00000000000000/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2011 Giovanni Campagna * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_UTIL_DBUS_H__ #define __GJS_UTIL_DBUS_H__ #include #include #include #include G_BEGIN_DECLS typedef struct _GjsDBusImplementation GjsDBusImplementation; typedef struct _GjsDBusImplementationClass GjsDBusImplementationClass; typedef struct _GjsDBusImplementationPrivate GjsDBusImplementationPrivate; #define GJS_TYPE_DBUS_IMPLEMENTATION (gjs_dbus_implementation_get_type ()) #define GJS_DBUS_IMPLEMENTATION(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GJS_TYPE_DBUS_IMPLEMENTATION, GjsDBusImplementation)) #define GJS_DBUS_IMPLEMENTATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GJS_TYPE_DBUS_IMPLEMENTATION, GjsDBusImplementationClass)) #define GJS_IS_DBUS_IMPLEMENTATION(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GJS_TYPE_DBUS_IMPLEMENTATION)) #define GJS_IS_DBUS_IMPLEMENTATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GJS_TYPE_DBUS_IMPLEMENTATION)) #define GJS_DBUS_IMPLEMENTATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GJS_TYPE_DBUS_IMPLEMENTATION, GjsDBusImplementationClass)) struct _GjsDBusImplementation { GDBusInterfaceSkeleton parent; GjsDBusImplementationPrivate *priv; }; struct _GjsDBusImplementationClass { GDBusInterfaceSkeletonClass parent_class; }; GJS_EXPORT GType gjs_dbus_implementation_get_type (void); void gjs_dbus_implementation_emit_property_changed (GjsDBusImplementation *self, gchar *property, GVariant *newvalue); void gjs_dbus_implementation_emit_signal (GjsDBusImplementation *self, gchar *signal_name, GVariant *parameters); G_END_DECLS #endif /* __GJS_UTIL_DBUS_H__ */ cjs-4.4.0/libgjs-private/gjs-gtk-util.c000066400000000000000000000052711356375132300177220ustar00rootroot00000000000000/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2014 Endless Mobile, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include "gjs-gtk-util.h" void gjs_gtk_container_child_set_property (GtkContainer *container, GtkWidget *child, const gchar *property, const GValue *value) { GParamSpec *pspec; pspec = gtk_container_class_find_child_property (G_OBJECT_GET_CLASS (container), property); if (pspec == NULL) { g_warning ("%s does not have a property called %s", g_type_name (G_OBJECT_TYPE (container)), property); return; } if ((G_VALUE_TYPE (value) == G_TYPE_POINTER) && (g_value_get_pointer (value) == NULL) && !g_value_type_transformable (G_VALUE_TYPE (value), pspec->value_type)) { /* Set an empty value. This will happen when we set a NULL value from JS. * Since GJS doesn't know the GParamSpec for this property, it * will just put NULL into a G_TYPE_POINTER GValue, which will later * fail when trying to transform it to the GParamSpec's GType. */ GValue null_value = G_VALUE_INIT; g_value_init (&null_value, pspec->value_type); gtk_container_child_set_property (container, child, property, &null_value); g_value_unset (&null_value); } else { gtk_container_child_set_property (container, child, property, value); } } cjs-4.4.0/libgjs-private/gjs-gtk-util.h000066400000000000000000000031721356375132300177250ustar00rootroot00000000000000/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2014 Endless Mobile, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_PRIVATE_GTK_UTIL_H__ #define __GJS_PRIVATE_GTK_UTIL_H__ #include "config.h" #ifdef ENABLE_GTK #include G_BEGIN_DECLS void gjs_gtk_container_child_set_property (GtkContainer *container, GtkWidget *child, const gchar *property, const GValue *value); G_END_DECLS #endif #endif /* __GJS_PRIVATE_GTK_UTIL_H__ */ cjs-4.4.0/libgjs-private/gjs-util.cpp000066400000000000000000000065171356375132300175030ustar00rootroot00000000000000/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2012 Giovanni Campagna * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include "gjs-util.h" char * gjs_format_int_alternative_output(int n) { #ifdef HAVE_PRINTF_ALTERNATIVE_INT return g_strdup_printf("%Id", n); #else return g_strdup_printf("%d", n); #endif } GType gjs_locale_category_get_type(void) { static volatile size_t g_define_type_id__volatile = 0; if (g_once_init_enter(&g_define_type_id__volatile)) { static const GEnumValue v[] = { { GJS_LOCALE_CATEGORY_ALL, "GJS_LOCALE_CATEGORY_ALL", "all" }, { GJS_LOCALE_CATEGORY_COLLATE, "GJS_LOCALE_CATEGORY_COLLATE", "collate" }, { GJS_LOCALE_CATEGORY_CTYPE, "GJS_LOCALE_CATEGORY_CTYPE", "ctype" }, { GJS_LOCALE_CATEGORY_MESSAGES, "GJS_LOCALE_CATEGORY_MESSAGES", "messages" }, { GJS_LOCALE_CATEGORY_MONETARY, "GJS_LOCALE_CATEGORY_MONETARY", "monetary" }, { GJS_LOCALE_CATEGORY_NUMERIC, "GJS_LOCALE_CATEGORY_NUMERIC", "numeric" }, { GJS_LOCALE_CATEGORY_TIME, "GJS_LOCALE_CATEGORY_TIME", "time" }, { 0, NULL, NULL } }; GType g_define_type_id = g_enum_register_static(g_intern_static_string("GjsLocaleCategory"), v); g_once_init_leave(&g_define_type_id__volatile, g_define_type_id); } return g_define_type_id__volatile; } /** * gjs_setlocale: * @category: * @locale: (allow-none): * * Returns: */ const char * gjs_setlocale(GjsLocaleCategory category, const char *locale) { /* According to man setlocale(3), the return value may be allocated in * static storage. */ return (const char *) setlocale(category, locale); } void gjs_textdomain(const char *domain) { textdomain(domain); } void gjs_bindtextdomain(const char *domain, const char *location) { bindtextdomain(domain, location); /* Always use UTF-8; we assume it internally here */ bind_textdomain_codeset(domain, "UTF-8"); } GParamFlags gjs_param_spec_get_flags(GParamSpec *pspec) { return pspec->flags; } GType gjs_param_spec_get_value_type(GParamSpec *pspec) { return pspec->value_type; } GType gjs_param_spec_get_owner_type(GParamSpec *pspec) { return pspec->owner_type; } cjs-4.4.0/libgjs-private/gjs-util.h000066400000000000000000000046241356375132300171450ustar00rootroot00000000000000/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2012 Giovanni Campagna * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_PRIVATE_UTIL_H__ #define __GJS_PRIVATE_UTIL_H__ #include #include #include #include G_BEGIN_DECLS /* For imports.format */ char * gjs_format_int_alternative_output (int n); /* For imports.gettext */ typedef enum { GJS_LOCALE_CATEGORY_ALL = LC_ALL, GJS_LOCALE_CATEGORY_COLLATE = LC_COLLATE, GJS_LOCALE_CATEGORY_CTYPE = LC_CTYPE, GJS_LOCALE_CATEGORY_MESSAGES = LC_MESSAGES, GJS_LOCALE_CATEGORY_MONETARY = LC_MONETARY, GJS_LOCALE_CATEGORY_NUMERIC = LC_NUMERIC, GJS_LOCALE_CATEGORY_TIME = LC_TIME } GjsLocaleCategory; const char *gjs_setlocale (GjsLocaleCategory category, const char *locale); void gjs_textdomain (const char *domain); void gjs_bindtextdomain (const char *domain, const char *location); GJS_EXPORT GType gjs_locale_category_get_type (void) G_GNUC_CONST; /* For imports.overrides.GObject */ GParamFlags gjs_param_spec_get_flags (GParamSpec *pspec); GType gjs_param_spec_get_value_type (GParamSpec *pspec); GType gjs_param_spec_get_owner_type (GParamSpec *pspec); G_END_DECLS #endif cjs-4.4.0/m4/000077500000000000000000000000001356375132300126265ustar00rootroot00000000000000cjs-4.4.0/m4/ax_code_coverage.m4000066400000000000000000000270741356375132300163570ustar00rootroot00000000000000# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_code_coverage.html # =========================================================================== # # SYNOPSIS # # AX_CODE_COVERAGE() # # DESCRIPTION # # Defines CODE_COVERAGE_CPPFLAGS, CODE_COVERAGE_CFLAGS, # CODE_COVERAGE_CXXFLAGS and CODE_COVERAGE_LIBS which should be included # in the CPPFLAGS, CFLAGS CXXFLAGS and LIBS/LIBADD variables of every # build target (program or library) which should be built with code # coverage support. Also defines CODE_COVERAGE_RULES which should be # substituted in your Makefile; and $enable_code_coverage which can be # used in subsequent configure output. CODE_COVERAGE_ENABLED is defined # and substituted, and corresponds to the value of the # --enable-code-coverage option, which defaults to being disabled. # # Test also for gcov program and create GCOV variable that could be # substituted. # # Note that all optimization flags in CFLAGS must be disabled when code # coverage is enabled. # # Usage example: # # configure.ac: # # AX_CODE_COVERAGE # # Makefile.am: # # @CODE_COVERAGE_RULES@ # my_program_LIBS = ... $(CODE_COVERAGE_LIBS) ... # my_program_CPPFLAGS = ... $(CODE_COVERAGE_CPPFLAGS) ... # my_program_CFLAGS = ... $(CODE_COVERAGE_CFLAGS) ... # my_program_CXXFLAGS = ... $(CODE_COVERAGE_CXXFLAGS) ... # # This results in a "check-code-coverage" rule being added to any # Makefile.am which includes "@CODE_COVERAGE_RULES@" (assuming the module # has been configured with --enable-code-coverage). Running `make # check-code-coverage` in that directory will run the module's test suite # (`make check`) and build a code coverage report detailing the code which # was touched, then print the URI for the report. # # In earlier versions of this macro, CODE_COVERAGE_LDFLAGS was defined # instead of CODE_COVERAGE_LIBS. They are both still defined, but use of # CODE_COVERAGE_LIBS is preferred for clarity; CODE_COVERAGE_LDFLAGS is # deprecated. They have the same value. # # This code was derived from Makefile.decl in GLib, originally licenced # under LGPLv2.1+. # # LICENSE # # Copyright (c) 2012, 2016 Philip Withnall # Copyright (c) 2012 Xan Lopez # Copyright (c) 2012 Christian Persch # Copyright (c) 2012 Paolo Borelli # Copyright (c) 2012 Dan Winship # Copyright (c) 2015 Bastien ROUCARIES # # This library is free software; you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation; either version 2.1 of the License, or (at # your option) any later version. # # This library is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser # General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . #serial 25 AC_DEFUN([AX_CODE_COVERAGE],[ dnl Check for --enable-code-coverage AC_REQUIRE([AC_PROG_SED]) # allow to override gcov location AC_ARG_WITH([gcov], [AS_HELP_STRING([--with-gcov[=GCOV]], [use given GCOV for coverage (GCOV=gcov).])], [_AX_CODE_COVERAGE_GCOV_PROG_WITH=$with_gcov], [_AX_CODE_COVERAGE_GCOV_PROG_WITH=gcov]) AC_MSG_CHECKING([whether to build with code coverage support]) AC_ARG_ENABLE([code-coverage], AS_HELP_STRING([--enable-code-coverage], [Whether to enable code coverage support]),, enable_code_coverage=no) AM_CONDITIONAL([CODE_COVERAGE_ENABLED], [test x$enable_code_coverage = xyes]) AC_SUBST([CODE_COVERAGE_ENABLED], [$enable_code_coverage]) AC_MSG_RESULT($enable_code_coverage) AS_IF([ test "$enable_code_coverage" = "yes" ], [ # check for gcov AC_CHECK_TOOL([GCOV], [$_AX_CODE_COVERAGE_GCOV_PROG_WITH], [:]) AS_IF([test "X$GCOV" = "X:"], [AC_MSG_ERROR([gcov is needed to do coverage])]) AC_SUBST([GCOV]) dnl Check if gcc is being used AS_IF([ test "$GCC" = "no" ], [ AC_MSG_ERROR([not compiling with gcc, which is required for gcov code coverage]) ]) AC_CHECK_PROG([LCOV], [lcov], [lcov]) AC_CHECK_PROG([GENHTML], [genhtml], [genhtml]) AS_IF([ test -z "$LCOV" ], [ AC_MSG_ERROR([To enable code coverage reporting you must have lcov installed]) ]) AS_IF([ test -z "$GENHTML" ], [ AC_MSG_ERROR([Could not find genhtml from the lcov package]) ]) dnl Build the code coverage flags dnl Define CODE_COVERAGE_LDFLAGS for backwards compatibility CODE_COVERAGE_CPPFLAGS="-DNDEBUG" CODE_COVERAGE_CFLAGS="-O0 -g -fprofile-arcs -ftest-coverage" CODE_COVERAGE_CXXFLAGS="-O0 -g -fprofile-arcs -ftest-coverage" CODE_COVERAGE_LIBS="-lgcov" CODE_COVERAGE_LDFLAGS="$CODE_COVERAGE_LIBS" AC_SUBST([CODE_COVERAGE_CPPFLAGS]) AC_SUBST([CODE_COVERAGE_CFLAGS]) AC_SUBST([CODE_COVERAGE_CXXFLAGS]) AC_SUBST([CODE_COVERAGE_LIBS]) AC_SUBST([CODE_COVERAGE_LDFLAGS]) [CODE_COVERAGE_RULES_CHECK=' -$(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) -k check $(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) code-coverage-capture '] [CODE_COVERAGE_RULES_CAPTURE=' $(code_coverage_v_lcov_cap)$(LCOV) $(code_coverage_quiet) $(addprefix --directory ,$(CODE_COVERAGE_DIRECTORY)) --capture --output-file "$(CODE_COVERAGE_OUTPUT_FILE).tmp" --test-name "$(call code_coverage_sanitize,$(PACKAGE_NAME)-$(PACKAGE_VERSION))" --no-checksum --compat-libtool $(CODE_COVERAGE_LCOV_SHOPTS) $(CODE_COVERAGE_LCOV_OPTIONS) $(code_coverage_v_lcov_ign)$(LCOV) $(code_coverage_quiet) $(addprefix --directory ,$(CODE_COVERAGE_DIRECTORY)) --remove "$(CODE_COVERAGE_OUTPUT_FILE).tmp" "/tmp/*" $(CODE_COVERAGE_IGNORE_PATTERN) --output-file "$(CODE_COVERAGE_OUTPUT_FILE)" $(CODE_COVERAGE_LCOV_SHOPTS) $(CODE_COVERAGE_LCOV_RMOPTS) -@rm -f $(CODE_COVERAGE_OUTPUT_FILE).tmp $(code_coverage_v_genhtml)LANG=C $(GENHTML) $(code_coverage_quiet) $(addprefix --prefix ,$(CODE_COVERAGE_DIRECTORY)) --output-directory "$(CODE_COVERAGE_OUTPUT_DIRECTORY)" --title "$(PACKAGE_NAME)-$(PACKAGE_VERSION) Code Coverage" --legend --show-details "$(CODE_COVERAGE_OUTPUT_FILE)" $(CODE_COVERAGE_GENHTML_OPTIONS) @echo "file://$(abs_builddir)/$(CODE_COVERAGE_OUTPUT_DIRECTORY)/index.html" '] [CODE_COVERAGE_RULES_CLEAN=' clean: code-coverage-clean distclean: code-coverage-clean code-coverage-clean: -$(LCOV) --directory $(top_builddir) -z -rm -rf $(CODE_COVERAGE_OUTPUT_FILE) $(CODE_COVERAGE_OUTPUT_FILE).tmp $(CODE_COVERAGE_OUTPUT_DIRECTORY) -find . \( -name "*.gcda" -o -name "*.gcno" -o -name "*.gcov" \) -delete '] ], [ [CODE_COVERAGE_RULES_CHECK=' @echo "Need to reconfigure with --enable-code-coverage" '] CODE_COVERAGE_RULES_CAPTURE="$CODE_COVERAGE_RULES_CHECK" CODE_COVERAGE_RULES_CLEAN='' ]) [CODE_COVERAGE_RULES=' # Code coverage # # Optional: # - CODE_COVERAGE_DIRECTORY: Top-level directory for code coverage reporting. # Multiple directories may be specified, separated by whitespace. # (Default: $(top_builddir)) # - CODE_COVERAGE_OUTPUT_FILE: Filename and path for the .info file generated # by lcov for code coverage. (Default: # $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info) # - CODE_COVERAGE_OUTPUT_DIRECTORY: Directory for generated code coverage # reports to be created. (Default: # $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage) # - CODE_COVERAGE_BRANCH_COVERAGE: Set to 1 to enforce branch coverage, # set to 0 to disable it and leave empty to stay with the default. # (Default: empty) # - CODE_COVERAGE_LCOV_SHOPTS_DEFAULT: Extra options shared between both lcov # instances. (Default: based on $CODE_COVERAGE_BRANCH_COVERAGE) # - CODE_COVERAGE_LCOV_SHOPTS: Extra options to shared between both lcov # instances. (Default: $CODE_COVERAGE_LCOV_SHOPTS_DEFAULT) # - CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH: --gcov-tool pathtogcov # - CODE_COVERAGE_LCOV_OPTIONS_DEFAULT: Extra options to pass to the # collecting lcov instance. (Default: $CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH) # - CODE_COVERAGE_LCOV_OPTIONS: Extra options to pass to the collecting lcov # instance. (Default: $CODE_COVERAGE_LCOV_OPTIONS_DEFAULT) # - CODE_COVERAGE_LCOV_RMOPTS_DEFAULT: Extra options to pass to the filtering # lcov instance. (Default: empty) # - CODE_COVERAGE_LCOV_RMOPTS: Extra options to pass to the filtering lcov # instance. (Default: $CODE_COVERAGE_LCOV_RMOPTS_DEFAULT) # - CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT: Extra options to pass to the # genhtml instance. (Default: based on $CODE_COVERAGE_BRANCH_COVERAGE) # - CODE_COVERAGE_GENHTML_OPTIONS: Extra options to pass to the genhtml # instance. (Default: $CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT) # - CODE_COVERAGE_IGNORE_PATTERN: Extra glob pattern of files to ignore # # The generated report will be titled using the $(PACKAGE_NAME) and # $(PACKAGE_VERSION). In order to add the current git hash to the title, # use the git-version-gen script, available online. # Optional variables CODE_COVERAGE_DIRECTORY ?= $(top_builddir) CODE_COVERAGE_OUTPUT_FILE ?= $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info CODE_COVERAGE_OUTPUT_DIRECTORY ?= $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage CODE_COVERAGE_BRANCH_COVERAGE ?= CODE_COVERAGE_LCOV_SHOPTS_DEFAULT ?= $(if $(CODE_COVERAGE_BRANCH_COVERAGE),\ --rc lcov_branch_coverage=$(CODE_COVERAGE_BRANCH_COVERAGE)) CODE_COVERAGE_LCOV_SHOPTS ?= $(CODE_COVERAGE_LCOV_SHOPTS_DEFAULT) CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH ?= --gcov-tool "$(GCOV)" CODE_COVERAGE_LCOV_OPTIONS_DEFAULT ?= $(CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH) CODE_COVERAGE_LCOV_OPTIONS ?= $(CODE_COVERAGE_LCOV_OPTIONS_DEFAULT) CODE_COVERAGE_LCOV_RMOPTS_DEFAULT ?= CODE_COVERAGE_LCOV_RMOPTS ?= $(CODE_COVERAGE_LCOV_RMOPTS_DEFAULT) CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT ?=\ $(if $(CODE_COVERAGE_BRANCH_COVERAGE),\ --rc genhtml_branch_coverage=$(CODE_COVERAGE_BRANCH_COVERAGE)) CODE_COVERAGE_GENHTML_OPTIONS ?= $(CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT) CODE_COVERAGE_IGNORE_PATTERN ?= GITIGNOREFILES ?= GITIGNOREFILES += $(CODE_COVERAGE_OUTPUT_FILE) $(CODE_COVERAGE_OUTPUT_DIRECTORY) code_coverage_v_lcov_cap = $(code_coverage_v_lcov_cap_$(V)) code_coverage_v_lcov_cap_ = $(code_coverage_v_lcov_cap_$(AM_DEFAULT_VERBOSITY)) code_coverage_v_lcov_cap_0 = @echo " LCOV --capture"\ $(CODE_COVERAGE_OUTPUT_FILE); code_coverage_v_lcov_ign = $(code_coverage_v_lcov_ign_$(V)) code_coverage_v_lcov_ign_ = $(code_coverage_v_lcov_ign_$(AM_DEFAULT_VERBOSITY)) code_coverage_v_lcov_ign_0 = @echo " LCOV --remove /tmp/*"\ $(CODE_COVERAGE_IGNORE_PATTERN); code_coverage_v_genhtml = $(code_coverage_v_genhtml_$(V)) code_coverage_v_genhtml_ = $(code_coverage_v_genhtml_$(AM_DEFAULT_VERBOSITY)) code_coverage_v_genhtml_0 = @echo " GEN " $(CODE_COVERAGE_OUTPUT_DIRECTORY); code_coverage_quiet = $(code_coverage_quiet_$(V)) code_coverage_quiet_ = $(code_coverage_quiet_$(AM_DEFAULT_VERBOSITY)) code_coverage_quiet_0 = --quiet # sanitizes the test-name: replaces with underscores: dashes and dots code_coverage_sanitize = $(subst -,_,$(subst .,_,$(1))) # Use recursive makes in order to ignore errors during check check-code-coverage:'"$CODE_COVERAGE_RULES_CHECK"' # Capture code coverage data code-coverage-capture: code-coverage-capture-hook'"$CODE_COVERAGE_RULES_CAPTURE"' # Hook rule executed before code-coverage-capture, overridable by the user code-coverage-capture-hook: '"$CODE_COVERAGE_RULES_CLEAN"' A''M_DISTCHECK_CONFIGURE_FLAGS ?= A''M_DISTCHECK_CONFIGURE_FLAGS += --disable-code-coverage .PHONY: check-code-coverage code-coverage-capture code-coverage-capture-hook code-coverage-clean '] AC_SUBST([CODE_COVERAGE_RULES]) m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([CODE_COVERAGE_RULES])]) ]) cjs-4.4.0/m4/extensions.m4000066400000000000000000000153651356375132300153010ustar00rootroot00000000000000# serial 17 -*- Autoconf -*- # Enable extensions on systems that normally disable them. # Copyright (C) 2003, 2006-2018 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This definition of AC_USE_SYSTEM_EXTENSIONS is stolen from git # Autoconf. Perhaps we can remove this once we can assume Autoconf # 2.70 or later everywhere, but since Autoconf mutates rapidly # enough in this area it's likely we'll need to redefine # AC_USE_SYSTEM_EXTENSIONS for quite some time. # If autoconf reports a warning # warning: AC_COMPILE_IFELSE was called before AC_USE_SYSTEM_EXTENSIONS # or warning: AC_RUN_IFELSE was called before AC_USE_SYSTEM_EXTENSIONS # the fix is # 1) to ensure that AC_USE_SYSTEM_EXTENSIONS is never directly invoked # but always AC_REQUIREd, # 2) to ensure that for each occurrence of # AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS]) # or # AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS]) # the corresponding gnulib module description has 'extensions' among # its dependencies. This will ensure that the gl_USE_SYSTEM_EXTENSIONS # invocation occurs in gl_EARLY, not in gl_INIT. # AC_USE_SYSTEM_EXTENSIONS # ------------------------ # Enable extensions on systems that normally disable them, # typically due to standards-conformance issues. # # Remember that #undef in AH_VERBATIM gets replaced with #define by # AC_DEFINE. The goal here is to define all known feature-enabling # macros, then, if reports of conflicts are made, disable macros that # cause problems on some platforms (such as __EXTENSIONS__). AC_DEFUN_ONCE([AC_USE_SYSTEM_EXTENSIONS], [AC_BEFORE([$0], [AC_COMPILE_IFELSE])dnl AC_BEFORE([$0], [AC_RUN_IFELSE])dnl AC_CHECK_HEADER([minix/config.h], [MINIX=yes], [MINIX=]) if test "$MINIX" = yes; then AC_DEFINE([_POSIX_SOURCE], [1], [Define to 1 if you need to in order for 'stat' and other things to work.]) AC_DEFINE([_POSIX_1_SOURCE], [2], [Define to 2 if the system does not provide POSIX.1 features except with this defined.]) AC_DEFINE([_MINIX], [1], [Define to 1 if on MINIX.]) AC_DEFINE([_NETBSD_SOURCE], [1], [Define to 1 to make NetBSD features available. MINIX 3 needs this.]) fi dnl Use a different key than __EXTENSIONS__, as that name broke existing dnl configure.ac when using autoheader 2.62. AH_VERBATIM([USE_SYSTEM_EXTENSIONS], [/* Enable extensions on AIX 3, Interix. */ #ifndef _ALL_SOURCE # undef _ALL_SOURCE #endif /* Enable general extensions on macOS. */ #ifndef _DARWIN_C_SOURCE # undef _DARWIN_C_SOURCE #endif /* Enable GNU extensions on systems that have them. */ #ifndef _GNU_SOURCE # undef _GNU_SOURCE #endif /* Enable NetBSD extensions on NetBSD. */ #ifndef _NETBSD_SOURCE # undef _NETBSD_SOURCE #endif /* Enable OpenBSD extensions on NetBSD. */ #ifndef _OPENBSD_SOURCE # undef _OPENBSD_SOURCE #endif /* Enable threading extensions on Solaris. */ #ifndef _POSIX_PTHREAD_SEMANTICS # undef _POSIX_PTHREAD_SEMANTICS #endif /* Enable extensions specified by ISO/IEC TS 18661-5:2014. */ #ifndef __STDC_WANT_IEC_60559_ATTRIBS_EXT__ # undef __STDC_WANT_IEC_60559_ATTRIBS_EXT__ #endif /* Enable extensions specified by ISO/IEC TS 18661-1:2014. */ #ifndef __STDC_WANT_IEC_60559_BFP_EXT__ # undef __STDC_WANT_IEC_60559_BFP_EXT__ #endif /* Enable extensions specified by ISO/IEC TS 18661-2:2015. */ #ifndef __STDC_WANT_IEC_60559_DFP_EXT__ # undef __STDC_WANT_IEC_60559_DFP_EXT__ #endif /* Enable extensions specified by ISO/IEC TS 18661-4:2015. */ #ifndef __STDC_WANT_IEC_60559_FUNCS_EXT__ # undef __STDC_WANT_IEC_60559_FUNCS_EXT__ #endif /* Enable extensions specified by ISO/IEC TS 18661-3:2015. */ #ifndef __STDC_WANT_IEC_60559_TYPES_EXT__ # undef __STDC_WANT_IEC_60559_TYPES_EXT__ #endif /* Enable extensions specified by ISO/IEC TR 24731-2:2010. */ #ifndef __STDC_WANT_LIB_EXT2__ # undef __STDC_WANT_LIB_EXT2__ #endif /* Enable extensions specified by ISO/IEC 24747:2009. */ #ifndef __STDC_WANT_MATH_SPEC_FUNCS__ # undef __STDC_WANT_MATH_SPEC_FUNCS__ #endif /* Enable extensions on HP NonStop. */ #ifndef _TANDEM_SOURCE # undef _TANDEM_SOURCE #endif /* Enable X/Open extensions if necessary. HP-UX 11.11 defines mbstate_t only if _XOPEN_SOURCE is defined to 500, regardless of whether compiling with -Ae or -D_HPUX_SOURCE=1. */ #ifndef _XOPEN_SOURCE # undef _XOPEN_SOURCE #endif /* Enable general extensions on Solaris. */ #ifndef __EXTENSIONS__ # undef __EXTENSIONS__ #endif ]) AC_CACHE_CHECK([whether it is safe to define __EXTENSIONS__], [ac_cv_safe_to_define___extensions__], [AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[ # define __EXTENSIONS__ 1 ]AC_INCLUDES_DEFAULT])], [ac_cv_safe_to_define___extensions__=yes], [ac_cv_safe_to_define___extensions__=no])]) test $ac_cv_safe_to_define___extensions__ = yes && AC_DEFINE([__EXTENSIONS__]) AC_DEFINE([_ALL_SOURCE]) AC_DEFINE([_DARWIN_C_SOURCE]) AC_DEFINE([_GNU_SOURCE]) AC_DEFINE([_NETBSD_SOURCE]) AC_DEFINE([_OPENBSD_SOURCE]) AC_DEFINE([_POSIX_PTHREAD_SEMANTICS]) AC_DEFINE([__STDC_WANT_IEC_60559_ATTRIBS_EXT__]) AC_DEFINE([__STDC_WANT_IEC_60559_BFP_EXT__]) AC_DEFINE([__STDC_WANT_IEC_60559_DFP_EXT__]) AC_DEFINE([__STDC_WANT_IEC_60559_FUNCS_EXT__]) AC_DEFINE([__STDC_WANT_IEC_60559_TYPES_EXT__]) AC_DEFINE([__STDC_WANT_LIB_EXT2__]) AC_DEFINE([__STDC_WANT_MATH_SPEC_FUNCS__]) AC_DEFINE([_TANDEM_SOURCE]) AC_CACHE_CHECK([whether _XOPEN_SOURCE should be defined], [ac_cv_should_define__xopen_source], [ac_cv_should_define__xopen_source=no AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[ #include mbstate_t x;]])], [], [AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[ #define _XOPEN_SOURCE 500 #include mbstate_t x;]])], [ac_cv_should_define__xopen_source=yes])])]) test $ac_cv_should_define__xopen_source = yes && AC_DEFINE([_XOPEN_SOURCE], [500]) ])# AC_USE_SYSTEM_EXTENSIONS # gl_USE_SYSTEM_EXTENSIONS # ------------------------ # Enable extensions on systems that normally disable them, # typically due to standards-conformance issues. AC_DEFUN_ONCE([gl_USE_SYSTEM_EXTENSIONS], [ dnl Require this macro before AC_USE_SYSTEM_EXTENSIONS. dnl gnulib does not need it. But if it gets required by third-party macros dnl after AC_USE_SYSTEM_EXTENSIONS is required, autoconf 2.62..2.63 emit a dnl warning: "AC_COMPILE_IFELSE was called before AC_USE_SYSTEM_EXTENSIONS". dnl Note: We can do this only for one of the macros AC_AIX, AC_GNU_SOURCE, dnl AC_MINIX. If people still use AC_AIX or AC_MINIX, they are out of luck. AC_REQUIRE([AC_GNU_SOURCE]) AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS]) ]) cjs-4.4.0/m4/timer_time.m4000066400000000000000000000034161356375132300152320ustar00rootroot00000000000000# timer_time.m4 serial 3 dnl Copyright (C) 2011-2018 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. # Check for timer_settime, and set LIB_TIMER_TIME. AC_DEFUN([gl_TIMER_TIME], [ dnl Based on clock_time.m4. See details there. AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS]) dnl Test whether the gnulib module 'threadlib' is in use. dnl Some packages like Emacs use --avoid=threadlib. dnl Write the symbol in such a way that it does not cause 'aclocal' to pick dnl the threadlib.m4 file that is installed in $PREFIX/share/aclocal/. m4_ifdef([gl_][THREADLIB], [AC_REQUIRE([gl_][THREADLIB])]) LIB_TIMER_TIME= AC_SUBST([LIB_TIMER_TIME]) gl_saved_libs=$LIBS AC_SEARCH_LIBS([timer_settime], [rt posix4], [test "$ac_cv_search_timer_settime" = "none required" || LIB_TIMER_TIME=$ac_cv_search_timer_settime]) m4_ifdef([gl_][THREADLIB], [dnl GLIBC uses threads to emulate posix timers when kernel support dnl is not available (like Linux < 2.6 or when used with kFreeBSD) dnl Now the pthread lib is linked automatically in the normal case, dnl but when linking statically, it needs to be explicitly specified. AC_EGREP_CPP([Thread], [#include #ifdef __GNU_LIBRARY__ #if ((__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2) || (__GLIBC__ > 2)) \ && !(__UCLIBC__ && __HAS_NO_THREADS__) Thread emulation available #endif #endif ], [LIB_TIMER_TIME="$LIB_TIMER_TIME $LIBMULTITHREAD"])]) AC_CHECK_FUNCS([timer_settime]) LIBS=$gl_saved_libs ]) cjs-4.4.0/modules/000077500000000000000000000000001356375132300137565ustar00rootroot00000000000000cjs-4.4.0/modules/_bootstrap/000077500000000000000000000000001356375132300161325ustar00rootroot00000000000000cjs-4.4.0/modules/_bootstrap/coverage.js000066400000000000000000000002341356375132300202620ustar00rootroot00000000000000(function(exports) { 'use strict'; exports.debugger = new Debugger(exports.debuggee); exports.debugger.collectCoverageInfo = true; })(window); cjs-4.4.0/modules/_bootstrap/default.js000066400000000000000000000001541356375132300201140ustar00rootroot00000000000000(function(exports) { 'use strict'; // Do early initialization here. void exports; })(window); cjs-4.4.0/modules/_legacy.js000066400000000000000000000573331356375132300157320ustar00rootroot00000000000000/* -*- mode: js; indent-tabs-mode: nil; -*- */ /* exported Class, Interface, defineGObjectLegacyObjects, defineGtkLegacyObjects */ // Copyright 2008 litl, LLC // Copyright 2011 Jasper St. Pierre // Class magic // Adapted from MooTools, MIT license // https://github.com/mootools/mootools-core function _Base() { throw new TypeError('Cannot instantiate abstract class _Base'); } _Base.__super__ = null; _Base.prototype._init = function() { }; _Base.prototype._construct = function() { this._init.apply(this, arguments); return this; }; _Base.prototype.__name__ = '_Base'; _Base.prototype.toString = function() { return '[object ' + this.__name__ + ']'; }; function _parent() { if (!this.__caller__) throw new TypeError("The method 'parent' cannot be called"); let caller = this.__caller__; let name = caller._name; let parent = caller._owner.__super__; let previous = parent ? parent.prototype[name] : undefined; if (!previous) throw new TypeError("The method '" + name + "' is not on the superclass"); return previous.apply(this, arguments); } function _interfacePresent(required, proto) { if (!proto.__interfaces__) return false; if (proto.__interfaces__.indexOf(required) !== -1) return true; // implemented here // Might be implemented on a parent class return _interfacePresent(required, proto.constructor.__super__.prototype); } function getMetaClass(params) { if (params.MetaClass) return params.MetaClass; if (params.Extends && params.Extends.prototype.__metaclass__) return params.Extends.prototype.__metaclass__; return null; } function Class(params) { let metaClass = getMetaClass(params); if (metaClass && metaClass != this.constructor) { return new metaClass(...arguments); } else { return this._construct.apply(this, arguments); } } Class.__super__ = _Base; Class.prototype = Object.create(_Base.prototype); Class.prototype.constructor = Class; Class.prototype.__name__ = 'Class'; Class.prototype.wrapFunction = function(name, meth) { if (meth._origin) meth = meth._origin; function wrapper() { let prevCaller = this.__caller__; this.__caller__ = wrapper; let result = meth.apply(this, arguments); this.__caller__ = prevCaller; return result; } wrapper._origin = meth; wrapper._name = name; wrapper._owner = this; return wrapper; }; Class.prototype.toString = function() { return '[object ' + this.__name__ + ' for ' + this.prototype.__name__ + ']'; }; Class.prototype._construct = function(params) { if (!params.Name) { throw new TypeError("Classes require an explicit 'Name' parameter."); } let name = params.Name; let parent = params.Extends; if (!parent) parent = _Base; let newClass = function() { if (params.Abstract && new.target.name === name) throw new TypeError('Cannot instantiate abstract class ' + name); this.__caller__ = null; return this._construct(...arguments); }; // Since it's not possible to create a constructor with // a custom [[Prototype]], we have to do this to make // "newClass instanceof Class" work, and so we can inherit // methods/properties of Class.prototype, like wrapFunction. Object.setPrototypeOf(newClass, this.constructor.prototype); newClass.__super__ = parent; newClass.prototype = Object.create(parent.prototype); newClass.prototype.constructor = newClass; newClass._init.apply(newClass, arguments); let interfaces = params.Implements || []; // If the parent already implements an interface, then we do too if (parent instanceof Class) interfaces = interfaces.filter((iface) => !parent.implements(iface)); Object.defineProperties(newClass.prototype, { '__metaclass__': { writable: false, configurable: false, enumerable: false, value: this.constructor, }, '__interfaces__': { writable: false, configurable: false, enumerable: false, value: interfaces, }, }); Object.defineProperty(newClass, 'name', { writable: false, configurable: true, enumerable: false, value: name, }); interfaces.forEach((iface) => { iface._check(newClass.prototype); }); return newClass; }; /** * Check whether this class conforms to the interface "iface". * @param {object} iface a Lang.Interface * @returns: whether this class implements iface * @type: boolean */ Class.prototype.implements = function (iface) { if (_interfacePresent(iface, this.prototype)) return true; if (this.__super__ instanceof Class) return this.__super__.implements(iface); return false; }; // key can be either a string or a symbol Class.prototype._copyPropertyDescriptor = function(params, propertyObj, key) { let descriptor = Object.getOwnPropertyDescriptor(params, key); if (typeof descriptor.value === 'function') descriptor.value = this.wrapFunction(key, descriptor.value); // we inherit writable and enumerable from the property // descriptor of params (they're both true if created from an // object literal) descriptor.configurable = false; propertyObj[key] = descriptor; }; Class.prototype._init = function(params) { let name = params.Name; let propertyObj = { }; let interfaces = params.Implements || []; interfaces.forEach((iface) => { Object.getOwnPropertyNames(iface.prototype) .filter((name) => !name.startsWith('__') && name !== 'constructor') .filter((name) => !(name in this.prototype)) .forEach((name) => { let descriptor = Object.getOwnPropertyDescriptor(iface.prototype, name); // writable and enumerable are inherited, see note above descriptor.configurable = false; propertyObj[name] = descriptor; }); }); Object.getOwnPropertyNames(params) .filter(name => ['Name', 'Extends', 'Abstract', 'Implements'].indexOf(name) === -1) .concat(Object.getOwnPropertySymbols(params)) .forEach(this._copyPropertyDescriptor.bind(this, params, propertyObj)); Object.defineProperties(this.prototype, propertyObj); Object.defineProperties(this.prototype, { '__name__': { writable: false, configurable: false, enumerable: false, value: name, }, 'parent': { writable: false, configurable: false, enumerable: false, value: _parent, }, }); }; // This introduces the concept of a "meta-interface" which is given by the // MetaInterface property on an object's metaclass. For objects whose metaclass // is Lang.Class, the meta-interface is Lang.Interface. Subclasses of Lang.Class // such as GObject.Class supply their own meta-interface. // This is in order to enable creating GObject interfaces with Lang.Interface, // much as you can create GObject classes with Lang.Class. function _getMetaInterface(params) { if (!params.Requires || params.Requires.length === 0) return null; let metaInterface = params.Requires.map((req) => { if (req instanceof Interface) return req.__super__; for (let metaclass = req.prototype.__metaclass__; metaclass; metaclass = metaclass.__super__) { if (metaclass.hasOwnProperty('MetaInterface')) return metaclass.MetaInterface; } return null; }) .reduce((best, candidate) => { // This function reduces to the "most derived" meta interface in the list. if (best === null) return candidate; if (candidate === null) return best; for (let sup = candidate; sup; sup = sup.__super__) { if (sup === best) return candidate; } return best; }, null); // If we reach this point and we don't know the meta-interface, then it's // most likely because there were only pure-C interfaces listed in Requires // (and those don't have our magic properties.) However, all pure-C // interfaces should require GObject.Object anyway. if (metaInterface === null) throw new Error('Did you forget to include GObject.Object in Requires?'); return metaInterface; } function Interface(params) { let metaInterface = _getMetaInterface(params); if (metaInterface && metaInterface !== this.constructor) return new metaInterface(...arguments); return this._construct.apply(this, arguments); } Class.MetaInterface = Interface; /** * Use this to signify a function that must be overridden in an implementation * of the interface. Creating a class that doesn't override the function will * throw an error. */ Interface.UNIMPLEMENTED = function UNIMPLEMENTED () { throw new Error('Not implemented'); }; Interface.__super__ = _Base; Interface.prototype = Object.create(_Base.prototype); Interface.prototype.constructor = Interface; Interface.prototype.__name__ = 'Interface'; Interface.prototype._construct = function (params) { if (!params.Name) throw new TypeError("Interfaces require an explicit 'Name' parameter."); let newInterface = Object.create(this.constructor.prototype); newInterface.__super__ = Interface; newInterface.prototype = Object.create(Interface.prototype); newInterface.prototype.constructor = newInterface; newInterface.prototype.__name__ = params.Name; newInterface._init.apply(newInterface, arguments); Object.defineProperty(newInterface.prototype, '__metaclass__', { writable: false, configurable: false, enumerable: false, value: this.constructor, }); Object.defineProperty(newInterface, 'name', { writable: false, configurable: true, enumerable: false, value: params.Name, }); return newInterface; }; Interface.prototype._check = function (proto) { // Check that proto implements all of this interface's required interfaces. // "proto" refers to the object's prototype (which implements the interface) // whereas "this.prototype" is the interface's prototype (which may still // contain unimplemented methods.) let unfulfilledReqs = this.prototype.__requires__.filter((required) => { // Either the interface is not present or it is not listed before the // interface that requires it or the class does not inherit it. This is // so that required interfaces don't copy over properties from other // interfaces that require them. let interfaces = proto.__interfaces__; return ((!_interfacePresent(required, proto) || interfaces.indexOf(required) > interfaces.indexOf(this)) && !(proto instanceof required)); }).map((required) => // __name__ is only present on GJS-created classes and will be the most // accurate name. required.name will be present on introspected GObjects // but is not preferred because it will be the C name. The last option // is just so that we print something if there is garbage in Requires. required.prototype.__name__ || required.name || required); if (unfulfilledReqs.length > 0) { throw new Error('The following interfaces must be implemented before ' + this.prototype.__name__ + ': ' + unfulfilledReqs.join(', ')); } // Check that this interface's required methods are implemented let unimplementedFns = Object.getOwnPropertyNames(this.prototype) .filter((p) => this.prototype[p] === Interface.UNIMPLEMENTED) .filter((p) => !(p in proto) || proto[p] === Interface.UNIMPLEMENTED); if (unimplementedFns.length > 0) throw new Error('The following members of ' + this.prototype.__name__ + ' are not implemented yet: ' + unimplementedFns.join(', ')); }; Interface.prototype.toString = function () { return '[interface ' + this.__name__ + ' for ' + this.prototype.__name__ + ']'; }; Interface.prototype._init = function (params) { let name = params.Name; let propertyObj = {}; Object.getOwnPropertyNames(params) .filter((name) => ['Name', 'Requires'].indexOf(name) === -1) .forEach((name) => { let descriptor = Object.getOwnPropertyDescriptor(params, name); // Create wrappers on the interface object so that generics work (e.g. // SomeInterface.some_function(this, blah) instead of // SomeInterface.prototype.some_function.call(this, blah) if (typeof descriptor.value === 'function') { let interfaceProto = this.prototype; // capture in closure this[name] = function () { return interfaceProto[name].call.apply(interfaceProto[name], arguments); }; } // writable and enumerable are inherited, see note in Class._init() descriptor.configurable = false; propertyObj[name] = descriptor; }); Object.defineProperties(this.prototype, propertyObj); Object.defineProperties(this.prototype, { '__name__': { writable: false, configurable: false, enumerable: false, value: name, }, '__requires__': { writable: false, configurable: false, enumerable: false, value: params.Requires || [], }, }); }; // GObject Lang.Class magic function defineGObjectLegacyObjects(GObject) { const Gi = imports._gi; // Some common functions between GObject.Class and GObject.Interface function _createSignals(gtype, signals) { for (let signalName in signals) { let obj = signals[signalName]; let flags = (obj.flags !== undefined) ? obj.flags : GObject.SignalFlags.RUN_FIRST; let accumulator = (obj.accumulator !== undefined) ? obj.accumulator : GObject.AccumulatorType.NONE; let rtype = (obj.return_type !== undefined) ? obj.return_type : GObject.TYPE_NONE; let paramtypes = (obj.param_types !== undefined) ? obj.param_types : []; try { obj.signal_id = Gi.signal_new(gtype, signalName, flags, accumulator, rtype, paramtypes); } catch (e) { throw new TypeError('Invalid signal ' + signalName + ': ' + e.message); } } } function _createGTypeName(params) { if (params.GTypeName) return params.GTypeName; else return 'Gjs_' + params.Name; } function _getGObjectInterfaces(interfaces) { return interfaces.filter((iface) => iface.hasOwnProperty('$gtype')); } function _propertiesAsArray(params) { let propertiesArray = []; if (params.Properties) { for (let prop in params.Properties) { propertiesArray.push(params.Properties[prop]); } } return propertiesArray; } const GObjectMeta = new Class({ Name: 'GObjectClass', Extends: Class, _init: function (params) { // retrieve signals and remove them from params before chaining let signals = params.Signals; delete params.Signals; this.parent(params); if (signals) _createSignals(this.$gtype, signals); Object.getOwnPropertyNames(params).forEach(function(name) { if (name == 'Name' || name == 'Extends' || name == 'Abstract') return; let descriptor = Object.getOwnPropertyDescriptor(params, name); if (typeof descriptor.value === 'function') { let wrapped = this.prototype[name]; if (name.slice(0, 6) == 'vfunc_') { Gi.hook_up_vfunc(this.prototype, name.slice(6), wrapped); } else if (name.slice(0, 3) == 'on_') { let id = GObject.signal_lookup(name.slice(3).replace('_', '-'), this.$gtype); if (id !== 0) { GObject.signal_override_class_closure(id, this.$gtype, function() { let argArray = Array.prototype.slice.call(arguments); let emitter = argArray.shift(); return wrapped.apply(emitter, argArray); }); } } } }.bind(this)); }, _isValidClass: function(klass) { let proto = klass.prototype; if (!proto) return false; // If proto == GObject.Object.prototype, then // proto.__proto__ is Object, so "proto instanceof GObject.Object" // will return false. return proto == GObject.Object.prototype || proto instanceof GObject.Object; }, // If we want an object with a custom JSClass, we can't just // use a function. We have to use a custom constructor here. _construct: function(params) { if (!params.Name) throw new TypeError("Classes require an explicit 'Name' parameter."); let name = params.Name; let gtypename = _createGTypeName(params); if (!params.Extends) params.Extends = GObject.Object; let parent = params.Extends; if (!this._isValidClass(parent)) throw new TypeError('GObject.Class used with invalid base class (is ' + parent + ')'); let interfaces = params.Implements || []; if (parent instanceof Class) interfaces = interfaces.filter(iface => !parent.implements(iface)); let gobjectInterfaces = _getGObjectInterfaces(interfaces); let propertiesArray = _propertiesAsArray(params); delete params.Properties; let newClass = Gi.register_type(parent.prototype, gtypename, gobjectInterfaces, propertiesArray); // See Class.prototype._construct for the reasoning // behind this direct prototype set. Object.setPrototypeOf(newClass, this.constructor.prototype); newClass.__super__ = parent; newClass._init.apply(newClass, arguments); Object.defineProperties(newClass.prototype, { '__metaclass__': { writable: false, configurable: false, enumerable: false, value: this.constructor, }, '__interfaces__': { writable: false, configurable: false, enumerable: false, value: interfaces, }, }); // Overwrite the C++-set class name, as if it were an ES6 class Object.defineProperty(newClass, 'name', { writable: false, configurable: true, enumerable: false, value: name, }); interfaces.forEach((iface) => { if (iface instanceof Interface) iface._check(newClass.prototype); }); return newClass; }, // Overrides Lang.Class.implements() implements: function (iface) { if (iface instanceof GObject.Interface) { return GObject.type_is_a(this.$gtype, iface.$gtype); } else { return this.parent(iface); } } }); function GObjectInterface() { return this._construct.apply(this, arguments); } GObjectMeta.MetaInterface = GObjectInterface; GObjectInterface.__super__ = Interface; GObjectInterface.prototype = Object.create(Interface.prototype); GObjectInterface.prototype.constructor = GObjectInterface; GObjectInterface.prototype.__name__ = 'GObjectInterface'; GObjectInterface.prototype._construct = function (params) { if (!params.Name) { throw new TypeError("Interfaces require an explicit 'Name' parameter."); } let gtypename = _createGTypeName(params); delete params.GTypeName; let interfaces = params.Requires || []; let gobjectInterfaces = _getGObjectInterfaces(interfaces); let properties = _propertiesAsArray(params); delete params.Properties; let newInterface = Gi.register_interface(gtypename, gobjectInterfaces, properties); // See Class.prototype._construct for the reasoning // behind this direct prototype set. Object.setPrototypeOf(newInterface, this.constructor.prototype); newInterface.__super__ = GObjectInterface; newInterface.prototype.constructor = newInterface; newInterface._init.apply(newInterface, arguments); Object.defineProperty(newInterface.prototype, '__metaclass__', { writable: false, configurable: false, enumerable: false, value: this.constructor, }); // Overwrite the C++-set class name, as if it were an ES6 class Object.defineProperty(newInterface, 'name', { writable: false, configurable: true, enumerable: false, value: params.Name, }); return newInterface; }; GObjectInterface.prototype._init = function (params) { let signals = params.Signals; delete params.Signals; Interface.prototype._init.call(this, params); _createSignals(this.$gtype, signals); }; return {GObjectMeta, GObjectInterface}; } function defineGtkLegacyObjects(GObject, Gtk) { const GtkWidgetClass = new Class({ Name: 'GtkWidgetClass', Extends: GObject.Class, _init: function(params) { let template = params.Template; delete params.Template; let children = params.Children; delete params.Children; let internalChildren = params.InternalChildren; delete params.InternalChildren; let cssName = params.CssName; delete params.CssName; if (template) { params._instance_init = function() { this.init_template(); }; } this.parent(params); if (cssName) //Gtk.Widget.set_css_name.call(this, cssName); if (template) { if (typeof template == 'string' && template.startsWith('resource:///')) Gtk.Widget.set_template_from_resource.call(this, template.slice(11)); else Gtk.Widget.set_template.call(this, template); } this[Gtk.template] = template; this[Gtk.children] = children; this[Gtk.internalChildren] = internalChildren; if (children) { for (let i = 0; i < children.length; i++) Gtk.Widget.bind_template_child_full.call(this, children[i], false, 0); } if (internalChildren) { for (let i = 0; i < internalChildren.length; i++) Gtk.Widget.bind_template_child_full.call(this, internalChildren[i], true, 0); } }, _isValidClass: function(klass) { let proto = klass.prototype; if (!proto) return false; // If proto == Gtk.Widget.prototype, then // proto.__proto__ is GObject.InitiallyUnowned, so // "proto instanceof Gtk.Widget" // will return false. return proto == Gtk.Widget.prototype || proto instanceof Gtk.Widget; }, }); return {GtkWidgetClass}; }cjs-4.4.0/modules/cairo-context.cpp000066400000000000000000001170451356375132300172510ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2010 litl, LLC. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include "gi/foreign.h" #include "cjs/jsapi-class.h" #include "cjs/jsapi-util-args.h" #include "cjs/jsapi-wrapper.h" #include #include #include "cairo-private.h" #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(mname) \ static bool \ mname##_func(JSContext *context, \ unsigned argc, \ JS::Value *vp) \ { \ GJS_GET_PRIV(context, argc, vp, argv, obj, GjsCairoContext, priv); \ cairo_t *cr = priv ? priv->cr : NULL; #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END \ return gjs_cairo_check_status(context, cairo_status(cr), "context"); \ } #define _GJS_CAIRO_CONTEXT_CHECK_NO_ARGS(m) \ if (argc > 0) { \ gjs_throw(context, "Context." #m "() takes no arguments"); \ return false; \ } #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(method, cfunc) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ cfunc(cr); \ argv.rval().setUndefined(); \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC0I(method, cfunc) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ int ret; \ _GJS_CAIRO_CONTEXT_CHECK_NO_ARGS(method) \ ret = (int)cfunc(cr); \ argv.rval().setInt32(ret); \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC0B(method, cfunc) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ cairo_bool_t ret; \ _GJS_CAIRO_CONTEXT_CHECK_NO_ARGS(method) \ ret = cfunc(cr); \ argv.rval().setBoolean(ret); \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC2FFAFF(method, cfunc, n1, n2) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ double arg1, arg2; \ if (!gjs_parse_call_args(context, #method, argv, "ff", \ #n1, &arg1, #n2, &arg2)) \ return false; \ cfunc(cr, &arg1, &arg2); \ if (cairo_status(cr) == CAIRO_STATUS_SUCCESS) { \ JS::RootedObject array(context, \ JS_NewArrayObject(context, JS::HandleValueArray::empty())); \ if (!array) \ return false; \ JS::RootedValue r(context, JS::NumberValue(arg1)); \ if (!JS_SetElement(context, array, 0, r)) return false; \ r.setNumber(arg2); \ if (!JS_SetElement(context, array, 1, r)) return false; \ argv.rval().setObject(*array); \ } \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC0AFF(method, cfunc) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ double arg1, arg2; \ _GJS_CAIRO_CONTEXT_CHECK_NO_ARGS(method) \ cfunc(cr, &arg1, &arg2); \ if (cairo_status(cr) == CAIRO_STATUS_SUCCESS) { \ JS::RootedObject array(context, \ JS_NewArrayObject(context, JS::HandleValueArray::empty())); \ if (!array) \ return false; \ JS::RootedValue r(context, JS::NumberValue(arg1)); \ if (!JS_SetElement(context, array, 0, r)) return false; \ r.setNumber(arg2); \ if (!JS_SetElement(context, array, 1, r)) return false; \ argv.rval().setObject(*array); \ } \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC0AFFFF(method, cfunc) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ double arg1, arg2, arg3, arg4; \ _GJS_CAIRO_CONTEXT_CHECK_NO_ARGS(method) \ cfunc(cr, &arg1, &arg2, &arg3, &arg4); \ { \ JS::RootedObject array(context, \ JS_NewArrayObject(context, JS::HandleValueArray::empty())); \ if (!array) \ return false; \ JS::RootedValue r(context, JS::NumberValue(arg1)); \ if (!JS_SetElement(context, array, 0, r)) return false; \ r.setNumber(arg2); \ if (!JS_SetElement(context, array, 1, r)) return false; \ r.setNumber(arg3); \ if (!JS_SetElement(context, array, 2, r)) return false; \ r.setNumber(arg4); \ if (!JS_SetElement(context, array, 3, r)) return false; \ argv.rval().setObject(*array); \ } \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC0F(method, cfunc) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ double ret; \ _GJS_CAIRO_CONTEXT_CHECK_NO_ARGS(method) \ ret = cfunc(cr); \ argv.rval().setNumber(ret); \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(method, cfunc, fmt, t1, n1) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ t1 arg1; \ if (!gjs_parse_call_args(context, #method, argv, fmt, \ #n1, &arg1)) \ return false; \ cfunc(cr, arg1); \ argv.rval().setUndefined(); \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC2(method, cfunc, fmt, t1, n1, t2, n2) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ t1 arg1; \ t2 arg2; \ if (!gjs_parse_call_args(context, #method, argv, fmt, \ #n1, &arg1, #n2, &arg2)) \ return false; \ cfunc(cr, arg1, arg2); \ argv.rval().setUndefined(); \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC2B(method, cfunc, fmt, t1, n1, t2, n2) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ t1 arg1; \ t2 arg2; \ cairo_bool_t ret; \ if (!gjs_parse_call_args(context, #method, argv, fmt, \ #n1, &arg1, #n2, &arg2)) \ return false; \ ret = cfunc(cr, arg1, arg2); \ argv.rval().setBoolean(ret); \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC3(method, cfunc, fmt, t1, n1, t2, n2, t3, n3) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ t1 arg1; \ t2 arg2; \ t3 arg3; \ if (!gjs_parse_call_args(context, #method, argv, fmt, \ #n1, &arg1, #n2, &arg2, #n3, &arg3)) \ return false; \ cfunc(cr, arg1, arg2, arg3); \ argv.rval().setUndefined(); \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC4(method, cfunc, fmt, t1, n1, t2, n2, t3, n3, t4, n4) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ t1 arg1; \ t2 arg2; \ t3 arg3; \ t4 arg4; \ if (!gjs_parse_call_args(context, #method, argv, fmt, \ #n1, &arg1, #n2, &arg2, \ #n3, &arg3, #n4, &arg4)) \ return false; \ cfunc(cr, arg1, arg2, arg3, arg4); \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC5(method, cfunc, fmt, t1, n1, t2, n2, t3, n3, t4, n4, t5, n5) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ t1 arg1; \ t2 arg2; \ t3 arg3; \ t4 arg4; \ t5 arg5; \ if (!gjs_parse_call_args(context, #method, argv, fmt, \ #n1, &arg1, #n2, &arg2, #n3, &arg3, \ #n4, &arg4, #n5, &arg5)) \ return false; \ cfunc(cr, arg1, arg2, arg3, arg4, arg5); \ argv.rval().setUndefined(); \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC6(method, cfunc, fmt, t1, n1, t2, n2, t3, n3, t4, n4, t5, n5, t6, n6) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ t1 arg1; \ t2 arg2; \ t3 arg3; \ t4 arg4; \ t5 arg5; \ t6 arg6; \ if (!gjs_parse_call_args(context, #method, argv, fmt, \ #n1, &arg1, #n2, &arg2, #n3, &arg3, \ #n4, &arg4, #n5, &arg5, #n6, &arg6)) \ return false; \ cfunc(cr, arg1, arg2, arg3, arg4, arg5, arg6); \ argv.rval().setUndefined(); \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END typedef struct { void *dummy; JSContext *context; JSObject *object; cairo_t * cr; } GjsCairoContext; static JSObject *gjs_cairo_context_get_proto(JSContext *); GJS_DEFINE_PROTO_WITH_GTYPE("Context", cairo_context, CAIRO_GOBJECT_TYPE_CONTEXT, JSCLASS_BACKGROUND_FINALIZE) GJS_DEFINE_PRIV_FROM_JS(GjsCairoContext, gjs_cairo_context_class); static void _gjs_cairo_context_construct_internal(JSContext *context, JS::HandleObject obj, cairo_t *cr) { GjsCairoContext *priv; priv = g_slice_new0(GjsCairoContext); g_assert(priv_from_js(context, obj) == NULL); JS_SetPrivate(obj, priv); priv->context = context; priv->object = obj; priv->cr = cairo_reference(cr); } GJS_NATIVE_CONSTRUCTOR_DECLARE(cairo_context) { GJS_NATIVE_CONSTRUCTOR_VARIABLES(cairo_context) cairo_surface_t *surface; cairo_t *cr; GJS_NATIVE_CONSTRUCTOR_PRELUDE(cairo_context); JS::RootedObject surface_wrapper(context); if (!gjs_parse_call_args(context, "Context", argv, "o", "surface", &surface_wrapper)) return false; surface = gjs_cairo_surface_get_surface(context, surface_wrapper); if (!surface) { gjs_throw(context, "first argument to Context() should be a surface"); return false; } cr = cairo_create(surface); if (!gjs_cairo_check_status(context, cairo_status(cr), "context")) return false; _gjs_cairo_context_construct_internal(context, object, cr); cairo_destroy(cr); GJS_NATIVE_CONSTRUCTOR_FINISH(cairo_context); return true; } static void gjs_cairo_context_finalize(JSFreeOp *fop, JSObject *obj) { GjsCairoContext *priv; priv = (GjsCairoContext*) JS_GetPrivate(obj); if (priv == NULL) return; if (priv->cr != NULL) cairo_destroy(priv->cr); g_slice_free(GjsCairoContext, priv); } /* Properties */ JSPropertySpec gjs_cairo_context_proto_props[] = { JS_PS_END }; /* Methods */ _GJS_CAIRO_CONTEXT_DEFINE_FUNC5(arc, cairo_arc, "fffff", double, xc, double, yc, double, radius, double, angle1, double, angle2) _GJS_CAIRO_CONTEXT_DEFINE_FUNC5(arcNegative, cairo_arc_negative, "fffff", double, xc, double, yc, double, radius, double, angle1, double, angle2) _GJS_CAIRO_CONTEXT_DEFINE_FUNC6(curveTo, cairo_curve_to, "ffffff", double, x1, double, y1, double, x2, double, y2, double, x3, double, y3) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(clip, cairo_clip) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(clipPreserve, cairo_clip_preserve) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0AFFFF(clipExtents, cairo_clip_extents) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(closePath, cairo_close_path) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(copyPage, cairo_copy_page) _GJS_CAIRO_CONTEXT_DEFINE_FUNC2FFAFF(deviceToUser, cairo_device_to_user, "x", "y") _GJS_CAIRO_CONTEXT_DEFINE_FUNC2FFAFF(deviceToUserDistance, cairo_device_to_user_distance, "x", "y") _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(fill, cairo_fill) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(fillPreserve, cairo_fill_preserve) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0AFFFF(fillExtents, cairo_fill_extents) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0I(getAntialias, cairo_get_antialias) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0AFF(getCurrentPoint, cairo_get_current_point) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0I(getDashCount, cairo_get_dash_count) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0I(getFillRule, cairo_get_fill_rule) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0I(getLineCap, cairo_get_line_cap) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0I(getLineJoin, cairo_get_line_join) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0F(getLineWidth, cairo_get_line_width) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0F(getMiterLimit, cairo_get_miter_limit) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0I(getOperator, cairo_get_operator) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0F(getTolerance, cairo_get_tolerance) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0B(hasCurrentPoint, cairo_has_current_point) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(identityMatrix, cairo_identity_matrix) _GJS_CAIRO_CONTEXT_DEFINE_FUNC2B(inFill, cairo_in_fill, "ff", double, x, double, y) _GJS_CAIRO_CONTEXT_DEFINE_FUNC2B(inStroke, cairo_in_stroke, "ff", double, x, double, y) _GJS_CAIRO_CONTEXT_DEFINE_FUNC2(lineTo, cairo_line_to, "ff", double, x, double, y) _GJS_CAIRO_CONTEXT_DEFINE_FUNC2(moveTo, cairo_move_to, "ff", double, x, double, y) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(newPath, cairo_new_path) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(newSubPath, cairo_new_sub_path) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(paint, cairo_paint) _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(paintWithAlpha, cairo_paint_with_alpha, "f", double, alpha) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0AFFFF(pathExtents, cairo_path_extents) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(pushGroup, cairo_push_group) _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(pushGroupWithContent, cairo_push_group_with_content, "i", cairo_content_t, content) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(popGroupToSource, cairo_pop_group_to_source) _GJS_CAIRO_CONTEXT_DEFINE_FUNC4(rectangle, cairo_rectangle, "ffff", double, x, double, y, double, width, double, height) _GJS_CAIRO_CONTEXT_DEFINE_FUNC6(relCurveTo, cairo_rel_curve_to, "ffffff", double, dx1, double, dy1, double, dx2, double, dy2, double, dx3, double, dy3) _GJS_CAIRO_CONTEXT_DEFINE_FUNC2(relLineTo, cairo_rel_line_to, "ff", double, dx, double, dy) _GJS_CAIRO_CONTEXT_DEFINE_FUNC2(relMoveTo, cairo_rel_move_to, "ff", double, dx, double, dy) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(resetClip, cairo_reset_clip) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(restore, cairo_restore) _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(rotate, cairo_rotate, "f", double, angle) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(save, cairo_save) _GJS_CAIRO_CONTEXT_DEFINE_FUNC2(scale, cairo_scale, "ff", double, sx, double, sy) _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(setAntialias, cairo_set_antialias, "i", cairo_antialias_t, antialias) _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(setFillRule, cairo_set_fill_rule, "i", cairo_fill_rule_t, fill_rule) _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(setFontSize, cairo_set_font_size, "f", double, size) _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(setLineCap, cairo_set_line_cap, "i", cairo_line_cap_t, line_cap) _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(setLineJoin, cairo_set_line_join, "i", cairo_line_join_t, line_join) _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(setLineWidth, cairo_set_line_width, "f", double, width) _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(setMiterLimit, cairo_set_miter_limit, "f", double, limit) _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(setOperator, cairo_set_operator, "i", cairo_operator_t, op) _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(setTolerance, cairo_set_tolerance, "f", double, tolerance) _GJS_CAIRO_CONTEXT_DEFINE_FUNC3(setSourceRGB, cairo_set_source_rgb, "fff", double, red, double, green, double, blue) _GJS_CAIRO_CONTEXT_DEFINE_FUNC4(setSourceRGBA, cairo_set_source_rgba, "ffff", double, red, double, green, double, blue, double, alpha) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(showPage, cairo_show_page) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(stroke, cairo_stroke) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(strokePreserve, cairo_stroke_preserve) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0AFFFF(strokeExtents, cairo_stroke_extents) _GJS_CAIRO_CONTEXT_DEFINE_FUNC2(translate, cairo_translate, "ff", double, tx, double, ty) _GJS_CAIRO_CONTEXT_DEFINE_FUNC2FFAFF(userToDevice, cairo_user_to_device, "x", "y") _GJS_CAIRO_CONTEXT_DEFINE_FUNC2FFAFF(userToDeviceDistance, cairo_user_to_device_distance, "x", "y") static bool dispose_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_PRIV(context, argc, vp, rec, obj, GjsCairoContext, priv); if (priv->cr != NULL) { cairo_destroy(priv->cr); priv->cr = NULL; } rec.rval().setUndefined(); return true; } static bool appendPath_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_PRIV(context, argc, vp, argv, obj, GjsCairoContext, priv); JS::RootedObject path_wrapper(context); cairo_path_t *path; cairo_t *cr = priv ? priv->cr : NULL; if (!gjs_parse_call_args(context, "path", argv, "o", "path", &path_wrapper)) return false; path = gjs_cairo_path_get_path(context, path_wrapper); if (!path) { gjs_throw(context, "first argument to appendPath() should be a path"); return false; } cairo_append_path(cr, path); argv.rval().setUndefined(); return true; } static bool copyPath_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_PRIV(context, argc, vp, argv, obj, GjsCairoContext, priv); cairo_path_t *path; cairo_t *cr = priv ? priv->cr : NULL; if (!gjs_parse_call_args(context, "", argv, "")) return false; path = cairo_copy_path(cr); argv.rval().setObjectOrNull(gjs_cairo_path_from_path(context, path)); return true; } static bool copyPathFlat_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_PRIV(context, argc, vp, argv, obj, GjsCairoContext, priv); cairo_path_t *path; cairo_t *cr = priv ? priv->cr : NULL; if (!gjs_parse_call_args(context, "", argv, "")) return false; path = cairo_copy_path_flat(cr); argv.rval().setObjectOrNull(gjs_cairo_path_from_path(context, path)); return true; } static bool mask_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_PRIV(context, argc, vp, argv, obj, GjsCairoContext, priv); JS::RootedObject pattern_wrapper(context); cairo_pattern_t *pattern; cairo_t *cr = priv ? priv->cr : NULL; if (!gjs_parse_call_args(context, "mask", argv, "o", "pattern", &pattern_wrapper)) return false; pattern = gjs_cairo_pattern_get_pattern(context, pattern_wrapper); if (!pattern) { gjs_throw(context, "first argument to mask() should be a pattern"); return false; } cairo_mask(cr, pattern); if (!gjs_cairo_check_status(context, cairo_status(cr), "context")) return false; argv.rval().setUndefined(); return true; } static bool maskSurface_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_PRIV(context, argc, vp, argv, obj, GjsCairoContext, priv); JS::RootedObject surface_wrapper(context); double x, y; cairo_surface_t *surface; cairo_t *cr = priv ? priv->cr : NULL; if (!gjs_parse_call_args(context, "maskSurface", argv, "off", "surface", &surface_wrapper, "x", &x, "y", &y)) return false; surface = gjs_cairo_surface_get_surface(context, surface_wrapper); if (!surface) { gjs_throw(context, "first argument to maskSurface() should be a surface"); return false; } cairo_mask_surface(cr, surface, x, y); if (!gjs_cairo_check_status(context, cairo_status(cr), "context")) return false; argv.rval().setUndefined(); return true; } static bool setDash_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_PRIV(context, argc, vp, argv, obj, GjsCairoContext, priv); guint i; cairo_t *cr = priv ? priv->cr : NULL; JS::RootedObject dashes(context); double offset; guint len; bool is_array; if (!gjs_parse_call_args(context, "setDash", argv, "of", "dashes", &dashes, "offset", &offset)) return false; if (!JS_IsArrayObject(context, dashes, &is_array)) return false; if (!is_array) { gjs_throw(context, "dashes must be an array"); return false; } if (!JS_GetArrayLength(context, dashes, &len)) { gjs_throw(context, "Can't get length of dashes"); return false; } std::vector dashes_c; dashes_c.reserve(len); JS::RootedValue elem(context); for (i = 0; i < len; ++i) { double b; elem.setUndefined(); if (!JS_GetElement(context, dashes, i, &elem)) { return false; } if (elem.isUndefined()) continue; if (!JS::ToNumber(context, elem, &b)) return false; if (b <= 0) { gjs_throw(context, "Dash value must be positive"); return false; } dashes_c.push_back(b); } cairo_set_dash(cr, &dashes_c[0], dashes_c.size(), offset); argv.rval().setUndefined(); return true; } static bool setSource_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_PRIV(context, argc, vp, argv, obj, GjsCairoContext, priv); JS::RootedObject pattern_wrapper(context); cairo_pattern_t *pattern; cairo_t *cr = priv ? priv->cr : NULL; if (!gjs_parse_call_args(context, "setSource", argv, "o", "pattern", &pattern_wrapper)) return false; pattern = gjs_cairo_pattern_get_pattern(context, pattern_wrapper); if (!pattern) { gjs_throw(context, "first argument to setSource() should be a pattern"); return false; } cairo_set_source(cr, pattern); if (!gjs_cairo_check_status(context, cairo_status(cr), "context")) return false; argv.rval().setUndefined(); return true; } static bool setSourceSurface_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_PRIV(context, argc, vp, argv, obj, GjsCairoContext, priv); JS::RootedObject surface_wrapper(context); double x, y; cairo_surface_t *surface; cairo_t *cr = priv ? priv->cr : NULL; if (!gjs_parse_call_args(context, "setSourceSurface", argv, "off", "surface", &surface_wrapper, "x", &x, "y", &y)) return false; surface = gjs_cairo_surface_get_surface(context, surface_wrapper); if (!surface) { gjs_throw(context, "first argument to setSourceSurface() should be a surface"); return false; } cairo_set_source_surface(cr, surface, x, y); if (!gjs_cairo_check_status(context, cairo_status(cr), "context")) return false; argv.rval().setUndefined(); return true; } static bool showText_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_PRIV(context, argc, vp, argv, obj, GjsCairoContext, priv); GjsAutoJSChar utf8; cairo_t *cr = priv ? priv->cr : NULL; if (!gjs_parse_call_args(context, "showText", argv, "s", "utf8", &utf8)) return false; cairo_show_text(cr, utf8); if (!gjs_cairo_check_status(context, cairo_status(cr), "context")) return false; argv.rval().setUndefined(); return true; } static bool selectFontFace_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_PRIV(context, argc, vp, argv, obj, GjsCairoContext, priv); GjsAutoJSChar family; cairo_font_slant_t slant; cairo_font_weight_t weight; cairo_t *cr = priv ? priv->cr : NULL; if (!gjs_parse_call_args(context, "selectFontFace", argv, "sii", "family", &family, "slang", &slant, "weight", &weight)) return false; cairo_select_font_face(cr, family, slant, weight); if (!gjs_cairo_check_status(context, cairo_status(cr), "context")) return false; argv.rval().setUndefined(); return true; } static bool popGroup_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_PRIV(context, argc, vp, rec, obj, GjsCairoContext, priv); cairo_t *cr = priv ? priv->cr : NULL; cairo_pattern_t *pattern; JSObject *pattern_wrapper; if (argc > 0) { gjs_throw(context, "Context.popGroup() takes no arguments"); return false; } pattern = cairo_pop_group(cr); if (!gjs_cairo_check_status(context, cairo_status(cr), "context")) return false; pattern_wrapper = gjs_cairo_pattern_from_pattern(context, pattern); cairo_pattern_destroy(pattern); if (!pattern_wrapper) { gjs_throw(context, "failed to create pattern"); return false; } rec.rval().setObject(*pattern_wrapper); return true; } static bool getSource_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_PRIV(context, argc, vp, rec, obj, GjsCairoContext, priv); cairo_t *cr = priv ? priv->cr : NULL; cairo_pattern_t *pattern; JSObject *pattern_wrapper; if (argc > 0) { gjs_throw(context, "Context.getSource() takes no arguments"); return false; } pattern = cairo_get_source(cr); if (!gjs_cairo_check_status(context, cairo_status(cr), "context")) return false; /* pattern belongs to the context, so keep the reference */ pattern_wrapper = gjs_cairo_pattern_from_pattern(context, pattern); if (!pattern_wrapper) { gjs_throw(context, "failed to create pattern"); return false; } rec.rval().setObject(*pattern_wrapper); return true; } static bool getTarget_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_PRIV(context, argc, vp, rec, obj, GjsCairoContext, priv); cairo_t *cr = priv ? priv->cr : NULL; cairo_surface_t *surface; JSObject *surface_wrapper; if (argc > 0) { gjs_throw(context, "Context.getTarget() takes no arguments"); return false; } surface = cairo_get_target(cr); if (!gjs_cairo_check_status(context, cairo_status(cr), "context")) return false; /* surface belongs to the context, so keep the reference */ surface_wrapper = gjs_cairo_surface_from_surface(context, surface); if (!surface_wrapper) { /* exception already set */ return false; } rec.rval().setObject(*surface_wrapper); return true; } static bool getGroupTarget_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_PRIV(context, argc, vp, rec, obj, GjsCairoContext, priv); cairo_t *cr = priv ? priv->cr : NULL; cairo_surface_t *surface; JSObject *surface_wrapper; if (argc > 0) { gjs_throw(context, "Context.getGroupTarget() takes no arguments"); return false; } surface = cairo_get_group_target(cr); if (!gjs_cairo_check_status(context, cairo_status(cr), "context")) return false; /* surface belongs to the context, so keep the reference */ surface_wrapper = gjs_cairo_surface_from_surface(context, surface); if (!surface_wrapper) { /* exception already set */ return false; } rec.rval().setObject(*surface_wrapper); return true; } JSFunctionSpec gjs_cairo_context_proto_funcs[] = { JS_FS("$dispose", dispose_func, 0, 0), JS_FS("appendPath", appendPath_func, 0, 0), JS_FS("arc", arc_func, 0, 0), JS_FS("arcNegative", arcNegative_func, 0, 0), JS_FS("clip", clip_func, 0, 0), JS_FS("clipExtents", clipExtents_func, 0, 0), JS_FS("clipPreserve", clipPreserve_func, 0, 0), JS_FS("closePath", closePath_func, 0, 0), JS_FS("copyPage", copyPage_func, 0, 0), JS_FS("copyPath", copyPath_func, 0, 0), JS_FS("copyPathFlat", copyPathFlat_func, 0, 0), JS_FS("curveTo", curveTo_func, 0, 0), JS_FS("deviceToUser", deviceToUser_func, 0, 0), JS_FS("deviceToUserDistance", deviceToUserDistance_func, 0, 0), JS_FS("fill", fill_func, 0, 0), JS_FS("fillPreserve", fillPreserve_func, 0, 0), JS_FS("fillExtents", fillExtents_func, 0, 0), // fontExtents JS_FS("getAntialias", getAntialias_func, 0, 0), JS_FS("getCurrentPoint", getCurrentPoint_func, 0, 0), // getDash JS_FS("getDashCount", getDashCount_func, 0, 0), JS_FS("getFillRule", getFillRule_func, 0, 0), // getFontFace // getFontMatrix // getFontOptions JS_FS("getGroupTarget", getGroupTarget_func, 0, 0), JS_FS("getLineCap", getLineCap_func, 0, 0), JS_FS("getLineJoin", getLineJoin_func, 0, 0), JS_FS("getLineWidth", getLineWidth_func, 0, 0), // getMatrix JS_FS("getMiterLimit", getMiterLimit_func, 0, 0), JS_FS("getOperator", getOperator_func, 0, 0), // getScaledFont JS_FS("getSource", getSource_func, 0, 0), JS_FS("getTarget", getTarget_func, 0, 0), JS_FS("getTolerance", getTolerance_func, 0, 0), // glyphPath // glyphExtents JS_FS("hasCurrentPoint", hasCurrentPoint_func, 0, 0), JS_FS("identityMatrix", identityMatrix_func, 0, 0), JS_FS("inFill", inFill_func, 0, 0), JS_FS("inStroke", inStroke_func, 0, 0), JS_FS("lineTo", lineTo_func, 0, 0), JS_FS("mask", mask_func, 0, 0), JS_FS("maskSurface", maskSurface_func, 0, 0), JS_FS("moveTo", moveTo_func, 0, 0), JS_FS("newPath", newPath_func, 0, 0), JS_FS("newSubPath", newSubPath_func, 0, 0), JS_FS("paint", paint_func, 0, 0), JS_FS("paintWithAlpha", paintWithAlpha_func, 0, 0), JS_FS("pathExtents", pathExtents_func, 0, 0), JS_FS("popGroup", popGroup_func, 0, 0), JS_FS("popGroupToSource", popGroupToSource_func, 0, 0), JS_FS("pushGroup", pushGroup_func, 0, 0), JS_FS("pushGroupWithContent", pushGroupWithContent_func, 0, 0), JS_FS("rectangle", rectangle_func, 0, 0), JS_FS("relCurveTo", relCurveTo_func, 0, 0), JS_FS("relLineTo", relLineTo_func, 0, 0), JS_FS("relMoveTo", relMoveTo_func, 0, 0), JS_FS("resetClip", resetClip_func, 0, 0), JS_FS("restore", restore_func, 0, 0), JS_FS("rotate", rotate_func, 0, 0), JS_FS("save", save_func, 0, 0), JS_FS("scale", scale_func, 0, 0), JS_FS("selectFontFace", selectFontFace_func, 0, 0), JS_FS("setAntialias", setAntialias_func, 0, 0), JS_FS("setDash", setDash_func, 0, 0), // setFontFace // setFontMatrix // setFontOptions JS_FS("setFontSize", setFontSize_func, 0, 0), JS_FS("setFillRule", setFillRule_func, 0, 0), JS_FS("setLineCap", setLineCap_func, 0, 0), JS_FS("setLineJoin", setLineJoin_func, 0, 0), JS_FS("setLineWidth", setLineWidth_func, 0, 0), // setMatrix JS_FS("setMiterLimit", setMiterLimit_func, 0, 0), JS_FS("setOperator", setOperator_func, 0, 0), // setScaledFont JS_FS("setSource", setSource_func, 0, 0), JS_FS("setSourceRGB", setSourceRGB_func, 0, 0), JS_FS("setSourceRGBA", setSourceRGBA_func, 0, 0), JS_FS("setSourceSurface", setSourceSurface_func, 0, 0), JS_FS("setTolerance", setTolerance_func, 0, 0), // showGlyphs JS_FS("showPage", showPage_func, 0, 0), JS_FS("showText", showText_func, 0, 0), // showTextGlyphs JS_FS("stroke", stroke_func, 0, 0), JS_FS("strokeExtents", strokeExtents_func, 0, 0), JS_FS("strokePreserve", strokePreserve_func, 0, 0), // textPath // textExtends // transform JS_FS("translate", translate_func, 0, 0), JS_FS("userToDevice", userToDevice_func, 0, 0), JS_FS("userToDeviceDistance", userToDeviceDistance_func, 0, 0), JS_FS_END }; JSFunctionSpec gjs_cairo_context_static_funcs[] = { JS_FS_END }; JSObject * gjs_cairo_context_from_context(JSContext *context, cairo_t *cr) { JS::RootedObject proto(context, gjs_cairo_context_get_proto(context)); JS::RootedObject object(context, JS_NewObjectWithGivenProto(context, &gjs_cairo_context_class, proto)); if (!object) return NULL; _gjs_cairo_context_construct_internal(context, object, cr); return object; } cairo_t * gjs_cairo_context_get_context(JSContext *context, JS::HandleObject object) { GjsCairoContext *priv; priv = priv_from_js(context, object); if (priv == NULL) return NULL; return priv->cr; } static bool context_to_g_argument(JSContext *context, JS::Value value, const char *arg_name, GjsArgumentType argument_type, GITransfer transfer, bool may_be_null, GArgument *arg) { JS::RootedObject obj(context, value.toObjectOrNull()); cairo_t *cr; cr = gjs_cairo_context_get_context(context, obj); if (!cr) return false; if (transfer == GI_TRANSFER_EVERYTHING) cairo_reference(cr); arg->v_pointer = cr; return true; } static bool context_from_g_argument(JSContext *context, JS::MutableHandleValue value_p, GIArgument *arg) { JSObject *obj; obj = gjs_cairo_context_from_context(context, (cairo_t*)arg->v_pointer); if (!obj) { gjs_throw(context, "Could not create Cairo context"); return false; } value_p.setObject(*obj); return true; } static bool context_release_argument(JSContext *context, GITransfer transfer, GArgument *arg) { cairo_destroy((cairo_t*)arg->v_pointer); return true; } static GjsForeignInfo foreign_info = { context_to_g_argument, context_from_g_argument, context_release_argument }; void gjs_cairo_context_init(JSContext *context) { gjs_struct_foreign_register("cairo", "Context", &foreign_info); } cjs-4.4.0/modules/cairo-gradient.cpp000066400000000000000000000072731356375132300173630ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2010 litl, LLC. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "cjs/jsapi-class.h" #include "cjs/jsapi-util-args.h" #include "cjs/jsapi-wrapper.h" #include #include "cairo-private.h" GJS_DEFINE_PROTO_ABSTRACT_WITH_PARENT("Gradient", cairo_gradient, cairo_pattern, JSCLASS_BACKGROUND_FINALIZE) static void gjs_cairo_gradient_finalize(JSFreeOp *fop, JSObject *obj) { gjs_cairo_pattern_finalize_pattern(fop, obj); } /* Properties */ JSPropertySpec gjs_cairo_gradient_proto_props[] = { JS_PS_END }; /* Methods */ static bool addColorStopRGB_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_THIS(context, argc, vp, argv, obj); double offset, red, green, blue; cairo_pattern_t *pattern; if (!gjs_parse_call_args(context, "addColorStopRGB", argv, "ffff", "offset", &offset, "red", &red, "green", &green, "blue", &blue)) return false; pattern = gjs_cairo_pattern_get_pattern(context, obj); cairo_pattern_add_color_stop_rgb(pattern, offset, red, green, blue); if (!gjs_cairo_check_status(context, cairo_pattern_status(pattern), "pattern")) return false; argv.rval().setUndefined(); return true; } static bool addColorStopRGBA_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_THIS(context, argc, vp, argv, obj); double offset, red, green, blue, alpha; cairo_pattern_t *pattern; if (!gjs_parse_call_args(context, "addColorStopRGBA", argv, "fffff", "offset", &offset, "red", &red, "green", &green, "blue", &blue, "alpha", &alpha)) return false; pattern = gjs_cairo_pattern_get_pattern(context, obj); cairo_pattern_add_color_stop_rgba(pattern, offset, red, green, blue, alpha); if (!gjs_cairo_check_status(context, cairo_pattern_status(pattern), "pattern")) return false; argv.rval().setUndefined(); return true; } JSFunctionSpec gjs_cairo_gradient_proto_funcs[] = { JS_FS("addColorStopRGB", addColorStopRGB_func, 0, 0), JS_FS("addColorStopRGBA", addColorStopRGBA_func, 0, 0), // getColorStopRGB // getColorStopRGBA JS_FS_END }; JSFunctionSpec gjs_cairo_gradient_static_funcs[] = { JS_FS_END }; cjs-4.4.0/modules/cairo-image-surface.cpp000066400000000000000000000163551356375132300202770ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2010 litl, LLC. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "cjs/jsapi-class.h" #include "cjs/jsapi-util.h" #include "cjs/jsapi-util-args.h" #include "cjs/jsapi-wrapper.h" #include #include "cairo-private.h" static JSObject *gjs_cairo_image_surface_get_proto(JSContext *); GJS_DEFINE_PROTO_WITH_PARENT("ImageSurface", cairo_image_surface, cairo_surface, JSCLASS_BACKGROUND_FINALIZE) GJS_NATIVE_CONSTRUCTOR_DECLARE(cairo_image_surface) { GJS_NATIVE_CONSTRUCTOR_VARIABLES(cairo_image_surface) int format, width, height; cairo_surface_t *surface; GJS_NATIVE_CONSTRUCTOR_PRELUDE(cairo_image_surface); // create_for_data optional parameter if (!gjs_parse_call_args(context, "ImageSurface", argv, "iii", "format", &format, "width", &width, "height", &height)) return false; surface = cairo_image_surface_create((cairo_format_t) format, width, height); if (!gjs_cairo_check_status(context, cairo_surface_status(surface), "surface")) return false; gjs_cairo_surface_construct(context, object, surface); cairo_surface_destroy(surface); GJS_NATIVE_CONSTRUCTOR_FINISH(cairo_image_surface); return true; } static void gjs_cairo_image_surface_finalize(JSFreeOp *fop, JSObject *obj) { gjs_cairo_surface_finalize_surface(fop, obj); } JSPropertySpec gjs_cairo_image_surface_proto_props[] = { JS_PS_END }; static bool createFromPNG_func(JSContext *context, unsigned argc, JS::Value *vp) { JS::CallArgs argv = JS::CallArgsFromVp (argc, vp); GjsAutoChar filename; cairo_surface_t *surface; if (!gjs_parse_call_args(context, "createFromPNG", argv, "F", "filename", &filename)) return false; surface = cairo_image_surface_create_from_png(filename); if (!gjs_cairo_check_status(context, cairo_surface_status(surface), "surface")) return false; JS::RootedObject proto(context, gjs_cairo_image_surface_get_proto(context)); JS::RootedObject surface_wrapper(context, JS_NewObjectWithGivenProto(context, &gjs_cairo_image_surface_class, proto)); if (!surface_wrapper) { gjs_throw(context, "failed to create surface"); return false; } gjs_cairo_surface_construct(context, surface_wrapper, surface); cairo_surface_destroy(surface); argv.rval().setObject(*surface_wrapper); return true; } static bool getFormat_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_THIS(context, argc, vp, rec, obj); cairo_surface_t *surface; cairo_format_t format; if (argc > 1) { gjs_throw(context, "ImageSurface.getFormat() takes no arguments"); return false; } surface = gjs_cairo_surface_get_surface(context, obj); format = cairo_image_surface_get_format(surface); if (!gjs_cairo_check_status(context, cairo_surface_status(surface), "surface")) return false; rec.rval().setInt32(format); return true; } static bool getWidth_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_THIS(context, argc, vp, rec, obj); cairo_surface_t *surface; int width; if (argc > 1) { gjs_throw(context, "ImageSurface.getWidth() takes no arguments"); return false; } surface = gjs_cairo_surface_get_surface(context, obj); width = cairo_image_surface_get_width(surface); if (!gjs_cairo_check_status(context, cairo_surface_status(surface), "surface")) return false; rec.rval().setInt32(width); return true; } static bool getHeight_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_THIS(context, argc, vp, rec, obj); cairo_surface_t *surface; int height; if (argc > 1) { gjs_throw(context, "ImageSurface.getHeight() takes no arguments"); return false; } surface = gjs_cairo_surface_get_surface(context, obj); height = cairo_image_surface_get_height(surface); if (!gjs_cairo_check_status(context, cairo_surface_status(surface), "surface")) return false; rec.rval().setInt32(height); return true; } static bool getStride_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_THIS(context, argc, vp, rec, obj); cairo_surface_t *surface; int stride; if (argc > 1) { gjs_throw(context, "ImageSurface.getStride() takes no arguments"); return false; } surface = gjs_cairo_surface_get_surface(context, obj); stride = cairo_image_surface_get_stride(surface); if (!gjs_cairo_check_status(context, cairo_surface_status(surface), "surface")) return false; rec.rval().setInt32(stride); return true; } JSFunctionSpec gjs_cairo_image_surface_proto_funcs[] = { JS_FS("createFromPNG", createFromPNG_func, 0, 0), // getData JS_FS("getFormat", getFormat_func, 0, 0), JS_FS("getWidth", getWidth_func, 0, 0), JS_FS("getHeight", getHeight_func, 0, 0), JS_FS("getStride", getStride_func, 0, 0), JS_FS_END }; JSFunctionSpec gjs_cairo_image_surface_static_funcs[] = { JS_FS("createFromPNG", createFromPNG_func, 1, GJS_MODULE_PROP_FLAGS), JS_FS_END }; JSObject * gjs_cairo_image_surface_from_surface(JSContext *context, cairo_surface_t *surface) { g_return_val_if_fail(context != NULL, NULL); g_return_val_if_fail(surface != NULL, NULL); g_return_val_if_fail(cairo_surface_get_type(surface) == CAIRO_SURFACE_TYPE_IMAGE, NULL); JS::RootedObject proto(context, gjs_cairo_image_surface_get_proto(context)); JS::RootedObject object(context, JS_NewObjectWithGivenProto(context, &gjs_cairo_image_surface_class, proto)); if (!object) { gjs_throw(context, "failed to create image surface"); return NULL; } gjs_cairo_surface_construct(context, object, surface); return object; } cjs-4.4.0/modules/cairo-linear-gradient.cpp000066400000000000000000000070241356375132300206250ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2010 litl, LLC. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "cjs/jsapi-class.h" #include "cjs/jsapi-util-args.h" #include "cjs/jsapi-wrapper.h" #include #include "cairo-private.h" static JSObject *gjs_cairo_linear_gradient_get_proto(JSContext *); GJS_DEFINE_PROTO_WITH_PARENT("LinearGradient", cairo_linear_gradient, cairo_gradient, JSCLASS_BACKGROUND_FINALIZE) GJS_NATIVE_CONSTRUCTOR_DECLARE(cairo_linear_gradient) { GJS_NATIVE_CONSTRUCTOR_VARIABLES(cairo_linear_gradient) double x0, y0, x1, y1; cairo_pattern_t *pattern; GJS_NATIVE_CONSTRUCTOR_PRELUDE(cairo_linear_gradient); if (!gjs_parse_call_args(context, "LinearGradient", argv, "ffff", "x0", &x0, "y0", &y0, "x1", &x1, "y1", &y1)) return false; pattern = cairo_pattern_create_linear(x0, y0, x1, y1); if (!gjs_cairo_check_status(context, cairo_pattern_status(pattern), "pattern")) return false; gjs_cairo_pattern_construct(context, object, pattern); cairo_pattern_destroy(pattern); GJS_NATIVE_CONSTRUCTOR_FINISH(cairo_linear_gradient); return true; } static void gjs_cairo_linear_gradient_finalize(JSFreeOp *fop, JSObject *obj) { gjs_cairo_pattern_finalize_pattern(fop, obj); } JSPropertySpec gjs_cairo_linear_gradient_proto_props[] = { JS_PS_END }; JSFunctionSpec gjs_cairo_linear_gradient_proto_funcs[] = { // getLinearPoints JS_FS_END }; JSFunctionSpec gjs_cairo_linear_gradient_static_funcs[] = { JS_FS_END }; JSObject * gjs_cairo_linear_gradient_from_pattern(JSContext *context, cairo_pattern_t *pattern) { g_return_val_if_fail(context != NULL, NULL); g_return_val_if_fail(pattern != NULL, NULL); g_return_val_if_fail(cairo_pattern_get_type(pattern) == CAIRO_PATTERN_TYPE_LINEAR, NULL); JS::RootedObject proto(context, gjs_cairo_linear_gradient_get_proto(context)); JS::RootedObject object(context, JS_NewObjectWithGivenProto(context, &gjs_cairo_linear_gradient_class, proto)); if (!object) { gjs_throw(context, "failed to create linear gradient pattern"); return NULL; } gjs_cairo_pattern_construct(context, object, pattern); return object; } cjs-4.4.0/modules/cairo-module.h000066400000000000000000000025601356375132300165120ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2010 litl, LLC. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __CAIRO_MODULE_H__ #define __CAIRO_MODULE_H__ bool gjs_js_define_cairo_stuff(JSContext *context, JS::MutableHandleObject module); #endif /* __CAIRO_MODULE_H__ */ cjs-4.4.0/modules/cairo-path.cpp000066400000000000000000000067741356375132300165270ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2010 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "cjs/jsapi-class.h" #include "cjs/jsapi-util.h" #include "cjs/jsapi-wrapper.h" #include #include "cairo-private.h" typedef struct { JSContext *context; JSObject *object; cairo_path_t *path; } GjsCairoPath; static JSObject *gjs_cairo_path_get_proto(JSContext *); GJS_DEFINE_PROTO_ABSTRACT("Path", cairo_path, JSCLASS_BACKGROUND_FINALIZE) GJS_DEFINE_PRIV_FROM_JS(GjsCairoPath, gjs_cairo_path_class) static void gjs_cairo_path_finalize(JSFreeOp *fop, JSObject *obj) { GjsCairoPath *priv; priv = (GjsCairoPath*) JS_GetPrivate(obj); if (priv == NULL) return; cairo_path_destroy(priv->path); g_slice_free(GjsCairoPath, priv); } /* Properties */ JSPropertySpec gjs_cairo_path_proto_props[] = { JS_PS_END }; JSFunctionSpec gjs_cairo_path_proto_funcs[] = { JS_FS_END }; JSFunctionSpec gjs_cairo_path_static_funcs[] = { JS_FS_END }; /** * gjs_cairo_path_from_path: * @context: the context * @path: cairo_path_t to attach to the object * * Constructs a pattern wrapper given cairo pattern. * NOTE: This function takes ownership of the path. */ JSObject * gjs_cairo_path_from_path(JSContext *context, cairo_path_t *path) { GjsCairoPath *priv; g_return_val_if_fail(context != NULL, NULL); g_return_val_if_fail(path != NULL, NULL); JS::RootedObject proto(context, gjs_cairo_path_get_proto(context)); JS::RootedObject object(context, JS_NewObjectWithGivenProto(context, &gjs_cairo_path_class, proto)); if (!object) { gjs_throw(context, "failed to create path"); return NULL; } priv = g_slice_new0(GjsCairoPath); g_assert(priv_from_js(context, object) == NULL); JS_SetPrivate(object, priv); priv->context = context; priv->object = object; priv->path = path; return object; } /** * gjs_cairo_path_get_path: * @context: the context * @object: path wrapper * * Returns: the path attached to the wrapper. * */ cairo_path_t * gjs_cairo_path_get_path(JSContext *context, JSObject *object) { GjsCairoPath *priv; g_return_val_if_fail(context != NULL, NULL); g_return_val_if_fail(object != NULL, NULL); priv = (GjsCairoPath*) JS_GetPrivate(object); if (priv == NULL) return NULL; return priv->path; } cjs-4.4.0/modules/cairo-pattern.cpp000066400000000000000000000137401356375132300172370ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2010 litl, LLC. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "cjs/jsapi-class.h" #include "cjs/jsapi-util.h" #include "cjs/jsapi-wrapper.h" #include #include #include "cairo-private.h" typedef struct { void *dummy; JSContext *context; JSObject *object; cairo_pattern_t *pattern; } GjsCairoPattern; GJS_DEFINE_PROTO_ABSTRACT_WITH_GTYPE("Pattern", cairo_pattern, CAIRO_GOBJECT_TYPE_PATTERN, JSCLASS_BACKGROUND_FINALIZE) GJS_DEFINE_PRIV_FROM_JS(GjsCairoPattern, gjs_cairo_pattern_class) static void gjs_cairo_pattern_finalize(JSFreeOp *fop, JSObject *obj) { GjsCairoPattern *priv; priv = (GjsCairoPattern*) JS_GetPrivate(obj); if (priv == NULL) return; cairo_pattern_destroy(priv->pattern); g_slice_free(GjsCairoPattern, priv); } /* Properties */ JSPropertySpec gjs_cairo_pattern_proto_props[] = { JS_PS_END }; /* Methods */ static bool getType_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_THIS(context, argc, vp, rec, obj); cairo_pattern_t *pattern; cairo_pattern_type_t type; if (argc > 1) { gjs_throw(context, "Pattern.getType() takes no arguments"); return false; } pattern = gjs_cairo_pattern_get_pattern(context, obj); type = cairo_pattern_get_type(pattern); if (!gjs_cairo_check_status(context, cairo_pattern_status(pattern), "pattern")) return false; rec.rval().setInt32(type); return true; } JSFunctionSpec gjs_cairo_pattern_proto_funcs[] = { // getMatrix JS_FS("getType", getType_func, 0, 0), // setMatrix JS_FS_END }; JSFunctionSpec gjs_cairo_pattern_static_funcs[] = { JS_FS_END }; /* Public API */ /** * gjs_cairo_pattern_construct: * @context: the context * @object: object to construct * @pattern: cairo_pattern to attach to the object * * Constructs a pattern wrapper giving an empty JSObject and a * cairo pattern. A reference to @pattern will be taken. * * This is mainly used for subclasses where object is already created. */ void gjs_cairo_pattern_construct(JSContext *context, JS::HandleObject object, cairo_pattern_t *pattern) { GjsCairoPattern *priv; g_return_if_fail(context != NULL); g_return_if_fail(object != nullptr); g_return_if_fail(pattern != NULL); priv = g_slice_new0(GjsCairoPattern); g_assert(priv_from_js(context, object) == NULL); JS_SetPrivate(object, priv); priv->context = context; priv->object = object; priv->pattern = cairo_pattern_reference(pattern); } /** * gjs_cairo_pattern_finalize: * @fop: the free op * @object: object to finalize * * Destroys the resources associated with a pattern wrapper. * * This is mainly used for subclasses. */ void gjs_cairo_pattern_finalize_pattern(JSFreeOp *fop, JSObject *object) { g_return_if_fail(fop != NULL); g_return_if_fail(object != NULL); gjs_cairo_pattern_finalize(fop, object); } /** * gjs_cairo_pattern_from_pattern: * @context: the context * @pattern: cairo_pattern to attach to the object * * Constructs a pattern wrapper given cairo pattern. * A reference to @pattern will be taken. * */ JSObject * gjs_cairo_pattern_from_pattern(JSContext *context, cairo_pattern_t *pattern) { g_return_val_if_fail(context != NULL, NULL); g_return_val_if_fail(pattern != NULL, NULL); switch (cairo_pattern_get_type(pattern)) { case CAIRO_PATTERN_TYPE_SOLID: return gjs_cairo_solid_pattern_from_pattern(context, pattern); case CAIRO_PATTERN_TYPE_SURFACE: return gjs_cairo_surface_pattern_from_pattern(context, pattern); case CAIRO_PATTERN_TYPE_LINEAR: return gjs_cairo_linear_gradient_from_pattern(context, pattern); case CAIRO_PATTERN_TYPE_RADIAL: return gjs_cairo_radial_gradient_from_pattern(context, pattern); case CAIRO_PATTERN_TYPE_MESH: case CAIRO_PATTERN_TYPE_RASTER_SOURCE: default: gjs_throw(context, "failed to create pattern, unsupported pattern type %d", cairo_pattern_get_type(pattern)); return NULL; } } /** * gjs_cairo_pattern_get_pattern: * @context: the context * @object: pattern wrapper * * Returns: the pattern attaches to the wrapper. * */ cairo_pattern_t * gjs_cairo_pattern_get_pattern(JSContext *context, JSObject *object) { GjsCairoPattern *priv; g_return_val_if_fail(context != NULL, NULL); g_return_val_if_fail(object != NULL, NULL); priv = (GjsCairoPattern*) JS_GetPrivate(object); if (priv == NULL) return NULL; return priv->pattern; } cjs-4.4.0/modules/cairo-pdf-surface.cpp000066400000000000000000000074501356375132300177620ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2010 litl, LLC. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "cjs/jsapi-class.h" #include "cjs/jsapi-util-args.h" #include "cjs/jsapi-wrapper.h" #include #include "cairo-private.h" #if CAIRO_HAS_PDF_SURFACE #include static JSObject *gjs_cairo_pdf_surface_get_proto(JSContext *); GJS_DEFINE_PROTO_WITH_PARENT("PDFSurface", cairo_pdf_surface, cairo_surface, JSCLASS_BACKGROUND_FINALIZE) GJS_NATIVE_CONSTRUCTOR_DECLARE(cairo_pdf_surface) { GJS_NATIVE_CONSTRUCTOR_VARIABLES(cairo_pdf_surface) GjsAutoChar filename; double width, height; cairo_surface_t *surface; GJS_NATIVE_CONSTRUCTOR_PRELUDE(cairo_pdf_surface); if (!gjs_parse_call_args(context, "PDFSurface", argv, "Fff", "filename", &filename, "width", &width, "height", &height)) return false; surface = cairo_pdf_surface_create(filename, width, height); if (!gjs_cairo_check_status(context, cairo_surface_status(surface), "surface")) return false; gjs_cairo_surface_construct(context, object, surface); cairo_surface_destroy(surface); GJS_NATIVE_CONSTRUCTOR_FINISH(cairo_pdf_surface); return true; } static void gjs_cairo_pdf_surface_finalize(JSFreeOp *fop, JSObject *obj) { gjs_cairo_surface_finalize_surface(fop, obj); } JSPropertySpec gjs_cairo_pdf_surface_proto_props[] = { JS_PS_END }; JSFunctionSpec gjs_cairo_pdf_surface_proto_funcs[] = { JS_FS_END }; JSFunctionSpec gjs_cairo_pdf_surface_static_funcs[] = { JS_FS_END }; JSObject * gjs_cairo_pdf_surface_from_surface(JSContext *context, cairo_surface_t *surface) { g_return_val_if_fail(context != NULL, NULL); g_return_val_if_fail(surface != NULL, NULL); g_return_val_if_fail(cairo_surface_get_type(surface) == CAIRO_SURFACE_TYPE_PDF, NULL); JS::RootedObject proto(context, gjs_cairo_pdf_surface_get_proto(context)); JS::RootedObject object(context, JS_NewObjectWithGivenProto(context, &gjs_cairo_pdf_surface_class, proto)); if (!object) { gjs_throw(context, "failed to create pdf surface"); return NULL; } gjs_cairo_surface_construct(context, object, surface); return object; } #else JSObject * gjs_cairo_pdf_surface_from_surface(JSContext *context, cairo_surface_t *surface) { gjs_throw(context, "could not create PDF surface, recompile cairo and gjs with " "PDF support."); return NULL; } #endif /* CAIRO_HAS_PDF_SURFACE */ cjs-4.4.0/modules/cairo-private.h000066400000000000000000000213261356375132300167000ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2010 litl, LLC. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __CAIRO_PRIVATE_H__ #define __CAIRO_PRIVATE_H__ #include "cairo-module.h" #include bool gjs_cairo_check_status (JSContext *context, cairo_status_t status, const char *name); bool gjs_cairo_region_define_proto(JSContext *cx, JS::HandleObject module, JS::MutableHandleObject proto); void gjs_cairo_region_init (JSContext *context); bool gjs_cairo_context_define_proto(JSContext *cx, JS::HandleObject module, JS::MutableHandleObject proto); cairo_t * gjs_cairo_context_get_context (JSContext *context, JS::HandleObject object); JSObject * gjs_cairo_context_from_context (JSContext *context, cairo_t *cr); void gjs_cairo_context_init (JSContext *context); void gjs_cairo_surface_init (JSContext *context); /* path */ bool gjs_cairo_path_define_proto(JSContext *cx, JS::HandleObject module, JS::MutableHandleObject proto); JSObject * gjs_cairo_path_from_path (JSContext *context, cairo_path_t *path); cairo_path_t * gjs_cairo_path_get_path (JSContext *context, JSObject *path_wrapper); /* surface */ JSObject *gjs_cairo_surface_get_proto(JSContext *cx); bool gjs_cairo_surface_define_proto(JSContext *cx, JS::HandleObject module, JS::MutableHandleObject proto); void gjs_cairo_surface_construct (JSContext *context, JS::HandleObject object, cairo_surface_t *surface); void gjs_cairo_surface_finalize_surface (JSFreeOp *fop, JSObject *object); JSObject * gjs_cairo_surface_from_surface (JSContext *context, cairo_surface_t *surface); cairo_surface_t* gjs_cairo_surface_get_surface (JSContext *context, JSObject *object); /* image surface */ bool gjs_cairo_image_surface_define_proto(JSContext *cx, JS::HandleObject module, JS::MutableHandleObject proto); void gjs_cairo_image_surface_init (JSContext *context, JS::HandleObject proto); JSObject * gjs_cairo_image_surface_from_surface (JSContext *context, cairo_surface_t *surface); /* postscript surface */ #ifdef CAIRO_HAS_PS_SURFACE bool gjs_cairo_ps_surface_define_proto(JSContext *cx, JS::HandleObject module, JS::MutableHandleObject proto); #endif JSObject * gjs_cairo_ps_surface_from_surface (JSContext *context, cairo_surface_t *surface); /* pdf surface */ #ifdef CAIRO_HAS_PDF_SURFACE bool gjs_cairo_pdf_surface_define_proto(JSContext *cx, JS::HandleObject module, JS::MutableHandleObject proto); #endif JSObject * gjs_cairo_pdf_surface_from_surface (JSContext *context, cairo_surface_t *surface); /* svg surface */ #ifdef CAIRO_HAS_SVG_SURFACE bool gjs_cairo_svg_surface_define_proto(JSContext *cx, JS::HandleObject module, JS::MutableHandleObject proto); #endif JSObject * gjs_cairo_svg_surface_from_surface (JSContext *context, cairo_surface_t *surface); /* pattern */ JSObject *gjs_cairo_pattern_get_proto(JSContext *cx); bool gjs_cairo_pattern_define_proto(JSContext *cx, JS::HandleObject module, JS::MutableHandleObject proto); void gjs_cairo_pattern_construct (JSContext *context, JS::HandleObject object, cairo_pattern_t *pattern); void gjs_cairo_pattern_finalize_pattern (JSFreeOp *fop, JSObject *object); JSObject* gjs_cairo_pattern_from_pattern (JSContext *context, cairo_pattern_t *pattern); cairo_pattern_t* gjs_cairo_pattern_get_pattern (JSContext *context, JSObject *object); /* gradient */ JSObject *gjs_cairo_gradient_get_proto(JSContext *cx); bool gjs_cairo_gradient_define_proto(JSContext *cx, JS::HandleObject module, JS::MutableHandleObject proto); /* linear gradient */ bool gjs_cairo_linear_gradient_define_proto(JSContext *cx, JS::HandleObject module, JS::MutableHandleObject proto); JSObject * gjs_cairo_linear_gradient_from_pattern (JSContext *context, cairo_pattern_t *pattern); /* radial gradient */ bool gjs_cairo_radial_gradient_define_proto(JSContext *cx, JS::HandleObject module, JS::MutableHandleObject proto); JSObject * gjs_cairo_radial_gradient_from_pattern (JSContext *context, cairo_pattern_t *pattern); /* surface pattern */ bool gjs_cairo_surface_pattern_define_proto(JSContext *cx, JS::HandleObject module, JS::MutableHandleObject proto); JSObject * gjs_cairo_surface_pattern_from_pattern (JSContext *context, cairo_pattern_t *pattern); /* solid pattern */ bool gjs_cairo_solid_pattern_define_proto(JSContext *cx, JS::HandleObject module, JS::MutableHandleObject proto); JSObject * gjs_cairo_solid_pattern_from_pattern (JSContext *context, cairo_pattern_t *pattern); #endif /* __CAIRO_PRIVATE_H__ */ cjs-4.4.0/modules/cairo-ps-surface.cpp000066400000000000000000000076221356375132300176340ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2010 litl, LLC. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "cjs/jsapi-class.h" #include "cjs/jsapi-util-args.h" #include "cjs/jsapi-wrapper.h" #include #include "cairo-private.h" #if CAIRO_HAS_PS_SURFACE #include static JSObject *gjs_cairo_ps_surface_get_proto(JSContext *); GJS_DEFINE_PROTO_WITH_PARENT("PSSurface", cairo_ps_surface, cairo_surface, JSCLASS_BACKGROUND_FINALIZE) GJS_NATIVE_CONSTRUCTOR_DECLARE(cairo_ps_surface) { GJS_NATIVE_CONSTRUCTOR_VARIABLES(cairo_ps_surface) GjsAutoChar filename; double width, height; cairo_surface_t *surface; GJS_NATIVE_CONSTRUCTOR_PRELUDE(cairo_ps_surface); if (!gjs_parse_call_args(context, "PSSurface", argv, "Fff", "filename", &filename, "width", &width, "height", &height)) return false; surface = cairo_ps_surface_create(filename, width, height); if (!gjs_cairo_check_status(context, cairo_surface_status(surface), "surface")) return false; gjs_cairo_surface_construct(context, object, surface); cairo_surface_destroy(surface); GJS_NATIVE_CONSTRUCTOR_FINISH(cairo_ps_surface); return true; } static void gjs_cairo_ps_surface_finalize(JSFreeOp *fop, JSObject *obj) { gjs_cairo_surface_finalize_surface(fop, obj); } JSPropertySpec gjs_cairo_ps_surface_proto_props[] = { JS_PS_END }; JSFunctionSpec gjs_cairo_ps_surface_proto_funcs[] = { // restrictToLevel // getLevels // levelToString // setEPS // getEPS // setSize // dscBeginSetup // dscBeginPageSetup // dscComment JS_FS_END }; JSFunctionSpec gjs_cairo_ps_surface_static_funcs[] = { JS_FS_END }; JSObject * gjs_cairo_ps_surface_from_surface(JSContext *context, cairo_surface_t *surface) { g_return_val_if_fail(context != NULL, NULL); g_return_val_if_fail(surface != NULL, NULL); g_return_val_if_fail(cairo_surface_get_type(surface) == CAIRO_SURFACE_TYPE_PS, NULL); JS::RootedObject proto(context, gjs_cairo_ps_surface_get_proto(context)); JS::RootedObject object(context, JS_NewObjectWithGivenProto(context, &gjs_cairo_ps_surface_class, proto)); if (!object) { gjs_throw(context, "failed to create ps surface"); return NULL; } gjs_cairo_surface_construct(context, object, surface); return object; } #else JSObject * gjs_cairo_ps_surface_from_surface(JSContext *context, cairo_surface_t *surface) { gjs_throw(context, "could not create PS surface, recompile cairo and gjs with " "PS support."); return NULL; } #endif /* CAIRO_HAS_PS_SURFACE */ cjs-4.4.0/modules/cairo-radial-gradient.cpp000066400000000000000000000072571356375132300206170ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2010 litl, LLC. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "cjs/jsapi-class.h" #include "cjs/jsapi-util-args.h" #include "cjs/jsapi-wrapper.h" #include #include "cairo-private.h" static JSObject *gjs_cairo_radial_gradient_get_proto(JSContext *); GJS_DEFINE_PROTO_WITH_PARENT("RadialGradient", cairo_radial_gradient, cairo_gradient, JSCLASS_BACKGROUND_FINALIZE) GJS_NATIVE_CONSTRUCTOR_DECLARE(cairo_radial_gradient) { GJS_NATIVE_CONSTRUCTOR_VARIABLES(cairo_radial_gradient) double cx0, cy0, radius0, cx1, cy1, radius1; cairo_pattern_t *pattern; GJS_NATIVE_CONSTRUCTOR_PRELUDE(cairo_radial_gradient); if (!gjs_parse_call_args(context, "RadialGradient", argv, "ffffff", "cx0", &cx0, "cy0", &cy0, "radius0", &radius0, "cx1", &cx1, "cy1", &cy1, "radius1", &radius1)) return false; pattern = cairo_pattern_create_radial(cx0, cy0, radius0, cx1, cy1, radius1); if (!gjs_cairo_check_status(context, cairo_pattern_status(pattern), "pattern")) return false; gjs_cairo_pattern_construct(context, object, pattern); cairo_pattern_destroy(pattern); GJS_NATIVE_CONSTRUCTOR_FINISH(cairo_radial_gradient); return true; } static void gjs_cairo_radial_gradient_finalize(JSFreeOp *fop, JSObject *obj) { gjs_cairo_pattern_finalize_pattern(fop, obj); } JSPropertySpec gjs_cairo_radial_gradient_proto_props[] = { JS_PS_END }; JSFunctionSpec gjs_cairo_radial_gradient_proto_funcs[] = { // getRadialCircles JS_FS_END }; JSFunctionSpec gjs_cairo_radial_gradient_static_funcs[] = { JS_FS_END }; JSObject * gjs_cairo_radial_gradient_from_pattern(JSContext *context, cairo_pattern_t *pattern) { g_return_val_if_fail(context != NULL, NULL); g_return_val_if_fail(pattern != NULL, NULL); g_return_val_if_fail(cairo_pattern_get_type(pattern) == CAIRO_PATTERN_TYPE_RADIAL, NULL); JS::RootedObject proto(context, gjs_cairo_radial_gradient_get_proto(context)); JS::RootedObject object(context, JS_NewObjectWithGivenProto(context, &gjs_cairo_radial_gradient_class, proto)); if (!object) { gjs_throw(context, "failed to create radial gradient pattern"); return NULL; } gjs_cairo_pattern_construct(context, object, pattern); return object; } cjs-4.4.0/modules/cairo-region.cpp000066400000000000000000000262021356375132300170420ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2014 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "gi/foreign.h" #include "cjs/jsapi-class.h" #include "cjs/jsapi-util-args.h" #include "cjs/jsapi-wrapper.h" #include #include #include "cairo-private.h" typedef struct { JSContext *context; JSObject *object; cairo_region_t *region; } GjsCairoRegion; static JSObject *gjs_cairo_region_get_proto(JSContext *); GJS_DEFINE_PROTO_WITH_GTYPE("Region", cairo_region, CAIRO_GOBJECT_TYPE_REGION, JSCLASS_BACKGROUND_FINALIZE) GJS_DEFINE_PRIV_FROM_JS(GjsCairoRegion, gjs_cairo_region_class); static cairo_region_t * get_region(JSContext *context, JS::HandleObject obj) { GjsCairoRegion *priv = priv_from_js(context, obj); if (priv == NULL) return NULL; else return priv->region; } static bool fill_rectangle(JSContext *context, JS::HandleObject obj, cairo_rectangle_int_t *rect); #define PRELUDE \ GJS_GET_PRIV(context, argc, vp, argv, obj, GjsCairoRegion, priv); \ cairo_region_t *this_region = priv ? priv->region : NULL; #define RETURN_STATUS \ return gjs_cairo_check_status(context, cairo_region_status(this_region), "region"); #define REGION_DEFINE_REGION_FUNC(method) \ static bool \ method##_func(JSContext *context, \ unsigned argc, \ JS::Value *vp) \ { \ PRELUDE; \ JS::RootedObject other_obj(context); \ cairo_region_t *other_region; \ if (!gjs_parse_call_args(context, #method, argv, "o", \ "other_region", &other_obj)) \ return false; \ \ other_region = get_region(context, other_obj); \ \ cairo_region_##method(this_region, other_region); \ argv.rval().setUndefined(); \ RETURN_STATUS; \ } #define REGION_DEFINE_RECT_FUNC(method) \ static bool \ method##_rectangle_func(JSContext *context, \ unsigned argc, \ JS::Value *vp) \ { \ PRELUDE; \ JS::RootedObject rect_obj(context); \ cairo_rectangle_int_t rect; \ if (!gjs_parse_call_args(context, #method, argv, "o", \ "rect", &rect_obj)) \ return false; \ \ if (!fill_rectangle(context, rect_obj, &rect)) \ return false; \ \ cairo_region_##method##_rectangle(this_region, &rect); \ argv.rval().setUndefined(); \ RETURN_STATUS; \ } REGION_DEFINE_REGION_FUNC(union) REGION_DEFINE_REGION_FUNC(subtract) REGION_DEFINE_REGION_FUNC(intersect) REGION_DEFINE_REGION_FUNC(xor) REGION_DEFINE_RECT_FUNC(union) REGION_DEFINE_RECT_FUNC(subtract) REGION_DEFINE_RECT_FUNC(intersect) REGION_DEFINE_RECT_FUNC(xor) static bool fill_rectangle(JSContext *context, JS::HandleObject obj, cairo_rectangle_int_t *rect) { JS::RootedValue val(context); if (!gjs_object_get_property(context, obj, GJS_STRING_X, &val)) return false; if (!JS::ToInt32(context, val, &rect->x)) return false; if (!gjs_object_get_property(context, obj, GJS_STRING_Y, &val)) return false; if (!JS::ToInt32(context, val, &rect->y)) return false; if (!gjs_object_get_property(context, obj, GJS_STRING_WIDTH, &val)) return false; if (!JS::ToInt32(context, val, &rect->width)) return false; if (!gjs_object_get_property(context, obj, GJS_STRING_HEIGHT, &val)) return false; if (!JS::ToInt32(context, val, &rect->height)) return false; return true; } static JSObject * make_rectangle(JSContext *context, cairo_rectangle_int_t *rect) { JS::RootedObject rect_obj(context, JS_NewPlainObject(context)); JS::RootedValue val(context); val = JS::Int32Value(rect->x); JS_SetProperty(context, rect_obj, "x", val); val = JS::Int32Value(rect->y); JS_SetProperty(context, rect_obj, "y", val); val = JS::Int32Value(rect->width); JS_SetProperty(context, rect_obj, "width", val); val = JS::Int32Value(rect->height); JS_SetProperty(context, rect_obj, "height", val); return rect_obj; } static bool num_rectangles_func(JSContext *context, unsigned argc, JS::Value *vp) { PRELUDE; int n_rects; if (!gjs_parse_call_args(context, "num_rectangles", argv, "")) return false; n_rects = cairo_region_num_rectangles(this_region); argv.rval().setInt32(n_rects); RETURN_STATUS; } static bool get_rectangle_func(JSContext *context, unsigned argc, JS::Value *vp) { PRELUDE; int i; JSObject *rect_obj; cairo_rectangle_int_t rect; if (!gjs_parse_call_args(context, "get_rectangle", argv, "i", "rect", &i)) return false; cairo_region_get_rectangle(this_region, i, &rect); rect_obj = make_rectangle(context, &rect); argv.rval().setObjectOrNull(rect_obj); RETURN_STATUS; } JSPropertySpec gjs_cairo_region_proto_props[] = { JS_PS_END }; JSFunctionSpec gjs_cairo_region_proto_funcs[] = { JS_FS("union", union_func, 0, 0), JS_FS("subtract", subtract_func, 0, 0), JS_FS("intersect", intersect_func, 0, 0), JS_FS("xor", xor_func, 0, 0), JS_FS("unionRectangle", union_rectangle_func, 0, 0), JS_FS("subtractRectangle", subtract_rectangle_func, 0, 0), JS_FS("intersectRectangle", intersect_rectangle_func, 0, 0), JS_FS("xorRectangle", xor_rectangle_func, 0, 0), JS_FS("numRectangles", num_rectangles_func, 0, 0), JS_FS("getRectangle", get_rectangle_func, 0, 0), JS_FS_END }; JSFunctionSpec gjs_cairo_region_static_funcs[] = { JS_FS_END }; static void _gjs_cairo_region_construct_internal(JSContext *context, JS::HandleObject obj, cairo_region_t *region) { GjsCairoRegion *priv; priv = g_slice_new0(GjsCairoRegion); g_assert(priv_from_js(context, obj) == NULL); JS_SetPrivate(obj, priv); priv->context = context; priv->object = obj; priv->region = cairo_region_reference(region); } GJS_NATIVE_CONSTRUCTOR_DECLARE(cairo_region) { GJS_NATIVE_CONSTRUCTOR_VARIABLES(cairo_region) cairo_region_t *region; GJS_NATIVE_CONSTRUCTOR_PRELUDE(cairo_region); if (!gjs_parse_call_args(context, "Region", argv, "")) return false; region = cairo_region_create(); _gjs_cairo_region_construct_internal(context, object, region); cairo_region_destroy(region); GJS_NATIVE_CONSTRUCTOR_FINISH(cairo_region); return true; } static void gjs_cairo_region_finalize(JSFreeOp *fop, JSObject *obj) { GjsCairoRegion *priv; priv = (GjsCairoRegion*) JS_GetPrivate(obj); if (priv == NULL) return; cairo_region_destroy(priv->region); g_slice_free(GjsCairoRegion, priv); } static JSObject * gjs_cairo_region_from_region(JSContext *context, cairo_region_t *region) { JS::RootedObject proto(context, gjs_cairo_region_get_proto(context)); JS::RootedObject object(context, JS_NewObjectWithGivenProto(context, &gjs_cairo_region_class, proto)); if (!object) return NULL; _gjs_cairo_region_construct_internal(context, object, region); return object; } static bool region_to_g_argument(JSContext *context, JS::Value value, const char *arg_name, GjsArgumentType argument_type, GITransfer transfer, bool may_be_null, GArgument *arg) { JS::RootedObject obj(context, &value.toObject()); cairo_region_t *region; region = get_region(context, obj); if (!region) return false; if (transfer == GI_TRANSFER_EVERYTHING) cairo_region_destroy(region); arg->v_pointer = region; return true; } static bool region_from_g_argument(JSContext *context, JS::MutableHandleValue value_p, GIArgument *arg) { JSObject *obj; obj = gjs_cairo_region_from_region(context, (cairo_region_t*)arg->v_pointer); if (!obj) return false; value_p.setObject(*obj); return true; } static bool region_release_argument(JSContext *context, GITransfer transfer, GArgument *arg) { cairo_region_destroy((cairo_region_t*)arg->v_pointer); return true; } static GjsForeignInfo foreign_info = { region_to_g_argument, region_from_g_argument, region_release_argument }; void gjs_cairo_region_init(JSContext *context) { gjs_struct_foreign_register("cairo", "Region", &foreign_info); } cjs-4.4.0/modules/cairo-solid-pattern.cpp000066400000000000000000000107201356375132300203420ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2010 litl, LLC. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "cjs/jsapi-class.h" #include "cjs/jsapi-util-args.h" #include "cjs/jsapi-wrapper.h" #include #include "cairo-private.h" static JSObject *gjs_cairo_solid_pattern_get_proto(JSContext *); GJS_DEFINE_PROTO_ABSTRACT_WITH_PARENT("SolidPattern", cairo_solid_pattern, cairo_pattern, JSCLASS_BACKGROUND_FINALIZE) static void gjs_cairo_solid_pattern_finalize(JSFreeOp *fop, JSObject *obj) { gjs_cairo_pattern_finalize_pattern(fop, obj); } JSPropertySpec gjs_cairo_solid_pattern_proto_props[] = { JS_PS_END }; static bool createRGB_func(JSContext *context, unsigned argc, JS::Value *vp) { JS::CallArgs argv = JS::CallArgsFromVp (argc, vp); double red, green, blue; cairo_pattern_t *pattern; JSObject *pattern_wrapper; if (!gjs_parse_call_args(context, "createRGB", argv, "fff", "red", &red, "green", &green, "blue", &blue)) return false; pattern = cairo_pattern_create_rgb(red, green, blue); if (!gjs_cairo_check_status(context, cairo_pattern_status(pattern), "pattern")) return false; pattern_wrapper = gjs_cairo_solid_pattern_from_pattern(context, pattern); cairo_pattern_destroy(pattern); argv.rval().setObjectOrNull(pattern_wrapper); return true; } static bool createRGBA_func(JSContext *context, unsigned argc, JS::Value *vp) { JS::CallArgs argv = JS::CallArgsFromVp (argc, vp); double red, green, blue, alpha; cairo_pattern_t *pattern; JSObject *pattern_wrapper; if (!gjs_parse_call_args(context, "createRGBA", argv, "ffff", "red", &red, "green", &green, "blue", &blue, "alpha", &alpha)) return false; pattern = cairo_pattern_create_rgba(red, green, blue, alpha); if (!gjs_cairo_check_status(context, cairo_pattern_status(pattern), "pattern")) return false; pattern_wrapper = gjs_cairo_solid_pattern_from_pattern(context, pattern); cairo_pattern_destroy(pattern); argv.rval().setObjectOrNull(pattern_wrapper); return true; } JSFunctionSpec gjs_cairo_solid_pattern_proto_funcs[] = { JS_FS("createRGB", createRGB_func, 0, 0), JS_FS("createRGBA", createRGBA_func, 0, 0), JS_FS_END }; JSFunctionSpec gjs_cairo_solid_pattern_static_funcs[] = { JS_FS_END }; JSObject * gjs_cairo_solid_pattern_from_pattern(JSContext *context, cairo_pattern_t *pattern) { g_return_val_if_fail(context != NULL, NULL); g_return_val_if_fail(pattern != NULL, NULL); g_return_val_if_fail(cairo_pattern_get_type(pattern) == CAIRO_PATTERN_TYPE_SOLID, NULL); JS::RootedObject proto(context, gjs_cairo_solid_pattern_get_proto(context)); JS::RootedObject object(context, JS_NewObjectWithGivenProto(context, &gjs_cairo_solid_pattern_class, proto)); if (!object) { gjs_throw(context, "failed to create solid pattern"); return NULL; } gjs_cairo_pattern_construct(context, object, pattern); return object; } cjs-4.4.0/modules/cairo-surface-pattern.cpp000066400000000000000000000143421356375132300206640ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2010 litl, LLC. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "cjs/jsapi-class.h" #include "cjs/jsapi-util-args.h" #include "cjs/jsapi-wrapper.h" #include #include "cairo-private.h" static JSObject *gjs_cairo_surface_pattern_get_proto(JSContext *); GJS_DEFINE_PROTO_WITH_PARENT("SurfacePattern", cairo_surface_pattern, cairo_pattern, JSCLASS_BACKGROUND_FINALIZE) GJS_NATIVE_CONSTRUCTOR_DECLARE(cairo_surface_pattern) { GJS_NATIVE_CONSTRUCTOR_VARIABLES(cairo_surface_pattern) cairo_surface_t *surface; cairo_pattern_t *pattern; GJS_NATIVE_CONSTRUCTOR_PRELUDE(cairo_surface_pattern); JS::RootedObject surface_wrapper(context); if (!gjs_parse_call_args(context, "SurfacePattern", argv, "o", "surface", &surface_wrapper)) return false; surface = gjs_cairo_surface_get_surface(context, surface_wrapper); if (!surface) { gjs_throw(context, "first argument to SurfacePattern() should be a surface"); return false; } pattern = cairo_pattern_create_for_surface(surface); if (!gjs_cairo_check_status(context, cairo_pattern_status(pattern), "pattern")) return false; gjs_cairo_pattern_construct(context, object, pattern); cairo_pattern_destroy(pattern); GJS_NATIVE_CONSTRUCTOR_FINISH(cairo_surface_pattern); return true; } static void gjs_cairo_surface_pattern_finalize(JSFreeOp *fop, JSObject *obj) { gjs_cairo_pattern_finalize_pattern(fop, obj); } JSPropertySpec gjs_cairo_surface_pattern_proto_props[] = { JS_PS_END }; static bool setExtend_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_THIS(context, argc, vp, argv, obj); cairo_extend_t extend; cairo_pattern_t *pattern; if (!gjs_parse_call_args(context, "setExtend", argv, "i", "extend", &extend)) return false; pattern = gjs_cairo_pattern_get_pattern(context, obj); cairo_pattern_set_extend(pattern, extend); if (!gjs_cairo_check_status(context, cairo_pattern_status(pattern), "pattern")) return false; argv.rval().setUndefined(); return true; } static bool getExtend_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_THIS(context, argc, vp, rec, obj); cairo_extend_t extend; cairo_pattern_t *pattern; if (argc > 0) { gjs_throw(context, "SurfacePattern.getExtend() requires no arguments"); return false; } pattern = gjs_cairo_pattern_get_pattern(context, obj); extend = cairo_pattern_get_extend(pattern); if (!gjs_cairo_check_status(context, cairo_pattern_status(pattern), "pattern")) return false; rec.rval().setInt32(extend); return true; } static bool setFilter_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_THIS(context, argc, vp, argv, obj); cairo_filter_t filter; cairo_pattern_t *pattern; if (!gjs_parse_call_args(context, "setFilter", argv, "i", "filter", &filter)) return false; pattern = gjs_cairo_pattern_get_pattern(context, obj); cairo_pattern_set_filter(pattern, filter); if (!gjs_cairo_check_status(context, cairo_pattern_status(pattern), "pattern")) return false; argv.rval().setUndefined(); return true; } static bool getFilter_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_THIS(context, argc, vp, rec, obj); cairo_filter_t filter; cairo_pattern_t *pattern; if (argc > 0) { gjs_throw(context, "SurfacePattern.getFilter() requires no arguments"); return false; } pattern = gjs_cairo_pattern_get_pattern(context, obj); filter = cairo_pattern_get_filter(pattern); if (!gjs_cairo_check_status(context, cairo_pattern_status(pattern), "pattern")) return false; rec.rval().setInt32(filter); return true; } JSFunctionSpec gjs_cairo_surface_pattern_proto_funcs[] = { JS_FS("setExtend", setExtend_func, 0, 0), JS_FS("getExtend", getExtend_func, 0, 0), JS_FS("setFilter", setFilter_func, 0, 0), JS_FS("getFilter", getFilter_func, 0, 0), JS_FS_END }; JSFunctionSpec gjs_cairo_surface_pattern_static_funcs[] = { JS_FS_END }; JSObject * gjs_cairo_surface_pattern_from_pattern(JSContext *context, cairo_pattern_t *pattern) { g_return_val_if_fail(context != NULL, NULL); g_return_val_if_fail(pattern != NULL, NULL); g_return_val_if_fail(cairo_pattern_get_type(pattern) == CAIRO_PATTERN_TYPE_SURFACE, NULL); JS::RootedObject proto(context, gjs_cairo_surface_pattern_get_proto(context)); JS::RootedObject object(context, JS_NewObjectWithGivenProto(context, &gjs_cairo_surface_pattern_class, proto)); if (!object) { gjs_throw(context, "failed to create surface pattern"); return NULL; } gjs_cairo_pattern_construct(context, object, pattern); return object; } cjs-4.4.0/modules/cairo-surface.cpp000066400000000000000000000207521356375132300172130ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2010 litl, LLC. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "gi/foreign.h" #include "cjs/jsapi-class.h" #include "cjs/jsapi-util-args.h" #include "cjs/jsapi-wrapper.h" #include #include #include "cairo-private.h" typedef struct { void *dummy; JSContext *context; JSObject *object; cairo_surface_t *surface; } GjsCairoSurface; GJS_DEFINE_PROTO_ABSTRACT_WITH_GTYPE("Surface", cairo_surface, CAIRO_GOBJECT_TYPE_SURFACE, JSCLASS_BACKGROUND_FINALIZE) GJS_DEFINE_PRIV_FROM_JS(GjsCairoSurface, gjs_cairo_surface_class) static void gjs_cairo_surface_finalize(JSFreeOp *fop, JSObject *obj) { GjsCairoSurface *priv; priv = (GjsCairoSurface*) JS_GetPrivate(obj); if (priv == NULL) return; cairo_surface_destroy(priv->surface); g_slice_free(GjsCairoSurface, priv); } /* Properties */ JSPropertySpec gjs_cairo_surface_proto_props[] = { JS_PS_END }; /* Methods */ static bool writeToPNG_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_THIS(context, argc, vp, argv, obj); GjsAutoChar filename; cairo_surface_t *surface; if (!gjs_parse_call_args(context, "writeToPNG", argv, "F", "filename", &filename)) return false; surface = gjs_cairo_surface_get_surface(context, obj); if (!surface) return false; cairo_surface_write_to_png(surface, filename); if (!gjs_cairo_check_status(context, cairo_surface_status(surface), "surface")) return false; argv.rval().setUndefined(); return true; } static bool getType_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_THIS(context, argc, vp, rec, obj); cairo_surface_t *surface; cairo_surface_type_t type; if (argc > 1) { gjs_throw(context, "Surface.getType() takes no arguments"); return false; } surface = gjs_cairo_surface_get_surface(context, obj); type = cairo_surface_get_type(surface); if (!gjs_cairo_check_status(context, cairo_surface_status(surface), "surface")) return false; rec.rval().setInt32(type); return true; } JSFunctionSpec gjs_cairo_surface_proto_funcs[] = { // flush // getContent // getFontOptions JS_FS("getType", getType_func, 0, 0), // markDirty // markDirtyRectangle // setDeviceOffset // getDeviceOffset // setFallbackResolution // getFallbackResolution // copyPage // showPage // hasShowTextGlyphs JS_FS("writeToPNG", writeToPNG_func, 0, 0), JS_FS_END }; JSFunctionSpec gjs_cairo_surface_static_funcs[] = { JS_FS_END }; /* Public API */ /** * gjs_cairo_surface_construct: * @context: the context * @object: object to construct * @surface: cairo_surface to attach to the object * * Constructs a surface wrapper giving an empty JSObject and a * cairo surface. A reference to @surface will be taken. * * This is mainly used for subclasses where object is already created. */ void gjs_cairo_surface_construct(JSContext *context, JS::HandleObject object, cairo_surface_t *surface) { GjsCairoSurface *priv; g_return_if_fail(context != NULL); g_return_if_fail(object != nullptr); g_return_if_fail(surface != NULL); priv = g_slice_new0(GjsCairoSurface); g_assert(priv_from_js(context, object) == NULL); JS_SetPrivate(object, priv); priv->context = context; priv->object = object; priv->surface = cairo_surface_reference(surface); } /** * gjs_cairo_surface_finalize: * @fop: the free op * @object: object to finalize * * Destroys the resources associated with a surface wrapper. * * This is mainly used for subclasses. */ void gjs_cairo_surface_finalize_surface(JSFreeOp *fop, JSObject *object) { g_return_if_fail(fop != NULL); g_return_if_fail(object != NULL); gjs_cairo_surface_finalize(fop, object); } /** * gjs_cairo_surface_from_surface: * @context: the context * @surface: cairo_surface to attach to the object * * Constructs a surface wrapper given cairo surface. * A reference to @surface will be taken. * */ JSObject * gjs_cairo_surface_from_surface(JSContext *context, cairo_surface_t *surface) { g_return_val_if_fail(context != NULL, NULL); g_return_val_if_fail(surface != NULL, NULL); cairo_surface_type_t type = cairo_surface_get_type(surface); if (type == CAIRO_SURFACE_TYPE_IMAGE) return gjs_cairo_image_surface_from_surface(context, surface); if (type == CAIRO_SURFACE_TYPE_PDF) return gjs_cairo_pdf_surface_from_surface(context, surface); if (type == CAIRO_SURFACE_TYPE_PS) return gjs_cairo_ps_surface_from_surface(context, surface); if (type == CAIRO_SURFACE_TYPE_SVG) return gjs_cairo_svg_surface_from_surface(context, surface); JS::RootedObject proto(context, gjs_cairo_surface_get_proto(context)); JS::RootedObject object(context, JS_NewObjectWithGivenProto(context, &gjs_cairo_surface_class, proto)); if (!object) { gjs_throw(context, "failed to create surface"); return NULL; } gjs_cairo_surface_construct(context, object, surface); return object; } /** * gjs_cairo_surface_get_surface: * @context: the context * @object: surface wrapper * * Returns: the surface attaches to the wrapper. * */ cairo_surface_t * gjs_cairo_surface_get_surface(JSContext *context, JSObject *object) { GjsCairoSurface *priv; g_return_val_if_fail(context != NULL, NULL); g_return_val_if_fail(object != NULL, NULL); priv = (GjsCairoSurface*) JS_GetPrivate(object); if (priv == NULL) return NULL; return priv->surface; } static bool surface_to_g_argument(JSContext *context, JS::Value value, const char *arg_name, GjsArgumentType argument_type, GITransfer transfer, bool may_be_null, GArgument *arg) { JSObject *obj; cairo_surface_t *s; obj = &value.toObject(); s = gjs_cairo_surface_get_surface(context, obj); if (!s) return false; if (transfer == GI_TRANSFER_EVERYTHING) cairo_surface_destroy(s); arg->v_pointer = s; return true; } static bool surface_from_g_argument(JSContext *context, JS::MutableHandleValue value_p, GIArgument *arg) { JSObject *obj; obj = gjs_cairo_surface_from_surface(context, (cairo_surface_t*)arg->v_pointer); if (!obj) return false; value_p.setObject(*obj); return true; } static bool surface_release_argument(JSContext *context, GITransfer transfer, GArgument *arg) { cairo_surface_destroy((cairo_surface_t*)arg->v_pointer); return true; } static GjsForeignInfo foreign_info = { surface_to_g_argument, surface_from_g_argument, surface_release_argument }; void gjs_cairo_surface_init(JSContext *context) { gjs_struct_foreign_register("cairo", "Surface", &foreign_info); } cjs-4.4.0/modules/cairo-svg-surface.cpp000066400000000000000000000074501356375132300200100ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2010 litl, LLC. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "cjs/jsapi-class.h" #include "cjs/jsapi-util-args.h" #include "cjs/jsapi-wrapper.h" #include #include "cairo-private.h" #if CAIRO_HAS_SVG_SURFACE #include static JSObject *gjs_cairo_svg_surface_get_proto(JSContext *); GJS_DEFINE_PROTO_WITH_PARENT("SVGSurface", cairo_svg_surface, cairo_surface, JSCLASS_BACKGROUND_FINALIZE) GJS_NATIVE_CONSTRUCTOR_DECLARE(cairo_svg_surface) { GJS_NATIVE_CONSTRUCTOR_VARIABLES(cairo_svg_surface) GjsAutoChar filename; double width, height; cairo_surface_t *surface; GJS_NATIVE_CONSTRUCTOR_PRELUDE(cairo_svg_surface); if (!gjs_parse_call_args(context, "SVGSurface", argv, "Fff", "filename", &filename, "width", &width, "height", &height)) return false; surface = cairo_svg_surface_create(filename, width, height); if (!gjs_cairo_check_status(context, cairo_surface_status(surface), "surface")) return false; gjs_cairo_surface_construct(context, object, surface); cairo_surface_destroy(surface); GJS_NATIVE_CONSTRUCTOR_FINISH(cairo_svg_surface); return true; } static void gjs_cairo_svg_surface_finalize(JSFreeOp *fop, JSObject *obj) { gjs_cairo_surface_finalize_surface(fop, obj); } JSPropertySpec gjs_cairo_svg_surface_proto_props[] = { JS_PS_END }; JSFunctionSpec gjs_cairo_svg_surface_proto_funcs[] = { JS_FS_END }; JSFunctionSpec gjs_cairo_svg_surface_static_funcs[] = { JS_FS_END }; JSObject * gjs_cairo_svg_surface_from_surface(JSContext *context, cairo_surface_t *surface) { g_return_val_if_fail(context != NULL, NULL); g_return_val_if_fail(surface != NULL, NULL); g_return_val_if_fail(cairo_surface_get_type(surface) == CAIRO_SURFACE_TYPE_SVG, NULL); JS::RootedObject proto(context, gjs_cairo_svg_surface_get_proto(context)); JS::RootedObject object(context, JS_NewObjectWithGivenProto(context, &gjs_cairo_svg_surface_class, proto)); if (!object) { gjs_throw(context, "failed to create svg surface"); return NULL; } gjs_cairo_surface_construct(context, object, surface); return object; } #else JSObject * gjs_cairo_svg_surface_from_surface(JSContext *context, cairo_surface_t *surface) { gjs_throw(context, "could not create SVG surface, recompile cairo and gjs with " "SVG support."); return NULL; } #endif /* CAIRO_HAS_SVG_SURFACE */ cjs-4.4.0/modules/cairo.cpp000066400000000000000000000064431356375132300155660ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2010 litl, LLC. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "cjs/jsapi-util.h" #include "cjs/jsapi-wrapper.h" #include "cairo-private.h" #ifdef CAIRO_HAS_XLIB_SURFACE #include "cairo-xlib.h" class XLibConstructor { public: XLibConstructor() { XInitThreads(); } }; static XLibConstructor constructor; #endif bool gjs_cairo_check_status(JSContext *context, cairo_status_t status, const char *name) { if (status != CAIRO_STATUS_SUCCESS) { gjs_throw(context, "cairo error on %s: \"%s\" (%d)", name, cairo_status_to_string(status), status); return false; } return true; } bool gjs_js_define_cairo_stuff(JSContext *context, JS::MutableHandleObject module) { module.set(JS_NewPlainObject(context)); JS::RootedObject proto(context); /* not used */ if (!gjs_cairo_region_define_proto(context, module, &proto)) return false; gjs_cairo_region_init(context); if (!gjs_cairo_context_define_proto(context, module, &proto)) return false; gjs_cairo_context_init(context); if (!gjs_cairo_surface_define_proto(context, module, &proto)) return false; gjs_cairo_surface_init(context); return gjs_cairo_image_surface_define_proto(context, module, &proto) && gjs_cairo_path_define_proto(context, module, &proto) && #if CAIRO_HAS_PS_SURFACE gjs_cairo_ps_surface_define_proto(context, module, &proto) && #endif #if CAIRO_HAS_PDF_SURFACE gjs_cairo_pdf_surface_define_proto(context, module, &proto) && #endif #if CAIRO_HAS_SVG_SURFACE gjs_cairo_svg_surface_define_proto(context, module, &proto) && #endif gjs_cairo_pattern_define_proto(context, module, &proto) && gjs_cairo_gradient_define_proto(context, module, &proto) && gjs_cairo_linear_gradient_define_proto(context, module, &proto) && gjs_cairo_radial_gradient_define_proto(context, module, &proto) && gjs_cairo_surface_pattern_define_proto(context, module, &proto) && gjs_cairo_solid_pattern_define_proto(context, module, &proto); } cjs-4.4.0/modules/cairo.js000066400000000000000000000055151356375132300154170ustar00rootroot00000000000000// Copyright 2010 litl, LLC. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. const Lang = imports.lang; var Antialias = { DEFAULT: 0, NONE: 1, GRAY: 2, SUBPIXEL: 3 }; var Content = { COLOR : 0x1000, ALPHA : 0x2000, COLOR_ALPHA : 0x3000 }; var Extend = { NONE : 0, REPEAT : 1, REFLECT : 2, PAD : 3 }; var FillRule = { WINDING: 0, EVEN_ODD: 1 }; var Filter = { FAST : 0, GOOD : 1, BEST : 2, NEAREST : 3, BILINEAR : 4, GAUSSIAN : 5 }; var FontSlant = { NORMAL: 0, ITALIC: 1, OBLIQUE: 2 }; var FontWeight = { NORMAL : 0, BOLD : 1 }; var Format = { ARGB32 : 0, RGB24 : 1, A8 : 2, A1 : 3, // The value of 4 is reserved by a deprecated enum value RGB16_565: 5 }; var LineCap = { BUTT: 0, ROUND: 1, SQUASH: 2 }; var LineJoin = { MITER: 0, ROUND: 1, BEVEL: 2 }; var Operator = { CLEAR: 0, SOURCE: 1, OVER: 2, IN : 3, OUT : 4, ATOP : 5, DEST : 6, DEST_OVER : 7, DEST_IN : 8, DEST_OUT : 9, DEST_ATOP : 10, XOR : 11, ADD : 12, SATURATE : 13, MULTIPLY : 14, SCREEN : 15, OVERLAY : 16, DARKEN : 17, LIGHTEN : 18, COLOR_DODGE : 19, COLOR_BURN : 20, HARD_LIGHT : 21, SOFT_LIGHT : 22, DIFFERENCE : 23, EXCLUSION : 24, HSL_HUE : 25, HSL_SATURATION : 26, HSL_COLOR : 27, HSL_LUMINOSITY : 28 }; var PatternType = { SOLID : 0, SURFACE : 1, LINEAR : 2, RADIAL : 3 }; var SurfaceType = { IMAGE : 0, PDF : 1, PS : 2, XLIB : 3, XCB : 4, GLITZ : 5, QUARTZ : 6, WIN32 : 7, BEOS : 8, DIRECTFB : 9, SVG : 10, OS2 : 11, WIN32_PRINTING : 12, QUARTZ_IMAGE : 13 }; // Merge stuff defined in native code Lang.copyProperties(imports.cairoNative, this); cjs-4.4.0/modules/console.cpp000066400000000000000000000237571356375132300161420ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* vim: set ts=8 sw=4 et tw=78: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * 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 Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "config.h" #include #include #ifdef HAVE_READLINE_READLINE_H #include #include #include #endif #include #include #include "console.h" #include "cjs/context.h" #include "cjs/context-private.h" #include "cjs/jsapi-wrapper.h" static void gjs_console_print_error(JSErrorReport *report) { /* Code modified from SpiderMonkey js/src/jscntxt.cpp, js::PrintError() */ g_assert(report); char *prefix = nullptr; if (report->filename) prefix = g_strdup_printf("%s:", report->filename); if (report->lineno) { char *tmp = prefix; prefix = g_strdup_printf("%s%u:%u ", tmp ? tmp : "", report->lineno, report->column); g_free(tmp); } if (JSREPORT_IS_WARNING(report->flags)) { char *tmp = prefix; prefix = g_strdup_printf("%s%swarning: ", tmp ? tmp : "", JSREPORT_IS_STRICT(report->flags) ? "strict " : ""); g_free(tmp); } const char *message = report->message().c_str(); /* embedded newlines -- argh! */ const char *ctmp; while ((ctmp = strchr(message, '\n')) != 0) { ctmp++; if (prefix) fputs(prefix, stderr); fwrite(message, 1, ctmp - message, stderr); message = ctmp; } /* If there were no filename or lineno, the prefix might be empty */ if (prefix) fputs(prefix, stderr); fputs(message, stderr); if (const char16_t* linebuf = report->linebuf()) { size_t n = report->linebufLength(); fputs(":\n", stderr); if (prefix) fputs(prefix, stderr); for (size_t i = 0; i < n; i++) fputc(static_cast(linebuf[i]), stderr); // linebuf usually ends with a newline. If not, add one here. if (n == 0 || linebuf[n - 1] != '\n') fputc('\n', stderr); if (prefix) fputs(prefix, stderr); n = report->tokenOffset(); for (size_t i = 0, j = 0; i < n; i++) { if (linebuf[i] == '\t') { for (size_t k = (j + 8) & ~7; j < k; j++) fputc('.', stderr); continue; } fputc('.', stderr); j++; } fputc('^', stderr); } fputc('\n', stderr); fflush(stderr); g_free(prefix); } static void gjs_console_warning_reporter(JSContext *cx, JSErrorReport *report) { gjs_console_print_error(report); } /* Based on js::shell::AutoReportException from SpiderMonkey. */ class AutoReportException { JSContext *m_cx; public: explicit AutoReportException(JSContext *cx) : m_cx(cx) {} ~AutoReportException() { if (!JS_IsExceptionPending(m_cx)) return; /* Get exception object before printing and clearing exception. */ JS::RootedValue v_exn(m_cx); (void) JS_GetPendingException(m_cx, &v_exn); JS::RootedObject exn(m_cx, &v_exn.toObject()); JSErrorReport *report = JS_ErrorFromException(m_cx, exn); if (report) { g_assert(!JSREPORT_IS_WARNING(report->flags)); gjs_console_print_error(report); } else { JS::RootedString message(m_cx, JS::ToString(m_cx, v_exn)); if (!message) { g_printerr("(could not convert thrown exception to string)\n"); } else { GjsAutoJSChar message_utf8 = JS_EncodeStringToUTF8(m_cx, message); g_printerr("%s\n", message_utf8.get()); } } JS::RootedObject stack(m_cx, ExceptionStackOrNull(exn)); if (stack) { GjsAutoChar stack_str = gjs_format_stack_trace(m_cx, stack); if (!stack_str) g_printerr("(Unable to print stack trace)\n"); else g_printerr("%s", stack_str.get()); } JS_ClearPendingException(m_cx); } }; #ifdef HAVE_READLINE_READLINE_H static bool gjs_console_readline(JSContext *cx, char **bufp, FILE *file, const char *prompt) { char *line; line = readline(prompt); if (!line) return false; if (line[0] != '\0') add_history(line); *bufp = line; return true; } #else static bool gjs_console_readline(JSContext *cx, char **bufp, FILE *file, const char *prompt) { char line[256]; fprintf(stdout, "%s", prompt); fflush(stdout); if (!fgets(line, sizeof line, file)) return false; *bufp = g_strdup(line); return true; } #endif /* Return value of false indicates an uncatchable exception, rather than any * exception. (This is because the exception should be auto-printed around the * invocation of this function.) */ static bool gjs_console_eval_and_print(JSContext *cx, const char *bytes, size_t length, int lineno) { JS::CompileOptions options(cx); options.setUTF8(true) .setFileAndLine("typein", lineno); JS::RootedValue result(cx); if (!JS::Evaluate(cx, options, bytes, length, &result)) { if (!JS_IsExceptionPending(cx)) return false; } gjs_schedule_gc_if_needed(cx); if (result.isUndefined()) return true; JS::RootedString str(cx, JS::ToString(cx, result)); if (!str) return true; char *display_str; display_str = gjs_value_debug_string(cx, result); if (display_str) { g_fprintf(stdout, "%s\n", display_str); g_free(display_str); } return true; } static bool gjs_console_interact(JSContext *context, unsigned argc, JS::Value *vp) { JS::CallArgs argv = JS::CallArgsFromVp(argc, vp); bool eof = false; JS::RootedObject global(context, gjs_get_import_global(context)); GString *buffer = NULL; char *temp_buf = NULL; int lineno; int startline; FILE *file = stdin; JS::SetWarningReporter(context, gjs_console_warning_reporter); /* It's an interactive filehandle; drop into read-eval-print loop. */ lineno = 1; do { /* * Accumulate lines until we get a 'compilable unit' - one that either * generates an error (before running out of source) or that compiles * cleanly. This should be whenever we get a complete statement that * coincides with the end of a line. */ startline = lineno; buffer = g_string_new(""); do { if (!gjs_console_readline(context, &temp_buf, file, startline == lineno ? "cjs> " : ".... ")) { eof = true; break; } g_string_append(buffer, temp_buf); g_free(temp_buf); lineno++; } while (!JS_BufferIsCompilableUnit(context, global, buffer->str, buffer->len)); bool ok; { AutoReportException are(context); ok = gjs_console_eval_and_print(context, buffer->str, buffer->len, startline); } g_string_free(buffer, true); auto gjs_context = static_cast(JS_GetContextPrivate(context)); ok = _gjs_context_run_jobs(gjs_context) && ok; if (!ok) { /* If this was an uncatchable exception, throw another uncatchable * exception on up to the surrounding JS::Evaluate() in main(). This * happens when you run gjs-console and type imports.system.exit(0); * at the prompt. If we don't throw another uncatchable exception * here, then it's swallowed and main() won't exit. */ return false; } } while (!eof); g_fprintf(stdout, "\n"); if (file != stdin) fclose(file); argv.rval().setUndefined(); return true; } bool gjs_define_console_stuff(JSContext *context, JS::MutableHandleObject module) { module.set(JS_NewPlainObject(context)); return JS_DefineFunction(context, module, "interact", gjs_console_interact, 1, GJS_MODULE_PROP_FLAGS); } cjs-4.4.0/modules/console.h000066400000000000000000000027221356375132300155740ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_CONSOLE_H__ #define __GJS_CONSOLE_H__ #include #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS bool gjs_define_console_stuff(JSContext *context, JS::MutableHandleObject module); G_END_DECLS #endif /* __GJS_CONSOLE_H__ */ cjs-4.4.0/modules/format.js000066400000000000000000000057641356375132300156200ustar00rootroot00000000000000// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const CjsPrivate = imports.gi.CjsPrivate; function vprintf(str, args) { let i = 0; let usePos = false; return str.replace(/%(?:([1-9][0-9]*)\$)?(I+)?([0-9]+)?(?:\.([0-9]+))?(.)/g, function (str, posGroup, flagsGroup, widthGroup, precisionGroup, genericGroup) { if (precisionGroup !== '' && precisionGroup !== undefined && genericGroup != 'f') throw new Error("Precision can only be specified for 'f'"); let hasAlternativeIntFlag = (flagsGroup && flagsGroup.indexOf('I') != -1); if (hasAlternativeIntFlag && genericGroup != 'd') throw new Error("Alternative output digits can only be specfied for 'd'"); let pos = parseInt(posGroup, 10) || 0; if (usePos == false && i == 0) usePos = pos > 0; if (usePos && pos == 0 || !usePos && pos > 0) throw new Error("Numbered and unnumbered conversion specifications cannot be mixed"); let fillChar = (widthGroup && widthGroup[0] == '0') ? '0' : ' '; let width = parseInt(widthGroup, 10) || 0; function fillWidth(s, c, w) { let fill = ''; for (let i = 0; i < w; i++) fill += c; return fill.substr(s.length) + s; } function getArg() { return usePos ? args[pos - 1] : args[i++]; } let s = ''; switch (genericGroup) { case '%': return '%'; case 's': s = String(getArg()); break; case 'd': let intV = parseInt(getArg()); if (hasAlternativeIntFlag) s = CjsPrivate.format_int_alternative_output(intV); else s = intV.toString(); break; case 'x': s = parseInt(getArg()).toString(16); break; case 'f': if (precisionGroup === '' || precisionGroup === undefined) s = parseFloat(getArg()).toString(); else s = parseFloat(getArg()).toFixed(parseInt(precisionGroup)); break; default: throw new Error('Unsupported conversion character %' + genericGroup); } return fillWidth(s, fillChar, width); }); } function printf() { let args = Array.prototype.slice.call(arguments); let fmt = args.shift(); print(vprintf(fmt, args)); } /* * This function is intended to extend the String object and provide * an String.format API for string formatting. * It has to be set up using String.prototype.format = Format.format; * Usage: * "somestring %s %d".format('hello', 5); * It supports %s, %d, %x and %f, for %f it also support precisions like * "%.2f".format(1.526). All specifiers can be prefixed with a minimum * field width, e.g. "%5s".format("foo"). Unless the width is prefixed * with '0', the formatted string will be padded with spaces. */ function format() { return vprintf(this, arguments); } cjs-4.4.0/modules/gettext.js000066400000000000000000000061151356375132300160030ustar00rootroot00000000000000// Copyright 2009 Red Hat, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. /** * This module provides a convenience layer for the "gettext" family of functions, * relying on GLib for the actual implementation. * * Usage: * * const Gettext = imports.gettext; * * Gettext.textdomain("myapp"); * Gettext.bindtextdomain("myapp", "/usr/share/locale"); * * let translated = Gettext.gettext("Hello world!"); */ const GLib = imports.gi.GLib; const CjsPrivate = imports.gi.CjsPrivate; const LocaleCategory = CjsPrivate.LocaleCategory; function setlocale(category, locale) { return CjsPrivate.setlocale(category, locale); } function textdomain(domain) { return CjsPrivate.textdomain(domain); } function bindtextdomain(domain, location) { return CjsPrivate.bindtextdomain(domain, location); } function gettext(msgid) { return GLib.dgettext(null, msgid); } function dgettext(domain, msgid) { return GLib.dgettext(domain, msgid); } function dcgettext(domain, msgid, category) { return GLib.dcgettext(domain, msgid, category); } function ngettext(msgid1, msgid2, n) { return GLib.dngettext(null, msgid1, msgid2, n); } function dngettext(domain, msgid1, msgid2, n) { return GLib.dngettext(domain, msgid1, msgid2, n); } // FIXME: missing dcngettext ? function pgettext(context, msgid) { return GLib.dpgettext2(null, context, msgid); } function dpgettext(domain, context, msgid) { return GLib.dpgettext2(domain, context, msgid); } /** * Create an object with bindings for gettext, ngettext, * and pgettext bound to a particular translation domain. * * @param domainName Translation domain string * @returns: an object with gettext bindings * @type: function */ var domain = function(domainName) { return { gettext: function(msgid) { return GLib.dgettext(domainName, msgid); }, ngettext: function(msgid1, msgid2, n) { return GLib.dngettext(domainName, msgid1, msgid2, n); }, pgettext: function(context, msgid) { return GLib.dpgettext2(domainName, context, msgid); } }; }; cjs-4.4.0/modules/jsUnit.js000066400000000000000000000352361356375132300156010ustar00rootroot00000000000000/* @author Edward Hieatt, edward@jsunit.net */ /* - JsUnit - Copyright (C) 2001-4 Edward Hieatt, edward@jsunit.net - Copyright (C) 2008 litl, LLC - - Version: MPL 1.1/GPL 2.0/LGPL 2.1 - - 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 Edward Hieatt code. - - The Initial Developer of the Original Code is - Edward Hieatt, edward@jsunit.net. - Portions created by the Initial Developer are Copyright (C) 2003 - the Initial Developer. All Rights Reserved. - - Author Edward Hieatt, edward@jsunit.net - - Alternatively, the contents of this file may be used under the terms of - either the GNU General Public License Version 2 or later (the "GPL"), or - the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - in which case the provisions of the GPL or the LGPL are applicable instead - of those above. If you wish to allow use of your version of this file only - under the terms of either the GPL or the LGPL, and not to allow others to - use your version of this file under the terms of the MPL, indicate your - decision by deleting the provisions above and replace them with the notice - and other provisions required by the LGPL or the GPL. If you do not delete - the provisions above, a recipient may use your version of this file under - the terms of any one of the MPL, the GPL or the LGPL. */ var JSUNIT_UNDEFINED_VALUE; var JSUNIT_VERSION="2.1"; var isTestPageLoaded = false; // GJS: introduce implicit variable to avoid exceptions var top = null; //hack for NS62 bug function jsUnitFixTop() { var tempTop = top; if (!tempTop) { tempTop = window; while (typeof tempTop.parent !== 'undefined') { tempTop = tempTop.parent; if (tempTop.top && tempTop.top.jsUnitTestSuite) { tempTop = tempTop.top; break; } } } top = tempTop; } jsUnitFixTop(); function _displayStringForValue(aVar) { if (aVar === null) return 'null'; if (aVar === top.JSUNIT_UNDEFINED_VALUE) return 'undefined'; return aVar; } function fail(failureMessage) { throw new JsUnitException(null, failureMessage); } function error(errorMessage) { throw new Error(errorMessage); } function argumentsIncludeComments(expectedNumberOfNonCommentArgs, args) { return args.length == expectedNumberOfNonCommentArgs + 1; } function commentArg(expectedNumberOfNonCommentArgs, args) { if (argumentsIncludeComments(expectedNumberOfNonCommentArgs, args)) return args[0]; return null; } function nonCommentArg(desiredNonCommentArgIndex, expectedNumberOfNonCommentArgs, args) { return argumentsIncludeComments(expectedNumberOfNonCommentArgs, args) ? args[desiredNonCommentArgIndex] : args[desiredNonCommentArgIndex - 1]; } function _validateArguments(expectedNumberOfNonCommentArgs, args) { if (!( args.length == expectedNumberOfNonCommentArgs || (args.length == expectedNumberOfNonCommentArgs + 1 && typeof(args[0]) == 'string') )) error('Incorrect arguments passed to assert function'); } function _assert(comment, booleanValue, failureMessage) { if (!booleanValue) throw new JsUnitException(comment, failureMessage); } function assert() { _validateArguments(1, arguments); var booleanValue=nonCommentArg(1, 1, arguments); if (typeof(booleanValue) != 'boolean') error('Bad argument to assert(boolean)'); _assert(commentArg(1, arguments), booleanValue === true, 'Call to assert(boolean) with false'); } function assertTrue() { _validateArguments(1, arguments); var booleanValue=nonCommentArg(1, 1, arguments); if (typeof(booleanValue) != 'boolean') error('Bad argument to assertTrue(boolean)'); _assert(commentArg(1, arguments), booleanValue === true, 'Call to assertTrue(boolean) with false'); } function assertFalse() { _validateArguments(1, arguments); var booleanValue=nonCommentArg(1, 1, arguments); if (typeof(booleanValue) != 'boolean') error('Bad argument to assertFalse(boolean)'); _assert(commentArg(1, arguments), booleanValue === false, 'Call to assertFalse(boolean) with true'); } function assertEquals() { _validateArguments(2, arguments); var var1=nonCommentArg(1, 2, arguments); var var2=nonCommentArg(2, 2, arguments); _assert(commentArg(2, arguments), var1 === var2, 'Expected ' + var1 + ' (' + typeof(var1) + ') but was ' + _displayStringForValue(var2) + ' (' + typeof(var2) + ')'); } function assertNotEquals() { _validateArguments(2, arguments); var var1=nonCommentArg(1, 2, arguments); var var2=nonCommentArg(2, 2, arguments); _assert(commentArg(2, arguments), var1 !== var2, 'Expected not to be ' + _displayStringForValue(var2)); } function assertNull() { _validateArguments(1, arguments); var aVar=nonCommentArg(1, 1, arguments); _assert(commentArg(1, arguments), aVar === null, 'Expected null but was ' + _displayStringForValue(aVar)); } function assertNotNull() { _validateArguments(1, arguments); var aVar=nonCommentArg(1, 1, arguments); _assert(commentArg(1, arguments), aVar !== null, 'Expected not to be null'); } function assertUndefined() { _validateArguments(1, arguments); var aVar=nonCommentArg(1, 1, arguments); _assert(commentArg(1, arguments), aVar === top.JSUNIT_UNDEFINED_VALUE, 'Expected undefined but was ' + _displayStringForValue(aVar)); } function assertNotUndefined() { _validateArguments(1, arguments); var aVar=nonCommentArg(1, 1, arguments); _assert(commentArg(1, arguments), aVar !== top.JSUNIT_UNDEFINED_VALUE, 'Expected not to be undefined'); } function assertNaN() { _validateArguments(1, arguments); var aVar=nonCommentArg(1, 1, arguments); _assert(commentArg(1, arguments), isNaN(aVar), 'Expected NaN'); } function assertNotNaN() { _validateArguments(1, arguments); var aVar=nonCommentArg(1, 1, arguments); _assert(commentArg(1, arguments), !isNaN(aVar), 'Expected not NaN'); } // GJS: assertRaises(function) function assertRaises() { _validateArguments(1, arguments); var fun=nonCommentArg(1, 1, arguments); var exception; if (typeof(fun) != 'function') error("Bad argument to assertRaises(function)"); var retval; try { retval = fun(); } catch (e) { exception = e; } _assert(commentArg(1, arguments), exception !== top.JSUNIT_UNDEFINED_VALUE, "Call to assertRaises(function) did not raise an exception. Return value was " + _displayStringForValue(retval) + ' (' + typeof(retval) + ')'); } function isLoaded() { return isTestPageLoaded; } function setUp() { } function tearDown() { } function getFunctionName(aFunction) { var name = aFunction.toString().match(/function (\w*)/)[1]; if ((name == null) || (name.length == 0)) name = 'anonymous'; return name; } function parseErrorStack(excp) { var stack = []; var name; if (!excp || !excp.stack) { return stack; } var stacklist = excp.stack.split('\n'); for (var i = 0; i < stacklist.length - 1; i++) { var framedata = stacklist[i]; name = framedata.match(/^(\w*)/)[1]; if (!name) { name = 'anonymous'; } var line = framedata.match(/(:\d+)$/)[1]; if (line) { name += line; } stack[stack.length] = name; } // remove top level anonymous functions to match IE while (stack.length && stack[stack.length - 1] == 'anonymous') { stack.length = stack.length - 1; } return stack; } function JsUnitException(comment, message) { this.isJsUnitException = true; this.comment = comment; this.message = message; this.stack = (new Error()).stack; } JsUnitException.prototype = Object.create(Error.prototype, {}); function warn() { if (top.tracer != null) top.tracer.warn(arguments[0], arguments[1]); } function inform() { if (top.tracer != null) top.tracer.inform(arguments[0], arguments[1]); } function info() { inform(arguments[0], arguments[1]); } function debug() { if (top.tracer != null) top.tracer.debug(arguments[0], arguments[1]); } function setjsUnitTracer(ajsUnitTracer) { top.tracer=ajsUnitTracer; } function trim(str) { if (str == null) return null; var startingIndex = 0; var endingIndex = str.length-1; while (str.substring(startingIndex, startingIndex+1) == ' ') startingIndex++; while (str.substring(endingIndex, endingIndex+1) == ' ') endingIndex--; if (endingIndex < startingIndex) return ''; return str.substring(startingIndex, endingIndex+1); } function isBlank(str) { return trim(str) == ''; } // the functions push(anArray, anObject) and pop(anArray) // exist because the JavaScript Array.push(anObject) and Array.pop() // functions are not available in IE 5.0 function push(anArray, anObject) { anArray[anArray.length]=anObject; } function pop(anArray) { if (anArray.length>=1) { delete anArray[anArray.length - 1]; anArray.length--; } } // safe, strict access to jsUnitParmHash function jsUnitGetParm(name) { if (typeof(top.jsUnitParmHash[name]) != 'undefined') { return top.jsUnitParmHash[name]; } return null; } if (top && typeof(top.xbDEBUG) != 'undefined' && top.xbDEBUG.on && top.testManager) { top.xbDebugTraceObject('top.testManager.containerTestFrame', 'JSUnitException'); // asserts top.xbDebugTraceFunction('top.testManager.containerTestFrame', '_displayStringForValue'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'error'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'argumentsIncludeComments'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'commentArg'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'nonCommentArg'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', '_validateArguments'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', '_assert'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'assert'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'assertTrue'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'assertEquals'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'assertNotEquals'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'assertNull'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'assertNotNull'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'assertUndefined'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'assertNotUndefined'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'assertNaN'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'assertNotNaN'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'isLoaded'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'setUp'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'tearDown'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'getFunctionName'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'warn'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'inform'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'debug'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'setjsUnitTracer'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'trim'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'isBlank'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'newOnLoadEvent'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'push'); top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'pop'); } function newOnLoadEvent() { isTestPageLoaded = true; } function jsUnitSetOnLoad(windowRef, onloadHandler) { var isKonqueror = navigator.userAgent.indexOf('Konqueror/') != -1 || navigator.userAgent.indexOf('Safari/') != -1; if (typeof(windowRef.attachEvent) != 'undefined') { // Internet Explorer, Opera windowRef.attachEvent("onload", onloadHandler); } else if (typeof(windowRef.addEventListener) != 'undefined' && !isKonqueror){ // Mozilla, Konqueror // exclude Konqueror due to load issues windowRef.addEventListener("load", onloadHandler, false); } else if (typeof(windowRef.document.addEventListener) != 'undefined' && !isKonqueror) { // DOM 2 Events // exclude Mozilla, Konqueror due to load issues windowRef.document.addEventListener("load", onloadHandler, false); } else if (typeof(windowRef.onload) != 'undefined' && windowRef.onload) { windowRef.jsunit_original_onload = windowRef.onload; windowRef.onload = function() { windowRef.jsunit_original_onload(); onloadHandler(); }; } else { // browsers that do not support windowRef.attachEvent or // windowRef.addEventListener will override a page's own onload event windowRef.onload=onloadHandler; } } // GJS: comment out as isLoaded() isn't terribly useful for us //jsUnitSetOnLoad(window, newOnLoadEvent); // GJS: entry point to run all functions named as test*, surrounded by // calls to setUp() and tearDown() function gjstestRun(window_, setUp, tearDown) { var propName; var rv = 0; var failures = []; if (!window_) window_ = window; if (!setUp) setUp = window_.setUp; if (!tearDown) tearDown = window_.tearDown; for (propName in window_) { if (!propName.match(/^test\w+/)) continue; var testFunction = window_[propName]; if (typeof(testFunction) != 'function') continue; log("running test " + propName); setUp(); try { testFunction(); } catch (e) { var result = null; if (typeof(e.isJsUnitException) != 'undefined' && e.isJsUnitException) { result = ''; if (e.comment != null) result += ('"' + e.comment + '"\n'); result += e.message; if (e.stack) result += '\n\nStack trace follows:\n' + e.stack; // assertion failure, kind of expected so just log it and flag the // whole test as failed log(result); rv = 1; failures.push(propName); } else { // unexpected error, let the shell handle it throw e; } } tearDown(); } if (failures.length > 0) { log(failures.length + " tests failed in this file"); log("Failures were: " + failures.join(", ")); } // if gjstestRun() is the last call in a file, this becomes the // exit code of the test program, so 0 = success, 1 = failed return rv; } cjs-4.4.0/modules/lang.js000066400000000000000000000066121356375132300152420ustar00rootroot00000000000000/* -*- mode: js; indent-tabs-mode: nil; -*- */ /* exported Class, Interface, bind, copyProperties, copyPublicProperties, countProperties */ // Copyright (c) 2008 litl, LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. // Utilities that are "meta-language" things like manipulating object props var {Class, Interface, getMetaClass} = imports._legacy; function countProperties(obj) { let count = 0; for (let property in obj) { count += 1; } return count; } function getPropertyDescriptor(obj, property) { if (obj.hasOwnProperty(property)) return Object.getOwnPropertyDescriptor(obj, property); return getPropertyDescriptor(Object.getPrototypeOf(obj), property); } function _copyProperty(source, dest, property) { let descriptor = getPropertyDescriptor(source, property); Object.defineProperty(dest, property, descriptor); } function copyProperties(source, dest) { for (let property in source) { _copyProperty(source, dest, property); } } function copyPublicProperties(source, dest) { for (let property in source) { if (typeof(property) == 'string' && property.substring(0, 1) == '_') { continue; } else { _copyProperty(source, dest, property); } } } /** * Binds obj to callback. Makes it possible to refer to "obj" * using this within the callback. * @param {object} obj the object to bind * @param {function} callback callback to bind obj in * @param arguments additional arguments to the callback * @returns: a new callback * @type: function */ function bind(obj, callback) { if (typeof(obj) != 'object') { throw new Error( "first argument to Lang.bind() must be an object, not " + typeof(obj)); } if (typeof(callback) != 'function') { throw new Error( "second argument to Lang.bind() must be a function, not " + typeof(callback)); } // Use ES5 Function.prototype.bind, but only if not passing any bindArguments, // because ES5 has them at the beginning, not at the end if (arguments.length == 2) return callback.bind(obj); let me = obj; let bindArguments = Array.prototype.slice.call(arguments, 2); return function() { let args = Array.prototype.slice.call(arguments); args = args.concat(bindArguments); return callback.apply(me, args); }; } cjs-4.4.0/modules/mainloop.js000066400000000000000000000054571356375132300161450ustar00rootroot00000000000000/* -*- mode: js; indent-tabs-mode: nil; -*- */ // Copyright (c) 2012 Giovanni Campagna // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. // A layer of convenience and backwards-compatibility over GLib MainLoop facilities const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; var _mainLoops = {}; function run(name) { if (!_mainLoops[name]) _mainLoops[name] = GLib.MainLoop.new(null, false); _mainLoops[name].run(); } function quit(name) { if (!_mainLoops[name]) throw new Error("No main loop with this id"); let loop = _mainLoops[name]; delete _mainLoops[name]; if (!loop.is_running()) throw new Error("Main loop was stopped already"); loop.quit(); } function idle_source(handler, priority) { let s = GLib.idle_source_new(); GObject.source_set_closure(s, handler); if (priority !== undefined) s.set_priority(priority); return s; } function idle_add(handler, priority) { return idle_source(handler, priority).attach(null); } function timeout_source(timeout, handler, priority) { let s = GLib.timeout_source_new(timeout); GObject.source_set_closure(s, handler); if (priority !== undefined) s.set_priority(priority); return s; } function timeout_seconds_source(timeout, handler, priority) { let s = GLib.timeout_source_new_seconds(timeout); GObject.source_set_closure(s, handler); if (priority !== undefined) s.set_priority(priority); return s; } function timeout_add(timeout, handler, priority) { return timeout_source(timeout, handler, priority).attach(null); } function timeout_add_seconds(timeout, handler, priority) { return timeout_seconds_source(timeout, handler, priority).attach(null); } function source_remove(id) { return GLib.source_remove(id); } cjs-4.4.0/modules/modules.cpp000066400000000000000000000031351356375132300161340ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright 2013 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "cjs/native.h" #include "modules.h" #ifdef ENABLE_CAIRO #include "cairo-module.h" #endif #include "system.h" #include "console.h" void gjs_register_static_modules (void) { #ifdef ENABLE_CAIRO gjs_register_native_module("cairoNative", gjs_js_define_cairo_stuff); #endif gjs_register_native_module("system", gjs_js_define_system_stuff); gjs_register_native_module("console", gjs_define_console_stuff); } cjs-4.4.0/modules/modules.gresource.xml000066400000000000000000000015731356375132300201530ustar00rootroot00000000000000 modules/_bootstrap/default.js modules/_bootstrap/coverage.js modules/tweener/equations.js modules/tweener/tweener.js modules/tweener/tweenList.js modules/overrides/cairo.js modules/overrides/GLib.js modules/overrides/Gio.js modules/overrides/GObject.js modules/overrides/Gtk.js modules/cairo.js modules/gettext.js modules/lang.js modules/_legacy.js modules/mainloop.js modules/jsUnit.js modules/signals.js modules/format.js modules/package.js cjs-4.4.0/modules/modules.h000066400000000000000000000025741356375132300156070ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright 2013 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_MODULES_H__ #define __GJS_MODULES_H__ #include #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS void gjs_register_static_modules (void); G_END_DECLS #endif /* __GJS_CONSOLE_H__ */ cjs-4.4.0/modules/overrides/000077500000000000000000000000001356375132300157605ustar00rootroot00000000000000cjs-4.4.0/modules/overrides/GLib.js000066400000000000000000000213551356375132300171410ustar00rootroot00000000000000// Copyright 2011 Giovanni Campagna // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. const ByteArray = imports.byteArray; let GLib; const SIMPLE_TYPES = ['b', 'y', 'n', 'q', 'i', 'u', 'x', 't', 'h', 'd', 's', 'o', 'g']; function _read_single_type(signature, forceSimple) { let char = signature.shift(); let isSimple = false; if (SIMPLE_TYPES.indexOf(char) == -1) { if (forceSimple) throw new TypeError('Invalid GVariant signature (a simple type was expected)'); } else isSimple = true; if (char == 'm' || char == 'a') return [char].concat(_read_single_type(signature, false)); if (char == '{') { let key = _read_single_type(signature, true); let val = _read_single_type(signature, false); let close = signature.shift(); if (close != '}') throw new TypeError('Invalid GVariant signature for type DICT_ENTRY (expected "}"'); return [char].concat(key, val, close); } if (char == '(') { let res = [char]; while (true) { if (signature.length == 0) throw new TypeError('Invalid GVariant signature for type TUPLE (expected ")")'); let next = signature[0]; if (next == ')') { signature.shift(); return res.concat(next); } let el = _read_single_type(signature); res = res.concat(el); } } // Valid types are simple types, arrays, maybes, tuples, dictionary entries and variants if (!isSimple && char != 'v') throw new TypeError('Invalid GVariant signature (' + char + ' is not a valid type)'); return [char]; } function _makeBytes(byteArray) { if (byteArray instanceof ByteArray.ByteArray) return byteArray.toGBytes(); else return new GLib.Bytes(byteArray); } function _pack_variant(signature, value) { if (signature.length == 0) throw new TypeError('GVariant signature cannot be empty'); let char = signature.shift(); switch (char) { case 'b': return GLib.Variant.new_boolean(value); case 'y': return GLib.Variant.new_byte(value); case 'n': return GLib.Variant.new_int16(value); case 'q': return GLib.Variant.new_uint16(value); case 'i': return GLib.Variant.new_int32(value); case 'u': return GLib.Variant.new_uint32(value); case 'x': return GLib.Variant.new_int64(value); case 't': return GLib.Variant.new_uint64(value); case 'h': return GLib.Variant.new_handle(value); case 'd': return GLib.Variant.new_double(value); case 's': return GLib.Variant.new_string(value); case 'o': return GLib.Variant.new_object_path(value); case 'g': return GLib.Variant.new_signature(value); case 'v': return GLib.Variant.new_variant(value); case 'm': if (value != null) return GLib.Variant.new_maybe(null, _pack_variant(signature, value)); else return GLib.Variant.new_maybe(new GLib.VariantType(_read_single_type(signature, false).join('')), null); case 'a': let arrayType = _read_single_type(signature, false); if (arrayType[0] == 's') { // special case for array of strings return GLib.Variant.new_strv(value); } if (arrayType[0] == 'y') { // special case for array of bytes return GLib.Variant.new_from_bytes(new GLib.VariantType('ay'), _makeBytes(value), true); } let arrayValue = []; if (arrayType[0] == '{') { // special case for dictionaries for (let key in value) { let copy = [].concat(arrayType); let child = _pack_variant(copy, [key, value[key]]); arrayValue.push(child); } } else { for (let i = 0; i < value.length; i++) { let copy = [].concat(arrayType); let child = _pack_variant(copy, value[i]); arrayValue.push(child); } } return GLib.Variant.new_array(new GLib.VariantType(arrayType.join('')), arrayValue); case '(': let children = [ ]; for (let i = 0; i < value.length; i++) { let next = signature[0]; if (next == ')') break; children.push(_pack_variant(signature, value[i])); } if (signature[0] != ')') throw new TypeError('Invalid GVariant signature for type TUPLE (expected ")")'); signature.shift(); return GLib.Variant.new_tuple(children); case '{': let key = _pack_variant(signature, value[0]); let child = _pack_variant(signature, value[1]); if (signature[0] != '}') throw new TypeError('Invalid GVariant signature for type DICT_ENTRY (expected "}")'); signature.shift(); return GLib.Variant.new_dict_entry(key, child); default: throw new TypeError('Invalid GVariant signature (unexpected character ' + char + ')'); } } function _unpack_variant(variant, deep) { switch (String.fromCharCode(variant.classify())) { case 'b': return variant.get_boolean(); case 'y': return variant.get_byte(); case 'n': return variant.get_int16(); case 'q': return variant.get_uint16(); case 'i': return variant.get_int32(); case 'u': return variant.get_uint32(); case 'x': return variant.get_int64(); case 't': return variant.get_uint64(); case 'h': return variant.get_handle(); case 'd': return variant.get_double(); case 'o': case 'g': case 's': // g_variant_get_string has length as out argument return variant.get_string()[0]; case 'v': return variant.get_variant(); case 'm': let val = variant.get_maybe(); if (deep && val) return _unpack_variant(val, deep); else return val; case 'a': if (variant.is_of_type(new GLib.VariantType('a{?*}'))) { // special case containers let ret = { }; let nElements = variant.n_children(); for (let i = 0; i < nElements; i++) { // always unpack the dictionary entry, and always unpack // the key (or it cannot be added as a key) let val = _unpack_variant(variant.get_child_value(i), deep); let key; if (!deep) key = _unpack_variant(val[0], true); else key = val[0]; ret[key] = val[1]; } return ret; } if (variant.is_of_type(new GLib.VariantType('ay'))) { // special case byte arrays return variant.get_data_as_bytes().toArray(); } // fall through case '(': case '{': let ret = [ ]; let nElements = variant.n_children(); for (let i = 0; i < nElements; i++) { let val = variant.get_child_value(i); if (deep) ret.push(_unpack_variant(val, deep)); else ret.push(val); } return ret; } throw new Error('Assertion failure: this code should not be reached'); } function _init() { // this is imports.gi.GLib GLib = this; // small HACK: we add a matches() method to standard Errors so that // you can do "catch(e if e.matches(Ns.FooError, Ns.FooError.SOME_CODE))" // without checking instanceof Error.prototype.matches = function() { return false; }; this.Variant._new_internal = function(sig, value) { let signature = Array.prototype.slice.call(sig); let variant = _pack_variant(signature, value); if (signature.length != 0) throw new TypeError('Invalid GVariant signature (more than one single complete type)'); return variant; }; // Deprecate version of new GLib.Variant() this.Variant.new = function(sig, value) { return new GLib.Variant(sig, value); }; this.Variant.prototype.unpack = function() { return _unpack_variant(this, false); }; this.Variant.prototype.deep_unpack = function() { return _unpack_variant(this, true); }; this.Variant.prototype.toString = function() { return '[object variant of type "' + this.get_type_string() + '"]'; }; this.Bytes.prototype.toArray = function() { return imports.byteArray.fromGBytes(this); }; this.log_structured = function(logDomain, logLevel, stringFields) { let fields = {}; for (let key in stringFields) { fields[key] = new GLib.Variant('s', stringFields[key]); } GLib.log_variant(logDomain, logLevel, new GLib.Variant('a{sv}', fields)); }; } cjs-4.4.0/modules/overrides/GObject.js000066400000000000000000000466511356375132300176470ustar00rootroot00000000000000/* exported _init, interfaces, properties, registerClass, requires, signals */ // Copyright 2011 Jasper St. Pierre // Copyright 2017 Philip Chimento , // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. const Gi = imports._gi; const CjsPrivate = imports.gi.CjsPrivate; const Legacy = imports._legacy; let GObject; var GTypeName = Symbol('GType name'); var interfaces = Symbol('GObject interfaces'); var properties = Symbol('GObject properties'); var requires = Symbol('GObject interface requires'); var signals = Symbol('GObject signals'); // These four will be aliased to GTK var _children = Symbol('GTK widget template children'); var _cssName = Symbol('GTK widget CSS name'); var _internalChildren = Symbol('GTK widget template internal children'); var _template = Symbol('GTK widget template'); function registerClass(klass) { if (arguments.length == 2) { // The two-argument form is the convenient syntax without ESnext // decorators and class data properties. The first argument is an // object with meta info such as properties and signals. The second // argument is the class expression for the class itself. // // var MyClass = GObject.registerClass({ // Properties: { ... }, // Signals: { ... }, // }, class MyClass extends GObject.Object { // _init() { ... } // }); // // When decorators and class data properties become part of the JS // standard, this function can be used directly as a decorator. let metaInfo = arguments[0]; klass = arguments[1]; if ('GTypeName' in metaInfo) klass[GTypeName] = metaInfo.GTypeName; if ('Implements' in metaInfo) klass[interfaces] = metaInfo.Implements; if ('Properties' in metaInfo) klass[properties] = metaInfo.Properties; if ('Signals' in metaInfo) klass[signals] = metaInfo.Signals; if ('Requires' in metaInfo) klass[requires] = metaInfo.Requires; if ('CssName' in metaInfo) klass[_cssName] = metaInfo.CssName; if ('Template' in metaInfo) klass[_template] = metaInfo.Template; if ('Children' in metaInfo) klass[_children] = metaInfo.Children; if ('InternalChildren' in metaInfo) klass[_internalChildren] = metaInfo.InternalChildren; } if (!(klass.prototype instanceof GObject.Object) && !(klass.prototype instanceof GObject.Interface)) throw new TypeError('GObject.registerClass() used with invalid base ' + `class (is ${Object.getPrototypeOf(klass).name})`); // Find the "least derived" class with a _classInit static function; there // definitely is one, since this class must inherit from GObject let initclass = klass; while (typeof initclass._classInit === 'undefined') initclass = Object.getPrototypeOf(initclass.prototype).constructor; return initclass._classInit(klass); } // Some common functions between GObject.Class and GObject.Interface function _createSignals(gtype, signals) { for (let signalName in signals) { let obj = signals[signalName]; let flags = (obj.flags !== undefined) ? obj.flags : GObject.SignalFlags.RUN_FIRST; let accumulator = (obj.accumulator !== undefined) ? obj.accumulator : GObject.AccumulatorType.NONE; let rtype = (obj.return_type !== undefined) ? obj.return_type : GObject.TYPE_NONE; let paramtypes = (obj.param_types !== undefined) ? obj.param_types : []; try { obj.signal_id = Gi.signal_new(gtype, signalName, flags, accumulator, rtype, paramtypes); } catch (e) { throw new TypeError('Invalid signal ' + signalName + ': ' + e.message); } } } function _createGTypeName(klass) { if (klass.hasOwnProperty(GTypeName)) return klass[GTypeName]; return `Gjs_${klass.name}`; } function _propertiesAsArray(klass) { let propertiesArray = []; if (klass.hasOwnProperty(properties)) { for (let prop in klass[properties]) { propertiesArray.push(klass[properties][prop]); } } return propertiesArray; } function _copyAllDescriptors(target, source) { Object.getOwnPropertyNames(source) .filter(key => !['prototype', 'constructor'].includes(key)) .concat(Object.getOwnPropertySymbols(source)) .forEach(key => { let descriptor = Object.getOwnPropertyDescriptor(source, key); Object.defineProperty(target, key, descriptor); }); } function _interfacePresent(required, klass) { if (!klass[interfaces]) return false; if (klass[interfaces].includes(required)) return true; // implemented here // Might be implemented on a parent class return _interfacePresent(required, Object.getPrototypeOf(klass)); } function _checkInterface(iface, proto) { // Check that proto implements all of this interface's required interfaces. // "proto" refers to the object's prototype (which implements the interface) // whereas "iface.prototype" is the interface's prototype (which may still // contain unimplemented methods.) if (typeof iface[requires] === 'undefined') return; let unfulfilledReqs = iface[requires].filter(required => { // Either the interface is not present or it is not listed before the // interface that requires it or the class does not inherit it. This is // so that required interfaces don't copy over properties from other // interfaces that require them. let ifaces = proto.constructor[interfaces]; return ((!_interfacePresent(required, proto.constructor) || ifaces.indexOf(required) > ifaces.indexOf(iface)) && !(proto instanceof required)); }).map(required => // required.name will be present on JS classes, but on introspected // GObjects it will be the C name. The alternative is just so that // we print something if there is garbage in Requires. required.name || required); if (unfulfilledReqs.length > 0) { throw new Error('The following interfaces must be implemented before ' + `${iface.name}: ${unfulfilledReqs.join(', ')}`); } } function _init() { GObject = this; function _makeDummyClass(obj, name, upperName, gtypeName, actual) { let gtype = GObject.type_from_name(gtypeName); obj['TYPE_' + upperName] = gtype; obj[name] = function(v) { return new actual(v); }; obj[name].$gtype = gtype; } _makeDummyClass(this, 'VoidType', 'NONE', 'void', function() {}); _makeDummyClass(this, 'Char', 'CHAR', 'gchar', Number); _makeDummyClass(this, 'UChar', 'UCHAR', 'guchar', Number); _makeDummyClass(this, 'Unichar', 'UNICHAR', 'gint', String); this.TYPE_BOOLEAN = GObject.type_from_name('gboolean'); this.Boolean = Boolean; Boolean.$gtype = this.TYPE_BOOLEAN; _makeDummyClass(this, 'Int', 'INT', 'gint', Number); _makeDummyClass(this, 'UInt', 'UINT', 'guint', Number); _makeDummyClass(this, 'Long', 'LONG', 'glong', Number); _makeDummyClass(this, 'ULong', 'ULONG', 'gulong', Number); _makeDummyClass(this, 'Int64', 'INT64', 'gint64', Number); _makeDummyClass(this, 'UInt64', 'UINT64', 'guint64', Number); this.TYPE_ENUM = GObject.type_from_name('GEnum'); this.TYPE_FLAGS = GObject.type_from_name('GFlags'); _makeDummyClass(this, 'Float', 'FLOAT', 'gfloat', Number); this.TYPE_DOUBLE = GObject.type_from_name('gdouble'); this.Double = Number; Number.$gtype = this.TYPE_DOUBLE; this.TYPE_STRING = GObject.type_from_name('gchararray'); this.String = String; String.$gtype = this.TYPE_STRING; this.TYPE_POINTER = GObject.type_from_name('gpointer'); this.TYPE_BOXED = GObject.type_from_name('GBoxed'); this.TYPE_PARAM = GObject.type_from_name('GParam'); this.TYPE_INTERFACE = GObject.type_from_name('GInterface'); this.TYPE_OBJECT = GObject.type_from_name('GObject'); this.TYPE_VARIANT = GObject.type_from_name('GVariant'); _makeDummyClass(this, 'Type', 'GTYPE', 'GType', GObject.type_from_name); this.ParamSpec.char = function(name, nick, blurb, flags, minimum, maximum, default_value) { return GObject.param_spec_char(name, nick, blurb, minimum, maximum, default_value, flags); }; this.ParamSpec.uchar = function(name, nick, blurb, flags, minimum, maximum, default_value) { return GObject.param_spec_uchar(name, nick, blurb, minimum, maximum, default_value, flags); }; this.ParamSpec.int = function(name, nick, blurb, flags, minimum, maximum, default_value) { return GObject.param_spec_int(name, nick, blurb, minimum, maximum, default_value, flags); }; this.ParamSpec.uint = function(name, nick, blurb, flags, minimum, maximum, default_value) { return GObject.param_spec_uint(name, nick, blurb, minimum, maximum, default_value, flags); }; this.ParamSpec.long = function(name, nick, blurb, flags, minimum, maximum, default_value) { return GObject.param_spec_long(name, nick, blurb, minimum, maximum, default_value, flags); }; this.ParamSpec.ulong = function(name, nick, blurb, flags, minimum, maximum, default_value) { return GObject.param_spec_ulong(name, nick, blurb, minimum, maximum, default_value, flags); }; this.ParamSpec.int64 = function(name, nick, blurb, flags, minimum, maximum, default_value) { return GObject.param_spec_int64(name, nick, blurb, minimum, maximum, default_value, flags); }; this.ParamSpec.uint64 = function(name, nick, blurb, flags, minimum, maximum, default_value) { return GObject.param_spec_uint64(name, nick, blurb, minimum, maximum, default_value, flags); }; this.ParamSpec.float = function(name, nick, blurb, flags, minimum, maximum, default_value) { return GObject.param_spec_float(name, nick, blurb, minimum, maximum, default_value, flags); }; this.ParamSpec.boolean = function(name, nick, blurb, flags, default_value) { return GObject.param_spec_boolean(name, nick, blurb, default_value, flags); }; this.ParamSpec.flags = function(name, nick, blurb, flags, flags_type, default_value) { return GObject.param_spec_flags(name, nick, blurb, flags_type, default_value, flags); }; this.ParamSpec.enum = function(name, nick, blurb, flags, enum_type, default_value) { return GObject.param_spec_enum(name, nick, blurb, enum_type, default_value, flags); }; this.ParamSpec.double = function(name, nick, blurb, flags, minimum, maximum, default_value) { return GObject.param_spec_double(name, nick, blurb, minimum, maximum, default_value, flags); }; this.ParamSpec.string = function(name, nick, blurb, flags, default_value) { return GObject.param_spec_string(name, nick, blurb, default_value, flags); }; this.ParamSpec.boxed = function(name, nick, blurb, flags, boxed_type) { return GObject.param_spec_boxed(name, nick, blurb, boxed_type, flags); }; this.ParamSpec.object = function(name, nick, blurb, flags, object_type) { return GObject.param_spec_object(name, nick, blurb, object_type, flags); }; this.ParamSpec.param = function(name, nick, blurb, flags, param_type) { return GObject.param_spec_param(name, nick, blurb, param_type, flags); }; this.ParamSpec.override = Gi.override_property; Object.defineProperties(this.ParamSpec.prototype, { 'name': { configurable: false, enumerable: false, get: function() { return this.get_name() } }, '_nick': { configurable: false, enumerable: false, get: function() { return this.get_nick() } }, 'nick': { configurable: false, enumerable: false, get: function() { return this.get_nick() } }, '_blurb': { configurable: false, enumerable: false, get: function() { return this.get_blurb() } }, 'blurb': { configurable: false, enumerable: false, get: function() { return this.get_blurb() } }, 'default_value': { configurable: false, enumerable: false, get: function() { return this.get_default_value() } }, 'flags': { configurable: false, enumerable: false, get: function() { return CjsPrivate.param_spec_get_flags(this) } }, 'value_type': { configurable: false, enumerable: false, get: function() { return CjsPrivate.param_spec_get_value_type(this) } }, 'owner_type': { configurable: false, enumerable: false, get: function() { return CjsPrivate.param_spec_get_owner_type(this) } }, }); let {GObjectMeta, GObjectInterface} = Legacy.defineGObjectLegacyObjects(GObject); this.Class = GObjectMeta; this.Interface = GObjectInterface; this.Object.prototype.__metaclass__ = this.Class; // For compatibility with Lang.Class... we need a _construct // or the Lang.Class constructor will fail. this.Object.prototype._construct = function() { this._init.apply(this, arguments); return this; }; GObject.registerClass = registerClass; GObject.Object._classInit = function(klass) { let gtypename = _createGTypeName(klass); let gobjectInterfaces = klass.hasOwnProperty(interfaces) ? klass[interfaces] : []; let propertiesArray = _propertiesAsArray(klass); let parent = Object.getPrototypeOf(klass); let gobjectSignals = klass.hasOwnProperty(signals) ? klass[signals] : []; let newClass = Gi.register_type(parent.prototype, gtypename, gobjectInterfaces, propertiesArray); Object.setPrototypeOf(newClass, parent); _createSignals(newClass.$gtype, gobjectSignals); _copyAllDescriptors(newClass, klass); gobjectInterfaces.forEach(iface => _copyAllDescriptors(newClass.prototype, iface.prototype)); _copyAllDescriptors(newClass.prototype, klass.prototype); Object.getOwnPropertyNames(newClass.prototype) .filter(name => name.startsWith('vfunc_') || name.startsWith('on_')) .forEach(name => { let descr = Object.getOwnPropertyDescriptor(newClass.prototype, name); if (typeof descr.value !== 'function') return; let func = newClass.prototype[name]; if (name.startsWith('vfunc_')) { Gi.hook_up_vfunc(newClass.prototype, name.slice(6), func); } else if (name.startsWith('on_')) { let id = GObject.signal_lookup(name.slice(3).replace('_', '-'), newClass.$gtype); if (id !== 0) { GObject.signal_override_class_closure(id, newClass.$gtype, function() { let argArray = Array.from(arguments); let emitter = argArray.shift(); return func.apply(emitter, argArray); }); } } }); gobjectInterfaces.forEach(iface => _checkInterface(iface, newClass.prototype)); // For backwards compatibility only. Use instanceof instead. newClass.implements = function(iface) { if (iface.$gtype) return GObject.type_is_a(newClass.$gtype, iface.$gtype); return false; }; return newClass; }; GObject.Interface._classInit = function(klass) { let gtypename = _createGTypeName(klass); let gobjectInterfaces = klass.hasOwnProperty(requires) ? klass[requires] : []; let properties = _propertiesAsArray(klass); let gobjectSignals = klass.hasOwnProperty(signals) ? klass[signals] : []; let newInterface = Gi.register_interface(gtypename, gobjectInterfaces, properties); _createSignals(newInterface.$gtype, gobjectSignals); _copyAllDescriptors(newInterface, klass); Object.getOwnPropertyNames(klass.prototype) .filter(key => key !== 'constructor') .concat(Object.getOwnPropertySymbols(klass.prototype)) .forEach(key => { let descr = Object.getOwnPropertyDescriptor(klass.prototype, key); // Create wrappers on the interface object so that generics work (e.g. // SomeInterface.some_function(this, blah) instead of // SomeInterface.prototype.some_function.call(this, blah) if (typeof descr.value === 'function') { let interfaceProto = klass.prototype; // capture in closure newInterface[key] = function () { return interfaceProto[key].call.apply(interfaceProto[key], arguments); }; } Object.defineProperty(newInterface.prototype, key, descr); }); return newInterface; }; /** * Use this to signify a function that must be overridden in an * implementation of the interface. */ GObject.NotImplementedError = class NotImplementedError extends Error { get name() { return 'NotImplementedError'; } }; GObject._cssName = _cssName; GObject._template = _template; GObject._children = _children; GObject._internalChildren = _internalChildren; // fake enum for signal accumulators, keep in sync with gi/object.c this.AccumulatorType = { NONE: 0, FIRST_WINS: 1, TRUE_HANDLED: 2 }; this.Object.prototype.disconnect = function(id) { return GObject.signal_handler_disconnect(this, id); }; // A simple workaround if you have a class with .connect, .disconnect or .emit // methods (such as Gio.Socket.connect or NMClient.Device.disconnect) // The original g_signal_* functions are not introspectable anyway, because // we need our own handling of signal argument marshalling this.signal_connect = function(object, name, handler) { return GObject.Object.prototype.connect.call(object, name, handler); }; this.signal_connect_after = function(object, name, handler) { return GObject.Object.prototype.connect_after.call(object, name, handler); }; this.signal_emit_by_name = function(object, ...nameAndArgs) { return GObject.Object.prototype.emit.apply(object, nameAndArgs); }; } cjs-4.4.0/modules/overrides/Gio.js000066400000000000000000000343641356375132300170460ustar00rootroot00000000000000// Copyright 2011 Giovanni Campagna // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. var GLib = imports.gi.GLib; var CjsPrivate = imports.gi.CjsPrivate; var Lang = imports.lang; var Signals = imports.signals; var Gio; function _proxyInvoker(methodName, sync, inSignature, arg_array) { var replyFunc; var flags = 0; var cancellable = null; /* Convert arg_array to a *real* array */ arg_array = Array.prototype.slice.call(arg_array); /* The default replyFunc only logs the responses */ replyFunc = _logReply; var signatureLength = inSignature.length; var minNumberArgs = signatureLength; var maxNumberArgs = signatureLength + 3; if (arg_array.length < minNumberArgs) { throw new Error("Not enough arguments passed for method: " + methodName + ". Expected " + minNumberArgs + ", got " + arg_array.length); } else if (arg_array.length > maxNumberArgs) { throw new Error("Too many arguments passed for method: " + methodName + ". Maximum is " + maxNumberArgs + " + one callback and/or flags"); } while (arg_array.length > signatureLength) { var argNum = arg_array.length - 1; var arg = arg_array.pop(); if (typeof(arg) == "function" && !sync) { replyFunc = arg; } else if (typeof(arg) == "number") { flags = arg; } else if (arg instanceof Gio.Cancellable) { cancellable = arg; } else { throw new Error("Argument " + argNum + " of method " + methodName + " is " + typeof(arg) + ". It should be a callback, flags or a Gio.Cancellable"); } } var inVariant = new GLib.Variant('(' + inSignature.join('') + ')', arg_array); var asyncCallback = function (proxy, result) { var outVariant = null, succeeded = false; try { outVariant = proxy.call_finish(result); succeeded = true; } catch (e) { replyFunc([], e); } if (succeeded) replyFunc(outVariant.deep_unpack(), null); }; if (sync) { return this.call_sync(methodName, inVariant, flags, -1, cancellable).deep_unpack(); } else { return this.call(methodName, inVariant, flags, -1, cancellable, asyncCallback); } } function _logReply(result, exc) { if (exc != null) { log("Ignored exception from dbus method: " + exc.toString()); } } function _makeProxyMethod(method, sync) { var i; var name = method.name; var inArgs = method.in_args; var inSignature = [ ]; for (i = 0; i < inArgs.length; i++) inSignature.push(inArgs[i].signature); return function() { return _proxyInvoker.call(this, name, sync, inSignature, arguments); }; } function _convertToNativeSignal(proxy, sender_name, signal_name, parameters) { Signals._emit.call(proxy, signal_name, sender_name, parameters.deep_unpack()); } function _propertyGetter(name) { let value = this.get_cached_property(name); return value ? value.deep_unpack() : null; } function _propertySetter(value, name, signature) { let variant = new GLib.Variant(signature, value); this.set_cached_property(name, variant); this.call('org.freedesktop.DBus.Properties.Set', new GLib.Variant('(ssv)', [this.g_interface_name, name, variant]), Gio.DBusCallFlags.NONE, -1, null, Lang.bind(this, function(proxy, result) { try { this.call_finish(result); } catch(e) { log('Could not set property ' + name + ' on remote object ' + this.g_object_path + ': ' + e.message); } })); } function _addDBusConvenience() { let info = this.g_interface_info; if (!info) return; if (info.signals.length > 0) this.connect('g-signal', _convertToNativeSignal); let i, methods = info.methods; for (i = 0; i < methods.length; i++) { var method = methods[i]; this[method.name + 'Remote'] = _makeProxyMethod(methods[i], false); this[method.name + 'Sync'] = _makeProxyMethod(methods[i], true); } let properties = info.properties; for (i = 0; i < properties.length; i++) { let name = properties[i].name; let signature = properties[i].signature; Object.defineProperty(this, name, { get: Lang.bind(this, _propertyGetter, name), set: Lang.bind(this, _propertySetter, name, signature), configurable: true, enumerable: true }); } } function _makeProxyWrapper(interfaceXml) { var info = _newInterfaceInfo(interfaceXml); var iname = info.name; return function(bus, name, object, asyncCallback, cancellable) { var obj = new Gio.DBusProxy({ g_connection: bus, g_interface_name: iname, g_interface_info: info, g_name: name, g_object_path: object }); if (!cancellable) cancellable = null; if (asyncCallback) obj.init_async(GLib.PRIORITY_DEFAULT, cancellable, function(initable, result) { let caughtErrorWhenInitting = null; try { initable.init_finish(result); } catch(e) { caughtErrorWhenInitting = e; } if (caughtErrorWhenInitting === null) { asyncCallback(initable, null); } else { asyncCallback(null, caughtErrorWhenInitting); } }); else obj.init(cancellable); return obj; }; } function _newNodeInfo(constructor, value) { if (typeof value == 'string') return constructor(value); throw TypeError(`Invalid type ${Object.prototype.toString.call(value)}`); } function _newInterfaceInfo(value) { var nodeInfo = Gio.DBusNodeInfo.new_for_xml(value); return nodeInfo.interfaces[0]; } function _injectToMethod(klass, method, addition) { var previous = klass[method]; klass[method] = function() { addition.apply(this, arguments); return previous.apply(this, arguments); }; } function _injectToStaticMethod(klass, method, addition) { var previous = klass[method]; klass[method] = function(...parameters) { let obj = previous.apply(this, parameters); addition.apply(obj, parameters); return obj; }; } function _wrapFunction(klass, method, addition) { var previous = klass[method]; klass[method] = function() { var args = Array.prototype.slice.call(arguments); args.unshift(previous); return addition.apply(this, args); }; } function _makeOutSignature(args) { var ret = '('; for (var i = 0; i < args.length; i++) ret += args[i].signature; return ret + ')'; } function _handleMethodCall(info, impl, method_name, parameters, invocation) { // prefer a sync version if available if (this[method_name]) { let retval; try { retval = this[method_name].apply(this, parameters.deep_unpack()); } catch (e) { if (e instanceof GLib.Error) { invocation.return_gerror(e); } else { let name = e.name; if (name.indexOf('.') == -1) { // likely to be a normal JS error name = 'org.gnome.gjs.JSError.' + name; } logError(e, "Exception in method call: " + method_name); invocation.return_dbus_error(name, e.message); } return; } if (retval === undefined) { // undefined (no return value) is the empty tuple retval = new GLib.Variant('()', []); } try { if (!(retval instanceof GLib.Variant)) { // attempt packing according to out signature let methodInfo = info.lookup_method(method_name); let outArgs = methodInfo.out_args; let outSignature = _makeOutSignature(outArgs); if (outArgs.length == 1) { // if one arg, we don't require the handler wrapping it // into an Array retval = [retval]; } retval = new GLib.Variant(outSignature, retval); } invocation.return_value(retval); } catch(e) { // if we don't do this, the other side will never see a reply invocation.return_dbus_error('org.gnome.gjs.JSError.ValueError', "Service implementation returned an incorrect value type"); } } else if (this[method_name + 'Async']) { this[method_name + 'Async'](parameters.deep_unpack(), invocation); } else { log('Missing handler for DBus method ' + method_name); invocation.return_gerror(new Gio.DBusError({ code: Gio.DBusError.UNKNOWN_METHOD, message: 'Method ' + method_name + ' is not implemented' })); } } function _handlePropertyGet(info, impl, property_name) { let propInfo = info.lookup_property(property_name); let jsval = this[property_name]; if (jsval != undefined) return new GLib.Variant(propInfo.signature, jsval); else return null; } function _handlePropertySet(info, impl, property_name, new_value) { this[property_name] = new_value.deep_unpack(); } function _wrapJSObject(interfaceInfo, jsObj) { var info; if (interfaceInfo instanceof Gio.DBusInterfaceInfo) info = interfaceInfo; else info = Gio.DBusInterfaceInfo.new_for_xml(interfaceInfo); info.cache_build(); var impl = new CjsPrivate.DBusImplementation({ g_interface_info: info }); impl.connect('handle-method-call', function(impl, method_name, parameters, invocation) { return _handleMethodCall.call(jsObj, info, impl, method_name, parameters, invocation); }); impl.connect('handle-property-get', function(impl, property_name) { return _handlePropertyGet.call(jsObj, info, impl, property_name); }); impl.connect('handle-property-set', function(impl, property_name, value) { return _handlePropertySet.call(jsObj, info, impl, property_name, value); }); return impl; } function* _listModelIterator() { let _index = 0; const _len = this.get_n_items(); while (_index < _len) { yield this.get_item(_index++); } } function _init() { Gio = this; Gio.DBus = { get session() { return Gio.bus_get_sync(Gio.BusType.SESSION, null); }, get system() { return Gio.bus_get_sync(Gio.BusType.SYSTEM, null); }, // Namespace some functions get: Gio.bus_get, get_finish: Gio.bus_get_finish, get_sync: Gio.bus_get_sync, own_name: Gio.bus_own_name, own_name_on_connection: Gio.bus_own_name_on_connection, unown_name: Gio.bus_unown_name, watch_name: Gio.bus_watch_name, watch_name_on_connection: Gio.bus_watch_name_on_connection, unwatch_name: Gio.bus_unwatch_name }; Gio.DBusConnection.prototype.watch_name = function(name, flags, appeared, vanished) { return Gio.bus_watch_name_on_connection(this, name, flags, appeared, vanished); }; Gio.DBusConnection.prototype.unwatch_name = function(id) { return Gio.bus_unwatch_name(id); }; Gio.DBusConnection.prototype.own_name = function(name, flags, acquired, lost) { return Gio.bus_own_name_on_connection(this, name, flags, acquired, lost); }; Gio.DBusConnection.prototype.unown_name = function(id) { return Gio.bus_unown_name(id); }; _injectToMethod(Gio.DBusProxy.prototype, 'init', _addDBusConvenience); _injectToMethod(Gio.DBusProxy.prototype, 'init_async', _addDBusConvenience); _injectToStaticMethod(Gio.DBusProxy, 'new_sync', _addDBusConvenience); _injectToStaticMethod(Gio.DBusProxy, 'new_finish', _addDBusConvenience); _injectToStaticMethod(Gio.DBusProxy, 'new_for_bus_sync', _addDBusConvenience); _injectToStaticMethod(Gio.DBusProxy, 'new_for_bus_finish', _addDBusConvenience); Gio.DBusProxy.prototype.connectSignal = Signals._connect; Gio.DBusProxy.prototype.disconnectSignal = Signals._disconnect; Gio.DBusProxy.makeProxyWrapper = _makeProxyWrapper; // Some helpers _wrapFunction(Gio.DBusNodeInfo, 'new_for_xml', _newNodeInfo); Gio.DBusInterfaceInfo.new_for_xml = _newInterfaceInfo; Gio.DBusExportedObject = CjsPrivate.DBusImplementation; Gio.DBusExportedObject.wrapJSObject = _wrapJSObject; // ListStore Gio.ListStore.prototype[Symbol.iterator] = _listModelIterator; } cjs-4.4.0/modules/overrides/Gtk.js000066400000000000000000000105761356375132300170540ustar00rootroot00000000000000// application/javascript;version=1.8 // Copyright 2013 Giovanni Campagna // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. const Legacy = imports._legacy; const GObject = imports.gi.GObject; var CjsPrivate = imports.gi.CjsPrivate; let Gtk; function _init() { Gtk = this; Gtk.children = GObject._children; Gtk.cssName = GObject._cssName; Gtk.internalChildren = GObject._internalChildren; Gtk.template = GObject._template; let {GtkWidgetClass} = Legacy.defineGtkLegacyObjects(GObject, Gtk); Gtk.Widget.prototype.__metaclass__ = GtkWidgetClass; if (CjsPrivate.gtk_container_child_set_property) { Gtk.Container.prototype.child_set_property = function(child, property, value) { CjsPrivate.gtk_container_child_set_property(this, child, property, value); }; } Gtk.Widget.prototype._init = function(params) { if (this.constructor[Gtk.template]) { Gtk.Widget.set_connect_func.call(this.constructor, (builder, obj, signalName, handlerName, connectObj, flags) => { if (connectObj !== null) { throw new Error('Unsupported template signal attribute "object"'); } else if (flags & GObject.ConnectFlags.SWAPPED) { throw new Error('Unsupported template signal flag "swapped"'); } else if (flags & GObject.ConnectFlags.AFTER) { obj.connect_after(signalName, this[handlerName].bind(this)); } else { obj.connect(signalName, this[handlerName].bind(this)); } }); } GObject.Object.prototype._init.call(this, params); if (this.constructor[Gtk.template]) { let children = this.constructor[Gtk.children] || []; for (let child of children) { this[child.replace(/-/g, '_')] = this.get_template_child(this.constructor, child); } let internalChildren = this.constructor[Gtk.internalChildren] || []; for (let child of internalChildren) { this['_' + child.replace(/-/g, '_')] = this.get_template_child(this.constructor, child); } } }; Gtk.Widget._classInit = function(klass) { let template = klass[Gtk.template]; let cssName = klass[Gtk.cssName]; let children = klass[Gtk.children]; let internalChildren = klass[Gtk.internalChildren]; if (template) { klass.prototype._instance_init = function() { this.init_template(); }; } klass = GObject.Object._classInit(klass); if (cssName) Gtk.Widget.set_css_name.call(klass, cssName); if (template) { if (typeof template === 'string' && template.startsWith('resource:///')) Gtk.Widget.set_template_from_resource.call(klass, template.slice(11)); else Gtk.Widget.set_template.call(klass, template); } if (children) { children.forEach(child => Gtk.Widget.bind_template_child_full.call(klass, child, false, 0)); } if (internalChildren) { internalChildren.forEach(child => Gtk.Widget.bind_template_child_full.call(klass, child, true, 0)); } return klass; }; } cjs-4.4.0/modules/overrides/cairo.js000066400000000000000000000003071356375132300174130ustar00rootroot00000000000000// This override adds the builtin Cairo bindings to imports.gi.cairo. // (It's confusing to have two incompatible ways to import Cairo.) function _init() { Object.assign(this, imports.cairo); } cjs-4.4.0/modules/package.js000066400000000000000000000262421356375132300157150ustar00rootroot00000000000000// Copyright 2012 Giovanni Campagna // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. /** * This module provides a set of convenience APIs for building packaged * applications. */ const GLib = imports.gi.GLib; const GIRepository = imports.gi.GIRepository; const Gio = imports.gi.Gio; const GObject = imports.gi.GObject; const System = imports.system; const Gettext = imports.gettext; /*< public >*/ var name; var version; var prefix; var datadir; var libdir; var pkgdatadir; var pkglibdir; var moduledir; var localedir; /*< private >*/ let _pkgname; let _base; let _submoduledir; function _findEffectiveEntryPointName() { let entryPoint = System.programInvocationName; while (GLib.file_test(entryPoint, GLib.FileTest.IS_SYMLINK)) entryPoint = GLib.file_read_link(entryPoint); return GLib.path_get_basename(entryPoint); } function _runningFromSource() { let binary = Gio.File.new_for_path(System.programInvocationName); let sourceBinary = Gio.File.new_for_path('./src/' + name); return binary.equal(sourceBinary); } function _runningFromMesonSource() { return GLib.getenv('MESON_BUILD_ROOT') && GLib.getenv('MESON_SOURCE_ROOT'); } function _makeNamePath(name) { return '/' + name.replace(/\./g, '/'); } /** * init: * @params: package parameters * * Initialize directories and global variables. Must be called * before any of other API in Package is used. * @params must be an object with at least the following keys: * - name: the package name ($(PACKAGE_NAME) in autotools, * eg. org.foo.Bar) * - version: the package version * - prefix: the installation prefix * * init() will take care to check if the program is running from * the source directory or not, by looking for a 'src' directory. * * At the end, the global variable 'pkg' will contain the * Package module (imports.package). Additionally, the following * module variables will be available: * - name: the base name of the entry point (eg. org.foo.Bar.App) * - version: same as in @params * - prefix: the installation prefix (as passed in @params) * - datadir, libdir: the final datadir and libdir when installed; * usually, these would be prefix + '/share' and * and prefix + '/lib' (or '/lib64') * - pkgdatadir: the directory to look for private data files, such as * images, stylesheets and UI definitions; * this will be datadir + name when installed and * './data' when running from the source tree * - pkglibdir: the directory to look for private typelibs and C * libraries; * this will be libdir + name when installed and * './lib' when running from the source tree * - moduledir: the directory to look for JS modules; * this will be pkglibdir when installed and * './src' when running from the source tree * - localedir: the directory containing gettext translation files; * this will be datadir + '/locale' when installed * and './po' in the source tree * * All paths are absolute and will not end with '/'. * * As a side effect, init() calls GLib.set_prgname(). */ function init(params) { window.pkg = imports.package; _pkgname = params.name; name = _findEffectiveEntryPointName(); version = params.version; // Must call it first, because it can only be called // once, and other library calls might have it as a // side effect GLib.set_prgname(name); prefix = params.prefix; libdir = params.libdir; datadir = GLib.build_filenamev([prefix, 'share']); let libpath, girpath; if (_runningFromSource()) { log('Running from source tree, using local files'); // Running from source directory _base = GLib.get_current_dir(); _submoduledir = _base; pkglibdir = GLib.build_filenamev([_base, 'lib']); libpath = GLib.build_filenamev([pkglibdir, '.libs']); girpath = pkglibdir; pkgdatadir = GLib.build_filenamev([_base, 'data']); localedir = GLib.build_filenamev([_base, 'po']); moduledir = GLib.build_filenamev([_base, 'src']); GLib.setenv('GSETTINGS_SCHEMA_DIR', pkgdatadir, true); } else if (_runningFromMesonSource()) { log('Running from Meson, using local files'); let bld = GLib.getenv('MESON_BUILD_ROOT'); let src = GLib.getenv('MESON_SOURCE_ROOT'); pkglibdir = libpath = girpath = GLib.build_filenamev([bld, 'lib']); pkgdatadir = GLib.build_filenamev([bld, 'data']); localedir = GLib.build_filenamev([bld, 'po']); _submoduledir = GLib.build_filenamev([bld, 'subprojects']); GLib.setenv('GSETTINGS_SCHEMA_DIR', pkgdatadir, true); try { let resource = Gio.Resource.load(GLib.build_filenamev([bld, 'src', name + '.src.gresource'])); resource._register(); moduledir = 'resource://' + _makeNamePath(name) + '/js'; } catch(e) { moduledir = GLib.build_filenamev([src, 'src']); } } else { _base = prefix; pkglibdir = GLib.build_filenamev([libdir, _pkgname]); libpath = pkglibdir; girpath = GLib.build_filenamev([pkglibdir, 'girepository-1.0']); pkgdatadir = GLib.build_filenamev([datadir, _pkgname]); localedir = GLib.build_filenamev([datadir, 'locale']); try { let resource = Gio.Resource.load(GLib.build_filenamev([pkgdatadir, name + '.src.gresource'])); resource._register(); moduledir = 'resource://' + _makeNamePath(name) + '/js'; } catch(e) { moduledir = pkgdatadir; } } imports.searchPath.unshift(moduledir); GIRepository.Repository.prepend_search_path(girpath); GIRepository.Repository.prepend_library_path(libpath); try { let resource = Gio.Resource.load(GLib.build_filenamev([pkgdatadir, name + '.data.gresource'])); resource._register(); } catch(e) { } } /** * start: * @params: see init() * * This is a convenience function if your package has a * single entry point. * You must define a main(ARGV) function inside a main.js * module in moduledir. */ function start(params) { init(params); run(imports.main); } /** * run: * @module: the module to run * * This is the function to use if you want to have multiple * entry points in one package. * You must define a main(ARGV) function inside the passed * in module, and then the launcher would be * * imports.package.init(...); * imports.package.run(imports.entrypoint); */ function run(module) { return module.main([System.programInvocationName].concat(ARGV)); } /** * require: * @libs: the external dependencies to import * * Mark a dependency on a specific version of one or more * external GI typelibs. * @libs must be an object whose keys are a typelib name, * and values are the respective version. The empty string * indicates any version. */ function require(libs) { for (let l in libs) requireSymbol(l, libs[l]); } /** * requireSymbol: * * As checkSymbol(), but exit with an error if the * dependency cannot be satisfied. */ function requireSymbol(lib, version, symbol) { if (!checkSymbol(lib, version, symbol)) { if (symbol) printerr(`Unsatisfied dependency: No ${symbol} in ${lib}`); else printerr(`Unsatisfied dependency: ${lib}`); System.exit(1); } } /** * checkSymbol: * @lib: an external dependency to import * @version: optional version of the dependency * @symbol: optional symbol to check for * * Check whether an external GI typelib can be imported * and provides @symbol. * * Symbols may refer to * - global functions ('main_quit') * - classes ('Window') * - class / instance methods ('IconTheme.get_default' / 'IconTheme.has_icon') * - GObject properties ('Window.default_height') * * Returns: %true if @lib can be imported and provides @symbol, %false otherwise */ function checkSymbol(lib, version, symbol) { let Lib = null; if (version) imports.gi.versions[lib] = version; try { Lib = imports.gi[lib]; } catch(e) { return false; } if (!symbol) return true; // Done let [klass, sym] = symbol.split('.'); if (klass === symbol) // global symbol return (typeof Lib[symbol] !== 'undefined'); let obj = Lib[klass]; if (typeof obj === 'undefined') return false; if (typeof obj[sym] !== 'undefined' || (obj.prototype && typeof obj.prototype[sym] !== 'undefined')) return true; // class- or object method // GObject property let pspec = null; if (GObject.type_is_a(obj.$gtype, GObject.TYPE_INTERFACE)) { let iface = GObject.type_default_interface_ref(obj.$gtype); pspec = GObject.Object.interface_find_property(iface, sym); } else if (GObject.type_is_a(obj.$gtype, GObject.TYPE_OBJECT)) { pspec = GObject.Object.find_property.call(obj.$gtype, sym); } return (pspec !== null); } function initGettext() { Gettext.bindtextdomain(_pkgname, localedir); Gettext.textdomain(_pkgname); let gettext = imports.gettext; window._ = gettext.gettext; window.C_ = gettext.pgettext; window.N_ = function(x) { return x; } } function initFormat() { let format = imports.format; String.prototype.format = format.format; } function initSubmodule(name) { if (_runningFromMesonSource() || _runningFromSource()) { // Running from source tree, add './name' to search paths let submoduledir = GLib.build_filenamev([_submoduledir, name]); let libpath; if (_runningFromMesonSource()) libpath = submoduledir; else libpath = GLib.build_filenamev([submoduledir, '.libs']); GIRepository.Repository.prepend_search_path(submoduledir); GIRepository.Repository.prepend_library_path(libpath); } else { // Running installed, submodule is in $(pkglibdir), nothing to do } } cjs-4.4.0/modules/signals.js000066400000000000000000000145241356375132300157620ustar00rootroot00000000000000/* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ // A couple principals of this simple signal system: // 1) should look just like our GObject signal binding // 2) memory and safety matter more than speed of connect/disconnect/emit // 3) the expectation is that a given object will have a very small number of // connections, but they may be to different signal names const Lang = imports.lang; function _connect(name, callback) { // be paranoid about callback arg since we'd start to throw from emit() // if it was messed up if (typeof(callback) != 'function') throw new Error("When connecting signal must give a callback that is a function"); // we instantiate the "signal machinery" only on-demand if anything // gets connected. if (!('_signalConnections' in this)) { this._signalConnections = []; this._nextConnectionId = 1; } let id = this._nextConnectionId; this._nextConnectionId += 1; // this makes it O(n) in total connections to emit, but I think // it's right to optimize for low memory and reentrancy-safety // rather than speed this._signalConnections.push({ 'id' : id, 'name' : name, 'callback' : callback, 'disconnected' : false }); return id; } function _disconnect(id) { if ('_signalConnections' in this) { let i; let length = this._signalConnections.length; for (i = 0; i < length; ++i) { let connection = this._signalConnections[i]; if (connection.id == id) { if (connection.disconnected) throw new Error("Signal handler id " + id + " already disconnected"); // set a flag to deal with removal during emission connection.disconnected = true; this._signalConnections.splice(i, 1); return; } } } throw new Error("No signal connection " + id + " found"); } function _signalHandlerIsConnected(id) { if (! '_signalConnections' in this) return false; for (let connection of this._signalConnections) { if (connection.id == id) { if (connection.disconnected) return false; else return true; } } return false; } function _disconnectAll() { if ('_signalConnections' in this) { while (this._signalConnections.length > 0) { _disconnect.call(this, this._signalConnections[0].id); } } } function _emit(name /* , arg1, arg2 */) { // may not be any signal handlers at all, if not then return if (!('_signalConnections' in this)) return; // To deal with re-entrancy (removal/addition while // emitting), we copy out a list of what was connected // at emission start; and just before invoking each // handler we check its disconnected flag. let handlers = []; let i; let length = this._signalConnections.length; for (i = 0; i < length; ++i) { let connection = this._signalConnections[i]; if (connection.name == name) { handlers.push(connection); } } // create arg array which is emitter + everything passed in except // signal name. Would be more convenient not to pass emitter to // the callback, but trying to be 100% consistent with GObject // which does pass it in. Also if we pass in the emitter here, // people don't create closures with the emitter in them, // which would be a cycle. let arg_array = [ this ]; // arguments[0] should be signal name so skip it length = arguments.length; for (i = 1; i < length; ++i) { arg_array.push(arguments[i]); } length = handlers.length; for (i = 0; i < length; ++i) { let connection = handlers[i]; if (!connection.disconnected) { try { // since we pass "null" for this, the global object will be used. let ret = connection.callback.apply(null, arg_array); // if the callback returns true, we don't call the next // signal handlers if (ret === true) { break; } } catch(e) { // just log any exceptions so that callbacks can't disrupt // signal emission logError(e, "Exception in callback for signal: "+name); } } } } function _addSignalMethod(proto, functionName, func) { if (proto[functionName] && proto[functionName] != func) { log("WARNING: addSignalMethods is replacing existing " + proto + " " + functionName + " method"); } proto[functionName] = func; } function addSignalMethods(proto) { _addSignalMethod(proto, "connect", _connect); _addSignalMethod(proto, "disconnect", _disconnect); _addSignalMethod(proto, "emit", _emit); _addSignalMethod(proto, "signalHandlerIsConnected", _signalHandlerIsConnected) // this one is not in GObject, but useful _addSignalMethod(proto, "disconnectAll", _disconnectAll); } var WithSignals = new Lang.Interface({ Name: 'WithSignals', connect: _connect, disconnect: _disconnect, emit: _emit, disconnectAll: _disconnectAll, }); cjs-4.4.0/modules/system.cpp000066400000000000000000000150271356375132300160130ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * Copyright (c) 2012 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include "cjs/jsapi-wrapper.h" #include #include #include "gi/object.h" #include "cjs/context-private.h" #include "cjs/jsapi-util-args.h" #include "system.h" /* Note that this cannot be relied on to test whether two objects are the same! * SpiderMonkey can move objects around in memory during garbage collection, * and it can also deduplicate identical instances of objects in memory. */ static bool gjs_address_of(JSContext *context, unsigned argc, JS::Value *vp) { JS::CallArgs argv = JS::CallArgsFromVp (argc, vp); JS::RootedObject target_obj(context); if (!gjs_parse_call_args(context, "addressOf", argv, "o", "object", &target_obj)) return false; GjsAutoChar pointer_string = g_strdup_printf("%p", target_obj.get()); return gjs_string_from_utf8(context, pointer_string, argv.rval()); } static bool gjs_refcount(JSContext *context, unsigned argc, JS::Value *vp) { JS::CallArgs argv = JS::CallArgsFromVp (argc, vp); JS::RootedObject target_obj(context); GObject *obj; if (!gjs_parse_call_args(context, "refcount", argv, "o", "object", &target_obj)) return false; if (!gjs_typecheck_object(context, target_obj, G_TYPE_OBJECT, true)) return false; obj = gjs_g_object_from_object(context, target_obj); if (obj == NULL) return false; argv.rval().setInt32(obj->ref_count); return true; } static bool gjs_breakpoint(JSContext *context, unsigned argc, JS::Value *vp) { JS::CallArgs argv = JS::CallArgsFromVp (argc, vp); if (!gjs_parse_call_args(context, "breakpoint", argv, "")) return false; G_BREAKPOINT(); argv.rval().setUndefined(); return true; } static bool gjs_dump_heap(JSContext *cx, unsigned argc, JS::Value *vp) { JS::CallArgs args = JS::CallArgsFromVp(argc, vp); GjsAutoChar filename; if (!gjs_parse_call_args(cx, "dumpHeap", args, "|F", "filename", &filename)) return false; if (filename) { FILE *fp = fopen(filename, "a"); js::DumpHeap(cx, fp, js::IgnoreNurseryObjects); fclose(fp); } else { js::DumpHeap(cx, stdout, js::IgnoreNurseryObjects); } args.rval().setUndefined(); return true; } static bool gjs_gc(JSContext *context, unsigned argc, JS::Value *vp) { JS::CallArgs argv = JS::CallArgsFromVp (argc, vp); if (!gjs_parse_call_args(context, "gc", argv, "")) return false; JS_GC(context); argv.rval().setUndefined(); return true; } static bool gjs_exit(JSContext *context, unsigned argc, JS::Value *vp) { JS::CallArgs argv = JS::CallArgsFromVp (argc, vp); gint32 ecode; if (!gjs_parse_call_args(context, "exit", argv, "i", "ecode", &ecode)) return false; GjsContext *gjs_context = static_cast(JS_GetContextPrivate(context)); _gjs_context_exit(gjs_context, ecode); return false; /* without gjs_throw() == "throw uncatchable exception" */ } static bool gjs_clear_date_caches(JSContext *context, unsigned argc, JS::Value *vp) { JS::CallArgs rec = JS::CallArgsFromVp(argc, vp); JS_BeginRequest(context); // Workaround for a bug in SpiderMonkey where tzset is not called before // localtime_r, see https://bugzilla.mozilla.org/show_bug.cgi?id=1004706 tzset(); JS::ResetTimeZone(); JS_EndRequest(context); rec.rval().setUndefined(); return true; } static JSFunctionSpec module_funcs[] = { JS_FS("addressOf", gjs_address_of, 1, GJS_MODULE_PROP_FLAGS), JS_FS("refcount", gjs_refcount, 1, GJS_MODULE_PROP_FLAGS), JS_FS("breakpoint", gjs_breakpoint, 0, GJS_MODULE_PROP_FLAGS), JS_FS("dumpHeap", gjs_dump_heap, 1, GJS_MODULE_PROP_FLAGS), JS_FS("gc", gjs_gc, 0, GJS_MODULE_PROP_FLAGS), JS_FS("exit", gjs_exit, 0, GJS_MODULE_PROP_FLAGS), JS_FS("clearDateCaches", gjs_clear_date_caches, 0, GJS_MODULE_PROP_FLAGS), JS_FS_END }; bool gjs_js_define_system_stuff(JSContext *context, JS::MutableHandleObject module) { GjsContext *gjs_context; char *program_name; bool retval; module.set(JS_NewPlainObject(context)); if (!JS_DefineFunctions(context, module, &module_funcs[0])) return false; retval = false; gjs_context = (GjsContext*) JS_GetContextPrivate(context); g_object_get(gjs_context, "program-name", &program_name, NULL); JS::RootedValue value(context); if (!gjs_string_from_utf8(context, program_name, &value)) goto out; /* The name is modeled after program_invocation_name, part of the glibc */ if (!JS_DefineProperty(context, module, "programInvocationName", value, GJS_MODULE_PROP_FLAGS | JSPROP_READONLY)) goto out; if (!JS_DefineProperty(context, module, "version", GJS_VERSION, GJS_MODULE_PROP_FLAGS | JSPROP_READONLY)) goto out; retval = true; out: g_free(program_name); return retval; } cjs-4.4.0/modules/system.h000066400000000000000000000027701356375132300154610ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * Copyright (c) 2012 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_SYSTEM_H__ #define __GJS_SYSTEM_H__ #include #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS bool gjs_js_define_system_stuff(JSContext *context, JS::MutableHandleObject module); G_END_DECLS #endif /* __GJS_SYSTEM_H__ */ cjs-4.4.0/modules/tweener/000077500000000000000000000000001356375132300154275ustar00rootroot00000000000000cjs-4.4.0/modules/tweener/equations.js000066400000000000000000000633241356375132300200050ustar00rootroot00000000000000/* -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2008 litl, LLC. */ /** * Equations * Main equations for the Tweener class * * @author Zeh Fernando, Nate Chatellier * @version 1.0.2 */ /* Disclaimer for Robert Penner's Easing Equations license: TERMS OF USE - EASING EQUATIONS Open source under the BSD License. Copyright © 2001 Robert Penner All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the author nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // ================================================================================================================================== // TWEENING EQUATIONS functions ----------------------------------------------------------------------------------------------------- // (the original equations are Robert Penner's work as mentioned on the disclaimer) /** * Easing equation function for a simple linear tweening, with no easing. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeNone (t, b, c, d, p_params) { return c*t/d + b; } /* Useful alias */ function linear (t, b, c ,d, p_params) { return easeNone (t, b, c, d, p_params); } /** * Easing equation function for a quadratic (t^2) easing in: accelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeInQuad (t, b, c, d, p_params) { return c*(t/=d)*t + b; } /** * Easing equation function for a quadratic (t^2) easing out: decelerating to zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeOutQuad (t, b, c, d, p_params) { return -c *(t/=d)*(t-2) + b; } /** * Easing equation function for a quadratic (t^2) easing in/out: acceleration until halfway, then deceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeInOutQuad (t, b, c, d, p_params) { if ((t/=d/2) < 1) return c/2*t*t + b; return -c/2 * ((--t)*(t-2) - 1) + b; } /** * Easing equation function for a quadratic (t^2) easing out/in: deceleration until halfway, then acceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeOutInQuad (t, b, c, d, p_params) { if (t < d/2) return easeOutQuad (t*2, b, c/2, d, p_params); return easeInQuad((t*2)-d, b+c/2, c/2, d, p_params); } /** * Easing equation function for a cubic (t^3) easing in: accelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeInCubic (t, b, c, d, p_params) { return c*(t/=d)*t*t + b; } /** * Easing equation function for a cubic (t^3) easing out: decelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeOutCubic (t, b, c, d, p_params) { return c*((t=t/d-1)*t*t + 1) + b; } /** * Easing equation function for a cubic (t^3) easing in/out: acceleration until halfway, then deceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeInOutCubic (t, b, c, d, p_params) { if ((t/=d/2) < 1) return c/2*t*t*t + b; return c/2*((t-=2)*t*t + 2) + b; } /** * Easing equation function for a cubic (t^3) easing out/in: deceleration until halfway, then acceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeOutInCubic (t, b, c, d, p_params) { if (t < d/2) return easeOutCubic (t*2, b, c/2, d, p_params); return easeInCubic((t*2)-d, b+c/2, c/2, d, p_params); } /** * Easing equation function for a quartic (t^4) easing in: accelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeInQuart (t, b, c, d, p_params) { return c*(t/=d)*t*t*t + b; } /** * Easing equation function for a quartic (t^4) easing out: decelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeOutQuart (t, b, c, d, p_params) { return -c * ((t=t/d-1)*t*t*t - 1) + b; } /** * Easing equation function for a quartic (t^4) easing in/out: acceleration until halfway, then deceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeInOutQuart (t, b, c, d, p_params) { if ((t/=d/2) < 1) return c/2*t*t*t*t + b; return -c/2 * ((t-=2)*t*t*t - 2) + b; } /** * Easing equation function for a quartic (t^4) easing out/in: deceleration until halfway, then acceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeOutInQuart (t, b, c, d, p_params) { if (t < d/2) return easeOutQuart (t*2, b, c/2, d, p_params); return easeInQuart((t*2)-d, b+c/2, c/2, d, p_params); } /** * Easing equation function for a quintic (t^5) easing in: accelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeInQuint (t, b, c, d, p_params) { return c*(t/=d)*t*t*t*t + b; } /** * Easing equation function for a quintic (t^5) easing out: decelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeOutQuint (t, b, c, d, p_params) { return c*((t=t/d-1)*t*t*t*t + 1) + b; } /** * Easing equation function for a quintic (t^5) easing in/out: acceleration until halfway, then deceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeInOutQuint (t, b, c, d, p_params) { if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b; return c/2*((t-=2)*t*t*t*t + 2) + b; } /** * Easing equation function for a quintic (t^5) easing out/in: deceleration until halfway, then acceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeOutInQuint (t, b, c, d, p_params) { if (t < d/2) return easeOutQuint (t*2, b, c/2, d, p_params); return easeInQuint((t*2)-d, b+c/2, c/2, d, p_params); } /** * Easing equation function for a sinusoidal (sin(t)) easing in: accelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeInSine (t, b, c, d, p_params) { return -c * Math.cos(t/d * (Math.PI/2)) + c + b; } /** * Easing equation function for a sinusoidal (sin(t)) easing out: decelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeOutSine (t, b, c, d, p_params) { return c * Math.sin(t/d * (Math.PI/2)) + b; } /** * Easing equation function for a sinusoidal (sin(t)) easing in/out: acceleration until halfway, then deceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeInOutSine (t, b, c, d, p_params) { return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b; } /** * Easing equation function for a sinusoidal (sin(t)) easing out/in: deceleration until halfway, then acceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeOutInSine (t, b, c, d, p_params) { if (t < d/2) return easeOutSine (t*2, b, c/2, d, p_params); return easeInSine((t*2)-d, b+c/2, c/2, d, p_params); } /** * Easing equation function for an exponential (2^t) easing in: accelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeInExpo (t, b, c, d, p_params) { return (t<=0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b; } /** * Easing equation function for an exponential (2^t) easing out: decelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeOutExpo (t, b, c, d, p_params) { return (t>=d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b; } /** * Easing equation function for an exponential (2^t) easing in/out: acceleration until halfway, then deceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeInOutExpo (t, b, c, d, p_params) { if (t<=0) return b; if (t>=d) return b+c; if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b; return c/2 * (-Math.pow(2, -10 * --t) + 2) + b; } /** * Easing equation function for an exponential (2^t) easing out/in: deceleration until halfway, then acceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeOutInExpo (t, b, c, d, p_params) { if (t < d/2) return easeOutExpo (t*2, b, c/2, d, p_params); return easeInExpo((t*2)-d, b+c/2, c/2, d, p_params); } /** * Easing equation function for a circular (sqrt(1-t^2)) easing in: accelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeInCirc (t, b, c, d, p_params) { return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b; } /** * Easing equation function for a circular (sqrt(1-t^2)) easing out: decelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeOutCirc (t, b, c, d, p_params) { return c * Math.sqrt(1 - (t=t/d-1)*t) + b; } /** * Easing equation function for a circular (sqrt(1-t^2)) easing in/out: acceleration until halfway, then deceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeInOutCirc (t, b, c, d, p_params) { if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b; return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b; } /** * Easing equation function for a circular (sqrt(1-t^2)) easing out/in: deceleration until halfway, then acceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeOutInCirc (t, b, c, d, p_params) { if (t < d/2) return easeOutCirc (t*2, b, c/2, d, p_params); return easeInCirc((t*2)-d, b+c/2, c/2, d, p_params); } /** * Easing equation function for an elastic (exponentially decaying sine wave) easing in: accelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @param a Amplitude. * @param p Period. * @return The correct value. */ function easeInElastic (t, b, c, d, p_params) { if (t<=0) return b; if ((t/=d)>=1) return b+c; var p = !Boolean(p_params) || isNaN(p_params.period) ? d*.3 : p_params.period; var s; var a = !Boolean(p_params) || isNaN(p_params.amplitude) ? 0 : p_params.amplitude; if (!Boolean(a) || a < Math.abs(c)) { a = c; s = p/4; } else { s = p/(2*Math.PI) * Math.asin (c/a); } return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; } /** * Easing equation function for an elastic (exponentially decaying sine wave) easing out: decelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @param a Amplitude. * @param p Period. * @return The correct value. */ function easeOutElastic (t, b, c, d, p_params) { if (t<=0) return b; if ((t/=d)>=1) return b+c; var p = !Boolean(p_params) || isNaN(p_params.period) ? d*.3 : p_params.period; var s; var a = !Boolean(p_params) || isNaN(p_params.amplitude) ? 0 : p_params.amplitude; if (!Boolean(a) || a < Math.abs(c)) { a = c; s = p/4; } else { s = p/(2*Math.PI) * Math.asin (c/a); } return (a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b); } /** * Easing equation function for an elastic (exponentially decaying sine wave) easing in/out: acceleration until halfway, then deceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @param a Amplitude. * @param p Period. * @return The correct value. */ function easeInOutElastic (t, b, c, d, p_params) { if (t<=0) return b; if ((t/=d/2)>=2) return b+c; var p = !Boolean(p_params) || isNaN(p_params.period) ? d*(.3*1.5) : p_params.period; var s; var a = !Boolean(p_params) || isNaN(p_params.amplitude) ? 0 : p_params.amplitude; if (!Boolean(a) || a < Math.abs(c)) { a = c; s = p/4; } else { s = p/(2*Math.PI) * Math.asin (c/a); } if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b; } /** * Easing equation function for an elastic (exponentially decaying sine wave) easing out/in: deceleration until halfway, then acceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @param a Amplitude. * @param p Period. * @return The correct value. */ function easeOutInElastic (t, b, c, d, p_params) { if (t < d/2) return easeOutElastic (t*2, b, c/2, d, p_params); return easeInElastic((t*2)-d, b+c/2, c/2, d, p_params); } /** * Easing equation function for a back (overshooting cubic easing: (s+1)*t^3 - s*t^2) easing in: accelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @param s Overshoot ammount: higher s means greater overshoot (0 produces cubic easing with no overshoot, and the default value of 1.70158 produces an overshoot of 10 percent). * @return The correct value. */ function easeInBack (t, b, c, d, p_params) { var s = !Boolean(p_params) || isNaN(p_params.overshoot) ? 1.70158 : p_params.overshoot; return c*(t/=d)*t*((s+1)*t - s) + b; } /** * Easing equation function for a back (overshooting cubic easing: (s+1)*t^3 - s*t^2) easing out: decelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @param s Overshoot ammount: higher s means greater overshoot (0 produces cubic easing with no overshoot, and the default value of 1.70158 produces an overshoot of 10 percent). * @return The correct value. */ function easeOutBack (t, b, c, d, p_params) { var s = !Boolean(p_params) || isNaN(p_params.overshoot) ? 1.70158 : p_params.overshoot; return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b; } /** * Easing equation function for a back (overshooting cubic easing: (s+1)*t^3 - s*t^2) easing in/out: acceleration until halfway, then deceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @param s Overshoot ammount: higher s means greater overshoot (0 produces cubic easing with no overshoot, and the default value of 1.70158 produces an overshoot of 10 percent). * @return The correct value. */ function easeInOutBack (t, b, c, d, p_params) { var s = !Boolean(p_params) || isNaN(p_params.overshoot) ? 1.70158 : p_params.overshoot; if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b; return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b; } /** * Easing equation function for a back (overshooting cubic easing: (s+1)*t^3 - s*t^2) easing out/in: deceleration until halfway, then acceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @param s Overshoot ammount: higher s means greater overshoot (0 produces cubic easing with no overshoot, and the default value of 1.70158 produces an overshoot of 10 percent). * @return The correct value. */ function easeOutInBack (t, b, c, d, p_params) { if (t < d/2) return easeOutBack (t*2, b, c/2, d, p_params); return easeInBack((t*2)-d, b+c/2, c/2, d, p_params); } /** * Easing equation function for a bounce (exponentially decaying parabolic bounce) easing in: accelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeInBounce (t, b, c, d, p_params) { return c - easeOutBounce (d-t, 0, c, d) + b; } /** * Easing equation function for a bounce (exponentially decaying parabolic bounce) easing out: decelerating from zero velocity. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeOutBounce (t, b, c, d, p_params) { if ((t/=d) < (1/2.75)) { return c*(7.5625*t*t) + b; } else if (t < (2/2.75)) { return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b; } else if (t < (2.5/2.75)) { return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b; } else { return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b; } } /** * Easing equation function for a bounce (exponentially decaying parabolic bounce) easing in/out: acceleration until halfway, then deceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeInOutBounce (t, b, c, d, p_params) { if (t < d/2) return easeInBounce (t*2, 0, c, d) * .5 + b; else return easeOutBounce (t*2-d, 0, c, d) * .5 + c*.5 + b; } /** * Easing equation function for a bounce (exponentially decaying parabolic bounce) easing out/in: deceleration until halfway, then acceleration. * * @param t Current time (in frames or seconds). * @param b Starting value. * @param c Change needed in value. * @param d Expected easing duration (in frames or seconds). * @return The correct value. */ function easeOutInBounce (t, b, c, d, p_params) { if (t < d/2) return easeOutBounce (t*2, b, c/2, d, p_params); return easeInBounce((t*2)-d, b+c/2, c/2, d, p_params); } cjs-4.4.0/modules/tweener/tweenList.js000066400000000000000000000103571356375132300177510ustar00rootroot00000000000000/* -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2008 litl, LLC. */ /** * The tween list object. Stores all of the properties and information that pertain to individual tweens. * * @author Nate Chatellier, Zeh Fernando * @version 1.0.4 * @private */ /* Licensed under the MIT License Copyright (c) 2006-2007 Zeh Fernando and Nate Chatellier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. http://code.google.com/p/tweener/ http://code.google.com/p/tweener/wiki/License */ function TweenList(scope, timeStart, timeComplete, useFrames, transition, transitionParams) { this._init(scope, timeStart, timeComplete, useFrames, transition, transitionParams); } TweenList.prototype = { _init: function(scope, timeStart, timeComplete, userFrames, transition, transitionParams) { this.scope = scope; this.timeStart = timeStart; this.timeComplete = timeComplete; this.userFrames = userFrames; this.transition = transition; this.transitionParams = transitionParams; /* Other default information */ this.properties = new Object(); this.isPaused = false; this.timePaused = undefined; this.isCaller = false; this.updatesSkipped = 0; this.timesCalled = 0; this.skipUpdates = 0; this.hasStarted = false; }, clone: function(omitEvents) { var tween = new TweenList(this.scope, this.timeStart, this.timeComplete, this.userFrames, this.transition, this.transitionParams); tween.properties = new Array(); for (let name in this.properties) { tween.properties[name] = this.properties[name]; } tween.skipUpdates = this.skipUpdates; tween.updatesSkipped = this.updatesSkipped; if (!omitEvents) { tween.onStart = this.onStart; tween.onUpdate = this.onUpdate; tween.onComplete = this.onComplete; tween.onOverwrite = this.onOverwrite; tween.onError = this.onError; tween.onStartParams = this.onStartParams; tween.onUpdateParams = this.onUpdateParams; tween.onCompleteParams = this.onCompleteParams; tween.onOverwriteParams = this.onOverwriteParams; tween.onStartScope = this.onStartScope; tween.onUpdateScope = this.onUpdateScope; tween.onCompleteScope = this.onCompleteScope; tween.onOverwriteScope = this.onOverwriteScope; tween.onErrorScope = this.onErrorScope; } tween.rounded = this.rounded; tween.min = this.min; tween.max = this.max; tween.isPaused = this.isPaused; tween.timePaused = this.timePaused; tween.isCaller = this.isCaller; tween.count = this.count; tween.timesCalled = this.timesCalled; tween.waitFrames = this.waitFrames; tween.hasStarted = this.hasStarted; return tween; } }; function makePropertiesChain(obj) { /* Tweener has a bunch of code here to get all the properties of all * the objects we inherit from (the objects in the 'base' property). * I don't think that applies to JavaScript... */ return obj; } cjs-4.4.0/modules/tweener/tweener.js000066400000000000000000000662201356375132300174440ustar00rootroot00000000000000/* -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil; -*- */ /* Copyright 2008 litl, LLC. */ /** * Tweener * Transition controller for movieclips, sounds, textfields and other objects * * @author Zeh Fernando, Nate Chatellier, Arthur Debert * @version 1.31.71 */ /* Licensed under the MIT License Copyright (c) 2006-2007 Zeh Fernando and Nate Chatellier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. http://code.google.com/p/tweener/ http://code.google.com/p/tweener/wiki/License */ const GLib = imports.gi.GLib; const TweenList = imports.tweener.tweenList; const Signals = imports.signals; var _inited = false; var _engineExists = false; var _tweenList = null; var _timeScale = 1; var _specialPropertyList = []; var _specialPropertyModifierList = []; var _specialPropertySplitterList = []; /* * Ticker should implement: * * property FRAME_RATE * start() * stop() * getTime() gets time in milliseconds from start() * signal prepare-frame * */ var _ticker = null; var _prepareFrameId = 0; /* default frame ticker */ function FrameTicker() { this._init(); } FrameTicker.prototype = { FRAME_RATE: 65, _init : function() { }, start : function() { this._currentTime = 0; let me = this; this._timeoutID = GLib.timeout_add( GLib.PRIORITY_DEFAULT, Math.floor(1000 / me.FRAME_RATE), function() { me._currentTime += 1000 / me.FRAME_RATE; me.emit('prepare-frame'); return true; }); }, stop : function() { if ('_timeoutID' in this) { GLib.source_remove(this._timeoutID); delete this._timeoutID; } this._currentTime = 0; }, getTime : function() { return this._currentTime; } }; Signals.addSignalMethods(FrameTicker.prototype); _ticker = new FrameTicker(); /* TODOs: * * Special properties: * * Special properties are 'proxy' properties used in Tweener to tween * (animate) things that are not proper properties per se. One example * given is the 'frame' of an object in ActionScript, which is not an * object property. Using the special property '_frame' you could animate * it like this: * * Tweener.addTween(myMovieClip, {_frame:20, time:1}); * * which would be equivalent to applying a fast-forward to it. * * This properties need a special support in the code, and I've removed it * for now until we see the need for it in our clutter based stuff. */ /* This is a bit pointless now, but let's keep it anyway... */ function _init() { if (_inited) return; _inited = true; } function setFrameTicker(ticker) { _ticker = ticker; } function _startEngine() { if (_engineExists) return; _engineExists = true; _tweenList = new Array(); if (!_ticker) { throw new Error('Must call setFrameTicker()'); } _prepareFrameId = _ticker.connect('prepare-frame', _onEnterFrame); _ticker.start(); } function _stopEngine() { if (!_engineExists) return; _engineExists = false; _tweenList = false; _ticker.disconnect(_prepareFrameId); _prepareFrameId = 0; _ticker.stop(); } function _getCurrentTweeningTime() { return _ticker.getTime(); } function _removeTweenByIndex(i) { _tweenList[i] = null; var finalRemoval = arguments[1]; if (finalRemoval != undefined && finalRemoval) _tweenList.splice(i, 1); return true; } function _resumeTweenByIndex(i) { var tweening = _tweenList[i]; if (tweening == null || !tweening.isPaused) return false; var currentTime = _getCurrentTweeningTime(tweening); tweening.timeStart += currentTime - tweening.timePaused; tweening.timeComplete += currentTime - tweening.timePaused; tweening.timePaused = undefined; tweening.isPaused = false; return true; } /* FIXME: any way to get the function name from the fn itself? */ function _callOnFunction(fn, fnname, scope, fallbackScope, params) { if (fn) { var eventScope = scope ? scope : fallbackScope; try { fn.apply(eventScope, params); } catch (e) { logError(e, 'Error calling ' + fnname); } } } function _updateTweenByIndex(i) { var tweening = _tweenList[i]; if (tweening == null || !tweening.scope) return false; var currentTime = _getCurrentTweeningTime(tweening); if (currentTime < tweening.timeStart) return true; // Hasn't started, so return true var scope = tweening.scope; var t, b, c, d, nv; var isOver = false; if (tweening.isCaller) { do { t = ((tweening.timeComplete - tweening.timeStart)/tweening.count) * (tweening.timesCalled + 1); b = tweening.timeStart; c = tweening.timeComplete - tweening.timeStart; d = tweening.timeComplete - tweening.timeStart; nv = tweening.transition(t, b, c, d); if (currentTime >= nv) { _callOnFunction(tweening.onUpdate, 'onUpdate', tweening.onUpdateScope, scope, tweening.onUpdateParams); tweening.timesCalled++; if (tweening.timesCalled >= tweening.count) { isOver = true; break; } if (tweening.waitFrames) break; } } while (currentTime >= nv); } else { var mustUpdate, name; if (currentTime >= tweening.timeComplete) { isOver = true; mustUpdate = true; } else { mustUpdate = tweening.skipUpdates < 1 || !tweening.skipUpdates || tweening.updatesSkipped >= tweening.skipUpdates; } if (!tweening.hasStarted) { _callOnFunction(tweening.onStart, 'onStart', tweening.onStartScope, scope, tweening.onStartParams); for (name in tweening.properties) { var pv; if (tweening.properties[name].isSpecialProperty) { // It's a special property, tunnel via the special property function if (_specialPropertyList[name].preProcess != undefined) { tweening.properties[name].valueComplete = _specialPropertyList[name].preProcess(scope, _specialPropertyList[name].parameters, tweening.properties[name].originalValueComplete, tweening.properties[name].extra); } pv = _specialPropertyList[name].getValue(scope, _specialPropertyList[name].parameters, tweening.properties[name].extra); } else { // Directly read property pv = scope[name]; } tweening.properties[name].valueStart = isNaN(pv) ? tweening.properties[name].valueComplete : pv; } mustUpdate = true; tweening.hasStarted = true; } if (mustUpdate) { for (name in tweening.properties) { var property = tweening.properties[name]; if (isOver) { // Tweening time has finished, just set it to the final value nv = property.valueComplete; } else { if (property.hasModifier) { // Modified t = currentTime - tweening.timeStart; d = tweening.timeComplete - tweening.timeStart; nv = tweening.transition(t, 0, 1, d, tweening.transitionParams); nv = property.modifierFunction(property.valueStart, property.valueComplete, nv, property.modifierParameters); } else { // Normal update t = currentTime - tweening.timeStart; b = property.valueStart; c = property.valueComplete - property.valueStart; d = tweening.timeComplete - tweening.timeStart; nv = tweening.transition(t, b, c, d, tweening.transitionParams); } } if (tweening.rounded) nv = Math.round(nv); if (tweening.min !== undefined && nv < tweening.min) nv = tweening.min; if (tweening.max !== undefined && nv > tweening.max) nv = tweening.max; if (property.isSpecialProperty) { // It's a special property, tunnel via the special property method _specialPropertyList[name].setValue(scope, nv, _specialPropertyList[name].parameters, tweening.properties[name].extra); } else { // Directly set property scope[name] = nv; } } tweening.updatesSkipped = 0; _callOnFunction(tweening.onUpdate, 'onUpdate', tweening.onUpdateScope, scope, tweening.onUpdateParams); } else { tweening.updatesSkipped++; } } if (isOver) { _callOnFunction(tweening.onComplete, 'onComplete', tweening.onCompleteScope, scope, tweening.onCompleteParams); } return !isOver; } function _updateTweens() { if (_tweenList.length == 0) return false; for (let i = 0; i < _tweenList.length; i++) { if (_tweenList[i] == undefined || !_tweenList[i].isPaused) { if (!_updateTweenByIndex(i)) _removeTweenByIndex(i); if (_tweenList[i] == null) { _removeTweenByIndex(i, true); i--; } } } return true; } /* Ran once every 'frame'. It's the main engine, updates all existing tweenings */ function _onEnterFrame() { if (!_updateTweens()) _stopEngine(); return true; } var restrictedWords = { time: true, delay: true, userFrames: true, skipUpdates: true, transition: true, transitionParams: true, onStart: true, onUpdate: true, onComplete: true, onOverwrite: true, onError: true, rounded: true, min: true, max: true, onStartParams: true, onUpdateParams: true, onCompleteParams: true, onOverwriteParams: true, onStartScope: true, onUpdateScope: true, onCompleteScope: true, onOverwriteScope: true, onErrorScope: true }; function _constructPropertyList(obj) { var properties = new Object(); var modifiedProperties = new Object(); for (let istr in obj) { if (restrictedWords[istr]) continue; if (_specialPropertySplitterList[istr] != undefined) { // Special property splitter var splitProperties = _specialPropertySplitterList[istr].splitValues(obj[istr], _specialPropertySplitterList[istr].parameters); for (let i = 0; i < splitProperties.length; i++) { if (_specialPropertySplitterList[splitProperties[i].name] != undefined) { var splitProperties2 = _specialPropertySplitterList[splitProperties[i].name].splitValues(splitProperties[i].value, _specialPropertySplitterList[splitProperties[i].name].parameters); for (let j = 0; j < splitProperties2.length; j++) { properties[splitProperties2[j].name] = { valueStart: undefined, valueComplete: splitProperties2[j].value, arrayIndex: splitProperties2[j].arrayIndex, isSpecialProperty: false }; } } else { properties[splitProperties[i].name] = { valueStart: undefined, valueComplete: splitProperties[i].value, arrayIndex: splitProperties[i].arrayIndex, isSpecialProperty: false }; } } } else if (_specialPropertyModifierList[istr] != undefined) { // Special property modifier let tempModifiedProperties = _specialPropertyModifierList[istr].modifyValues(obj[istr]); for (let i = 0; i < tempModifiedProperties.length; i++) { modifiedProperties[tempModifiedProperties[i].name] = { modifierParameters: tempModifiedProperties[i].parameters, modifierFunction: _specialPropertyModifierList[istr].getValue }; } } else { properties[istr] = { valueStart: undefined, valueComplete: obj[istr] }; } } // Adds the modifiers to the list of properties for (let istr in modifiedProperties) { if (properties[istr]) { properties[istr].modifierParameters = modifiedProperties[istr].modifierParameters; properties[istr].modifierFunction = modifiedProperties[istr].modifierFunction; } } return properties; } function PropertyInfo(valueStart, valueComplete, originalValueComplete, arrayIndex, extra, isSpecialProperty, modifierFunction, modifierParameters) { this._init(valueStart, valueComplete, originalValueComplete, arrayIndex, extra, isSpecialProperty, modifierFunction, modifierParameters); } PropertyInfo.prototype = { _init: function(valueStart, valueComplete, originalValueComplete, arrayIndex, extra, isSpecialProperty, modifierFunction, modifierParameters) { this.valueStart = valueStart; this.valueComplete = valueComplete; this.originalValueComplete = originalValueComplete; this.arrayIndex = arrayIndex; this.extra = extra; this.isSpecialProperty = isSpecialProperty; this.hasModifier = Boolean(modifierFunction); this.modifierFunction = modifierFunction; this.modifierParameters = modifierParameters; } }; function _addTweenOrCaller(target, tweeningParameters, isCaller) { if (!target) return false; var scopes; // List of objects to tween if (target instanceof Array) { // The first argument is an array scopes = target.concat(); // XXX: To copy the array I guess } else { // The first argument(s) is(are) object(s) scopes = new Array(target); } var obj, istr; if (isCaller) { obj = tweeningParameters; } else { obj = TweenList.makePropertiesChain(tweeningParameters); var properties = _constructPropertyList(obj); // Verifies whether the properties exist or not, for warning messages for (istr in properties) { if (_specialPropertyList[istr] != undefined) { properties[istr].isSpecialProperty = true; } else { for (var i = 0; i < scopes.length; i++) { if (scopes[i][istr] == undefined) log('The property ' + istr + ' doesn\'t seem to be a normal object property of ' + scopes[i] + ' or a registered special property'); } properties[istr].isSpecialProperty = false; } } } // Creates the main engine if it isn't active if (!_inited) _init(); if (!_engineExists) _startEngine(); // Creates a "safer", more strict tweening object var time = obj.time || 0; var delay = obj.delay || 0; var transition; // FIXME: Tweener allows you to use functions with an all lower-case name if (typeof obj.transition == 'string') { transition = imports.tweener.equations[obj.transition]; } else { transition = obj.transition; } if (!transition) transition = imports.tweener.equations['easeOutExpo']; var tween; for (let i = 0; i < scopes.length; i++) { if (!isCaller) { // Make a copy of the properties var copyProperties = new Object(); for (istr in properties) { copyProperties[istr] = new PropertyInfo(properties[istr].valueStart, properties[istr].valueComplete, properties[istr].valueComplete, properties[istr].arrayIndex || 0, {}, properties[istr].isSpecialProperty || false, properties[istr].modifierFunction || null, properties[istr].modifierParameters || null); } } tween = new TweenList.TweenList(scopes[i], _ticker.getTime() + ((delay * 1000) / _timeScale), _ticker.getTime() + (((delay * 1000) + (time * 1000)) / _timeScale), false, transition, obj.transitionParams || null); tween.properties = isCaller ? null : copyProperties; tween.onStart = obj.onStart; tween.onUpdate = obj.onUpdate; tween.onComplete = obj.onComplete; tween.onOverwrite = obj.onOverwrite; tween.onError = obj.onError; tween.onStartParams = obj.onStartParams; tween.onUpdateParams = obj.onUpdateParams; tween.onCompleteParams = obj.onCompleteParams; tween.onOverwriteParams = obj.onOverwriteParams; tween.onStartScope = obj.onStartScope; tween.onUpdateScope = obj.onUpdateScope; tween.onCompleteScope = obj.onCompleteScope; tween.onOverwriteScope = obj.onOverwriteScope; tween.onErrorScope = obj.onErrorScope; tween.rounded = obj.rounded; tween.min = obj.min; tween.max = obj.max; tween.skipUpdates = obj.skipUpdates; tween.isCaller = isCaller; if (isCaller) { tween.count = obj.count; tween.waitFrames = obj.waitFrames; } if (!isCaller) { // Remove other tweenings that occur at the same time removeTweensByTime(tween.scope, tween.properties, tween.timeStart, tween.timeComplete); } // And finally adds it to the list _tweenList.push(tween); // Immediate update and removal if it's an immediate tween // If not deleted, it executes at the end of this frame execution if (time == 0 && delay == 0) { var myT = _tweenList.length-1; _updateTweenByIndex(myT); _removeTweenByIndex(myT); } } return true; } function addTween(target, tweeningParameters) { return _addTweenOrCaller(target, tweeningParameters, false); } function addCaller(target, tweeningParameters) { return _addTweenOrCaller(target, tweeningParameters, true); } function _getNumberOfProperties(object) { var totalProperties = 0; // the following line is disabled becasue eslint was picking up the following error: the variable name is defined but never used, however since it is required to search the object it is used and we'll allow the line to be ignored to get rid of the error message /* eslint-disable-next-line */ for (let name in object) { totalProperties ++; } return totalProperties; } function removeTweensByTime(scope, properties, timeStart, timeComplete) { var removed = false; var removedLocally; var name; for (let i = 0; i < _tweenList.length; i++) { removedLocally = false; if (_tweenList[i] && scope == _tweenList[i].scope && timeComplete > _tweenList[i].timeStart && timeStart < _tweenList[i].timeComplete) { for (name in _tweenList[i].properties) { if (properties[name]) { if (!removedLocally) { _callOnFunction(_tweenList[i].onOverwrite, 'onOverwrite', _tweenList[i].onOverwriteScope, _tweenList[i].scope, _tweenList[i].onOverwriteParams); } _tweenList[i].properties[name] = undefined; delete _tweenList[i].properties[name]; removedLocally = true; removed = true; } } if (removedLocally && _getNumberOfProperties(_tweenList[i].properties) == 0) { _removeTweenByIndex(i); } } } return removed; } function _pauseTweenByIndex(i) { var tweening = _tweenList[i]; if (tweening == null || tweening.isPaused) return false; tweening.timePaused = _getCurrentTweeningTime(tweening); tweening.isPaused = true; return true; } function _splitTweens(tween, properties) { var originalTween = _tweenList[tween]; var newTween = originalTween.clone(); var name; for (let i = 0; i < properties.length; i++) { name = properties[i]; if (originalTween.properties[name]) { originalTween.properties[name] = undefined; delete originalTween.properties[name]; } } var found = false; for (name in newTween.properties) { found = false; for (let i = 0; i < properties.length; i++) { if (properties[i] == name) { found = true; break; } } if (!found) { newTween.properties[name] = undefined; delete newTween.properties[name]; } } _tweenList.push(newTween); return _tweenList.length - 1; } function _affectTweens(affectFunction, scope, properties) { var affected = false; if (!_tweenList) return false; for (let i = 0; i < _tweenList.length; i++) { if (!_tweenList[i] || _tweenList[i].scope != scope) continue; if (properties.length == 0) { // Can check everything affectFunction(i); affected = true; } else { // Must check whether this tween must have specific properties affected var affectedProperties = new Array(); for (let j = 0; j < properties.length; j++) { if (_tweenList[i].properties[properties[j]]) { affectedProperties.push(properties[j]); } } if (affectedProperties.length > 0) { var objectProperties = _getNumberOfProperties(_tweenList[i].properties); if (objectProperties == affectedProperties.length) { // The list of properties is the same as all properties, so affect it all affectFunction(i); affected = true; } else { // The properties are mixed, so split the tween and affect only certian specific // properties var splicedTweenIndex = _splitTweens(i, affectedProperties); affectFunction(splicedTweenIndex); affected = true; } } } } return affected; } function _isInArray(string, array) { var l = array.length; for (let i = 0; i < l; i++) { if (array[i] == string) return true; } return false; } function _affectTweensWithFunction(func, args) { var properties = new Array(); var scope = args[0]; var affected = false; var scopes; if (scope instanceof Array) { scopes = scope.concat(); } else { scopes = new Array(scope); } for (let i = 1; args[i] != undefined; i++) { if (typeof(args[i]) == 'string' && !_isInArray(args[i], properties)) { if (_specialPropertySplitterList[args[i]]) { // special property, get splitter array first var sps = _specialPropertySplitterList[arguments[i]]; var specialProps = sps.splitValues(scope, null); for (let j = 0; j < specialProps.length; j++) properties.push(specialProps[j].name); } else properties.push(args[i]); } } // the return now value means: "affect at least one tween" for (let i = 0; i < scopes.length; i++) { affected = affected || _affectTweens(func, scopes[i], properties); } return affected; } function resumeTweens() { return _affectTweensWithFunction(_resumeTweenByIndex, arguments); } function pauseTweens() { return _affectTweensWithFunction(_pauseTweenByIndex, arguments); } function removeTweens() { return _affectTweensWithFunction(_removeTweenByIndex, arguments); } function _mapOverTweens(func) { var rv = false; if (_tweenList == null) return false; for (let i = 0; i < _tweenList.length; i++) { if (func(i)) rv = true; } return rv; } function pauseAllTweens() { return _mapOverTweens(_pauseTweenByIndex); } function resumeAllTweens() { return _mapOverTweens(_resumeTweenByIndex); } function removeAllTweens() { return _mapOverTweens(_removeTweenByIndex); } function getTweenCount(scope) { if (!_tweenList) return 0; var c = 0; for (let i = 0; i < _tweenList.length; i++) { if (_tweenList[i] && _tweenList[i].scope == scope) c += _getNumberOfProperties(_tweenList[i].properties); } return c; } function registerSpecialProperty(name, getFunction, setFunction, parameters, preProcessFunction) { _specialPropertyList[name] = { getValue: getFunction, setValue: setFunction, parameters: parameters, preProcess: preProcessFunction }; } function registerSpecialPropertyModifier(name, modifyFunction, getFunction) { _specialPropertyModifierList[name] = { modifyValues: modifyFunction, getValue: getFunction }; } function registerSpecialPropertySplitter(name, splitFunction, parameters) { _specialPropertySplitterList[name] = { splitValues: splitFunction, parameters: parameters }; } function setTimeScale(scale) { _timeScale = scale; } function getTimeScale() { return _timeScale; } cjs-4.4.0/test/000077500000000000000000000000001356375132300132655ustar00rootroot00000000000000cjs-4.4.0/test/gjs-test-call-args.cpp000066400000000000000000000410331356375132300173750ustar00rootroot00000000000000#include #include #include "cjs/context.h" #include "cjs/jsapi-util-args.h" #include "cjs/jsapi-wrapper.h" #include "test/gjs-test-utils.h" #define assert_match(str, pattern) \ G_STMT_START { \ const char *__s1 = (str), *__s2 = (pattern); \ if (!g_pattern_match_simple(__s2, __s1)) { \ g_printerr("**\nExpected \"%s\" to match \"%s\"\n", __s1, __s2); \ g_assert_not_reached(); \ } \ } G_STMT_END typedef enum _test_enum { ZERO, ONE, TWO, THREE } test_enum_t; typedef enum _test_signed_enum { MINUS_THREE = -3, MINUS_TWO, MINUS_ONE } test_signed_enum_t; #define JSNATIVE_TEST_FUNC_BEGIN(name) \ static bool \ name(JSContext *cx, \ unsigned argc, \ JS::Value *vp) \ { \ JS::CallArgs args = JS::CallArgsFromVp(argc, vp); \ bool retval; #define JSNATIVE_TEST_FUNC_END \ if (retval) \ args.rval().setUndefined(); \ return retval; \ } JSNATIVE_TEST_FUNC_BEGIN(no_args) retval = gjs_parse_call_args(cx, "noArgs", args, ""); JSNATIVE_TEST_FUNC_END JSNATIVE_TEST_FUNC_BEGIN(no_args_ignore_trailing) retval = gjs_parse_call_args(cx, "noArgsIgnoreTrailing", args, "!"); JSNATIVE_TEST_FUNC_END #define JSNATIVE_NO_ASSERT_TYPE_TEST_FUNC(type, fmt) \ JSNATIVE_TEST_FUNC_BEGIN(type##_arg_no_assert) \ type val; \ retval = gjs_parse_call_args(cx, #type "ArgNoAssert", args, fmt, \ "val", &val); \ JSNATIVE_TEST_FUNC_END JSNATIVE_NO_ASSERT_TYPE_TEST_FUNC(bool, "b"); JSNATIVE_NO_ASSERT_TYPE_TEST_FUNC(int, "i"); #undef JSNATIVE_NO_ASSERT_TYPE_TEST_FUNC JSNATIVE_TEST_FUNC_BEGIN(object_arg_no_assert) JS::RootedObject val(cx); retval = gjs_parse_call_args(cx, "objectArgNoAssert", args, "o", "val", &val); JSNATIVE_TEST_FUNC_END JSNATIVE_TEST_FUNC_BEGIN(optional_int_args_no_assert) int val1, val2; retval = gjs_parse_call_args(cx, "optionalIntArgsNoAssert", args, "i|i", "val1", &val1, "val2", &val2); JSNATIVE_TEST_FUNC_END JSNATIVE_TEST_FUNC_BEGIN(args_ignore_trailing) int val; retval = gjs_parse_call_args(cx, "argsIgnoreTrailing", args, "!i", "val", &val); JSNATIVE_TEST_FUNC_END JSNATIVE_TEST_FUNC_BEGIN(one_of_each_type) bool boolval; GjsAutoJSChar strval; GjsAutoChar fileval; int intval; unsigned uintval; int64_t int64val; double dblval; JS::RootedObject objval(cx); retval = gjs_parse_call_args(cx, "oneOfEachType", args, "bsFiutfo", "bool", &boolval, "str", &strval, "file", &fileval, "int", &intval, "uint", &uintval, "int64", &int64val, "dbl", &dblval, "obj", &objval); g_assert_cmpint(boolval, ==, true); g_assert_cmpstr(strval, ==, "foo"); g_assert_cmpstr(fileval, ==, "foo"); g_assert_cmpint(intval, ==, 1); g_assert_cmpint(uintval, ==, 1); g_assert_cmpint(int64val, ==, 1); g_assert_cmpfloat(dblval, ==, 1.0); g_assert_true(objval); JSNATIVE_TEST_FUNC_END JSNATIVE_TEST_FUNC_BEGIN(optional_args_all) bool val1, val2, val3; retval = gjs_parse_call_args(cx, "optionalArgsAll", args, "b|bb", "val1", &val1, "val2", &val2, "val3", &val3); g_assert_cmpint(val1, ==, true); g_assert_cmpint(val2, ==, true); g_assert_cmpint(val3, ==, true); JSNATIVE_TEST_FUNC_END JSNATIVE_TEST_FUNC_BEGIN(optional_args_only_required) bool val1 = false, val2 = false, val3 = false; retval = gjs_parse_call_args(cx, "optionalArgsOnlyRequired", args, "b|bb", "val1", &val1, "val2", &val2, "val3", &val3); g_assert_cmpint(val1, ==, true); g_assert_cmpint(val2, ==, false); g_assert_cmpint(val3, ==, false); JSNATIVE_TEST_FUNC_END JSNATIVE_TEST_FUNC_BEGIN(only_optional_args) int val1, val2; retval = gjs_parse_call_args(cx, "onlyOptionalArgs", args, "|ii", "val1", &val1, "val2", &val2); JSNATIVE_TEST_FUNC_END JSNATIVE_TEST_FUNC_BEGIN(unsigned_enum_arg) test_enum_t val; retval = gjs_parse_call_args(cx, "unsignedEnumArg", args, "i", "enum_param", &val); g_assert_cmpint(val, ==, ONE); JSNATIVE_TEST_FUNC_END JSNATIVE_TEST_FUNC_BEGIN(signed_enum_arg) test_signed_enum_t val; retval = gjs_parse_call_args(cx, "signedEnumArg", args, "i", "enum_param", &val); g_assert_cmpint(val, ==, MINUS_ONE); JSNATIVE_TEST_FUNC_END JSNATIVE_TEST_FUNC_BEGIN(one_of_each_nullable_type) GjsAutoJSChar strval; GjsAutoChar fileval; JS::RootedObject objval(cx); retval = gjs_parse_call_args(cx, "oneOfEachNullableType", args, "?s?F?o", "strval", &strval, "fileval", &fileval, "objval", &objval); g_assert_null(strval); g_assert_null(fileval); g_assert_false(objval); JSNATIVE_TEST_FUNC_END JSNATIVE_TEST_FUNC_BEGIN(unwind_free_test) int intval; unsigned uval; JS::RootedObject objval(cx); retval = gjs_parse_call_args(cx, "unwindFreeTest", args, "oiu", "objval", &objval, "intval", &intval, "error", &uval); g_assert_false(objval); JSNATIVE_TEST_FUNC_END #define JSNATIVE_BAD_NULLABLE_TEST_FUNC(type, fmt) \ JSNATIVE_TEST_FUNC_BEGIN(type##_invalid_nullable) \ type val; \ retval = gjs_parse_call_args(cx, #type "InvalidNullable", \ args, "?" fmt, \ "val", &val); \ JSNATIVE_TEST_FUNC_END JSNATIVE_BAD_NULLABLE_TEST_FUNC(bool, "b"); JSNATIVE_BAD_NULLABLE_TEST_FUNC(int, "i"); JSNATIVE_BAD_NULLABLE_TEST_FUNC(unsigned, "u"); JSNATIVE_BAD_NULLABLE_TEST_FUNC(int64_t, "t"); JSNATIVE_BAD_NULLABLE_TEST_FUNC(double, "f"); #undef JSNATIVE_BAD_NULLABLE_TEST_FUNC #define JSNATIVE_BAD_TYPE_TEST_FUNC(type, ch) \ JSNATIVE_TEST_FUNC_BEGIN(type##_invalid_type) \ type val; \ retval = gjs_parse_call_args(cx, #type "InvalidType", args, ch, \ "val", &val); \ JSNATIVE_TEST_FUNC_END JSNATIVE_BAD_TYPE_TEST_FUNC(bool, "i"); JSNATIVE_BAD_TYPE_TEST_FUNC(int, "u"); JSNATIVE_BAD_TYPE_TEST_FUNC(unsigned, "t"); JSNATIVE_BAD_TYPE_TEST_FUNC(int64_t, "f"); JSNATIVE_BAD_TYPE_TEST_FUNC(double, "b"); JSNATIVE_BAD_TYPE_TEST_FUNC(GjsAutoChar, "i"); JSNATIVE_BAD_TYPE_TEST_FUNC(GjsAutoJSChar, "i"); #undef JSNATIVE_BAD_TYPE_TEST_FUNC JSNATIVE_TEST_FUNC_BEGIN(object_invalid_type) JS::RootedObject val(cx); retval = gjs_parse_call_args(cx, "objectInvalidType", args, "i", "val", &val); JSNATIVE_TEST_FUNC_END static JSFunctionSpec native_test_funcs[] = { JS_FS("noArgs", no_args, 0, 0), JS_FS("noArgsIgnoreTrailing", no_args_ignore_trailing, 0, 0), JS_FS("boolArgNoAssert", bool_arg_no_assert, 0, 0), JS_FS("intArgNoAssert", int_arg_no_assert, 0, 0), JS_FS("objectArgNoAssert", object_arg_no_assert, 0, 0), JS_FS("optionalIntArgsNoAssert", optional_int_args_no_assert, 0, 0), JS_FS("argsIgnoreTrailing", args_ignore_trailing, 0, 0), JS_FS("oneOfEachType", one_of_each_type, 0, 0), JS_FS("optionalArgsAll", optional_args_all, 0, 0), JS_FS("optionalArgsOnlyRequired", optional_args_only_required, 0, 0), JS_FS("onlyOptionalArgs", only_optional_args, 0, 0), JS_FS("unsignedEnumArg", unsigned_enum_arg, 0, 0), JS_FS("signedEnumArg", signed_enum_arg, 0, 0), JS_FS("oneOfEachNullableType", one_of_each_nullable_type, 0, 0), JS_FS("unwindFreeTest", unwind_free_test, 0, 0), JS_FS("boolInvalidNullable", bool_invalid_nullable, 0, 0), JS_FS("intInvalidNullable", int_invalid_nullable, 0, 0), JS_FS("unsignedInvalidNullable", unsigned_invalid_nullable, 0, 0), JS_FS("int64_tInvalidNullable", int64_t_invalid_nullable, 0, 0), JS_FS("doubleInvalidNullable", double_invalid_nullable, 0, 0), JS_FS("boolInvalidType", bool_invalid_type, 0, 0), JS_FS("intInvalidType", int_invalid_type, 0, 0), JS_FS("unsignedInvalidType", unsigned_invalid_type, 0, 0), JS_FS("int64_tInvalidType", int64_t_invalid_type, 0, 0), JS_FS("doubleInvalidType", double_invalid_type, 0, 0), JS_FS("GjsAutoCharInvalidType", GjsAutoChar_invalid_type, 0, 0), JS_FS("GjsAutoJSCharInvalidType", GjsAutoJSChar_invalid_type, 0, 0), JS_FS("objectInvalidType", object_invalid_type, 0, 0), JS_FS_END }; static void setup(GjsUnitTestFixture *fx, gconstpointer unused) { gjs_unit_test_fixture_setup(fx, unused); JS::RootedObject global(fx->cx, gjs_get_import_global(fx->cx)); bool success = JS_DefineFunctions(fx->cx, global, native_test_funcs); g_assert_true(success); } static void run_code(GjsUnitTestFixture *fx, gconstpointer code) { const char *script = (const char *) code; JS::CompileOptions options(fx->cx, JSVERSION_UNKNOWN); options.setFileAndLine("unit test", 1); JS::RootedValue ignored(fx->cx); bool ok = JS::Evaluate(fx->cx, options, script, strlen(script), &ignored); g_assert_null(gjs_unit_test_exception_message(fx)); g_assert_true(ok); } static void run_code_expect_exception(GjsUnitTestFixture *fx, gconstpointer code) { const char *script = (const char *) code; JS::CompileOptions options(fx->cx, JSVERSION_UNKNOWN); options.setFileAndLine("unit test", 1); JS::RootedValue ignored(fx->cx); bool ok = JS::Evaluate(fx->cx, options, script, strlen(script), &ignored); g_assert_false(ok); GjsAutoChar message = gjs_unit_test_exception_message(fx); g_assert_nonnull(message); /* Cheap way to shove an expected exception message into the data argument */ const char *expected_msg = strstr((const char *) code, "//"); if (expected_msg != NULL) { expected_msg += 2; assert_match(message, expected_msg); } } void gjs_test_add_tests_for_parse_call_args(void) { #define ADD_CALL_ARGS_TEST_BASE(path, code, f) \ g_test_add("/callargs/" path, GjsUnitTestFixture, code, setup, f, \ gjs_unit_test_fixture_teardown) #define ADD_CALL_ARGS_TEST(path, code) \ ADD_CALL_ARGS_TEST_BASE(path, code, run_code) #define ADD_CALL_ARGS_TEST_XFAIL(path, code) \ ADD_CALL_ARGS_TEST_BASE(path, code, run_code_expect_exception) ADD_CALL_ARGS_TEST("no-args-works", "noArgs()"); ADD_CALL_ARGS_TEST_XFAIL("no-args-fails-on-extra-args", "noArgs(1, 2, 3)//*Expected 0 arguments, got 3"); ADD_CALL_ARGS_TEST("no-args-ignores-trailing", "noArgsIgnoreTrailing(1, 2, 3)"); ADD_CALL_ARGS_TEST_XFAIL("too-many-args-fails", "intArgNoAssert(1, 2)" "//*Expected 1 arguments, got 2"); ADD_CALL_ARGS_TEST_XFAIL("too-many-args-fails-when-more-than-optional", "optionalIntArgsNoAssert(1, 2, 3)" "//*Expected minimum 1 arguments (and 1 optional), got 3"); ADD_CALL_ARGS_TEST_XFAIL("too-few-args-fails", "intArgNoAssert()//*Expected 1 arguments, got 0"); ADD_CALL_ARGS_TEST_XFAIL("too-few-args-fails-with-optional", "optionalIntArgsNoAssert()" "//*Expected minimum 1 arguments (and 1 optional), got 0"); ADD_CALL_ARGS_TEST("args-ignores-trailing", "argsIgnoreTrailing(1, 2, 3)"); ADD_CALL_ARGS_TEST("one-of-each-type-works", "oneOfEachType(true, 'foo', 'foo', 1, 1, 1, 1, {})"); ADD_CALL_ARGS_TEST("optional-args-work-when-passing-all-args", "optionalArgsAll(true, true, true)"); ADD_CALL_ARGS_TEST("optional-args-work-when-passing-only-required-args", "optionalArgsOnlyRequired(true)"); ADD_CALL_ARGS_TEST("enum-types-work", "unsignedEnumArg(1)"); ADD_CALL_ARGS_TEST("signed-enum-types-work", "signedEnumArg(-1)"); ADD_CALL_ARGS_TEST("one-of-each-nullable-type-works", "oneOfEachNullableType(null, null, null)"); ADD_CALL_ARGS_TEST("passing-no-arguments-when-all-optional", "onlyOptionalArgs()"); ADD_CALL_ARGS_TEST("passing-some-arguments-when-all-optional", "onlyOptionalArgs(1)"); ADD_CALL_ARGS_TEST("passing-all-arguments-when-all-optional", "onlyOptionalArgs(1, 1)"); ADD_CALL_ARGS_TEST_XFAIL("allocated-args-are-freed-on-error", "unwindFreeTest({}, 1, -1)" "//*Value * is out of range"); ADD_CALL_ARGS_TEST_XFAIL("nullable-bool-is-invalid", "boolInvalidNullable(true)" "//*Invalid format string combination ?b"); ADD_CALL_ARGS_TEST_XFAIL("nullable-int-is-invalid", "intInvalidNullable(1)" "//*Invalid format string combination ?i"); ADD_CALL_ARGS_TEST_XFAIL("nullable-unsigned-is-invalid", "unsignedInvalidNullable(1)" "//*Invalid format string combination ?u"); ADD_CALL_ARGS_TEST_XFAIL("nullable-int64-is-invalid", "int64_tInvalidNullable(1)" "//*Invalid format string combination ?t"); ADD_CALL_ARGS_TEST_XFAIL("nullable-double-is-invalid", "doubleInvalidNullable(1)" "//*Invalid format string combination ?f"); ADD_CALL_ARGS_TEST_XFAIL("invalid-bool-type", "boolInvalidType(1)" "//*Wrong type for i, got bool?"); ADD_CALL_ARGS_TEST_XFAIL("invalid-int-type", "intInvalidType(1)" "//*Wrong type for u, got int32_t?"); ADD_CALL_ARGS_TEST_XFAIL("invalid-unsigned-type", "unsignedInvalidType(1)" "//*Wrong type for t, got uint32_t?"); ADD_CALL_ARGS_TEST_XFAIL("invalid-int64-type", "int64_tInvalidType(1)" "//*Wrong type for f, got int64_t?"); ADD_CALL_ARGS_TEST_XFAIL("invalid-double-type", "doubleInvalidType(false)" "//*Wrong type for b, got double?"); ADD_CALL_ARGS_TEST_XFAIL("invalid-autochar-type", "GjsAutoCharInvalidType(1)" "//*Wrong type for i, got GjsAutoChar?"); ADD_CALL_ARGS_TEST_XFAIL("invalid-autojschar-type", "GjsAutoJSCharInvalidType(1)" "//*Wrong type for i, got GjsAutoJSChar?"); ADD_CALL_ARGS_TEST_XFAIL("invalid-object-type", "objectInvalidType(1)" "//*Wrong type for i, got JS::MutableHandleObject"); ADD_CALL_ARGS_TEST_XFAIL("invalid-boolean", "boolArgNoAssert({})//*Not a boolean"); ADD_CALL_ARGS_TEST_XFAIL("invalid-object", "objectArgNoAssert(3)//*Not an object"); #undef ADD_CALL_ARGS_TEST_XFAIL #undef ADD_CALL_ARGS_TEST #undef ADD_CALL_ARGS_TEST_BASE } cjs-4.4.0/test/gjs-test-coverage.cpp000066400000000000000000001507631356375132300173360ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright © 2014 Endless Mobile, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. * * Authored By: Sam Spilsbury */ #include #include #include #include #include #include #include #include #include #include #include #include #include "cjs/coverage.h" #include "cjs/jsapi-util.h" #include "gjs-test-utils.h" typedef struct _GjsCoverageFixture { GjsContext *context; GjsCoverage *coverage; GFile *tmp_output_dir; GFile *tmp_js_script; GFile *lcov_output_dir; GFile *lcov_output; } GjsCoverageFixture; static void replace_file(GFile *file, const char *contents) { GError *error = NULL; g_file_replace_contents(file, contents, strlen(contents), NULL /* etag */, FALSE /* make backup */, G_FILE_CREATE_NONE, NULL /* etag out */, NULL /* cancellable */, &error); g_assert_no_error(error); } static void recursive_delete_dir(GFile *dir) { GFileEnumerator *files = g_file_enumerate_children(dir, G_FILE_ATTRIBUTE_STANDARD_TYPE, G_FILE_QUERY_INFO_NONE, NULL, NULL); while (TRUE) { GFile *file; GFileInfo *info; if (!g_file_enumerator_iterate(files, &info, &file, NULL, NULL) || !file || !info) break; if (g_file_info_get_file_type(info) == G_FILE_TYPE_DIRECTORY) { recursive_delete_dir(file); continue; } g_file_delete(file, NULL, NULL); } g_file_delete(dir, NULL, NULL); g_object_unref(files); } static void gjs_coverage_fixture_set_up(gpointer fixture_data, gconstpointer user_data) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; const char *js_script = "\nvar f = function () { return 1; }\n"; char *tmp_output_dir_name = g_strdup("/tmp/gjs_coverage_tmp.XXXXXX"); tmp_output_dir_name = mkdtemp(tmp_output_dir_name); if (!tmp_output_dir_name) g_error("Failed to create temporary directory for test files: %s\n", strerror(errno)); fixture->tmp_output_dir = g_file_new_for_path(tmp_output_dir_name); fixture->tmp_js_script = g_file_get_child(fixture->tmp_output_dir, "gjs_coverage_script.js"); fixture->lcov_output_dir = g_file_get_child(fixture->tmp_output_dir, "gjs_coverage_test_coverage"); fixture->lcov_output = g_file_get_child(fixture->lcov_output_dir, "coverage.lcov"); g_file_make_directory_with_parents(fixture->lcov_output_dir, NULL, NULL); char *tmp_js_script_filename = g_file_get_path(fixture->tmp_js_script); /* Allocate a strv that we can pass over to gjs_coverage_new */ char *coverage_paths[] = { tmp_js_script_filename, NULL }; char *search_paths[] = { tmp_output_dir_name, NULL }; fixture->context = gjs_context_new_with_search_path((char **) search_paths); fixture->coverage = gjs_coverage_new(coverage_paths, fixture->context, fixture->lcov_output_dir); replace_file(fixture->tmp_js_script, js_script); g_free(tmp_output_dir_name); g_free(tmp_js_script_filename); } static void gjs_coverage_fixture_tear_down(gpointer fixture_data, gconstpointer user_data) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; recursive_delete_dir(fixture->tmp_output_dir); g_object_unref(fixture->tmp_js_script); g_object_unref(fixture->tmp_output_dir); g_object_unref(fixture->lcov_output_dir); g_object_unref(fixture->lcov_output); g_object_unref(fixture->coverage); g_object_unref(fixture->context); } static const char * line_starting_with(const char *data, const char *needle) { const gsize needle_length = strlen(needle); const char *iter = data; while (iter) { if (strncmp(iter, needle, needle_length) == 0) return iter; iter = strstr(iter, "\n"); if (iter) iter += 1; } return NULL; } static char * write_statistics_and_get_coverage_data(GjsCoverage *coverage, GFile *lcov_output) { gjs_coverage_write_statistics(coverage); char *coverage_data_contents; g_file_load_contents(lcov_output, NULL /* cancellable */, &coverage_data_contents, nullptr, /* length out */ NULL /* etag */, NULL /* error */); g_debug("Coverage data:\n%s", coverage_data_contents); return coverage_data_contents; } static char * get_script_identifier(GFile *script) { char *filename = g_file_get_path(script); if (!filename) filename = g_file_get_uri(script); return filename; } static bool eval_script(GjsContext *cx, GFile *script) { char *filename = get_script_identifier(script); bool retval = gjs_context_eval_file(cx, filename, NULL, NULL); g_free(filename); return retval; } static char * eval_script_and_get_coverage_data(GjsContext *context, GjsCoverage *coverage, GFile *script, GFile *lcov_output) { eval_script(context, script); return write_statistics_and_get_coverage_data(coverage, lcov_output); } static void assert_coverage_data_contains_value_for_key(const char *data, const char *key, const char *value) { const char *sf_line = line_starting_with(data, key); g_assert_nonnull(sf_line); GjsAutoChar actual = g_strndup(&sf_line[strlen(key)], strlen(value)); g_assert_cmpstr(value, ==, actual); } using CoverageDataMatchFunc = void (*)(const char *value, const void *user_data); static void assert_coverage_data_matches_value_for_key(const char *data, const char *key, CoverageDataMatchFunc match, const void *user_data) { const char *line = line_starting_with(data, key); g_assert_nonnull(line); (*match)(line, user_data); } static void assert_coverage_data_matches_values_for_key(const char *data, const char *key, size_t n, CoverageDataMatchFunc match, const void *user_data, size_t data_size) { const char *line = line_starting_with (data, key); /* Keep matching. If we fail to match one of them then * bail out */ char *data_iterator = (char *) user_data; while (line && n > 0) { (*match)(line, data_iterator); line = line_starting_with(line + 1, key); --n; data_iterator += data_size; } /* If n is zero then we've found all available matches */ g_assert_cmpuint(n, ==, 0); } static void test_covered_file_is_duplicated_into_output_if_resource(gpointer fixture_data, gconstpointer user_data) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; const char *mock_resource_filename = "resource:///org/cinnamon/cjs/mock/test/gjs-test-coverage/loadedJSFromResource.js"; const char *coverage_scripts[] = { mock_resource_filename, NULL }; g_object_unref(fixture->context); g_object_unref(fixture->coverage); char *js_script_dirname = g_file_get_path(fixture->tmp_output_dir); char *search_paths[] = { js_script_dirname, NULL }; fixture->context = gjs_context_new_with_search_path(search_paths); fixture->coverage = gjs_coverage_new(coverage_scripts, fixture->context, fixture->lcov_output_dir); gjs_context_eval_file(fixture->context, mock_resource_filename, NULL, NULL); gjs_coverage_write_statistics(fixture->coverage); GFile *expected_temporary_js_script = g_file_resolve_relative_path(fixture->lcov_output_dir, "org/cinnamon/cjs/mock/test/gjs-test-coverage/loadedJSFromResource.js"); g_assert_true(g_file_query_exists(expected_temporary_js_script, NULL)); g_object_unref(expected_temporary_js_script); g_free(js_script_dirname); } static GFile * get_output_file_for_script_on_disk(GFile *script, GFile *output_dir) { char *base = g_file_get_basename(script); GFile *output = g_file_get_child(output_dir, base); g_free(base); return output; } static char * get_output_path_for_script_on_disk(GFile *script, GFile *output_dir) { GFile *output = get_output_file_for_script_on_disk(script, output_dir); char *output_path = g_file_get_path(output); g_object_unref(output); return output_path; } static void test_covered_file_is_duplicated_into_output_if_path(gpointer fixture_data, gconstpointer user_data) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; eval_script(fixture->context, fixture->tmp_js_script); gjs_coverage_write_statistics(fixture->coverage); GFile *expected_temporary_js_script = get_output_file_for_script_on_disk(fixture->tmp_js_script, fixture->lcov_output_dir); g_assert_true(g_file_query_exists(expected_temporary_js_script, NULL)); g_object_unref(expected_temporary_js_script); } static void test_previous_contents_preserved(gpointer fixture_data, gconstpointer user_data) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; const char *existing_contents = "existing_contents\n"; replace_file(fixture->lcov_output, existing_contents); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, fixture->tmp_js_script, fixture->lcov_output); g_assert(strstr(coverage_data_contents, existing_contents) != NULL); g_free(coverage_data_contents); } static void test_new_contents_written(gpointer fixture_data, gconstpointer user_data) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; const char *existing_contents = "existing_contents\n"; replace_file(fixture->lcov_output, existing_contents); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, fixture->tmp_js_script, fixture->lcov_output); /* We have new content in the coverage data */ g_assert(strlen(existing_contents) != strlen(coverage_data_contents)); g_free(coverage_data_contents); } static void test_expected_source_file_name_written_to_coverage_data(gpointer fixture_data, gconstpointer user_data) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, fixture->tmp_js_script, fixture->lcov_output); char *expected_source_filename = get_output_path_for_script_on_disk(fixture->tmp_js_script, fixture->lcov_output_dir); assert_coverage_data_contains_value_for_key(coverage_data_contents, "SF:", expected_source_filename); g_free(expected_source_filename); g_free(coverage_data_contents); } static void silence_log_func(const gchar *domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data) { } static void test_expected_entry_not_written_for_nonexistent_file(gpointer fixture_data, gconstpointer user_data) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; const char *coverage_paths[] = { "doesnotexist", NULL }; g_object_unref(fixture->coverage); fixture->coverage = gjs_coverage_new(coverage_paths, fixture->context, fixture->lcov_output_dir); /* Temporarily disable fatal mask and silence warnings */ GLogLevelFlags old_flags = g_log_set_always_fatal((GLogLevelFlags) G_LOG_LEVEL_ERROR); GLogFunc old_log_func = g_log_set_default_handler(silence_log_func, NULL); GFile *doesnotexist = g_file_new_for_path("doesnotexist"); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, doesnotexist, fixture->lcov_output); g_log_set_always_fatal(old_flags); g_log_set_default_handler(old_log_func, NULL); const char *sf_line = line_starting_with(coverage_data_contents, "SF:"); g_assert_null(sf_line); g_free(coverage_data_contents); g_object_unref(doesnotexist); } typedef enum _BranchTaken { NOT_EXECUTED, NOT_TAKEN, TAKEN } BranchTaken; typedef struct _BranchLineData { int expected_branch_line; int expected_id; BranchTaken taken; } BranchLineData; static void branch_at_line_should_be_taken(const char *line, const void *user_data) { auto branch_data = static_cast(user_data); int line_no, branch_id, block_no, hit_count_num, nmatches; char hit_count[20]; /* can hold maxint64 (19 digits) + nul terminator */ /* Advance past "BRDA:" */ line += 5; nmatches = sscanf(line, "%i,%i,%i,%19s", &line_no, &block_no, &branch_id, hit_count); g_assert_cmpint(nmatches, ==, 4); /* Determine the branch hit count. It will be either: * > -1 if the line containing the branch was never executed, or * > N times the branch was taken. * * The value of -1 is represented by a single "-" character, so * we should detect this case and set the value based on that */ if (strlen(hit_count) == 1 && *hit_count == '-') hit_count_num = -1; else hit_count_num = atoi(hit_count); g_assert_cmpint(line_no, ==, branch_data->expected_branch_line); g_assert_cmpint(branch_id, ==, branch_data->expected_id); switch (branch_data->taken) { case NOT_EXECUTED: g_assert_cmpint(hit_count_num, ==, -1); break; case NOT_TAKEN: g_assert_cmpint(hit_count_num, ==, 0); break; case TAKEN: g_assert_cmpint(hit_count_num, >, 0); break; default: g_assert_not_reached(); }; } static void test_single_branch_coverage_written_to_coverage_data(gpointer fixture_data, gconstpointer user_data) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; const char *script_with_basic_branch = "let x = 0;\n" "if (x > 0)\n" " x++;\n" "else\n" " x++;\n"; replace_file(fixture->tmp_js_script, script_with_basic_branch); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, fixture->tmp_js_script, fixture->lcov_output); const BranchLineData expected_branches[] = { { 2, 0, TAKEN }, { 2, 1, NOT_EXECUTED } }; const gsize expected_branches_len = G_N_ELEMENTS(expected_branches); /* There are two possible branches here, the second should be taken * and the first should not have been */ assert_coverage_data_matches_values_for_key(coverage_data_contents, "BRDA:", expected_branches_len, branch_at_line_should_be_taken, expected_branches, sizeof(BranchLineData)); assert_coverage_data_contains_value_for_key(coverage_data_contents, "BRF:", "2"); assert_coverage_data_contains_value_for_key(coverage_data_contents, "BRH:", "1"); g_free(coverage_data_contents); } static void test_multiple_branch_coverage_written_to_coverage_data(gpointer fixture_data, gconstpointer user_data) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; const char *script_with_case_statements_branch = "let y;\n" "for (let x = 0; x < 3; x++) {\n" " switch (x) {\n" " case 0:\n" " y = x + 1;\n" " break;\n" " case 1:\n" " y = x + 1;\n" " break;\n" " case 2:\n" " y = x + 1;\n" " break;\n" " }\n" "}\n"; replace_file(fixture->tmp_js_script, script_with_case_statements_branch); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, fixture->tmp_js_script, fixture->lcov_output); const BranchLineData expected_branches[] = { { 3, 0, TAKEN }, { 3, 1, TAKEN }, { 3, 2, TAKEN } }; const gsize expected_branches_len = G_N_ELEMENTS(expected_branches); /* There are two possible branches here, the second should be taken * and the first should not have been */ assert_coverage_data_matches_values_for_key(coverage_data_contents, "BRDA:", expected_branches_len, branch_at_line_should_be_taken, expected_branches, sizeof(BranchLineData)); g_free(coverage_data_contents); } static void test_branches_for_multiple_case_statements_fallthrough(gpointer fixture_data, gconstpointer user_data) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; const char *script_with_case_statements_branch = "let y;\n" "for (let x = 0; x < 3; x++) {\n" " switch (x) {\n" " case 0:\n" " case 1:\n" " y = x + 1;\n" " break;\n" " case 2:\n" " y = x + 1;\n" " break;\n" " case 3:\n" " y = x +1;\n" " break;\n" " }\n" "}\n"; replace_file(fixture->tmp_js_script, script_with_case_statements_branch); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, fixture->tmp_js_script, fixture->lcov_output); const BranchLineData expected_branches[] = { { 3, 0, TAKEN }, { 3, 1, TAKEN }, { 3, 2, NOT_EXECUTED } }; const gsize expected_branches_len = G_N_ELEMENTS(expected_branches); /* There are two possible branches here, the second should be taken * and the first should not have been */ assert_coverage_data_matches_values_for_key(coverage_data_contents, "BRDA:", expected_branches_len, branch_at_line_should_be_taken, expected_branches, sizeof(BranchLineData)); g_free(coverage_data_contents); } static void any_line_matches_not_executed_branch(const char *data) { const char *line = line_starting_with(data, "BRDA:"); while (line) { int line_no, branch_id, block_no; char hit_count; /* Advance past "BRDA:" */ line += 5; int nmatches = sscanf(line, "%i,%i,%i,%c", &line_no, &block_no, &branch_id, &hit_count); g_assert_cmpint(nmatches, ==, 4); if (line_no == 3 && branch_id == 0 && hit_count == '-') return; line = line_starting_with(line + 1, "BRDA:"); } g_assert_not_reached(); } static void test_branch_not_hit_written_to_coverage_data(gpointer fixture_data, gconstpointer user_data) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; const char *script_with_never_executed_branch = "let x = 0;\n" "if (x > 0) {\n" " if (x > 0)\n" " x++;\n" "} else {\n" " x++;\n" "}\n"; replace_file(fixture->tmp_js_script, script_with_never_executed_branch); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, fixture->tmp_js_script, fixture->lcov_output); any_line_matches_not_executed_branch(coverage_data_contents); g_free(coverage_data_contents); } static void has_function_name(const char *line, const void *user_data) { const char *expected_function_name = *(static_cast(user_data)); /* Advance past "FN:" */ line += 3; /* Advance past the first comma */ while (*(line - 1) != ',') ++line; GjsAutoChar actual = g_strndup(line, strlen(expected_function_name)); g_assert_cmpstr(actual, ==, expected_function_name); } static void test_function_names_written_to_coverage_data(gpointer fixture_data, gconstpointer user_data) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; const char *script_with_named_and_unnamed_functions = "function f(){}\n" "let b = function(){}\n"; replace_file(fixture->tmp_js_script, script_with_named_and_unnamed_functions); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, fixture->tmp_js_script, fixture->lcov_output); const char * expected_function_names[] = { "top-level", "f", "b", }; const gsize expected_function_names_len = G_N_ELEMENTS(expected_function_names); /* Just expect that we've got an FN matching out expected function names */ assert_coverage_data_matches_values_for_key(coverage_data_contents, "FN:", expected_function_names_len, has_function_name, expected_function_names, sizeof(const char *)); g_free(coverage_data_contents); } static void has_function_line(const char *line, const void *user_data) { const char *expected_function_line = *(static_cast(user_data)); /* Advance past "FN:" */ line += 3; GjsAutoChar actual = g_strndup(line, strlen(expected_function_line)); g_assert_cmpstr(actual, ==, expected_function_line); } static void test_function_lines_written_to_coverage_data(gpointer fixture_data, gconstpointer user_data) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; const char *script_with_functions = "function f(){}\n" "\n" "function g(){}\n"; replace_file(fixture->tmp_js_script, script_with_functions); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, fixture->tmp_js_script, fixture->lcov_output); const char * const expected_function_lines[] = { "1", "1", "3" }; const gsize expected_function_lines_len = G_N_ELEMENTS(expected_function_lines); assert_coverage_data_matches_values_for_key(coverage_data_contents, "FN:", expected_function_lines_len, has_function_line, expected_function_lines, sizeof(const char *)); g_free(coverage_data_contents); } typedef struct _FunctionHitCountData { const char *function; unsigned int hit_count_minimum; } FunctionHitCountData; static void hit_count_is_more_than_for_function(const char *line, const void *user_data) { auto data = static_cast(user_data); char *detected_function = NULL; unsigned int hit_count; size_t max_buf_size; int nmatches; /* Advance past "FNDA:" */ line += 5; max_buf_size = strcspn(line, "\n"); detected_function = g_new(char, max_buf_size + 1); nmatches = sscanf(line, "%u,%s", &hit_count, detected_function); g_assert_cmpint(nmatches, ==, 2); g_assert_cmpstr(data->function, ==, detected_function); g_assert_cmpuint(hit_count, >=, data->hit_count_minimum); g_free(detected_function); } /* For functions with whitespace between their definition and * first executable line, its possible that the JS engine might * enter their frame a little later in the script than where their * definition starts. We need to handle that case */ static void test_function_hit_counts_for_big_functions_written_to_coverage_data(gpointer fixture_data, gconstpointer user_data) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; const char *script_with_executed_functions = "function f(){\n" "\n" "\n" "var x = 1;\n" "}\n" "let b = function(){}\n" "f();\n" "b();\n"; replace_file(fixture->tmp_js_script, script_with_executed_functions); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, fixture->tmp_js_script, fixture->lcov_output); const FunctionHitCountData expected_hit_counts[] = { { "top-level", 1 }, { "f", 1 }, { "b", 1 }, }; const gsize expected_hit_count_len = G_N_ELEMENTS(expected_hit_counts); /* There are two possible branches here, the second should be taken * and the first should not have been */ assert_coverage_data_matches_values_for_key(coverage_data_contents, "FNDA:", expected_hit_count_len, hit_count_is_more_than_for_function, expected_hit_counts, sizeof(FunctionHitCountData)); g_free(coverage_data_contents); } /* For functions which start executing at a function declaration * we also need to make sure that we roll back to the real function, */ static void test_function_hit_counts_for_little_functions_written_to_coverage_data(gpointer fixture_data, gconstpointer user_data) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; const char *script_with_executed_functions = "function f(){\n" "var x = function(){};\n" "}\n" "let b = function(){}\n" "f();\n" "b();\n"; replace_file(fixture->tmp_js_script, script_with_executed_functions); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, fixture->tmp_js_script, fixture->lcov_output); const FunctionHitCountData expected_hit_counts[] = { { "top-level", 1 }, { "f", 1 }, { "b", 1 }, }; const gsize expected_hit_count_len = G_N_ELEMENTS(expected_hit_counts); /* There are two possible branches here, the second should be taken * and the first should not have been */ assert_coverage_data_matches_values_for_key(coverage_data_contents, "FNDA:", expected_hit_count_len, hit_count_is_more_than_for_function, expected_hit_counts, sizeof(FunctionHitCountData)); g_free(coverage_data_contents); } static void test_function_hit_counts_written_to_coverage_data(gpointer fixture_data, gconstpointer user_data) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; const char *script_with_executed_functions = "function f(){}\n" "let b = function(){}\n" "f();\n" "b();\n"; replace_file(fixture->tmp_js_script, script_with_executed_functions); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, fixture->tmp_js_script, fixture->lcov_output); const FunctionHitCountData expected_hit_counts[] = { { "top-level", 1 }, { "f", 1 }, { "b", 1 }, }; const gsize expected_hit_count_len = G_N_ELEMENTS(expected_hit_counts); /* There are two possible branches here, the second should be taken * and the first should not have been */ assert_coverage_data_matches_values_for_key(coverage_data_contents, "FNDA:", expected_hit_count_len, hit_count_is_more_than_for_function, expected_hit_counts, sizeof(FunctionHitCountData)); g_free(coverage_data_contents); } static void test_total_function_coverage_written_to_coverage_data(gpointer fixture_data, gconstpointer user_data) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; const char *script_with_some_executed_functions = "function f(){}\n" "let b = function(){}\n" "f();\n"; replace_file(fixture->tmp_js_script, script_with_some_executed_functions); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, fixture->tmp_js_script, fixture->lcov_output); /* More than one assert per test is bad, but we are testing interlinked concepts */ assert_coverage_data_contains_value_for_key(coverage_data_contents, "FNF:", "3"); assert_coverage_data_contains_value_for_key(coverage_data_contents, "FNH:", "2"); g_free(coverage_data_contents); } typedef struct _LineCountIsMoreThanData { unsigned int expected_lineno; unsigned int expected_to_be_more_than; } LineCountIsMoreThanData; static void line_hit_count_is_more_than(const char *line, const void *user_data) { auto data = static_cast(user_data); const char *coverage_line = &line[3]; char *comma_ptr = NULL; unsigned int lineno = strtol(coverage_line, &comma_ptr, 10); g_assert(comma_ptr[0] == ','); char *end_ptr = NULL; unsigned int value = strtol(&comma_ptr[1], &end_ptr, 10); g_assert(end_ptr[0] == '\0' || end_ptr[0] == '\n'); g_assert_cmpuint(lineno, ==, data->expected_lineno); g_assert_cmpuint(value, >, data->expected_to_be_more_than); } static void test_single_line_hit_written_to_coverage_data(gpointer fixture_data, gconstpointer user_data) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, fixture->tmp_js_script, fixture->lcov_output); const LineCountIsMoreThanData data = { 2, /* FIXME: line 1 is never hit */ 0 }; assert_coverage_data_matches_value_for_key(coverage_data_contents, "DA:", line_hit_count_is_more_than, &data); g_free(coverage_data_contents); } static void test_hits_on_multiline_if_cond(gpointer fixture_data, gconstpointer user_data) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; const char *script_with_multine_if_cond = "let a = 1;\n" "let b = 1;\n" "if (a &&\n" " b) {\n" "}\n"; replace_file(fixture->tmp_js_script, script_with_multine_if_cond); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, fixture->tmp_js_script, fixture->lcov_output); /* Hits on all lines, including both lines with a condition (3 and 4) */ const LineCountIsMoreThanData data[] = { { 2, 0 }, { 3, 0 }, { 4, 0 } }; assert_coverage_data_matches_value_for_key(coverage_data_contents, "DA:", line_hit_count_is_more_than, data); g_free(coverage_data_contents); } static void test_full_line_tally_written_to_coverage_data(gpointer fixture_data, gconstpointer user_data) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, fixture->tmp_js_script, fixture->lcov_output); /* More than one assert per test is bad, but we are testing interlinked concepts */ assert_coverage_data_contains_value_for_key(coverage_data_contents, "LF:", "1"); assert_coverage_data_contains_value_for_key(coverage_data_contents, "LH:", "1"); g_free(coverage_data_contents); } static void test_no_hits_to_coverage_data_for_unexecuted(gpointer fixture_data, gconstpointer user_data) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; char *coverage_data_contents = write_statistics_and_get_coverage_data(fixture->coverage, fixture->lcov_output); /* No files were executed, so the coverage data is empty. */ g_assert_cmpstr(coverage_data_contents, ==, "\n"); g_free(coverage_data_contents); } static void test_end_of_record_section_written_to_coverage_data(gpointer fixture_data, gconstpointer user_data) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, fixture->tmp_js_script, fixture->lcov_output); g_assert(strstr(coverage_data_contents, "end_of_record") != NULL); g_free(coverage_data_contents); } typedef struct _GjsCoverageMultipleSourcesFixture { GjsCoverageFixture base_fixture; GFile *second_js_source_file; } GjsCoverageMultpleSourcesFixutre; static void gjs_coverage_multiple_source_files_to_single_output_fixture_set_up(gpointer fixture_data, gconstpointer user_data) { gjs_coverage_fixture_set_up(fixture_data, user_data); GjsCoverageMultpleSourcesFixutre *fixture = (GjsCoverageMultpleSourcesFixutre *) fixture_data; fixture->second_js_source_file = g_file_get_child(fixture->base_fixture.tmp_output_dir, "gjs_coverage_second_source_file.js"); /* Because GjsCoverage searches the coverage paths at object-creation time, * we need to destroy the previously constructed one and construct it again */ char *first_js_script_path = g_file_get_path(fixture->base_fixture.tmp_js_script); char *second_js_script_path = g_file_get_path(fixture->second_js_source_file); char *coverage_paths[] = { first_js_script_path, second_js_script_path, NULL }; g_object_unref(fixture->base_fixture.context); g_object_unref(fixture->base_fixture.coverage); char *output_path = g_file_get_path(fixture->base_fixture.tmp_output_dir); char *search_paths[] = { output_path, NULL }; fixture->base_fixture.context = gjs_context_new_with_search_path(search_paths); fixture->base_fixture.coverage = gjs_coverage_new(coverage_paths, fixture->base_fixture.context, fixture->base_fixture.lcov_output_dir); g_free(output_path); g_free(first_js_script_path); g_free(second_js_script_path); char *base_name = g_file_get_basename(fixture->base_fixture.tmp_js_script); char *base_name_without_extension = g_strndup(base_name, strlen(base_name) - 3); char *mock_script = g_strconcat("\nconst FirstScript = imports.", base_name_without_extension, ";\n", "let a = FirstScript.f;\n" "\n", NULL); replace_file(fixture->second_js_source_file, mock_script); g_free(mock_script); g_free(base_name_without_extension); g_free(base_name); } static void gjs_coverage_multiple_source_files_to_single_output_fixture_tear_down(gpointer fixture_data, gconstpointer user_data) { GjsCoverageMultpleSourcesFixutre *fixture = (GjsCoverageMultpleSourcesFixutre *) fixture_data; g_object_unref(fixture->second_js_source_file); gjs_coverage_fixture_tear_down(fixture_data, user_data); } static void test_multiple_source_file_records_written_to_coverage_data(gpointer fixture_data, gconstpointer user_data) { GjsCoverageMultpleSourcesFixutre *fixture = (GjsCoverageMultpleSourcesFixutre *) fixture_data; char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->base_fixture.context, fixture->base_fixture.coverage, fixture->second_js_source_file, fixture->base_fixture.lcov_output); const char *first_sf_record = line_starting_with(coverage_data_contents, "SF:"); g_assert(first_sf_record != NULL); const char *second_sf_record = line_starting_with(first_sf_record + 1, "SF:"); g_assert(second_sf_record != NULL); g_free(coverage_data_contents); } typedef struct _ExpectedSourceFileCoverageData { const char *source_file_path; LineCountIsMoreThanData *more_than; unsigned int n_more_than_matchers; const char expected_lines_hit_character; const char expected_lines_found_character; } ExpectedSourceFileCoverageData; static void assert_coverage_data_for_source_file(ExpectedSourceFileCoverageData *expected, const size_t expected_size, const char *section_start) { gsize i; for (i = 0; i < expected_size; ++i) { if (strncmp(§ion_start[3], expected[i].source_file_path, strlen (expected[i].source_file_path)) == 0) { assert_coverage_data_matches_values_for_key(section_start, "DA:", expected[i].n_more_than_matchers, line_hit_count_is_more_than, expected[i].more_than, sizeof (LineCountIsMoreThanData)); const char *total_hits_record = line_starting_with(section_start, "LH:"); g_assert_cmpint(total_hits_record[3], ==, expected[i].expected_lines_hit_character); const char *total_found_record = line_starting_with(section_start, "LF:"); g_assert_cmpint(total_found_record[3], ==, expected[i].expected_lines_found_character); return; } } g_assert_not_reached(); } static void test_correct_line_coverage_data_written_for_both_source_file_sections(void *fixture_data, const void *user_data) { GjsCoverageMultpleSourcesFixutre *fixture = (GjsCoverageMultpleSourcesFixutre *) fixture_data; char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->base_fixture.context, fixture->base_fixture.coverage, fixture->second_js_source_file, fixture->base_fixture.lcov_output); LineCountIsMoreThanData first_script_matcher = { 2, /* FIXME: line 1 is never hit */ 0 }; LineCountIsMoreThanData second_script_matchers[] = { { 2, /* FIXME: line 1 is never hit */ 0 }, { 3, /* FIXME: line 1 is never hit */ 0 } }; char *first_script_output_path = get_output_path_for_script_on_disk(fixture->base_fixture.tmp_js_script, fixture->base_fixture.lcov_output_dir); char *second_script_output_path = get_output_path_for_script_on_disk(fixture->second_js_source_file, fixture->base_fixture.lcov_output_dir); ExpectedSourceFileCoverageData expected[] = { { first_script_output_path, &first_script_matcher, 1, '1', '1' }, { second_script_output_path, second_script_matchers, 2, '2', '2' } }; const gsize expected_len = G_N_ELEMENTS(expected); const char *first_sf_record = line_starting_with(coverage_data_contents, "SF:"); assert_coverage_data_for_source_file(expected, expected_len, first_sf_record); const char *second_sf_record = line_starting_with(first_sf_record + 3, "SF:"); assert_coverage_data_for_source_file(expected, expected_len, second_sf_record); g_free(first_script_output_path); g_free(second_script_output_path); g_free(coverage_data_contents); } typedef struct _FixturedTest { gsize fixture_size; GTestFixtureFunc set_up; GTestFixtureFunc tear_down; } FixturedTest; static void add_test_for_fixture(const char *name, FixturedTest *fixture, GTestFixtureFunc test_func, gconstpointer user_data) { g_test_add_vtable(name, fixture->fixture_size, user_data, fixture->set_up, test_func, fixture->tear_down); } void gjs_test_add_tests_for_coverage() { FixturedTest coverage_fixture = { sizeof(GjsCoverageFixture), gjs_coverage_fixture_set_up, gjs_coverage_fixture_tear_down }; add_test_for_fixture("/gjs/coverage/file_duplicated_into_output_path", &coverage_fixture, test_covered_file_is_duplicated_into_output_if_path, NULL); add_test_for_fixture("/gjs/coverage/file_duplicated_full_resource_path", &coverage_fixture, test_covered_file_is_duplicated_into_output_if_resource, NULL); add_test_for_fixture("/gjs/coverage/contents_preserved_accumulate_mode", &coverage_fixture, test_previous_contents_preserved, NULL); add_test_for_fixture("/gjs/coverage/new_contents_appended_accumulate_mode", &coverage_fixture, test_new_contents_written, NULL); add_test_for_fixture("/gjs/coverage/expected_source_file_name_written_to_coverage_data", &coverage_fixture, test_expected_source_file_name_written_to_coverage_data, NULL); add_test_for_fixture("/gjs/coverage/entry_not_written_for_nonexistent_file", &coverage_fixture, test_expected_entry_not_written_for_nonexistent_file, NULL); add_test_for_fixture("/gjs/coverage/single_branch_coverage_written_to_coverage_data", &coverage_fixture, test_single_branch_coverage_written_to_coverage_data, NULL); add_test_for_fixture("/gjs/coverage/multiple_branch_coverage_written_to_coverage_data", &coverage_fixture, test_multiple_branch_coverage_written_to_coverage_data, NULL); add_test_for_fixture("/gjs/coverage/branches_for_multiple_case_statements_fallthrough", &coverage_fixture, test_branches_for_multiple_case_statements_fallthrough, NULL); add_test_for_fixture("/gjs/coverage/not_hit_branch_point_written_to_coverage_data", &coverage_fixture, test_branch_not_hit_written_to_coverage_data, NULL); add_test_for_fixture("/gjs/coverage/function_names_written_to_coverage_data", &coverage_fixture, test_function_names_written_to_coverage_data, NULL); add_test_for_fixture("/gjs/coverage/function_lines_written_to_coverage_data", &coverage_fixture, test_function_lines_written_to_coverage_data, NULL); add_test_for_fixture("/gjs/coverage/function_hit_counts_written_to_coverage_data", &coverage_fixture, test_function_hit_counts_written_to_coverage_data, NULL); add_test_for_fixture("/gjs/coverage/big_function_hit_counts_written_to_coverage_data", &coverage_fixture, test_function_hit_counts_for_big_functions_written_to_coverage_data, NULL); add_test_for_fixture("/gjs/coverage/little_function_hit_counts_written_to_coverage_data", &coverage_fixture, test_function_hit_counts_for_little_functions_written_to_coverage_data, NULL); add_test_for_fixture("/gjs/coverage/total_function_coverage_written_to_coverage_data", &coverage_fixture, test_total_function_coverage_written_to_coverage_data, NULL); add_test_for_fixture("/gjs/coverage/single_line_hit_written_to_coverage_data", &coverage_fixture, test_single_line_hit_written_to_coverage_data, NULL); add_test_for_fixture("/gjs/coverage/hits_on_multiline_if_cond", &coverage_fixture, test_hits_on_multiline_if_cond, NULL); add_test_for_fixture("/gjs/coverage/full_line_tally_written_to_coverage_data", &coverage_fixture, test_full_line_tally_written_to_coverage_data, NULL); add_test_for_fixture("/gjs/coverage/no_hits_for_unexecuted_file", &coverage_fixture, test_no_hits_to_coverage_data_for_unexecuted, NULL); add_test_for_fixture("/gjs/coverage/end_of_record_section_written_to_coverage_data", &coverage_fixture, test_end_of_record_section_written_to_coverage_data, NULL); FixturedTest coverage_for_multiple_files_to_single_output_fixture = { sizeof(GjsCoverageMultpleSourcesFixutre), gjs_coverage_multiple_source_files_to_single_output_fixture_set_up, gjs_coverage_multiple_source_files_to_single_output_fixture_tear_down }; add_test_for_fixture("/gjs/coverage/multiple_source_file_records_written_to_coverage_data", &coverage_for_multiple_files_to_single_output_fixture, test_multiple_source_file_records_written_to_coverage_data, NULL); add_test_for_fixture("/gjs/coverage/correct_line_coverage_data_written_for_both_sections", &coverage_for_multiple_files_to_single_output_fixture, test_correct_line_coverage_data_written_for_both_source_file_sections, NULL); }cjs-4.4.0/test/gjs-test-coverage/000077500000000000000000000000001356375132300166165ustar00rootroot00000000000000cjs-4.4.0/test/gjs-test-coverage/loadedJSFromResource.js000066400000000000000000000000341356375132300231720ustar00rootroot00000000000000function mock_function() {} cjs-4.4.0/test/gjs-test-rooting.cpp000066400000000000000000000204741356375132300172170ustar00rootroot00000000000000#include "cjs/jsapi-util.h" #include "cjs/jsapi-util-root.h" #include "gjs-test-utils.h" static GMutex gc_lock; static GCond gc_finished; static volatile int gc_counter; #define PARENT(fx) ((GjsUnitTestFixture *)fx) typedef struct _GjsRootingFixture GjsRootingFixture; struct _GjsRootingFixture { GjsUnitTestFixture parent; bool finalized; bool notify_called; GjsMaybeOwned *obj; /* only used in callback test cases */ }; static void test_obj_finalize(JSFreeOp *fop, JSObject *obj) { bool *finalized_p = static_cast(JS_GetPrivate(obj)); g_assert_false(*finalized_p); *finalized_p = true; } static const JSClassOps test_obj_class_ops = { NULL, /* addProperty */ NULL, /* deleteProperty */ NULL, /* getProperty */ NULL, /* setProperty */ NULL, /* enumerate */ NULL, /* resolve */ nullptr, /* mayResolve */ test_obj_finalize }; static JSClass test_obj_class = { "TestObj", JSCLASS_HAS_PRIVATE | JSCLASS_FOREGROUND_FINALIZE, &test_obj_class_ops }; static JSObject * test_obj_new(GjsRootingFixture *fx) { JSObject *retval = JS_NewObject(PARENT(fx)->cx, &test_obj_class); JS_SetPrivate(retval, &fx->finalized); return retval; } static void on_gc(JSContext *cx, JSGCStatus status, void *data) { if (status != JSGC_END) return; g_mutex_lock(&gc_lock); g_atomic_int_inc(&gc_counter); g_cond_broadcast(&gc_finished); g_mutex_unlock(&gc_lock); } static void setup(GjsRootingFixture *fx, gconstpointer unused) { gjs_unit_test_fixture_setup(PARENT(fx), unused); JS_SetGCCallback(PARENT(fx)->cx, on_gc, fx); } static void teardown(GjsRootingFixture *fx, gconstpointer unused) { gjs_unit_test_fixture_teardown(PARENT(fx), unused); } static void wait_for_gc(GjsRootingFixture *fx) { int count = g_atomic_int_get(&gc_counter); JS_GC(PARENT(fx)->cx); g_mutex_lock(&gc_lock); while (count == g_atomic_int_get(&gc_counter)) { g_cond_wait(&gc_finished, &gc_lock); } g_mutex_unlock(&gc_lock); } static void test_maybe_owned_rooted_flag_set_when_rooted(GjsRootingFixture *fx, gconstpointer unused) { auto obj = new GjsMaybeOwned(); obj->root(PARENT(fx)->cx, JS::TrueValue()); g_assert_true(obj->rooted()); delete obj; } static void test_maybe_owned_rooted_flag_not_set_when_not_rooted(GjsRootingFixture *fx, gconstpointer unused) { auto obj = new GjsMaybeOwned(); *obj = JS::TrueValue(); g_assert_false(obj->rooted()); delete obj; } static void test_maybe_owned_rooted_keeps_alive_across_gc(GjsRootingFixture *fx, gconstpointer unused) { auto obj = new GjsMaybeOwned(); obj->root(PARENT(fx)->cx, test_obj_new(fx)); wait_for_gc(fx); g_assert_false(fx->finalized); delete obj; wait_for_gc(fx); g_assert_true(fx->finalized); } static void test_maybe_owned_rooted_is_collected_after_reset(GjsRootingFixture *fx, gconstpointer unused) { auto obj = new GjsMaybeOwned(); obj->root(PARENT(fx)->cx, test_obj_new(fx)); obj->reset(); wait_for_gc(fx); g_assert_true(fx->finalized); delete obj; } static void test_maybe_owned_weak_pointer_is_collected_by_gc(GjsRootingFixture *fx, gconstpointer unused) { auto obj = new GjsMaybeOwned(); *obj = test_obj_new(fx); wait_for_gc(fx); g_assert_true(fx->finalized); delete obj; } static void test_maybe_owned_heap_rooted_keeps_alive_across_gc(GjsRootingFixture *fx, gconstpointer unused) { auto obj = new GjsMaybeOwned(); obj->root(PARENT(fx)->cx, test_obj_new(fx)); wait_for_gc(fx); g_assert_false(fx->finalized); delete obj; wait_for_gc(fx); g_assert_true(fx->finalized); } static void test_maybe_owned_switching_mode_keeps_same_value(GjsRootingFixture *fx, gconstpointer unused) { JSObject *test_obj = test_obj_new(fx); auto obj = new GjsMaybeOwned(); *obj = test_obj; g_assert_true(*obj == test_obj); obj->switch_to_rooted(PARENT(fx)->cx); g_assert_true(obj->rooted()); g_assert_true(*obj == test_obj); obj->switch_to_unrooted(); g_assert_false(obj->rooted()); g_assert_true(*obj == test_obj); delete obj; } static void test_maybe_owned_switch_to_rooted_prevents_collection(GjsRootingFixture *fx, gconstpointer unused) { auto obj = new GjsMaybeOwned(); *obj = test_obj_new(fx); obj->switch_to_rooted(PARENT(fx)->cx); wait_for_gc(fx); g_assert_false(fx->finalized); delete obj; } static void test_maybe_owned_switch_to_unrooted_allows_collection(GjsRootingFixture *fx, gconstpointer unused) { auto obj = new GjsMaybeOwned(); obj->root(PARENT(fx)->cx, test_obj_new(fx)); obj->switch_to_unrooted(); wait_for_gc(fx); g_assert_true(fx->finalized); delete obj; } static void context_destroyed(JS::HandleObject obj, void *data) { auto fx = static_cast(data); g_assert_false(fx->notify_called); g_assert_false(fx->finalized); fx->notify_called = true; fx->obj->reset(); } static void test_maybe_owned_notify_callback_called_on_context_destroy(GjsRootingFixture *fx, gconstpointer unused) { fx->obj = new GjsMaybeOwned(); fx->obj->root(PARENT(fx)->cx, test_obj_new(fx), context_destroyed, fx); gjs_unit_test_destroy_context(PARENT(fx)); g_assert_true(fx->notify_called); delete fx->obj; } static void test_maybe_owned_object_destroyed_after_notify(GjsRootingFixture *fx, gconstpointer unused) { fx->obj = new GjsMaybeOwned(); fx->obj->root(PARENT(fx)->cx, test_obj_new(fx), context_destroyed, fx); gjs_unit_test_destroy_context(PARENT(fx)); g_assert_true(fx->finalized); delete fx->obj; } void gjs_test_add_tests_for_rooting(void) { #define ADD_ROOTING_TEST(path, f) \ g_test_add("/rooting/" path, GjsRootingFixture, NULL, setup, f, teardown); ADD_ROOTING_TEST("maybe-owned/rooted-flag-set-when-rooted", test_maybe_owned_rooted_flag_set_when_rooted); ADD_ROOTING_TEST("maybe-owned/rooted-flag-not-set-when-not-rooted", test_maybe_owned_rooted_flag_not_set_when_not_rooted); ADD_ROOTING_TEST("maybe-owned/rooted-keeps-alive-across-gc", test_maybe_owned_rooted_keeps_alive_across_gc); ADD_ROOTING_TEST("maybe-owned/rooted-is-collected-after-reset", test_maybe_owned_rooted_is_collected_after_reset); ADD_ROOTING_TEST("maybe-owned/weak-pointer-is-collected-by-gc", test_maybe_owned_weak_pointer_is_collected_by_gc); ADD_ROOTING_TEST("maybe-owned/heap-rooted-keeps-alive-across-gc", test_maybe_owned_heap_rooted_keeps_alive_across_gc); ADD_ROOTING_TEST("maybe-owned/switching-mode-keeps-same-value", test_maybe_owned_switching_mode_keeps_same_value); ADD_ROOTING_TEST("maybe-owned/switch-to-rooted-prevents-collection", test_maybe_owned_switch_to_rooted_prevents_collection); ADD_ROOTING_TEST("maybe-owned/switch-to-unrooted-allows-collection", test_maybe_owned_switch_to_unrooted_allows_collection); #undef ADD_ROOTING_TEST #define ADD_CONTEXT_DESTROY_TEST(path, f) \ g_test_add("/rooting/" path, GjsRootingFixture, nullptr, setup, f, nullptr); ADD_CONTEXT_DESTROY_TEST("maybe-owned/notify-callback-called-on-context-destroy", test_maybe_owned_notify_callback_called_on_context_destroy); ADD_CONTEXT_DESTROY_TEST("maybe-owned/object-destroyed-after-notify", test_maybe_owned_object_destroyed_after_notify); #undef ADD_CONTEXT_DESTROY_TEST } cjs-4.4.0/test/gjs-test-utils.cpp000066400000000000000000000120641356375132300166720ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * Copyright (c) 2016 Endless Mobile, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include #include #include "cjs/context.h" #include "cjs/jsapi-util.h" #include "cjs/jsapi-wrapper.h" #include "gjs-test-utils.h" void gjs_unit_test_fixture_setup(GjsUnitTestFixture *fx, gconstpointer unused) { fx->gjs_context = gjs_context_new(); fx->cx = (JSContext *) gjs_context_get_native_context(fx->gjs_context); JS_BeginRequest(fx->cx); JS::RootedObject global(fx->cx, gjs_get_import_global(fx->cx)); fx->compartment = JS_EnterCompartment(fx->cx, global); } void gjs_unit_test_destroy_context(GjsUnitTestFixture *fx) { GjsAutoChar message = gjs_unit_test_exception_message(fx); if (message) g_printerr("**\n%s\n", message.get()); JS_LeaveCompartment(fx->cx, fx->compartment); JS_EndRequest(fx->cx); g_object_unref(fx->gjs_context); } void gjs_unit_test_fixture_teardown(GjsUnitTestFixture *fx, gconstpointer unused) { gjs_unit_test_destroy_context(fx); } char * gjs_unit_test_exception_message(GjsUnitTestFixture *fx) { if (!JS_IsExceptionPending(fx->cx)) return nullptr; JS::RootedValue v_exc(fx->cx); g_assert_true(JS_GetPendingException(fx->cx, &v_exc)); g_assert_true(v_exc.isObject()); JS::RootedObject exc(fx->cx, &v_exc.toObject()); JSErrorReport *report = JS_ErrorFromException(fx->cx, exc); g_assert_nonnull(report); char *retval = g_strdup(report->message().c_str()); g_assert_nonnull(retval); JS_ClearPendingException(fx->cx); return retval; } /* Fork a process that waits the given time then * sends us ABRT */ void gjs_crash_after_timeout(int seconds) { pid_t parent_pid; int pipe_fds[2]; fd_set read_fds; struct timeval term_time; struct timeval remaining; struct timeval now; int old_flags; /* We use a pipe to know in the child when the parent exited */ if (pipe(pipe_fds) != 0) { fprintf(stderr, "Failed to create pipe to crash-in-timeout process: %s\n", strerror(errno)); return; } /* We want pipe_fds[1] to only be open in the parent process; when it closes * the child will see an EOF. Setting FD_CLOEXEC is protection in case the * parent spawns off some process without properly closing fds. */ old_flags = fcntl(pipe_fds[1], F_GETFD); if (old_flags == -1 || fcntl(pipe_fds[1], F_SETFD, old_flags | FD_CLOEXEC) != 0) { fprintf(stderr, "Couldn't make crash-timeout pipe FD_CLOEXEC: %s\n", strerror(errno)); return; } parent_pid = getpid(); switch (fork()) { case -1: fprintf(stderr, "Failed to fork crash-in-timeout process: %s\n", strerror(errno)); return; case 0: /* child */ break; default: /* parent */ close(pipe_fds[0]); return; } close (pipe_fds[1]); gettimeofday (&now, NULL); term_time = now; term_time.tv_sec += seconds; FD_ZERO(&read_fds); FD_SET(pipe_fds[0], &read_fds); while (true) { remaining.tv_sec = term_time.tv_sec - now.tv_sec; remaining.tv_usec = term_time.tv_usec - now.tv_usec; if (remaining.tv_usec < 0) { remaining.tv_usec += 1000; remaining.tv_sec -= 1; } if (remaining.tv_sec < 0) /* expired */ break; select(pipe_fds[0] + 1, &read_fds, NULL, NULL, &remaining); if (FD_ISSET(pipe_fds[0], &read_fds)) { /* The parent exited */ _exit(0); } gettimeofday(&now, NULL); } if (kill(parent_pid, 0) == 0) { fprintf(stderr, "Timeout of %d seconds expired; aborting process %d\n", seconds, (int) parent_pid); kill(parent_pid, SIGABRT); } _exit(1); } cjs-4.4.0/test/gjs-test-utils.h000066400000000000000000000040371356375132300163400ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright © 2013 Endless Mobile, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. * * Authored By: Sam Spilsbury */ #ifndef GJS_TEST_UTILS_H #define GJS_TEST_UTILS_H #include "cjs/context.h" #include "cjs/jsapi-wrapper.h" typedef struct _GjsUnitTestFixture GjsUnitTestFixture; struct _GjsUnitTestFixture { GjsContext *gjs_context; JSContext *cx; JSCompartment *compartment; }; void gjs_unit_test_fixture_setup(GjsUnitTestFixture *fx, gconstpointer unused); void gjs_unit_test_destroy_context(GjsUnitTestFixture *fx); void gjs_unit_test_fixture_teardown(GjsUnitTestFixture *fx, gconstpointer unused); char *gjs_unit_test_exception_message(GjsUnitTestFixture *fx); void gjs_crash_after_timeout(int seconds); void gjs_test_add_tests_for_coverage (); void gjs_test_add_tests_for_parse_call_args(void); void gjs_test_add_tests_for_rooting(void); #endif cjs-4.4.0/test/gjs-tests.cpp000066400000000000000000000356061356375132300157260ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include #include #include "cjs/jsapi-util.h" #include "cjs/jsapi-wrapper.h" #include "gjs-test-utils.h" #include "util/error.h" #define VALID_UTF8_STRING "\303\211\303\226 foobar \343\203\237" static void gjstest_test_func_gjs_context_construct_destroy(void) { GjsContext *context; /* Construct twice just to possibly a case where global state from * the first leaks. */ context = gjs_context_new (); g_object_unref (context); context = gjs_context_new (); g_object_unref (context); } static void gjstest_test_func_gjs_context_construct_eval(void) { GjsContext *context; int estatus; GError *error = NULL; context = gjs_context_new (); if (!gjs_context_eval (context, "1+1", -1, "", &estatus, &error)) g_error ("%s", error->message); g_object_unref (context); } static void gjstest_test_func_gjs_context_exit(void) { GjsContext *context = gjs_context_new(); GError *error = NULL; int status; bool ok = gjs_context_eval(context, "imports.system.exit(0);", -1, "", &status, &error); g_assert_false(ok); g_assert_error(error, GJS_ERROR, GJS_ERROR_SYSTEM_EXIT); g_assert_cmpuint(status, ==, 0); g_clear_error(&error); ok = gjs_context_eval(context, "imports.system.exit(42);", -1, "", &status, &error); g_assert_false(ok); g_assert_error(error, GJS_ERROR, GJS_ERROR_SYSTEM_EXIT); g_assert_cmpuint(status, ==, 42); g_clear_error(&error); g_object_unref(context); } #define JS_CLASS "\ const GObject = imports.gi.GObject; \ const FooBar = GObject.registerClass(class FooBar extends GObject.Object {}); \ " static void gjstest_test_func_gjs_gobject_js_defined_type(void) { GjsContext *context = gjs_context_new(); GError *error = NULL; int status; bool ok = gjs_context_eval(context, JS_CLASS, -1, "", &status, &error); g_assert_no_error(error); g_assert_true(ok); GType foo_type = g_type_from_name("Gjs_FooBar"); g_assert_cmpuint(foo_type, !=, G_TYPE_INVALID); gpointer foo = g_object_new(foo_type, NULL); g_assert(G_IS_OBJECT(foo)); g_object_unref(foo); g_object_unref(context); } static void gjstest_test_func_gjs_jsapi_util_string_js_string_utf8(GjsUnitTestFixture *fx, gconstpointer unused) { JS::RootedValue js_string(fx->cx); g_assert_true(gjs_string_from_utf8(fx->cx, VALID_UTF8_STRING, &js_string)); g_assert(js_string.isString()); GjsAutoJSChar utf8_result; g_assert(gjs_string_to_utf8(fx->cx, js_string, &utf8_result)); g_assert_cmpstr(VALID_UTF8_STRING, ==, utf8_result); } static void gjstest_test_func_gjs_jsapi_util_error_throw(GjsUnitTestFixture *fx, gconstpointer unused) { JS::RootedValue exc(fx->cx), value(fx->cx); /* Test that we can throw */ gjs_throw(fx->cx, "This is an exception %d", 42); g_assert(JS_IsExceptionPending(fx->cx)); JS_GetPendingException(fx->cx, &exc); g_assert(!exc.isUndefined()); JS::RootedObject exc_obj(fx->cx, &exc.toObject()); JS_GetProperty(fx->cx, exc_obj, "message", &value); g_assert(value.isString()); GjsAutoJSChar s; gjs_string_to_utf8(fx->cx, value, &s); g_assert_nonnull(s); g_assert_cmpstr(s, ==, "This is an exception 42"); /* keep this around before we clear it */ JS::RootedValue previous(fx->cx, exc); JS_ClearPendingException(fx->cx); g_assert(!JS_IsExceptionPending(fx->cx)); /* Check that we don't overwrite a pending exception */ JS_SetPendingException(fx->cx, previous); g_assert(JS_IsExceptionPending(fx->cx)); gjs_throw(fx->cx, "Second different exception %s", "foo"); g_assert(JS_IsExceptionPending(fx->cx)); exc = JS::UndefinedValue(); JS_GetPendingException(fx->cx, &exc); g_assert(!exc.isUndefined()); g_assert(&exc.toObject() == &previous.toObject()); } static void test_jsapi_util_string_utf8_nchars_to_js(GjsUnitTestFixture *fx, const void *unused) { JS::RootedValue v_out(fx->cx); bool ok = gjs_string_from_utf8_n(fx->cx, VALID_UTF8_STRING, strlen(VALID_UTF8_STRING), &v_out); g_assert_true(ok); g_assert_true(v_out.isString()); } static void test_jsapi_util_string_char16_data(GjsUnitTestFixture *fx, gconstpointer unused) { char16_t *chars; size_t len; JS::ConstUTF8CharsZ jschars(VALID_UTF8_STRING, strlen(VALID_UTF8_STRING)); JS::RootedString str(fx->cx, JS_NewStringCopyUTF8Z(fx->cx, jschars)); g_assert_true(gjs_string_get_char16_data(fx->cx, str, &chars, &len)); std::u16string result(chars, len); g_assert_true(result == u"\xc9\xd6 foobar \u30df"); g_free(chars); /* Try with a string that is likely to be stored as Latin-1 */ str = JS_NewStringCopyZ(fx->cx, "abcd"); g_assert_true(gjs_string_get_char16_data(fx->cx, str, &chars, &len)); result.assign(chars, len); g_assert_true(result == u"abcd"); g_free(chars); } static void test_jsapi_util_string_to_ucs4(GjsUnitTestFixture *fx, gconstpointer unused) { gunichar *chars; size_t len; JS::ConstUTF8CharsZ jschars(VALID_UTF8_STRING, strlen(VALID_UTF8_STRING)); JS::RootedString str(fx->cx, JS_NewStringCopyUTF8Z(fx->cx, jschars)); g_assert_true(gjs_string_to_ucs4(fx->cx, str, &chars, &len)); std::u32string result(chars, chars + len); g_assert_true(result == U"\xc9\xd6 foobar \u30df"); g_free(chars); /* Try with a string that is likely to be stored as Latin-1 */ str = JS_NewStringCopyZ(fx->cx, "abcd"); g_assert_true(gjs_string_to_ucs4(fx->cx, str, &chars, &len)); result.assign(chars, chars + len); g_assert_true(result == U"abcd"); g_free(chars); } static void test_jsapi_util_debug_string_valid_utf8(GjsUnitTestFixture *fx, gconstpointer unused) { JS::RootedValue v_string(fx->cx); g_assert_true(gjs_string_from_utf8(fx->cx, VALID_UTF8_STRING, &v_string)); char *debug_output = gjs_value_debug_string(fx->cx, v_string); g_assert_nonnull(debug_output); g_assert_cmpstr("\"" VALID_UTF8_STRING "\"", ==, debug_output); g_free(debug_output); } static void test_jsapi_util_debug_string_invalid_utf8(GjsUnitTestFixture *fx, gconstpointer unused) { g_test_skip("SpiderMonkey doesn't validate UTF-8 after encoding it"); JS::RootedValue v_string(fx->cx); const char16_t invalid_unicode[] = { 0xffff, 0xffff }; v_string.setString(JS_NewUCStringCopyN(fx->cx, invalid_unicode, 2)); char *debug_output = gjs_value_debug_string(fx->cx, v_string); g_assert_nonnull(debug_output); /* g_assert_cmpstr("\"\\xff\\xff\\xff\\xff\"", ==, debug_output); */ g_free(debug_output); } static void test_jsapi_util_debug_string_object_with_complicated_to_string(GjsUnitTestFixture *fx, gconstpointer unused) { const char16_t desserts[] = { 0xd83c, 0xdf6a, /* cookie */ 0xd83c, 0xdf69, /* doughnut */ }; JS::AutoValueArray<2> contents(fx->cx); contents[0].setString(JS_NewUCStringCopyN(fx->cx, desserts, 2)); contents[1].setString(JS_NewUCStringCopyN(fx->cx, desserts + 2, 2)); JS::RootedObject array(fx->cx, JS_NewArrayObject(fx->cx, contents)); JS::RootedValue v_array(fx->cx, JS::ObjectValue(*array)); char *debug_output = gjs_value_debug_string(fx->cx, v_array); g_assert_nonnull(debug_output); g_assert_cmpstr(u8"🍪,🍩", ==, debug_output); g_free(debug_output); } static void gjstest_test_func_util_glib_strv_concat_null(void) { char **ret; ret = gjs_g_strv_concat(NULL, 0); g_assert(ret != NULL); g_assert(ret[0] == NULL); g_strfreev(ret); } static void gjstest_test_func_util_glib_strv_concat_pointers(void) { char *strv0[2] = {(char*)"foo", NULL}; char *strv1[1] = {NULL}; char **strv2 = NULL; char *strv3[2] = {(char*)"bar", NULL}; char **stuff[4]; char **ret; stuff[0] = strv0; stuff[1] = strv1; stuff[2] = strv2; stuff[3] = strv3; ret = gjs_g_strv_concat(stuff, 4); g_assert(ret != NULL); g_assert_cmpstr(ret[0], ==, strv0[0]); /* same string */ g_assert(ret[0] != strv0[0]); /* different pointer */ g_assert_cmpstr(ret[1], ==, strv3[0]); g_assert(ret[1] != strv3[0]); g_assert(ret[2] == NULL); g_strfreev(ret); } static void gjstest_test_strip_shebang_no_advance_for_no_shebang(void) { const char *script = "foo\nbar"; size_t script_len_original = strlen(script); size_t script_len = script_len_original; int line_number = 1; const char *stripped = gjs_strip_unix_shebang(script, &script_len, &line_number); g_assert_cmpstr(script, ==, stripped); g_assert(script_len == script_len_original); g_assert(line_number == 1); } static void gjstest_test_strip_shebang_advance_for_shebang(void) { const char *script = "#!foo\nbar"; size_t script_len_original = strlen(script); size_t script_len = script_len_original; int line_number = 1; const char *stripped = gjs_strip_unix_shebang(script, &script_len, &line_number); g_assert_cmpstr(stripped, ==, "bar"); g_assert(script_len == 3); g_assert(line_number == 2); } static void gjstest_test_strip_shebang_return_null_for_just_shebang(void) { const char *script = "#!foo"; size_t script_len_original = strlen(script); size_t script_len = script_len_original; int line_number = 1; const char *stripped = gjs_strip_unix_shebang(script, &script_len, &line_number); g_assert(stripped == NULL); g_assert(script_len == 0); g_assert(line_number == -1); } static void gjstest_test_profiler_start_stop(void) { GjsAutoUnref context = static_cast(g_object_new(GJS_TYPE_CONTEXT, "profiler-enabled", TRUE, nullptr)); GjsProfiler *profiler = gjs_context_get_profiler(context); gjs_profiler_start(profiler); for (size_t ix = 0; ix < 100; ix++) { GError *error = nullptr; int estatus; #define TESTJS "[1,5,7,1,2,3,67,8].sort()" if (!gjs_context_eval(context, TESTJS, -1, "", &estatus, &error)) g_printerr("ERROR: %s", error->message); #undef TESTJS } gjs_profiler_stop(profiler); } int main(int argc, char **argv) { /* give the unit tests 7 minutes to complete, unless an environment variable * is set; use this when running under GDB, for example */ if (!g_getenv("GJS_TEST_SKIP_TIMEOUT")) gjs_crash_after_timeout(60 * 7); /* Avoid interference in the tests from stray environment variable */ g_unsetenv("GJS_ENABLE_PROFILER"); g_test_init(&argc, &argv, NULL); g_test_add_func("/gjs/context/construct/destroy", gjstest_test_func_gjs_context_construct_destroy); g_test_add_func("/gjs/context/construct/eval", gjstest_test_func_gjs_context_construct_eval); g_test_add_func("/gjs/context/exit", gjstest_test_func_gjs_context_exit); g_test_add_func("/gjs/gobject/js_defined_type", gjstest_test_func_gjs_gobject_js_defined_type); g_test_add_func("/gjs/jsutil/strip_shebang/no_shebang", gjstest_test_strip_shebang_no_advance_for_no_shebang); g_test_add_func("/gjs/jsutil/strip_shebang/have_shebang", gjstest_test_strip_shebang_advance_for_shebang); g_test_add_func("/gjs/jsutil/strip_shebang/only_shebang", gjstest_test_strip_shebang_return_null_for_just_shebang); g_test_add_func("/gjs/profiler/start_stop", gjstest_test_profiler_start_stop); g_test_add_func("/util/glib/strv/concat/null", gjstest_test_func_util_glib_strv_concat_null); g_test_add_func("/util/glib/strv/concat/pointers", gjstest_test_func_util_glib_strv_concat_pointers); #define ADD_JSAPI_UTIL_TEST(path, func) \ g_test_add("/gjs/jsapi/util/" path, GjsUnitTestFixture, NULL, \ gjs_unit_test_fixture_setup, func, \ gjs_unit_test_fixture_teardown) ADD_JSAPI_UTIL_TEST("error/throw", gjstest_test_func_gjs_jsapi_util_error_throw); ADD_JSAPI_UTIL_TEST("string/js/string/utf8", gjstest_test_func_gjs_jsapi_util_string_js_string_utf8); ADD_JSAPI_UTIL_TEST("string/utf8-nchars-to-js", test_jsapi_util_string_utf8_nchars_to_js); ADD_JSAPI_UTIL_TEST("string/char16_data", test_jsapi_util_string_char16_data); ADD_JSAPI_UTIL_TEST("string/to_ucs4", test_jsapi_util_string_to_ucs4); ADD_JSAPI_UTIL_TEST("debug_string/valid-utf8", test_jsapi_util_debug_string_valid_utf8); ADD_JSAPI_UTIL_TEST("debug_string/invalid-utf8", test_jsapi_util_debug_string_invalid_utf8); ADD_JSAPI_UTIL_TEST("debug_string/object-with-complicated-to-string", test_jsapi_util_debug_string_object_with_complicated_to_string); #undef ADD_JSAPI_UTIL_TEST gjs_test_add_tests_for_coverage (); gjs_test_add_tests_for_parse_call_args(); gjs_test_add_tests_for_rooting(); g_test_run(); return 0; } cjs-4.4.0/test/mock-js-resources.gresource.xml000066400000000000000000000002741356375132300213620ustar00rootroot00000000000000 test/gjs-test-coverage/loadedJSFromResource.js cjs-4.4.0/test/run-test000077500000000000000000000002751356375132300150000ustar00rootroot00000000000000#!/bin/sh -e # Run a GTester binary with TAP output if test -z "$1"; then echo "Need a test-binary filename!" exit 1 fi $LOG_COMPILER $LOG_FLAGS "$1" --tap --keep-going --verbose cjs-4.4.0/test/test-bus.conf000066400000000000000000000036711356375132300157110ustar00rootroot00000000000000 session unix:tmpdir=/tmp 1000000000 1000000000 1000000000 120000 240000 100000 10000 100000 10000 50000 50000 50000 300000 cjs-4.4.0/tools/000077500000000000000000000000001356375132300134465ustar00rootroot00000000000000cjs-4.4.0/tools/heapdot.py000066400000000000000000000136001356375132300154440ustar00rootroot00000000000000#!/usr/bin/env python3 # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. # # heapdot.py - DOT Graph output import re func_regex = re.compile('Function(?: ([^/]+)(?:/([<|\w]+))?)?') gobj_regex = re.compile('([^ ]+) (\(nil\)|0x[a-fA-F0-9]+$)') ############################################################################### # DOT Graph Output ############################################################################### dot_graph_paths = [] def add_dot_graph_path(path): dot_graph_paths.append(path) def output_dot_file(args, graph, targs, fname): # build the set of nodes nodes = set([]) for p in dot_graph_paths: for x in p: nodes.add(x) # build the edge map edges = {} for p in dot_graph_paths: prevNode = None for x in p: if prevNode: edges.setdefault(prevNode, set([])).add(x) prevNode = x # Write out the DOT graph outf = open(fname, 'w') outf.write('digraph {\n') # Nodes for addr in nodes: label = graph.node_labels.get(addr, '') color = 'black' style = 'solid' shape = 'rect' native = '' if label.endswith(''): label = label[:-13] # Lookup the edge label for this node elabel = '' for origin in graph.edge_labels.values(): if addr in origin: elabels = origin[addr] elabel = elabels[0] break # GObject or something else with a native address gm = gobj_regex.match(label) if gm: label = gm.group(1) color = 'orange' style = 'bold' if not args.no_addr: native = gm.group(2) # Some kind of GObject if label.startswith('GObject_'): shape = 'circle' if elabel in ['prototype', 'group_proto']: style += ',dashed' # Another object native to Gjs elif label.startswith('Gjs') or label.startswith('GIR'): shape = 'octagon' elif label.startswith('Function'): fm = func_regex.match(label) if fm.group(2) == '<': label = 'Function via {}()'.format(fm.group(1)) elif fm.group(2): label = 'Function {} in {}'.format(fm.group(2), fm.group(1)) else: if len(label) > 10: label = label[9:] label += '()' color = 'green' style = 'bold,rounded' # A function context elif label == 'Call' or label == 'LexicalEnvironment': color = 'green' style = 'bold,dashed' # A file/script reference elif label.startswith('script'): label = label[7:].split('/')[-1] shape = 'note' color = 'blue' # A WeakMap elif label.startswith('WeakMap'): label = 'WeakMap' style = 'dashed' # Mostly uninteresting objects elif label in ['base_shape', 'object_group', 'type_object']: style = 'dotted' if label == 'base_shape': label = 'shape' elif label == 'type_object': label = 'type' # Only mark the target if it's a single match if addr == targs[0] and len(targs) == 1: color = 'red' style = 'bold' if args.no_addr: outf.write(' node [label="{0}", color={1}, shape={2}, style="{3}"] q{4};\n'.format(label, color, shape, style, addr)) else: if native: outf.write(' node [label="{0}\\njsobj@{4}\\nnative@{5}", color={1}, shape={2}, style="{3}"] q{4};\n'.format(label, color, shape, style, addr, native)) else: outf.write(' node [label="{0}\\njsobj@{4}", color={1}, shape={2}, style="{3}"] q{4};\n'.format(label, color, shape, style, addr)) # Edges (relationships) for origin, destinations in edges.items(): for destination in destinations: labels = graph.edge_labels.get(origin, {}).get(destination, []) ll = [] for l in labels: if len(l) == 2: l = l[0] if l.startswith('**UNKNOWN SLOT '): continue ll.append(l) label = '' style = 'solid' color = 'black' if len(ll) == 1: label = ll[0] # Object children if label.startswith('objects['): label = label[7:] # Array elements elif label.startswith('objectElements['): label = label[14:] # prototype/constructor function elif label in ['prototype', 'group_proto']: color = 'orange' style = 'bold,dashed' # fun_environment elif label == 'fun_environment': label = '' color = 'green' style = 'bold,dashed' elif label == 'script': label = '' color = 'blue' # Signals # TODO: better heap label via gi/closure.cpp & gi/object.cpp elif label == 'signal connection': color = 'red' style = 'bold,dashed' if len(label) > 18: label = label[:8] + '...' + label[-8:] else: label = ',\\n'.join(ll) outf.write(' q{0} -> q{1} [label="{2}", color={3}, style="{4}"];\n'.format(origin, destination, label, color, style)) outf.write('}\n') outf.close() cjs-4.4.0/tools/heapgraph.md000066400000000000000000000143351356375132300157350ustar00rootroot00000000000000# gjs-heapgraph A heap analyzer for Gjs based on https://github.com/amccreight/heapgraph to aid in debugging and plugging memory leaks. ## Resource Usage Be aware that parsing a heap can take a fair amount of RAM depending on the heap size and time depending on the amount of target objects and path length. Examples of approximate memory and time required to build DOT graphs on an IvyBridge i7: | Heap Size | RAM | Targets | Time | |-----------|-------|---------|-------------| | 5MB | 80MB | 1500 | 1.5 Minutes | | 30MB | 425MB | 7700 | 40 Minutes | ## Basic Usage ### Getting a Heap Dump The more convenient way to dump a heap is to send `SIGUSR1` to a GJS process with the env variable `GJS_DEBUG_HEAP_OUTPUT` set: ```sh $ GJS_DEBUG_HEAP_OUTPUT=myApp.heap gjs myApp.js & $ kill -USR1 ``` It's also possible to dump a heap from within a script via the `System` import: ```js const System = imports.system; // Dumping the heap before the "leak" has happened System.dumpHeap('/home/user/myApp1.heap.'); // Code presumably resulting in a leak... // Running the garbage collector before dumping can avoid some false positives System.gc(); // Dumping the heap after the "leak" has happened System.dumpHeap('/home/user/myApp2.heap.'); ``` ### Output The default output of `./heapgraph.py` is a tiered tree of paths from root to rooted objects. If the output is being sent to a terminal (TTY) some minimal ANSI styling is used to make the output more readable. Additionally, anything that isn't part of the graph will be sent to `stderr` so the output can be directed to a file as plain text. Below is a snippet: ```sh $ ./heapgraph.py myApp2.heap Object > myApp2.tree Parsing file.heap...done Found 343 targets with type "Object" $ cat file.tree ├─[vm_stack[1]]─➤ [Object jsobj@0x7fce60683440] │ ├─[vm_stack[1]]─➤ [Object jsobj@0x7fce606833c0] │ ├─[exact-Object]─➤ [Object jsobj@0x7fce60683380] │ ├─[exact-Object]─➤ [GjsGlobal jsobj@0x7fce60680060] │ ├─[Debugger]─➤ [Function Debugger jsobj@0x7fce606a4540] │ │ ╰─[Object]─➤ [Function Object jsobj@0x7fce606a9cc0] │ │ ╰─[prototype]─➤ [Object (nil) jsobj@0x7fce60681160] │ │ ...and so on ``` `heapgraph.py` can also output DOT graphs that can be a useful way to visualize the heap graph, especially if you don't know exactly what you're looking for. Passing the `--dot-graph` option will output a DOT graph to `.dot` in the current working directory. There are a few choices for viewing dot graphs, and many utilities for converting them to other formats like PDF, Tex or GraphML. For Gnome desktops [`xdot`](https://github.com/jrfonseca/xdot.py) is a nice lightweight Python/Cairo viewer available on PyPi and in most distributions. ```sh $ ./heapgraph.py --dot-graph /home/user/myApp2.heap Object Parsing file.heap...done Found 343 targets with type "Object" $ xdot myApp2.heap.dot ``` ### Excluding Nodes from the Graph The exclusion switch you are most likely to use is `--diff-heap` which will exclude all nodes in the graph common to that heap, allowing you to easily see what's not being collected between two states. ```sh $ ./heapgraph --diff-heap myApp1.heap myApp2.heap GObject ``` You can also exclude Gray Roots, WeakMaps, nodes with a heap address or nodes with labels containing a string. Because GObject addresses are part of the node label, these can be excluded with `--hide-node` as well. By default the global object (GjsGlobal aka `window`), imports (GjsModule, GjsFileImporter), and namespaces (GIRepositoryNamespace) aren't shown in the graph since these are less useful and can't be garbage collected anyways. ```sh $ ./heapgraph.py --hide-addr 0x7f6ef022c060 \ --hide-node 'self-hosting-global' \ --no-gray-roots \ /home/user/myApp2.heap Object $ ./heapgraph.py --hide-node 0x55e93cf5deb0 /home/user/myApp2.heap Object ``` ### Command-Line Arguments > **NOTE:** Command line arguments are subject to change; Check > `./heapgraph.py --help` before running. ``` usage: heapgraph.py [-h] [--edge | --function | --string] [--count] [--dot-graph] [--no-addr] [--diff-heap FILE] [--no-gray-roots] [--no-weak-maps] [--show-global] [--show-imports] [--hide-addr ADDR] [--hide-node LABEL] FILE TARGET Find what is rooting or preventing an object from being collected in a GJS heap using a shortest-path breadth-first algorithm. positional arguments: FILE Garbage collector heap from System.dumpHeap() TARGET Heap address (eg. 0x7fa814054d00) or type prefix (eg. Array, Object, GObject, Function...) optional arguments: -h, --help show this help message and exit --edge, -e Treat TARGET as a function name --function, -f Treat TARGET as a function name --string, -s Treat TARGET as a string literal or String() Output Options: --count, -c Only count the matches for TARGET --dot-graph, -d Output a DOT graph to FILE.dot --no-addr, -na Don't show addresses Node/Root Filtering: --diff-heap FILE, -dh FILE Don't show roots common to the heap FILE --no-gray-roots, -ng Don't show gray roots (marked to be collected) --no-weak-maps, -nwm Don't show WeakMaps --show-global, -g Show the global object (eg. window/GjsGlobal) --show-imports, -i Show import and module nodes (eg. imports.foo) --hide-addr ADDR, -ha ADDR Don't show roots with the heap address ADDR --hide-node LABEL, -hn LABEL Don't show nodes with labels containing LABEL ``` ## See Also Below are some links to information relevant to SpiderMonkey garbage collection and heap parsing: * [GC.cpp Comments](https://searchfox.org/mozilla-central/source/js/src/gc/GC.cpp) * [How JavaScript Objects Are Implemented](https://www.infoq.com/presentations/javascript-objects-spidermonkey) * [Tracing garbage collection](https://en.wikipedia.org/wiki/Tracing_garbage_collection#Tri-color_marking) on Wikipedia * [SpiderMonkey Memory](https://gitlab.gnome.org/GNOME/gjs/blob/master/doc/SpiderMonkey_Memory.md) via GJS Repo cjs-4.4.0/tools/heapgraph.py000077500000000000000000000511651356375132300157720ustar00rootroot00000000000000#!/usr/bin/env python3 # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. # # heapgraph.py - Top-level script for interpreting Garbage Collector heaps import argparse import copy from collections import namedtuple from collections import deque import os import re import sys try: from heapdot import output_dot_file from heapdot import add_dot_graph_path except ImportError: sys.stderr.write('DOT graph output not available\n') ######################################################## # Command line arguments. ######################################################## parser = argparse.ArgumentParser(description='Find what is rooting or preventing an object from being collected in a GJS heap using a shortest-path breadth-first algorithm.') parser.add_argument('heap_file', metavar='FILE', help='Garbage collector heap from System.dumpHeap()') parser.add_argument('target', metavar='TARGET', help='Heap address (eg. 0x7fa814054d00) or type prefix (eg. Array, Object, GObject, Function...)') ### Target Options targ_opts = parser.add_mutually_exclusive_group() targ_opts.add_argument('--edge', '-e', dest='edge_target', action='store_true', default=False, help='Treat TARGET as a function name') targ_opts.add_argument('--function', '-f', dest='func_target', action='store_true', default=False, help='Treat TARGET as a function name') targ_opts.add_argument('--string', '-s', dest='string_target', action='store_true', default=False, help='Treat TARGET as a string literal or String()') ### Output Options out_opts = parser.add_argument_group('Output Options') out_opts.add_argument('--count', '-c', dest='count', action='store_true', default=False, help='Only count the matches for TARGET') out_opts.add_argument('--dot-graph', '-d', dest='dot_graph', action='store_true', default=False, help='Output a DOT graph to FILE.dot') out_opts.add_argument('--no-addr', '-na', dest='no_addr', action='store_true', default=False, help='Don\'t show addresses') ### Node and Root Filtering filt_opts = parser.add_argument_group('Node/Root Filtering') filt_opts.add_argument('--diff-heap', '-dh', dest='diff_heap', action='store', metavar='FILE', help='Don\'t show roots common to the heap FILE') filt_opts.add_argument('--no-gray-roots', '-ng', dest='no_gray_roots', action='store_true', default=False, help='Don\'t show gray roots (marked to be collected)') filt_opts.add_argument('--no-weak-maps', '-nwm', dest='no_weak_maps', action='store_true', default=False, help='Don\'t show WeakMaps') filt_opts.add_argument('--show-global', '-g', dest='show_global', action='store_true', default=False, help='Show the global object (eg. window/GjsGlobal)') filt_opts.add_argument('--show-imports', '-i', dest='show_imports', action='store_true', default=False, help='Show import and module nodes (eg. imports.foo)') filt_opts.add_argument('--hide-addr', '-ha', dest='hide_addrs', action='append', metavar='ADDR', default=[], help='Don\'t show roots with the heap address ADDR') filt_opts.add_argument('--hide-node', '-hn', dest='hide_nodes', action='append', metavar='LABEL', default=['self-hosting-global', 'GIRepositoryNamespace', 'GjsFileImporter', 'GjsGlobal', 'GjsModule'], help='Don\'t show nodes with labels containing LABEL') ############################################################################### # Heap Patterns ############################################################################### GraphAttribs = namedtuple('GraphAttribs', 'edge_labels node_labels roots root_labels weakMapEntries') WeakMapEntry = namedtuple('WeakMapEntry', 'weakMap key keyDelegate value') addr_regex = re.compile('[A-F0-9]+$|0x[a-f0-9]+$') node_regex = re.compile ('((?:0x)?[a-fA-F0-9]+) (?:(B|G|W) )?([^\r\n]*)\r?$') edge_regex = re.compile ('> ((?:0x)?[a-fA-F0-9]+) (?:(B|G|W) )?([^\r\n]*)\r?$') wme_regex = re.compile ('WeakMapEntry map=([a-zA-Z0-9]+|\(nil\)) key=([a-zA-Z0-9]+|\(nil\)) keyDelegate=([a-zA-Z0-9]+|\(nil\)) value=([a-zA-Z0-9]+)\r?$') func_regex = re.compile('Function(?: ([^/]+)(?:/([<|\w]+))?)?') gobj_regex = re.compile('([^ ]+) (0x[a-fA-F0-9]+$)') ############################################################################### # Heap Parsing ############################################################################### def parse_roots(fobj): """Parse the roots portion of a garbage collector heap.""" roots = {} root_labels = {} weakMapEntries = [] for line in fobj: node = node_regex.match(line) if node: addr = node.group(1) color = node.group(2) label = node.group(3) # Only overwrite an existing root with a black root. if addr not in roots or color == 'B': roots[addr] = (color == 'B') # It would be classier to save all the root labels, though then # we have to worry about gray vs black. root_labels[addr] = label else: wme = wme_regex.match(line) if wme: weakMapEntries.append(WeakMapEntry(weakMap=wme.group(1), key=wme.group(2), keyDelegate=wme.group(3), value=wme.group(4))) # Skip comments, arenas, compartments and zones elif line[0] == '#': continue # Marks the end of the roots section elif line[:10] == '==========': break else: sys.stderr.write('Error: unknown line {}\n'.format(line)) exit(-1) return [roots, root_labels, weakMapEntries] def parse_graph(fobj): """Parse the node and edges of a garbage collector heap.""" edges = {} edge_labels = {} node_labels = {} def addNode (addr, node_label): edges[addr] = {} edge_labels[addr] = {} if node_label != '': node_labels[addr] = node_label def addEdge(source, target, edge_label): edges[source][target] = edges[source].get(target, 0) + 1 if edge_label != '': edge_labels[source].setdefault(target, []).append(edge_label) node_addr = None for line in fobj: e = edge_regex.match(line) if e: if node_addr not in args.hide_addrs: addEdge(node_addr, e.group(1), e.group(3)) else: node = node_regex.match(line) if node: node_addr = node.group(1) node_color = node.group(2) node_label = node.group(3) # Use this opportunity to map hide_nodes to addresses for hide_node in args.hide_nodes: if hide_node in node_label: args.hide_addrs.append(node_addr) break else: addNode(node_addr, node_label) # Skip comments, arenas, compartments and zones elif line[0] == '#': continue else: sys.stderr.write('Error: Unknown line: {}\n'.format(line[:-1])) # yar, should pass the root crud in and wedge it in here, or somewhere return [edges, edge_labels, node_labels] def parse_heap(fname): """Parse a garbage collector heap.""" try: fobj = open(fname, 'r') except: sys.stderr.write('Error opening file {}\n'.format(fname)) exit(-1) [roots, root_labels, weakMapEntries] = parse_roots(fobj) [edges, edge_labels, node_labels] = parse_graph(fobj) fobj.close() graph = GraphAttribs(edge_labels=edge_labels, node_labels=node_labels, roots=roots, root_labels=root_labels, weakMapEntries=weakMapEntries) return (edges, graph) def find_nodes(fname): """Parse a garbage collector heap and return a list of node addresses.""" addrs = []; try: fobj = open(fname, 'r') sys.stderr.write('Parsing {0}...'.format(fname)) except: sys.stderr.write('Error opening file {}\n'.format(fname)) exit(-1) # Whizz past the roots for line in fobj: if '==========\n' in line: break for line in fobj: node = node_regex.match(line) if node: addrs.append(node.group(1)) fobj.close() sys.stderr.write('done\n') sys.stderr.flush() return addrs # Some applications may not care about multiple edges. # They can instead use a single graph, which is represented as a map # from a source node to a set of its destinations. def to_single_graph(edges): single_graph = {} for origin, destinations in edges.items(): d = set([]) for destination, distance in destinations.items(): d.add(destination) single_graph[origin] = d return single_graph def load_graph(fname): sys.stderr.write('Parsing {0}...'.format(fname)) (edges, graph) = parse_heap(fname) edges = to_single_graph(edges) sys.stderr.write('done\n') sys.stderr.flush() return (edges, graph) ############################################################################### # Path printing ############################################################################### tree_graph_paths = {} class style: BOLD = '\033[1m' ITALIC = '\033[3m' UNDERLINE = '\033[4m' END = '\033[0m' def get_edge_label(graph, origin, destination): elabel = lambda l: l[0] if len(l) == 2 else l labels = graph.edge_labels.get(origin, {}).get(destination, []) if len(labels) == 1: label = labels[0] if label == 'signal connection': return 'GSignal' else: return label elif len(labels) > 1: return ', '.join([elabel(l) for l in labels]) else: return '' def get_node_label(graph, addr): label = graph.node_labels[addr] if label.endswith(' '): label = label[:-13] if label.startswith('Function '): fm = func_regex.match(label) if fm.group(2) == '<': return 'Function via {}'.format(fm.group(1)) elif fm.group(2): return 'Function {} in {}'.format(fm.group(2), fm.group(1)) else: return label if label.startswith('script'): label = label[7:].split('/')[-1] elif label.startswith('WeakMap'): label = 'WeakMap' elif label == 'base_shape': label = 'shape' elif label == 'type_object': label = 'type' return label[:50] def output_tree_graph(graph, data, base='', parent=''): while data: addr, children = data.popitem() # Labels if parent: edge = get_edge_label(graph, parent, addr) else: edge = graph.root_labels[addr] node = get_node_label(graph, addr) has_native = gobj_regex.match(node) # Color/Style if os.isatty(1): if parent: edge = style.ITALIC + edge + style.END else: edge = style.BOLD + edge + style.END orig = style.UNDERLINE + 'jsobj@' + addr + style.END if has_native: node = style.BOLD + has_native.group(1) + style.END orig += ' ' + style.UNDERLINE + 'native@' + has_native.group(2) + style.END else: node = style.BOLD + node + style.END else: orig = 'jsobj@' + addr if has_native: node = has_native.group(1) orig += ' native@' + has_native.group(2) # Print the path segment if args.no_addr: if data: print('{0}├─[{1}]─➤ [{2}]'.format(base, edge, node)) else: print('{0}╰─[{1}]─➤ [{2}]'.format(base, edge, node)) else: if data: print('{0}├─[{1}]─➤ [{2} {3}]'.format(base, edge, node, orig)) else: print('{0}╰─[{1}]─➤ [{2} {3}]'.format(base, edge, node, orig)) # Print child segments if children: if data: output_tree_graph(graph, children, base + '│ ', addr) else: output_tree_graph(graph, children, base + ' ', addr) else: if data: print(base + '│ ') else: print(base + ' ') def add_tree_graph_path(owner, path): o = owner.setdefault(path.pop(0), {}) if path: add_tree_graph_path(o, path) def add_path(args, graph, path): if args.dot_graph: add_dot_graph_path(path) else: add_tree_graph_path(tree_graph_paths, path) ############################################################################### # Breadth-first shortest path finding. ############################################################################### def find_roots_bfs(args, edges, graph, target): workList = deque() distances = {} def traverseWeakMapEntry(dist, k, m, v, label): if not k in distances or not m in distances: # Haven't found either the key or map yet. return if distances[k][0] > dist or distances[m][0] > dist: # Either the key or the weak map is farther away, so we # must wait for the farther one before processing it. return if v in distances: return distances[v] = (dist + 1, k, m, label) workList.append(v) # For now, ignore keyDelegates. weakData = {} for wme in graph.weakMapEntries: weakData.setdefault(wme.weakMap, set([])).add(wme) weakData.setdefault(wme.key, set([])).add(wme) if wme.keyDelegate != '0x0': weakData.setdefault(wme.keyDelegate, set([])).add(wme) # Unlike JavaScript objects, GObjects can be "rooted" by their refcount so # we have to use a fake root (repetitively) startObject = 'FAKE START OBJECT' rootEdges = set([]) for addr, isBlack in graph.roots.items(): if isBlack or not args.no_gray_roots: rootEdges.add(addr) #FIXME: edges[startObject] = rootEdges distances[startObject] = (-1, None) workList.append(startObject) # Search the graph. while workList: origin = workList.popleft() dist = distances[origin][0] # Found the target, stop digging if origin == target: continue # origin does not point to any other nodes. if not origin in edges: continue for destination in edges[origin]: if destination not in distances: distances[destination] = (dist + 1, origin) workList.append(destination) if origin in weakData: for wme in weakData[origin]: traverseWeakMapEntry(dist, wme.key, wme.weakMap, wme.value, "value in WeakMap " + wme.weakMap) traverseWeakMapEntry(dist, wme.keyDelegate, wme.weakMap, wme.key, "key delegate in WeakMap " + wme.weakMap) # Print out the paths by unwinding backwards to generate a path, # then print the path. Accumulate any weak maps found during this # process into the printWorkList queue, and print out what keeps # them alive. Only print out why each map is alive once. printWorkList = deque() printWorkList.append(target) printedThings = set([target]) while printWorkList: p = printWorkList.popleft() path = [] while p in distances: path.append(p) dist = distances[p] if len(dist) == 2: [_, p] = dist else: # The weak map key is probably more interesting, # so follow it, and worry about the weak map later. [_, k, m, label] = dist graph.edge_labels[k].setdefault(p, []).append(label) p = k if not m in printedThings and not args.no_weak_maps: printWorkList.append(m) printedThings.add(m) if path: path.pop() path.reverse() add_path(args, graph, path) ######################################################## # Target selection ######################################################## def target_edge(graph, target): targets = [] for origin, destinations in graph.edge_labels.items(): for destination in destinations: if target in graph.edge_labels[origin][destination]: targets.append(destination) sys.stderr.write('Found {} objects with edge label of {}\n'.format(len(targets), target)) return targets def target_func(graph, target): targets = [] for addr, label in graph.node_labels.items(): if not label[:9] == 'Function ': continue if label[9:] == target: targets.append(addr) sys.stderr.write('Found {} functions named "{}"\n'.format(len(targets), target)) return targets def target_gobject(graph, target): targets = [] for addr, label in graph.node_labels.items(): if label.endswith(target): targets.append(addr) sys.stderr.write('Found GObject with address of {}\n'.format(target)) return targets def target_string(graph, target): targets = [] for addr, label in graph.node_labels.items(): if label[:7] == 'string ' and target in label[7:]: targets.append(addr) elif label[:10] == 'substring ' and target in label[10:]: targets.append(addr) sys.stderr.write('Found {} strings containing "{}"\n'.format(len(targets), target)) return targets def target_type(graph, target): targets = [] for addr in edges.keys(): if graph.node_labels.get(addr, '')[0:len(args.target)] == args.target: targets.append(addr) sys.stderr.write('Found {} targets with type "{}"\n'.format(len(targets), target)) return targets def select_targets(args, edges, graph): if args.edge_target: return target_edge(graph, args.target) elif args.func_target: return target_func(graph, args.target) elif args.string_target: return target_string(graph, args.target) # If args.target seems like an address search for a JS Object, then GObject elif addr_regex.match(args.target): if args.target in edges: sys.stderr.write('Found object with address "{}"\n'.format(args.target)) return [args.target] else: return target_gobject(graph, args.target) # Fallback to looking for JavaScript objects by class name return target_type(graph, args.target) if __name__ == "__main__": args = parser.parse_args() # Node and Root Filtering if args.show_global: args.hide_nodes.remove('GjsGlobal') if args.show_imports: args.hide_nodes.remove('GjsFileImporter') args.hide_nodes.remove('GjsModule') args.hide_nodes.remove('GIRepositoryNamespace') # Make sure we don't filter an explicit target if args.target in args.hide_nodes: args.hide_nodes.remove(args.target) # Heap diffing; we do these addrs separately due to the sheer amount diff_addrs = [] if args.diff_heap: diff_addrs = find_nodes(args.diff_heap) # Load the graph (edges, graph) = load_graph(args.heap_file) targets = select_targets(args, edges, graph) if len(targets) == 0: sys.stderr.write('No targets found for "{}".\n'.format(args.target)) sys.exit(-1) elif args.count: sys.exit(-1); for addr in targets: if addr in edges and addr not in diff_addrs: find_roots_bfs(args, edges, graph, addr) if args.dot_graph: output_dot_file(args, graph, targets, args.heap_file + ".dot") else: output_tree_graph(graph, tree_graph_paths) cjs-4.4.0/util/000077500000000000000000000000001356375132300132635ustar00rootroot00000000000000cjs-4.4.0/util/error.cpp000066400000000000000000000045201356375132300151210ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include "error.h" GQuark gjs_error_quark (void) { return g_quark_from_static_string ("gjs-error-quark"); } GQuark gjs_js_error_quark(void) { return g_quark_from_static_string("gjs-js-error-quark"); } GType gjs_js_error_get_type(void) { static volatile GType g_type_id; if (g_once_init_enter(&g_type_id)) { static GEnumValue errors[] = { { GJS_JS_ERROR_ERROR, "Error", "error" }, { GJS_JS_ERROR_EVAL_ERROR, "EvalError", "eval-error" }, { GJS_JS_ERROR_INTERNAL_ERROR, "InternalError", "internal-error" }, { GJS_JS_ERROR_RANGE_ERROR, "RangeError", "range-error" }, { GJS_JS_ERROR_REFERENCE_ERROR, "ReferenceError", "reference-error" }, { GJS_JS_ERROR_STOP_ITERATION, "StopIteration", "stop-iteration" }, { GJS_JS_ERROR_SYNTAX_ERROR, "SyntaxError", "syntax-error" }, { GJS_JS_ERROR_TYPE_ERROR, "TypeError", "type-error" }, { GJS_JS_ERROR_URI_ERROR, "URIError", "uri-error" }, { 0, nullptr, nullptr } }; g_type_id = g_enum_register_static("GjsJSError", errors); } return g_type_id; } cjs-4.4.0/util/error.h000066400000000000000000000037141356375132300145720ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_UTIL_ERROR_H__ #define __GJS_UTIL_ERROR_H__ #include #include G_BEGIN_DECLS GJS_EXPORT GQuark gjs_error_quark(void); #define GJS_ERROR gjs_error_quark() typedef enum { GJS_ERROR_FAILED, GJS_ERROR_SYSTEM_EXIT, } GjsError; GJS_EXPORT GQuark gjs_js_error_quark(void); #define GJS_JS_ERROR gjs_js_error_quark() GJS_EXPORT GType gjs_js_error_get_type(void); #define GJS_TYPE_JS_ERROR gjs_js_error_get_type() typedef enum { GJS_JS_ERROR_ERROR, GJS_JS_ERROR_EVAL_ERROR, GJS_JS_ERROR_INTERNAL_ERROR, GJS_JS_ERROR_RANGE_ERROR, GJS_JS_ERROR_REFERENCE_ERROR, GJS_JS_ERROR_STOP_ITERATION, GJS_JS_ERROR_SYNTAX_ERROR, GJS_JS_ERROR_TYPE_ERROR, GJS_JS_ERROR_URI_ERROR, } GjsJSError; G_END_DECLS #endif /* __GJS_UTIL_ERROR_H__ */ cjs-4.4.0/util/glib.cpp000066400000000000000000000040371356375132300147100ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "util/glib.h" /** gjs_g_strv_concat: * * Concate an array of string arrays to one string array. The strings in each * array is copied to the resulting array. * * @strv_array: array of NULL-terminated arrays of strings. NULL elements are * allowed. * @len: number of arrays in @strv_array * * @return: a newly allocated NULL-terminated array of strings. Use * g_strfreev() to free it */ char** gjs_g_strv_concat(char ***strv_array, int len) { GPtrArray *array; int i; array = g_ptr_array_sized_new(16); for (i = 0; i < len; i++) { char **strv; int j; strv = strv_array[i]; if (strv == NULL) continue; for (j = 0; strv[j] != NULL; ++j) g_ptr_array_add(array, g_strdup(strv[j])); } g_ptr_array_add(array, NULL); return (char**)g_ptr_array_free(array, false); } cjs-4.4.0/util/glib.h000066400000000000000000000026421356375132300143550ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_UTIL_GLIB_H__ #define __GJS_UTIL_GLIB_H__ #include G_BEGIN_DECLS char** gjs_g_strv_concat (char ***strv_array, int len); G_END_DECLS #endif /* __GJS_UTIL_GLIB_H__ */ cjs-4.4.0/util/log.cpp000066400000000000000000000162621356375132300145570ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "config.h" #include "log.h" #include "misc.h" #include #include #include #include #include #ifdef G_OS_WIN32 # include # include # ifndef F_OK # define F_OK 0 # endif #else # include #endif /* prefix is allowed if it's in the ;-delimited environment variable * GJS_DEBUG_TOPICS or if that variable is not set. */ static bool is_allowed_prefix (const char *prefix) { static const char *topics = NULL; static char **prefixes = NULL; bool found = false; int i; if (topics == NULL) { topics = g_getenv("GJS_DEBUG_TOPICS"); if (!topics) return true; /* We never really free this, should be gone when the process exits */ prefixes = g_strsplit(topics, ";", -1); } if (!prefixes) return true; for (i = 0; prefixes[i] != NULL; i++) { if (!strcmp(prefixes[i], prefix)) { found = true; break; } } return found; } #define PREFIX_LENGTH 12 static void write_to_stream(FILE *logfp, const char *prefix, const char *s) { /* seek to end to avoid truncating in case we're using shared logfile */ (void)fseek(logfp, 0, SEEK_END); fprintf(logfp, "%*s: %s", PREFIX_LENGTH, prefix, s); if (!g_str_has_suffix(s, "\n")) fputs("\n", logfp); fflush(logfp); } void gjs_debug(GjsDebugTopic topic, const char *format, ...) { static FILE *logfp = NULL; static bool debug_log_enabled = false; static bool checked_for_timestamp = false; static bool print_timestamp = false; static bool checked_for_thread = false; static bool print_thread = false; static GTimer *timer = NULL; const char *prefix; va_list args; char *s; if (!checked_for_timestamp) { print_timestamp = gjs_environment_variable_is_set("GJS_DEBUG_TIMESTAMP"); checked_for_timestamp = true; } if (!checked_for_thread) { print_thread = gjs_environment_variable_is_set("GJS_DEBUG_THREAD"); checked_for_thread = true; } if (print_timestamp && !timer) { timer = g_timer_new(); } if (logfp == NULL) { const char *debug_output = g_getenv("GJS_DEBUG_OUTPUT"); if (debug_output != NULL && strcmp(debug_output, "stderr") == 0) { debug_log_enabled = true; } else if (debug_output != NULL) { const char *log_file; char *free_me; char *c; /* Allow debug-%u.log for per-pid logfiles as otherwise log * messages from multiple processes can overwrite each other. * * (printf below should be safe as we check '%u' is the only format * string) */ c = strchr((char *) debug_output, '%'); if (c && c[1] == 'u' && !strchr(c+1, '%')) { #if defined(__clang__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"") #endif free_me = g_strdup_printf(debug_output, (guint)getpid()); #if defined(__clang__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) _Pragma("GCC diagnostic pop") #endif log_file = free_me; } else { log_file = debug_output; free_me = NULL; } /* avoid truncating in case we're using shared logfile */ logfp = fopen(log_file, "a"); if (!logfp) fprintf(stderr, "Failed to open log file `%s': %s\n", log_file, g_strerror(errno)); g_free(free_me); debug_log_enabled = true; } if (logfp == NULL) logfp = stderr; } if (!debug_log_enabled) return; switch (topic) { case GJS_DEBUG_GI_USAGE: prefix = "JS GI USE"; break; case GJS_DEBUG_MEMORY: prefix = "JS MEMORY"; break; case GJS_DEBUG_CONTEXT: prefix = "JS CTX"; break; case GJS_DEBUG_IMPORTER: prefix = "JS IMPORT"; break; case GJS_DEBUG_NATIVE: prefix = "JS NATIVE"; break; case GJS_DEBUG_KEEP_ALIVE: prefix = "JS KP ALV"; break; case GJS_DEBUG_GREPO: prefix = "JS G REPO"; break; case GJS_DEBUG_GNAMESPACE: prefix = "JS G NS"; break; case GJS_DEBUG_GOBJECT: prefix = "JS G OBJ"; break; case GJS_DEBUG_GFUNCTION: prefix = "JS G FUNC"; break; case GJS_DEBUG_GFUNDAMENTAL: prefix = "JS G FNDMTL"; break; case GJS_DEBUG_GCLOSURE: prefix = "JS G CLSR"; break; case GJS_DEBUG_GBOXED: prefix = "JS G BXD"; break; case GJS_DEBUG_GENUM: prefix = "JS G ENUM"; break; case GJS_DEBUG_GPARAM: prefix = "JS G PRM"; break; case GJS_DEBUG_GERROR: prefix = "JS G ERR"; break; default: prefix = "???"; break; } if (!is_allowed_prefix(prefix)) return; va_start (args, format); s = g_strdup_vprintf (format, args); va_end (args); if (print_timestamp) { static gdouble previous = 0.0; gdouble total = g_timer_elapsed(timer, NULL) * 1000.0; gdouble since = total - previous; const char *ts_suffix; char *s2; if (since > 50.0) { ts_suffix = "!! "; } else if (since > 100.0) { ts_suffix = "!!! "; } else if (since > 200.0) { ts_suffix = "!!!!"; } else { ts_suffix = " "; } s2 = g_strdup_printf("%g %s%s", total, ts_suffix, s); g_free(s); s = s2; previous = total; } if (print_thread) { char *s2 = g_strdup_printf("(thread %p) %s", g_thread_self(), s); g_free(s); s = s2; } write_to_stream(logfp, prefix, s); g_free(s); } cjs-4.4.0/util/log.h000066400000000000000000000112031356375132300142120ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_UTIL_LOG_H__ #define __GJS_UTIL_LOG_H__ #include G_BEGIN_DECLS /* The idea of this is to be able to have one big log file for the entire * environment, and grep out what you care about. So each module or app * should have its own entry in the enum. Be sure to add new enum entries * to the switch in log.c */ typedef enum { GJS_DEBUG_GI_USAGE, GJS_DEBUG_MEMORY, GJS_DEBUG_CONTEXT, GJS_DEBUG_IMPORTER, GJS_DEBUG_NATIVE, GJS_DEBUG_KEEP_ALIVE, GJS_DEBUG_GREPO, GJS_DEBUG_GNAMESPACE, GJS_DEBUG_GOBJECT, GJS_DEBUG_GFUNCTION, GJS_DEBUG_GCLOSURE, GJS_DEBUG_GBOXED, GJS_DEBUG_GENUM, GJS_DEBUG_GPARAM, GJS_DEBUG_GERROR, GJS_DEBUG_GFUNDAMENTAL, } GjsDebugTopic; /* These defines are because we have some pretty expensive and * extremely verbose debug output in certain areas, that's useful * sometimes, but just too much to compile in by default. The areas * tend to be broader and less focused than the ones represented by * GjsDebugTopic. * * Don't use these special "disabled by default" log macros to print * anything that's an abnormal or error situation. * * Don't use them for one-time events, either. They are for routine * stuff that happens over and over and would deluge the logs, so * should be off by default. */ /* Whether to be verbose about JavaScript property access and resolution */ #ifndef GJS_VERBOSE_ENABLE_PROPS #define GJS_VERBOSE_ENABLE_PROPS 0 #endif /* Whether to be verbose about JavaScript function arg and closure marshaling */ #ifndef GJS_VERBOSE_ENABLE_MARSHAL #define GJS_VERBOSE_ENABLE_MARSHAL 0 #endif /* Whether to be verbose about constructing, destroying, and gc-rooting * various kinds of JavaScript thingy */ #ifndef GJS_VERBOSE_ENABLE_LIFECYCLE #define GJS_VERBOSE_ENABLE_LIFECYCLE 0 #endif /* Whether to log all gobject-introspection types and methods we use */ #ifndef GJS_VERBOSE_ENABLE_GI_USAGE #define GJS_VERBOSE_ENABLE_GI_USAGE 0 #endif /* Whether to log all callback GClosure debugging (finalizing, invalidating etc) */ #ifndef GJS_VERBOSE_ENABLE_GCLOSURE #define GJS_VERBOSE_ENABLE_GCLOSURE 0 #endif /* Whether to log all GObject signal debugging */ #ifndef GJS_VERBOSE_ENABLE_GSIGNAL #define GJS_VERBOSE_ENABLE_GSIGNAL 0 #endif #if GJS_VERBOSE_ENABLE_PROPS #define gjs_debug_jsprop(topic, ...) \ do { gjs_debug(topic, __VA_ARGS__); } while(0) #else #define gjs_debug_jsprop(topic, ...) ((void)0) #endif #if GJS_VERBOSE_ENABLE_MARSHAL #define gjs_debug_marshal(topic, ...) \ do { gjs_debug(topic, __VA_ARGS__); } while(0) #else #define gjs_debug_marshal(topic, ...) ((void)0) #endif #if GJS_VERBOSE_ENABLE_LIFECYCLE #define gjs_debug_lifecycle(topic, ...) \ do { gjs_debug(topic, __VA_ARGS__); } while(0) #else #define gjs_debug_lifecycle(topic, ...) ((void)0) #endif #if GJS_VERBOSE_ENABLE_GI_USAGE #define gjs_debug_gi_usage(...) \ do { gjs_debug(GJS_DEBUG_GI_USAGE, __VA_ARGS__); } while(0) #else #define gjs_debug_gi_usage(...) ((void)0) #endif #if GJS_VERBOSE_ENABLE_GCLOSURE #define gjs_debug_closure(...) \ do { gjs_debug(GJS_DEBUG_GCLOSURE, __VA_ARGS__); } while(0) #else #define gjs_debug_closure(...) ((void)0) #endif #if GJS_VERBOSE_ENABLE_GSIGNAL #define gjs_debug_gsignal(...) \ do { gjs_debug(GJS_DEBUG_GOBJECT, __VA_ARGS__); } while(0) #else #define gjs_debug_gsignal(...) ((void)0) #endif void gjs_debug(GjsDebugTopic topic, const char *format, ...) G_GNUC_PRINTF (2, 3); G_END_DECLS #endif /* __GJS_UTIL_LOG_H__ */ cjs-4.4.0/util/misc.cpp000066400000000000000000000026551356375132300147320ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "misc.h" bool gjs_environment_variable_is_set(const char *env_variable_name) { const char *s; s = g_getenv(env_variable_name); if (s == NULL) return false; if (*s == '\0') return false; return true; } cjs-4.4.0/util/misc.h000066400000000000000000000025651356375132300143770ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __GJS_UTIL_MISC_H__ #define __GJS_UTIL_MISC_H__ #include G_BEGIN_DECLS bool gjs_environment_variable_is_set (const char *env_variable_name); G_END_DECLS #endif /* __GJS_UTIL_MISC_H__ */ cjs-4.4.0/util/sp-capture-types.h000066400000000000000000000066501356375132300166700ustar00rootroot00000000000000/* sp-capture-types.h * * Copyright (C) 2016 Christian Hergert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ /* The original source of this file is: * https://git.gnome.org/browse/sysprof/tree/lib/capture/sp-capture-types.c * It has been modified to remove unneeded functionality. */ #ifndef SP_CAPTURE_FORMAT_H #define SP_CAPTURE_FORMAT_H #include #include G_BEGIN_DECLS #define SP_CAPTURE_MAGIC (GUINT32_TO_LE(0xFDCA975E)) #define SP_CAPTURE_ALIGN (sizeof(SpCaptureAddress)) #if __WORDSIZE == 64 # define SP_CAPTURE_JITMAP_MARK G_GUINT64_CONSTANT(0xE000000000000000) # define SP_CAPTURE_ADDRESS_FORMAT "0x%016lx" #else # define SP_CAPTURE_JITMAP_MARK G_GUINT64_CONSTANT(0xE0000000) # define SP_CAPTURE_ADDRESS_FORMAT "0x%016llx" #endif #define SP_CAPTURE_CURRENT_TIME (g_get_monotonic_time() * 1000L) typedef struct _SpCaptureWriter SpCaptureWriter; typedef guint64 SpCaptureAddress; typedef enum { SP_CAPTURE_FRAME_TIMESTAMP = 1, SP_CAPTURE_FRAME_SAMPLE = 2, SP_CAPTURE_FRAME_MAP = 3, SP_CAPTURE_FRAME_PROCESS = 4, SP_CAPTURE_FRAME_FORK = 5, SP_CAPTURE_FRAME_EXIT = 6, SP_CAPTURE_FRAME_JITMAP = 7, SP_CAPTURE_FRAME_CTRDEF = 8, SP_CAPTURE_FRAME_CTRSET = 9, } SpCaptureFrameType; #pragma pack(push, 1) typedef struct { guint32 magic; guint8 version; guint32 little_endian : 1; guint32 padding : 23; gchar capture_time[64]; gint64 time; gint64 end_time; gchar suffix[168]; } SpCaptureFileHeader; typedef struct { guint16 len; gint16 cpu; gint32 pid; gint64 time; guint8 type; guint64 padding : 56; guint8 data[0]; } SpCaptureFrame; typedef struct { SpCaptureFrame frame; guint64 start; guint64 end; guint64 offset; guint64 inode; gchar filename[0]; } SpCaptureMap; typedef struct { SpCaptureFrame frame; guint32 n_jitmaps; guint8 data[0]; } SpCaptureJitmap; typedef struct { SpCaptureFrame frame; guint16 n_addrs; guint64 padding : 48; SpCaptureAddress addrs[0]; } SpCaptureSample; #pragma pack(pop) G_STATIC_ASSERT (sizeof (SpCaptureFileHeader) == 256); G_STATIC_ASSERT (sizeof (SpCaptureFrame) == 24); G_STATIC_ASSERT (sizeof (SpCaptureMap) == 56); G_STATIC_ASSERT (sizeof (SpCaptureJitmap) == 28); G_STATIC_ASSERT (sizeof (SpCaptureSample) == 32); G_END_DECLS #endif /* SP_CAPTURE_FORMAT_H */ cjs-4.4.0/util/sp-capture-writer.c000066400000000000000000000362741356375132300170400ustar00rootroot00000000000000/* sp-capture-writer.c * * Copyright (C) 2016 Christian Hergert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ /* The original source of this file is: * https://git.gnome.org/browse/sysprof/tree/lib/capture/sp-capture-writer.c * It has been modified to remove unneeded functionality. */ #ifndef _GNU_SOURCE # define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include "sp-capture-writer.h" #define DEFAULT_BUFFER_SIZE (getpagesize() * 64L) #define INVALID_ADDRESS (G_GUINT64_CONSTANT(0)) typedef struct { /* A pinter into the string buffer */ const gchar *str; /* The unique address for the string */ guint64 addr; } SpCaptureJitmapBucket; struct _SpCaptureWriter { /* * This is our buffer location for incoming strings. This is used * similarly to GStringChunk except there is only one-page, and after * it fills, we flush to disk. * * This is paired with a closed hash table for deduplication. */ gchar addr_buf[4096*4]; /* Our hashtable for deduplication. */ SpCaptureJitmapBucket addr_hash[512]; /* We keep the large fields above so that our allocation will be page * alinged for the write buffer. This improves the performance of large * writes to the target file-descriptor. */ volatile gint ref_count; /* * Our address sequence counter. The value that comes from * monotonically increasing this is OR'd with JITMAP_MARK to denote * the address name should come from the JIT map. */ gsize addr_seq; /* Our position in addr_buf. */ gsize addr_buf_pos; /* * The number of hash table items in @addr_hash. This is an * optimization so that we can avoid calculating the number of strings * when flushing out the jitmap. */ guint addr_hash_size; /* Capture file handle */ int fd; /* Our write buffer for fd */ guint8 *buf; gsize pos; gsize len; /* counter id sequence */ gint next_counter_id; /* Statistics while recording */ SpCaptureStat stat; }; G_DEFINE_BOXED_TYPE (SpCaptureWriter, sp_capture_writer, sp_capture_writer_ref, sp_capture_writer_unref) static inline void sp_capture_writer_frame_init (SpCaptureFrame *frame_, gint len, gint cpu, GPid pid, gint64 time_, SpCaptureFrameType type) { g_assert (frame_ != NULL); frame_->len = len; frame_->cpu = cpu; frame_->pid = pid; frame_->time = time_; frame_->type = type; frame_->padding = 0; } static void sp_capture_writer_finalize (SpCaptureWriter *self) { if (self != NULL) { sp_capture_writer_flush (self); close (self->fd); g_free (self->buf); g_free (self); } } SpCaptureWriter * sp_capture_writer_ref (SpCaptureWriter *self) { g_assert (self != NULL); g_assert (self->ref_count > 0); g_atomic_int_inc (&self->ref_count); return self; } void sp_capture_writer_unref (SpCaptureWriter *self) { g_assert (self != NULL); g_assert (self->ref_count > 0); if (g_atomic_int_dec_and_test (&self->ref_count)) sp_capture_writer_finalize (self); } static gboolean sp_capture_writer_flush_data (SpCaptureWriter *self) { const guint8 *buf; gssize written; gsize to_write; g_assert (self != NULL); g_assert (self->pos <= self->len); g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); buf = self->buf; to_write = self->pos; while (to_write > 0) { written = write (self->fd, buf, to_write); if (written < 0) return FALSE; if (written == 0 && errno != EAGAIN) return FALSE; g_assert (written <= (gssize)to_write); buf += written; to_write -= written; } self->pos = 0; return TRUE; } static inline void sp_capture_writer_realign (gsize *pos) { *pos = (*pos + SP_CAPTURE_ALIGN - 1) & ~(SP_CAPTURE_ALIGN - 1); } static inline gboolean sp_capture_writer_ensure_space_for (SpCaptureWriter *self, gsize len) { /* Check for max frame size */ if (len > G_MAXUSHORT) return FALSE; if ((self->len - self->pos) < len) { if (!sp_capture_writer_flush_data (self)) return FALSE; } return TRUE; } static inline gpointer sp_capture_writer_allocate (SpCaptureWriter *self, gsize *len) { gpointer p; g_assert (self != NULL); g_assert (len != NULL); g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); sp_capture_writer_realign (len); if (!sp_capture_writer_ensure_space_for (self, *len)) return NULL; p = (gpointer)&self->buf[self->pos]; self->pos += *len; g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); return p; } static gboolean sp_capture_writer_flush_jitmap (SpCaptureWriter *self) { SpCaptureJitmap jitmap; gssize r; gsize len; g_assert (self != NULL); if (self->addr_hash_size == 0) return TRUE; g_assert (self->addr_buf_pos > 0); len = sizeof jitmap + self->addr_buf_pos; sp_capture_writer_realign (&len); sp_capture_writer_frame_init (&jitmap.frame, len, -1, getpid (), SP_CAPTURE_CURRENT_TIME, SP_CAPTURE_FRAME_JITMAP); jitmap.n_jitmaps = self->addr_hash_size; if (sizeof jitmap != write (self->fd, &jitmap, sizeof jitmap)) return FALSE; r = write (self->fd, self->addr_buf, len - sizeof jitmap); if (r < 0 || (gsize)r != (len - sizeof jitmap)) return FALSE; self->addr_buf_pos = 0; self->addr_hash_size = 0; memset (self->addr_hash, 0, sizeof self->addr_hash); self->stat.frame_count[SP_CAPTURE_FRAME_JITMAP]++; return TRUE; } static gboolean sp_capture_writer_lookup_jitmap (SpCaptureWriter *self, const gchar *name, SpCaptureAddress *addr) { guint hash; guint i; g_assert (self != NULL); g_assert (name != NULL); g_assert (addr != NULL); hash = g_str_hash (name) % G_N_ELEMENTS (self->addr_hash); for (i = hash; i < G_N_ELEMENTS (self->addr_hash); i++) { SpCaptureJitmapBucket *bucket = &self->addr_hash[i]; if (bucket->str == NULL) return FALSE; if (strcmp (bucket->str, name) == 0) { *addr = bucket->addr; return TRUE; } } for (i = 0; i < hash; i++) { SpCaptureJitmapBucket *bucket = &self->addr_hash[i]; if (bucket->str == NULL) return FALSE; if (strcmp (bucket->str, name) == 0) { *addr = bucket->addr; return TRUE; } } return FALSE; } static SpCaptureAddress sp_capture_writer_insert_jitmap (SpCaptureWriter *self, const gchar *str) { SpCaptureAddress addr; gchar *dst; gsize len; guint hash; guint i; g_assert (self != NULL); g_assert (str != NULL); g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); len = sizeof addr + strlen (str) + 1; if ((self->addr_hash_size == G_N_ELEMENTS (self->addr_hash)) || ((sizeof self->addr_buf - self->addr_buf_pos) < len)) { if (!sp_capture_writer_flush_jitmap (self)) return INVALID_ADDRESS; g_assert (self->addr_hash_size == 0); g_assert (self->addr_buf_pos == 0); } g_assert (self->addr_hash_size < G_N_ELEMENTS (self->addr_hash)); g_assert (len > sizeof addr); /* Allocate the next unique address */ addr = SP_CAPTURE_JITMAP_MARK | ++self->addr_seq; /* Copy the address into the buffer */ dst = (gchar *)&self->addr_buf[self->addr_buf_pos]; memcpy (dst, &addr, sizeof addr); /* * Copy the string into the buffer, keeping dst around for * when we insert into the hashtable. */ dst += sizeof addr; memcpy (dst, str, len - sizeof addr); /* Advance our string cache position */ self->addr_buf_pos += len; g_assert (self->addr_buf_pos <= sizeof self->addr_buf); /* Now place the address into the hashtable */ hash = g_str_hash (str) % G_N_ELEMENTS (self->addr_hash); /* Start from the current hash bucket and go forward */ for (i = hash; i < G_N_ELEMENTS (self->addr_hash); i++) { SpCaptureJitmapBucket *bucket = &self->addr_hash[i]; if (G_LIKELY (bucket->str == NULL)) { bucket->str = dst; bucket->addr = addr; self->addr_hash_size++; return addr; } } /* Wrap around to the beginning */ for (i = 0; i < hash; i++) { SpCaptureJitmapBucket *bucket = &self->addr_hash[i]; if (G_LIKELY (bucket->str == NULL)) { bucket->str = dst; bucket->addr = addr; self->addr_hash_size++; return addr; } } g_assert_not_reached (); return INVALID_ADDRESS; } SpCaptureWriter * sp_capture_writer_new_from_fd (int fd, gsize buffer_size) { g_autofree gchar *nowstr = NULL; SpCaptureWriter *self; SpCaptureFileHeader *header; GTimeVal tv; gsize header_len = sizeof(*header); if (buffer_size == 0) buffer_size = DEFAULT_BUFFER_SIZE; g_assert (fd != -1); g_assert (buffer_size % getpagesize() == 0); if (ftruncate (fd, 0) != 0) return NULL; self = g_new0 (SpCaptureWriter, 1); self->ref_count = 1; self->fd = fd; self->buf = (guint8 *)g_malloc0 (buffer_size); self->len = buffer_size; self->next_counter_id = 1; g_get_current_time (&tv); nowstr = g_time_val_to_iso8601 (&tv); header = sp_capture_writer_allocate (self, &header_len); if (header == NULL) { sp_capture_writer_finalize (self); return NULL; } header->magic = SP_CAPTURE_MAGIC; header->version = 1; #ifdef G_LITTLE_ENDIAN header->little_endian = TRUE; #else header->little_endian = FALSE; #endif header->padding = 0; g_strlcpy (header->capture_time, nowstr, sizeof header->capture_time); header->time = SP_CAPTURE_CURRENT_TIME; header->end_time = 0; memset (header->suffix, 0, sizeof header->suffix); if (!sp_capture_writer_flush_data (self)) { sp_capture_writer_finalize (self); return NULL; } g_assert (self->pos == 0); g_assert (self->len > 0); g_assert (self->len % getpagesize() == 0); g_assert (self->buf != NULL); g_assert (self->addr_hash_size == 0); g_assert (self->fd != -1); return self; } SpCaptureWriter * sp_capture_writer_new (const gchar *filename, gsize buffer_size) { SpCaptureWriter *self; int fd; g_assert (filename != NULL); g_assert (buffer_size % getpagesize() == 0); if ((-1 == (fd = open (filename, O_CREAT | O_RDWR, 0640))) || (-1 == ftruncate (fd, 0L))) return NULL; self = sp_capture_writer_new_from_fd (fd, buffer_size); if (self == NULL) close (fd); return self; } gboolean sp_capture_writer_add_map (SpCaptureWriter *self, gint64 time, gint cpu, GPid pid, guint64 start, guint64 end, guint64 offset, guint64 inode, const gchar *filename) { SpCaptureMap *ev; gsize len; if (filename == NULL) filename = ""; g_assert (self != NULL); g_assert (filename != NULL); len = sizeof *ev + strlen (filename) + 1; ev = (SpCaptureMap *)sp_capture_writer_allocate (self, &len); if (!ev) return FALSE; sp_capture_writer_frame_init (&ev->frame, len, cpu, pid, time, SP_CAPTURE_FRAME_MAP); ev->start = start; ev->end = end; ev->offset = offset; ev->inode = inode; g_strlcpy (ev->filename, filename, len - sizeof *ev); ev->filename[len - sizeof *ev - 1] = '\0'; self->stat.frame_count[SP_CAPTURE_FRAME_MAP]++; return TRUE; } SpCaptureAddress sp_capture_writer_add_jitmap (SpCaptureWriter *self, const gchar *name) { SpCaptureAddress addr = INVALID_ADDRESS; if (name == NULL) name = ""; g_assert (self != NULL); g_assert (name != NULL); if (!sp_capture_writer_lookup_jitmap (self, name, &addr)) addr = sp_capture_writer_insert_jitmap (self, name); return addr; } gboolean sp_capture_writer_add_sample (SpCaptureWriter *self, gint64 time, gint cpu, GPid pid, const SpCaptureAddress *addrs, guint n_addrs) { SpCaptureSample *ev; gsize len; g_assert (self != NULL); len = sizeof *ev + (n_addrs * sizeof (SpCaptureAddress)); ev = (SpCaptureSample *)sp_capture_writer_allocate (self, &len); if (!ev) return FALSE; sp_capture_writer_frame_init (&ev->frame, len, cpu, pid, time, SP_CAPTURE_FRAME_SAMPLE); ev->n_addrs = n_addrs; memcpy (ev->addrs, addrs, (n_addrs * sizeof (SpCaptureAddress))); self->stat.frame_count[SP_CAPTURE_FRAME_SAMPLE]++; return TRUE; } static gboolean sp_capture_writer_flush_end_time (SpCaptureWriter *self) { gint64 end_time = SP_CAPTURE_CURRENT_TIME; ssize_t ret; g_assert (self != NULL); /* This field is opportunistic, so a failure is okay. */ again: ret = pwrite (self->fd, &end_time, sizeof (end_time), G_STRUCT_OFFSET (SpCaptureFileHeader, end_time)); if (ret < 0 && errno == EAGAIN) goto again; return TRUE; } gboolean sp_capture_writer_flush (SpCaptureWriter *self) { g_assert (self != NULL); return (sp_capture_writer_flush_jitmap (self) && sp_capture_writer_flush_data (self) && sp_capture_writer_flush_end_time (self)); } cjs-4.4.0/util/sp-capture-writer.h000066400000000000000000000075471356375132300170460ustar00rootroot00000000000000/* sp-capture-writer.h * * Copyright (C) 2016 Christian Hergert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ /* The original source of this file is: * https://git.gnome.org/browse/sysprof/tree/lib/capture/sp-capture-writer.h * It has been modified to remove unneeded functionality. */ #ifndef SP_CAPTURE_WRITER_H #define SP_CAPTURE_WRITER_H #include "sp-capture-types.h" G_BEGIN_DECLS typedef struct _SpCaptureWriter SpCaptureWriter; typedef struct { /* * The number of frames indexed by SpCaptureFrameType */ gsize frame_count[16]; /* * Padding for future expansion. */ gsize padding[48]; } SpCaptureStat; SpCaptureWriter *sp_capture_writer_new (const gchar *filename, gsize buffer_size); SpCaptureWriter *sp_capture_writer_new_from_fd (int fd, gsize buffer_size); SpCaptureWriter *sp_capture_writer_ref (SpCaptureWriter *self); void sp_capture_writer_unref (SpCaptureWriter *self); gboolean sp_capture_writer_add_map (SpCaptureWriter *self, gint64 time, gint cpu, GPid pid, guint64 start, guint64 end, guint64 offset, guint64 inode, const gchar *filename); guint64 sp_capture_writer_add_jitmap (SpCaptureWriter *self, const gchar *name); gboolean sp_capture_writer_add_sample (SpCaptureWriter *self, gint64 time, gint cpu, GPid pid, const SpCaptureAddress *addrs, guint n_addrs); gboolean sp_capture_writer_flush (SpCaptureWriter *self); #define SP_TYPE_CAPTURE_WRITER (sp_capture_writer_get_type()) GType sp_capture_writer_get_type (void); G_END_DECLS #endif /* SP_CAPTURE_WRITER_H */ cjs-4.4.0/verbump.py000066400000000000000000000027071356375132300143460ustar00rootroot00000000000000#!/usr/bin/env python2 # Automakes a release preparation for a post-release project # * Create a git tag # * Bump version in configure.ac and commit it import re import os import sys import subprocess micro_version_re = re.compile('m4_define.*pkg_micro_version, ([0-9]+)') micro_version_replace = 'm4_define(pkg_micro_version, %d)\n' def _extract_config_log_variable(name): f = open('config.log') keystart = name + '=\'' for line in f: if line.startswith(keystart): return line[len(keystart):-2] f.close() fatal("Failed to find '%s' in config.status" % (name, )) if not os.path.isfile('config.log'): fatal("Couldn't find config.log; did you run configure?") package = _extract_config_log_variable('PACKAGE_TARNAME') version = _extract_config_log_variable('VERSION') configure_path=os.path.join(os.environ['top_srcdir'], 'configure.ac') f = open(configure_path) newf = open(configure_path + '.tmp', 'w') for line in f: m = micro_version_re.match(line) if not m: newf.write(line) continue v = int(m.group(1)) newv = v+1 print "Will update micro version from %s to %s" % (v, newv) newf.write(micro_version_replace % (newv, )) newf.close() os.rename(configure_path + '.tmp', configure_path) print "Successfully wrote new 'configure.ac' with post-release version bump" args=['git', 'commit', '-m', "configure: Post-release version bump", configure_path] print "Running: %r" % (args, ) subprocess.check_call(args) cjs-4.4.0/win32/000077500000000000000000000000001356375132300132505ustar00rootroot00000000000000cjs-4.4.0/win32/Makefile.vc000066400000000000000000000032421356375132300153200ustar00rootroot00000000000000# NMake Makefile for building GJS on Windows # The items below this line should not be changed, unless one is maintaining # the NMake Makefiles. Customizations can be done in the following NMake Makefile # portions (please see comments in the these files to see what can be customized): # # detectenv-msvc.mak # config-msvc.mak !include detectenv-msvc.mak # Include the Makefile portions with the source listings !include ..\gjs-srcs.mk !include ..\gjs-modules-srcs.mk # Include the Makefile portion that enables features based on user input !include config-msvc.mak !if "$(VALID_CFGSET)" == "TRUE" # We need Visual Studio 2013 or later !if $(VSVER) < 12 VALID_MSC = FALSE !else VALID_MSC = TRUE !endif !if "$(VALID_MSC)" == "TRUE" # Include the Makefile portion to convert the source and header lists # into the lists we need for compilation and introspection !include create-lists-msvc.mak all: $(GJS_LIBS) $(EXTRA_TARGETS) $(GJS_UTILS) all-build-info tests: all # Include the build rules for sources, DLLs and executables !include build-rules-msvc.mak # Include the rules for build directory creation and code generation !include generate-msvc.mak # Generate the introspection files !if "$(INTROSPECTION)" == "1" # Include the rules for building the introspection files !include introspection-msvc.mak !include gjs-introspection-msvc.mak !endif !include install.mak !else # "$(VALID_MSC)" == "TRUE" all: @echo You need Visual Studio 2013 or later. !endif # "$(VALID_MSC)" == "TRUE" !else # "$(VALID_CFGSET)" == "TRUE" all: help @echo You need to specify a valid configuration, via @echo CFG=release or CFG=debug !endif # "$(VALID_CFGSET)" == "TRUE" !include info-msvc.mak cjs-4.4.0/win32/README.txt000066400000000000000000000076361356375132300147620ustar00rootroot00000000000000Instructions for building GJS on Visual Studio ============================================== Building the GJS on Windows is now supported using Visual Studio versions 2013 or later in both 32-bit and 64-bit (x64) flavors, via NMake Makefiles. Due to C++-11 usage, Visual Studio 2012 or earlier is not supported. You will need the following items to build GJS using Visual Studio: -SpiderMonkey 52 (mozjs-52) -GObject-Introspection (G-I) 1.41.4 or later -GLib 2.50.x or later, (which includes GIO, GObject, and the associated tools) -Cairo including Cairo-GObject support, unless NO_CAIRO=1 is specified. -GTK+-3.20.x or later, unless NO_GTK=1 is specified. -and anything that the above items depends on. Note that SpiderMonkey must be built with Visual Studio, and the rest should preferably be built with Visual Studio as well. The Visual Studio version used should preferably be the one that is used here to build GJS. If you built SpiderMonkey 38 using the normal build instructions as described on Mozilla's website, you may notice that the output library, DLLs and include directory might not be what one may expect, which is likely due to bugs in its build scripts. If this is the case, rename mozjs-.lib to mozjs-38.lib, and the include directory from mozjs- to mozjs-38 (but please do *not* rename mozjs-.dll and mozjs-.pdb, as they will be searched for when gjs-console.exe/gjs.exe runs, along with any program that uses the GJS DLL). Otherwise, do (or redo) the SpiderMonkey build process (including running configure) after applying the patch from https://git.gnome.org/browse/jhbuild/tree/patches/mozjs38-release-number.patch The following are instructions for performing such a build, as there is a number of build configurations supported for the build. Note that the default build (where no options (see below) are specified, the GJS library is built with Cairo and GTK+ support. A 'clean' target is provided-it is recommended that one cleans the build and redo the build if any configuration option changed. An 'install' target is also provided to copy the built items in their appropriate locations under $(PREFIX), which is described below. Invoke the build by issuing the command: nmake /f Makefile.vc CFG=[release|debug] [PREFIX=...] where: CFG: Required. Choose from a release or debug build. Note that all builds generate a .pdb file for each .dll and .exe built--this refers to the C/C++ runtime that the build uses. PREFIX: Optional. Base directory of where the third-party headers, libraries and needed tools can be found, i.e. headers in $(PREFIX)\include, libraries in $(PREFIX)\lib and tools in $(PREFIX)\bin. If not specified, $(PREFIX) is set as $(srcroot)\..\vs$(X)\$(platform), where $(platform) is win32 for 32-bit builds or x64 for 64-bit builds, and $(X) is the short version of the Visual Studio used, as follows: 2013: 12 2015: 14 Explanation of options, set by